Compare commits

..

79 Commits

Author SHA1 Message Date
b0992d220f Merge pull request #429 from andrewhsu/v
[17.12] bump version to 17.12.1-ce-rc2
2018-02-21 15:24:57 -08:00
5152611926 Merge pull request #431 from jose-bigio/17.12-changelog
[17.12] update changelog for 17.12.1-ce-rc2
2018-02-21 15:20:22 -08:00
893276b2e8 Changelog update for 17.12.1
Signed-off-by: jose-bigio <jose.bigio@docker.com>
2018-02-21 15:12:04 -08:00
319872e09a Merge pull request #433 from kolyshkin/17.12-backport-layer-not-retained
[17.12] backport layer not retained
2018-02-21 09:50:26 -08:00
71498a13be c.RWLayer: check for nil before use
Since commit e9b9e4ace294230c6b8eb has landed, there is a chance that
container.RWLayer is nil (due to some half-removed container). Let's
check the pointer before use to avoid any potential nil pointer
dereferences, resulting in a daemon crash.

Note that even without the abovementioned commit, it's better to perform
an extra check (even it's totally redundant) rather than to have a
possibility of a daemon crash. In other words, better be safe than
sorry.

[v2: add a test case for daemon.getInspectData]
[v3: add a check for container.Dead and a special error for the case]

Fixes: e9b9e4ace294230c6b8eb
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
(cherry picked from commit 195893d38160c0893e326b8674e05ef6714aeaa4)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2018-02-20 15:43:30 -08:00
deff200c90 daemon.cleanupContainer: nullify container RWLayer upon release
ReleaseRWLayer can and should only be called once (unless it returns
an error), but might be called twice in case of a failure from
`system.EnsureRemoveAll(container.Root)`. This results in the
following error:

> Error response from daemon: driver "XXX" failed to remove root filesystem for YYY: layer not retained

The obvious fix is to set container.RWLayer to nil as soon as
ReleaseRWLayer() succeeds.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
(cherry picked from commit e9b9e4ace294230c6b8eb010eda564a2541c4564)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-20 15:41:42 -08:00
b24bd87f20 bump version to 17.12.1-ce-rc2
Signed-off-by: Andrew Hsu <andrewhsu@docker.com>
2018-02-20 07:55:44 -08:00
e634ffeb95 Merge pull request #422 from thaJeztah/17.12-backport-36142-TaskState
[17.12] Add `REMOVE` and `ORPHANED` to TaskState
2018-02-20 07:55:11 -08:00
80c6fa7153 Merge pull request #430 from thaJeztah/17.12-backport-cleanup_daemon_root_mount
[17.12] Ensure daemon root is unmounted on shutdown
2018-02-20 07:53:20 -08:00
731f1c37f0 Merge pull request #428 from cpuguy83/backport_36055_slave_mounts_for_root
[17.12] Use rslave propagation for mounts from daemon root
2018-02-20 07:53:09 -08:00
1251f23e0d Merge pull request #416 from cpuguy83/17.12_backport_36096_use_rshared_prop_for_daemon_root
[17.12] Set daemon root to use shared propagation
2018-02-20 07:52:46 -08:00
1c24f4566e Merge pull request #415 from cpuguy83/17.12_backport_36047_graphdriver_improvements
[17.12] Do not make graphdriver homes private mounts.
2018-02-20 07:52:23 -08:00
a27f508a6c Ensure daemon root is unmounted on shutdown
This is only for the case when dockerd has had to re-mount the daemon
root as shared.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 487c6c7e73dbb7871e80d75f176dd2a3539a2947)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-20 13:11:57 +01:00
31e4ca26a3 Merge pull request #425 from thaJeztah/17.12-backport-36083-network-inspect-created-time
[17.12] Fix issue where network inspect does not show Created time in swarm scope
2018-02-20 00:39:11 -08:00
5bc96351ab Merge pull request #391 from thaJeztah/17.12-backport-fix-mount-creation-on-start
[17.12] Re-validate Mounts on container start
2018-02-20 00:38:14 -08:00
ee633783d6 Merge pull request #393 from thaJeztah/17.12-bump-libnetwork
[17.12] bump libnetwork to 0ef1f8c94fa85acacf88b9be75ae07de91d0b2a5
2018-02-20 00:22:44 -08:00
72f6b9a3d7 Merge pull request #420 from thaJeztah/17.12-backport-support-proxy-in-splunk-driver
[17.12] Support a proxy in splunk log driver
2018-02-19 13:27:49 -08:00
7cb8389345 Use rslave propagation for mounts from daemon root
By default, if a user requests a bind mount it uses private propagation.
When the source path is a path within the daemon root this, along with
some other propagation values that the user can use, causes issues when
the daemon tries to remove a mountpoint because a container will then
have a private reference to that mount which prevents removal.

Unmouting with MNT_DETATCH can help this scenario on newer kernels, but
ultimately this is just covering up the problem and doesn't actually
free up the underlying resources until all references are destroyed.

This change does essentially 2 things:

1. Change the default propagation when unspecified to `rslave` when the
source path is within the daemon root path or a parent of the daemon
root (because everything is using rbinds).
2. Creates a validation error on create when the user tries to specify
an unacceptable propagation mode for these paths...
basically the only two acceptable modes are `rslave` and `rshared`.

In cases where we have used the new default propagation but the
underlying filesystem is not setup to handle it (fs must hvae at least
rshared propagation) instead of erroring out like we normally would,
this falls back to the old default mode of `private`, which preserves
backwards compatibility.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 589a0afa8cbe39b6512662fd1705873e2d236dd0)
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2018-02-15 10:20:07 -05:00
a22eabf65d Do not recursive unmount on cleanup of zfs/btrfs
This was added in #36047 just as a way to make sure the tree is fully
unmounted on shutdown.

For ZFS this could be a breaking change since there was no unmount before.
Someone could have setup the zfs tree themselves. It would be better, if
we really do want the cleanup to actually the unpacked layers checking
for mounts rather than a blind recursive unmount of the root.

BTRFS does not use mounts and does not need to unmount anyway.
These was only an unmount to begin with because for some reason the
btrfs tree was being moutned with `private` propagation.

For the other graphdrivers that still have a recursive unmount here...
these were already being unmounted and performing the recursive unmount
shouldn't break anything. If anyone had anything mounted at the
graphdriver location it would have been unmounted on shutdown anyway.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 2fe4f888bee52b1f256d6fa5e20f9b061d30221c)
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2018-02-14 09:56:00 -05:00
feff709de8 Merge pull request #421 from docker/v12
[17.12] bump version to 17.12.1-ce-rc1
2018-02-13 13:20:10 -08:00
36e98850f2 Merge pull request #426 from jose-bigio/17.12-changelog
[17.12] changelog update for 17.12.1 rc1
2018-02-13 13:19:56 -08:00
675493e24d Updated changelog for 17.12.1
Signed-off-by: jose-bigio <jose.bigio@docker.com>
2018-02-13 13:16:20 -08:00
602216ce56 Merge pull request #395 from thaJeztah/17.12-backport-runc-hang
[17.12] Update runc to fix hang during start and exec
2018-02-13 09:36:55 -08:00
7ec8b355f2 Merge pull request #423 from thaJeztah/17.12-backport-orca-11380
[17.12] fix verbose for partial overlay ID
2018-02-13 09:17:41 -08:00
f5152d8714 Merge pull request #392 from thaJeztah/17.12-backport-ramdisk
[17.12] Honor DOCKER_RAMDISK with containerd 1.0
2018-02-13 09:05:01 -08:00
3869e4896d Merge pull request #373 from thaJeztah/backport-fix-missing-errors
[17.12] Return errors from client in stack deploy configs
2018-02-13 09:03:43 -08:00
e9f1a359d7 Merge pull request #404 from thaJeztah/17.12-backport-fix-plural-singular-node-generic-resources
[17.12] backport fix plural singular node generic resources
2018-02-13 09:02:50 -08:00
68dbbc33ca Fix issue where network inspect does not show Created time in swarm scope
This fix tries to address the issue raised in 36083 where
`network inspect` does not show Created time if the network is
created in swarm scope.

The issue was that Created was not converted from swarm api.
This fix addresses the issue.

An unit test has been added.

This fix fixes 36083.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
(cherry picked from commit 090c439fb8a863731cc80fcb9932ce5958d8166d)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-13 16:05:03 +01:00
8d3d4fa90a fix verbose for partial overlay ID
Signed-off-by: Dani Louca <dani.louca@docker.com>
(cherry picked from commit 2e0990f1655d151b741e7f7f78ac55e14398339f)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-13 14:11:40 +01:00
246956ffb1 Add REMOVE and ORPHANED to TaskState
This fix tries to address the issue raised in 36142 where
there are discrepancies between Swarm API and swagger.yaml.

This fix adds two recently added state `REMOVE` and `ORPHANED` to TaskState.

This fix fixes 36142.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
(cherry picked from commit a40687f5ac7df27bc6c6c3a6f69513a397a1a05a)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-13 14:07:02 +01:00
831e67711b Merge pull request #383 from thaJeztah/17.12-backport-32838-partial-fix
[17.12] Vendor Microsoft/hcsshim @ v0.6.8
2018-02-12 23:10:42 -08:00
0a43e1edf9 bump version to 17.12.1-ce-rc1
Signed-off-by: Andrew Hsu <andrewhsu@docker.com>
2018-02-12 23:06:07 -08:00
d70d9c910a Merge pull request #412 from kolyshkin/17.12-tasksmax
[17.12] Uncomment TasksMax=unlimited for recent distros
2018-02-12 23:00:07 -08:00
3cfc217709 Merge pull request #371 from thaJeztah/backport-FIX35843
[17.12] Backport "fix #35843 regression on health check workingdir"
2018-02-12 22:55:36 -08:00
fa49979990 Merge pull request #368 from anusha-ragunathan/backport_35726
[17.12] awslogs: Use batching type for ergonomics and correct counting
2018-02-12 22:53:57 -08:00
62a24759f6 Merge pull request #372 from thaJeztah/backport-vfs-quota
[17.12] Fix VFS vs quota regression
2018-02-12 18:44:41 -08:00
2d24bc5e5f Merge pull request #374 from thaJeztah/17.12-backport-fix-namespace-filtering
[17.12] Fix event filter filtering on "or"
2018-02-12 18:43:13 -08:00
d852c51a7d Merge pull request #389 from thaJeztah/17.12-backport-upgrade_fix
[17.12] Fixing ingress network when upgrading from 17.09 to 17.12.
2018-02-12 18:38:31 -08:00
c85f7d7628 Merge pull request #418 from thaJeztah/17.12-backport-bump-golang-1.9.4
[17.12] Bump Golang to 1.9.4
2018-02-12 18:33:45 -08:00
0d1f2df861 Merge pull request #417 from thaJeztah/17.12-backport-fix_containerd_crash_spin
[17.12] Refresh containerd remotes on containerd restarted
2018-02-12 14:52:59 -08:00
5fcd931d5f update vendor
Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit 7d296522f68a80d540b20753ea0648171ee12825)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-10 13:10:36 -08:00
38ba0c6ef7 Support a proxy in splunk log driver
Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit 3c4537d5b33d951237ea5e4cc123953eda7a37e7)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-10 13:10:06 -08:00
b1267c5341 update gotestyourself
pickup changes which use t.Helper()

Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit 4ac4b690f78a645cc50030b81077fd5319b53501)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-10 13:09:57 -08:00
bd3930dfb1 Bump Golang to 1.9.4
This fixes a vulnerability in `go get` (CVE-2018-6574, http://golang.org/issue/23672),
but shouldn't really affect our code, but it's good to keep in sync.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 6263b1254b179af81ff4ef97563fe2e1a053993a)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-08 15:00:56 -08:00
f312cb1bb1 Bump golang to 1.9.4
This fixes a vulnerability in `go get` (CVE-2018-6574, http://golang.org/issue/23672),
but shouldn't really affect our code, but it's good to keep in sync.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit b32599761f)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-08 14:59:44 -08:00
69b85c633e Remove workaround for Nano server TP5
This workaround for golang/go#15286 was added for Nano server TP5 in
fa82c0aa10cfac8c6d5e2446876dc79b2b0c1bf9, and should no longer be
needed

Due to a security fix in Go 1.9.4/1.8.7, loading the .dll is no longer
allowed, and produces an error:

   .\docker_windows.go:9:3: //go:cgo_import_dynamic main.dummy CommandLineToArgvW%2 "shell32.dll" only allowed in cgo-generated code

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 250193387c98a4ad69a6591d5fe5a39c1409ffba)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-08 14:58:10 -08:00
84d4132c8d Bump Golang to 1.9.4
This fixes a vulnerability in `go get` (CVE-2018-6574, http://golang.org/issue/23672),
but shouldn't really affect our code, but it's good to keep in sync.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit caeab268430a033fedd27c53be16758ac1a0f71e)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-08 14:58:03 -08:00
dbf4d3a8ca Refresh containerd remotes on containerd restarted
Before this patch, when containerd is restarted (due to a crash, or
kill, whatever), the daemon would keep trying to process the event
stream against the old socket handles. This would lead to a CPU spin due
to the error handling when the client can't connect to containerd.

This change makes sure the containerd remote client is updated for all
registered libcontainerd clients.

This is not neccessarily the ideal fix which would likely require a
major refactor, but at least gets things to a working state with a
minimal patch.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 400126f8698233099259da967378c0a76bc3ea31)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-08 13:59:07 -08:00
092f60f9eb Fix typo in log-message
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 5c3418e38b9603e8ff582d53c2face57f0f01cce)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-02-08 13:58:57 -08:00
b8eb1db1d3 Set daemon root to use shared propagation
This change sets an explicit mount propagation for the daemon root.
This is useful for people who need to bind mount the docker daemon root
into a container.

Since bind mounting the daemon root should only ever happen with at
least `rlsave` propagation (to prevent the container from holding
references to mounts making it impossible for the daemon to clean up its
resources), we should make sure the user is actually able to this.

Most modern systems have shared root (`/`) propagation by default
already, however there are some cases where this may not be so
(e.g. potentially docker-in-docker scenarios, but also other cases).
So this just gives the daemon a little more control here and provides
a more uniform experience across different systems.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit a510192b86e7eb1e1112f3f625d80687fdec6578)
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2018-02-07 15:23:33 -05:00
8136093f88 Do not make graphdriver homes private mounts.
The idea behind making the graphdrivers private is to prevent leaking
mounts into other namespaces.
Unfortunately this is not really what happens.

There is one case where this does work, and that is when the namespace
was created before the daemon's namespace.
However with systemd each system servie winds up with it's own mount
namespace. This causes a race betwen daemon startup and other system
services as to if the mount is actually private.

This also means there is a negative impact when other system services
are started while the daemon is running.

Basically there are too many things that the daemon does not have
control over (nor should it) to be able to protect against these kinds
of leakages. One thing is certain, setting the graphdriver roots to
private disconnects the mount ns heirarchy preventing propagation of
unmounts... new mounts are of course not propagated either, but the
behavior is racey (or just bad in the case of restarting services)... so
it's better to just be able to keep mount propagation in tact.

It also does not protect situations like `-v
/var/lib/docker:/var/lib/docker` where all mounts are recursively bound
into the container anyway.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 9803272f2db84df7955b16c0d847ad72cdc494d1)
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2018-02-07 14:34:47 -05:00
1c517bd52c rpm/fedora-2x/spec: enable TasksMax in service file
Since systemd version 228, a new setting, `TasksMax`, has appeared, which
limits the number of tasks used by a service (via pids cgroup
controller). Unfortunately, a default for this setting, `DefaultTaskMax`,
is set to 512. In systemd version 231 it is changed to 15% which
practically is 4195, as the value from /proc/sys/kernel/pid_max is
treated like 100%).

Either 512 or 4195 is severily limited value for Docker Engine, as it
can run thousands of containers with thousands of tasks in each, and
the number of tasks limit should be set on a per-container basis by the
Docker user. So, the most reasonable setting for `TasksMax` is `unlimited`.

Unfortunately, older versions of systemd warn about unknown `TasksMax`
parameter in `docker.service` file, and the warning is rather annoying,
therefore this setting is commented out by default, and is supposed to
be uncommented by the user.

The problem with that is, once the limit is hit, all sorts of bad things
happen and it's not really clear even to an advanced user that this
setting is the source of issues.

As Fedora 25 ships systemd 231, it (and later Fedora releases) support
TasksMax, so it makes total sense to uncomment the setting, this is what
this commit does.

[17.12: added patch for Fedora 25 spec]

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Upstream-commit: 9055832bb0725f05d518c3ebc9b7cc93a69420c7
Component: packaging
(cherry picked from commit 02b6af2e96)
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2018-02-02 15:32:29 -08:00
7a399f3d9a deb/common/rules: fix uncommenting TasksMax
Since systemd version 228, a new setting, `TasksMax`, has appeared,
which limits the number of tasks used by a service (via pids cgroup
controller). Unfortunately, a default for this setting, `DefaultTaskMax`,
is set to 512. In systemd version 231 it is changed to 15% which
practically is 4195, as the value from /proc/sys/kernel/pid_max is
treated like 100%).

Either 512 or 4195 is severily limited value for Docker Engine,
as it can run thousands of containers with thousands of tasks in each,
and the number of tasks limit should be set on a per-container basis
by the Docker user. So, the most reasonable setting for `TasksMax`
is `unlimited`.

Unfortunately, older versions of systemd warn about unknown `TasksMax`
parameter in `docker.service` file, and the warning is rather annoying,
therefore this setting is commented out by default, and is supposed
to be uncommented by the user.

The problem with that is, once the limit is hit, all sorts of bad things
happen and it's not really clear even to an advanced user that this
setting is the source of issues.

Now, `rules` file already contain a hack to check for the systemd
version (during build time) and in case the version is greater than 227,
uncomment the `TasksMax=unlimited` line. Alas, it does not work
during normal builds, the reason being systemd is not installed
into build environments.

An obvious fix would be to add systemd to the list of installed
packages in all Dockerfiles used to build debs. Fortunately,
there is a simpler way, as libsystemd-dev is installed, and
it's a subpackage of systemd built from the same source and
carrying the same version, so it can also be checked.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Upstream-commit: d80738e4b4459816c64757a2a63e5d8058d0ccf4
Component: packaging
(cherry picked from commit 1530820600)
2018-02-02 15:30:35 -08:00
ea3ea188f6 Fix "--node-generic-resource" singular/plural
Daemon flags that can be specified multiple times use
singlar names for flags, but plural names for the configuration
file.

To make the daemon configuration know how to correlate
the flag with the corresponding configuration option,
`opt.NewNamedListOptsRef()` should be used instead of
`opt.NewListOptsRef()`.

Commit 6702ac590e6148cb3f606388dde93a011cb14931 attempted
to fix the daemon not corresponding the flag with the configuration
file option, but did so by changing the name of the flag
to plural.

This patch reverts that change, and uses `opt.NewNamedListOptsRef()`
instead.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 6e7715d65ba892a47d355e16bf9ad87fb537a2d0)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-30 14:44:15 -08:00
f0cfc346fd Fix node-generic-resources CLI typo
Signed-off-by: Renaud Gaubert <rgaubert@nvidia.com>
(cherry picked from commit 6702ac590e6148cb3f606388dde93a011cb14931)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-30 14:44:08 -08:00
4822efab31 bump libnetwork to 0ef1f8c94fa85acacf88b9be75ae07de91d0b2a5
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-26 16:39:26 -08:00
f5829ca5cf Update to go 1.9.3
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 0e676c4bde1d429d21ea083a8bc9f40c0fc51269)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-26 15:35:26 -08:00
1d1bcb2fca Bump Go to 1.9.3
release notes: https://golang.org/doc/devel/release.html#go1.9.minor

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 3cc13511f0c8d7f3aeb382f0444e37592a8b5e69)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-26 15:32:47 -08:00
8c22dc2e68 Bump Go to 1.9.3
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit ffc7648322)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-26 15:32:40 -08:00
09d84539fa Update runc to fix hang during start and exec
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
(cherry picked from commit d10091c86e75fb78eaba96f433dc2cc06c0a54de)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-23 16:51:30 -08:00
e3eb25711d [17.12] bump libnetwork to cb462375d91926adfce98bd8a1f47a325ed6f8f2
Brings in a cherry pick of 76bfcb0eb1

In sandbox creation we disable IPV6 config. But this causes problem in live-restore case
where all IPV6 configs are wiped out on running container. Hence extra check has been added
take care of this issue.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-22 18:27:18 -08:00
67d4bb5888 Honor DOCKER_RAMDISK with containerd 1.0
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
(cherry picked from commit 54051e9e64185e442e034c7e49a5707459a9eed2)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-20 01:59:41 +01:00
7d2c7004af Re-validate Mounts on container start
Validation of Mounts was only performed on container _creation_, not on
container _start_. As a result, if the host-path no longer existed
when the container was started, a directory was created in the given
location.

This is the wrong behavior, because when using the `Mounts` API, host paths
should never be created, and an error should be produced instead.

This patch adds a validation step on container start, and produces an
error if the host path is not found.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 7cb96ba308dc53824d2203fd343a4a297d17976e)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-20 01:52:05 +01:00
5507d73275 Fixes for libcontainer changes
Libcontainer no longer provides placeholders for
unsupported platforms, which cause the Windows
builds to fail.

This patch moves features that are not supported
to platform-specific files.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit d1c34831e930c1f6b3de28cab3f4a358845a79d5)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-20 01:14:44 +01:00
1e67593a37 Bump runc to 7f24b40cc5423969b4554ef04ba0b00e2b4ba010
matching the version that's used by containerd 1.0.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit f58aa31075bf74ab8d2369dafb591ae43ed36ee6)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-20 00:43:37 +01:00
25c4322a30 Bump containerd to 1.0.1 (9b55aab90508bd389d7654c4baf173a981477d55)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 9047f66b1edd4dffcafc34f9c7f3390ddd65d10b)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-20 00:22:51 +01:00
5f1c192c19 Fixing ingress network when upgrading from 17.09 to 17.12.
Signed-off-by: Pradip Dhara <pradipd@microsoft.com>

Signed-off-by: Pradip Dhara <pradipd@microsoft.com>
(cherry picked from commit 2d7a50e5855ad0571e76d29cd1ab9f8f3a48433b)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-20 00:03:55 +01:00
276c2ad6a4 Vendor Microsoft/hcsshim @ v0.6.8
Signed-off-by: John Howard <jhoward@microsoft.com>
(cherry picked from commit 172a442c27ed35778662980809824fdf15a722a6)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-04 17:53:07 +01:00
ae80c6aedf Fix event filter filtering on "or"
The event filter used two separate filter-conditions for
"namespace" and "topic". As a result, both events matching
"topic" and events matching "namespace" were subscribed to,
causing events to be handled both by the "plugin" client, and
"container" client.

This patch rewrites the filter to match only if both namespace
and topic match.

Thanks to Stephen Day for providing the correct filter :)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 295bb09184fe473933498bb0efb59b8acb124f55)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-01-02 13:43:28 +01:00
6da2ecc95c Return errors from client in stack deploy configs
Signed-off-by: Paweł Szczekutowicz <pszczekutowicz@gmail.com>
(cherry picked from commit a30dd1b6f3)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-12-29 16:12:53 +01:00
a7ee159424 vfs gd: ignore quota setup errors
This is a fix to regression in vfs graph driver introduced by
commit 7a1618ced359a3ac92 ("add quota support to VFS graphdriver").

On some filesystems, vfs fails to init with the following error:

> Error starting daemon: error initializing graphdriver: Failed to mknod
> /go/src/github.com/docker/docker/bundles/test-integration/d6bcf6de610e9/root/vfs/backingFsBlockDev:
> function not implemented

As quota is not essential for vfs, let's ignore (but log as a warning) any error
from quota init.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
(cherry picked from commit 1e8a087850aa9f96c5000a3ad90757d2e9c0499f)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-12-29 16:08:02 +01:00
092e59ef76 projectquota: treat ENOSYS as quota unsupported
If mknod() returns ENOSYS, it most probably means quota is not supported
here, so return the appropriate error.

This is a conservative* fix to regression in vfs graph driver introduced
by commit 7a1618ced359a3ac92 ("add quota support to VFS graphdriver").
On some filesystems, vfs fails to init with the following error:

> Error starting daemon: error initializing graphdriver: Failed to mknod
> /go/src/github.com/docker/docker/bundles/test-integration/d6bcf6de610e9/root/vfs/backingFsBlockDev:
> function not implemented

Reported-by: Brian Goff <cpuguy83@gmail.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
(cherry picked from commit 2dd39b7841bdb9968884bbedc5db97ff77d4fe3e)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-12-29 16:07:54 +01:00
bf9d7adabe Add integration test for healthcheck workdir
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 5be2f2be243a52eb1b051c981bac5442b6e85606)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-12-29 16:02:24 +01:00
88f57f81f9 fix #35843 regression on health check workingdir
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
(cherry picked from commit 852a943c773382df09cdda4f29f9e93807523178)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2017-12-29 16:02:15 +01:00
c97c6d62c2 Merge pull request #370 from jose-bigio/17.12_version
[17.12] bump version to 17.12.0-ce
2017-12-27 13:57:15 -06:00
2861174d81 Merge pull request #369 from jose-bigio/17.12_chng_log
[17.12] Update changelog
2017-12-27 13:56:58 -06:00
8673a4245b Update changelog
Signed-off-by: jose-bigio <jose.bigio@docker.com>
2017-12-27 10:21:27 -08:00
5dee703312 bump version to 17.12.0-ce
Signed-off-by: jose-bigio <jose.bigio@docker.com>
2017-12-21 12:16:43 -08:00
351bf41f51 awslogs: Use batching type for ergonomics and correct counting
The previous bytes counter was moved out of scope was not counting the
total number of bytes in the batch. This type encapsulates the counter
and the batch for consideration and code ergonomics.

Signed-off-by: Jacob Vallejo <jakeev@amazon.com>
(cherry picked from commit ad14dbf1346742f0607d7c28a8ef3d4064f5f9fd)
Signed-off-by: Anusha Ragunathan <anusha.ragunathan@docker.com>
2017-12-19 13:39:22 -08:00
174 changed files with 4733 additions and 713 deletions

View File

@ -5,13 +5,57 @@ information on the list of deprecated flags and APIs please have a look at
https://docs.docker.com/engine/deprecated/ where target removal dates can also
be found.
IMPORTANT:
You should stop all containers and plugins **BEFORE** upgrading to Docker CE 17.12.
**IMPORTANT**: You must stop all containers and plugins **BEFORE** upgrading to Docker CE 17.12.
See related PR: [moby/moby#35812](https://github.com/moby/moby/pull/35812)
## 17.12.0-ce (2017-12-DD)
## 17.12.1-ce (2018-02-DD)
### Client
- Fix `node-generic-resource` typo [moby/moby#35970](https://github.com/moby/moby/pull/35970) and [moby/moby#36125](https://github.com/moby/moby/pull/36125)
* Return errors from daemon on stack deploy configs create/update [docker/cli#757](https://github.com/docker/cli/pull/757)
### Logging
- awslogs: fix batch size calculation for large logs [moby/moby#35726](https://github.com/moby/moby/pull/35726)
* Support a proxy in splunk log driver [moby/moby#36220](https://github.com/moby/moby/pull/36220)
### Networking
- Fix ingress network when upgrading from 17.09 to 17.12 [moby/moby#36003](https://github.com/moby/moby/pull/36003)
* Add verbose info to partial overlay ID [moby/moby#35989](https://github.com/moby/moby/pull/35989)
- Fix IPv6 networking being deconfigured if live-restore is being enabled [docker/libnetwork#2043](https://github.com/docker/libnetwork/pull/2043)
- Fix watchMiss thread context [docker/libnetwork#2051](https://github.com/docker/libnetwork/pull/2051)
### Packaging
- Set TasksMax in docker.service [docker/docker-ce-packaging#78](https://github.com/docker/docker-ce-packaging/pull/78)
### Runtime
* Bump Golang to 1.9.4
* Bump containerd to 1.0.1
- Fix dockerd not being able to reconnect to containerd when it is restarted [moby/moby#36173](https://github.com/moby/moby/pull/36173)
- Fix containerd events from being processed twice [moby/moby#35891](https://github.com/moby/moby/issues/35891)
- Fix vfs graph driver failure to initialize because of failure to setup fs quota [moby/moby#35827](https://github.com/moby/moby/pull/35827)
- Fix regression of health check not using container's working directory [moby/moby#35845](https://github.com/moby/moby/pull/35845)
- Honor `DOCKER_RAMDISK` with containerd 1.0 [moby/moby#35957](https://github.com/moby/moby/pull/35957)
- Update runc to fix hang during start and exec [moby/moby#36097](https://github.com/moby/moby/pull/36097)
- Windows: Vendor of Microsoft/hcsshim @v.0.6.8 partial fix for import layer failing [moby/moby#35924](https://github.com/moby/moby/pull/35924)
* Do not make graphdriver homes private mounts [moby/moby#36047](https://github.com/moby/moby/pull/36047)
* Use rslave propogation for mounts from daemon root [moby/moby#36055](https://github.com/moby/moby/pull/36055)
* Set daemon root to use shared mount propagation [moby/moby#36096](https://github.com/moby/moby/pull/36096)
* Validate that mounted paths exist when container is started, not just during creation [moby/moby#35833](https://github.com/moby/moby/pull/35833)
* Add `REMOVE` and `ORPHANED` to TaskState [moby/moby#36146](https://github.com/moby/moby/pull/36146)
- Fix issue where network inspect does not show Created time for networks in swarm scope [moby/moby#36095](https://github.com/moby/moby/pull/36095)
* Nullify container read write layer upon release [moby/moby#36130](https://github.com/moby/moby/pull/36160) and [moby/moby#36343](https://github.com/moby/moby/pull/36242)
### Swarm
* Remove watchMiss from swarm mode [docker/libnetwork#2047](https://github.com/docker/libnetwork/pull/2047)
## 17.12.0-ce (2017-12-27)
## Known Issues
* AWS logs batch size calculation [moby/moby#35726](https://github.com/moby/moby/pull/35726)
* Health check no longer uses the container's working directory [moby/moby#35843](https://github.com/moby/moby/issues/35843)
* Errors not returned from client in stack deploy configs [moby/moby#757](https://github.com/docker/cli/pull/757)
* Daemon aborts when project quota fails [moby/moby#35827](https://github.com/moby/moby/pull/35827)
* Docker cannot use memory limit when using systemd options [moby/moby#35123](https://github.com/moby/moby/issues/35123)
### Builder
@ -43,9 +87,8 @@ See related PR: [moby/moby#35812](https://github.com/moby/moby/pull/35812)
* Logentries line-only logopt fix to maintain backwards compatibility [moby/moby#35628](https://github.com/moby/moby/pull/35628)
+ Add `--until` flag for docker logs [moby/moby#32914](https://github.com/moby/moby/pull/32914)
+ Add gelf log driver plugin to Windows build [moby/moby#35073](https://github.com/moby/moby/pull/35073)
* Set timeout on splunk batch send [moby/moby#35496](https://github.com/moby/moby/pull/35496)
* Set timeout on Splunk batch send [moby/moby#35496](https://github.com/moby/moby/pull/35496)
* Update Graylog2/go-gelf [moby/moby#35765](https://github.com/moby/moby/pull/35765)
- Fix aws logs batch size calculation [moby/moby#35726](https://github.com/moby/moby/pull/35726)
### Networking
@ -105,3 +148,4 @@ See related PR: [moby/moby#35812](https://github.com/moby/moby/pull/35812)
+ Add Packaging for Fedora 27 [docker/docker-ce-packaging#59](https://github.com/docker/docker-ce-packaging/pull/59)
* Change default versioning scheme to 0.0.0-dev unless specified for packaging [docker/docker-ce-packaging#67](https://github.com/docker/docker-ce-packaging/pull/67)
* Pass Version to engine static builds [docker/docker-ce-packaging#70](https://github.com/docker/docker-ce-packaging/pull/70)
+ Added support for aarch64 on Debian (stretch/jessie) and Ubuntu Zesty or newer [docker/docker-ce-packaging#35](https://github.com/docker/docker-ce-packaging/pull/35)

View File

@ -1 +1 @@
17.12.0-ce-rc4
17.12.1-ce-rc2

View File

@ -1 +1 @@
17.12.0-ce-rc4
17.12.1-ce-rc2

View File

@ -248,13 +248,13 @@ func createConfigs(
case err == nil:
// config already exists, then we update that
if err := client.ConfigUpdate(ctx, config.ID, config.Meta.Version, configSpec); err != nil {
errors.Wrapf(err, "failed to update config %s", configSpec.Name)
return errors.Wrapf(err, "failed to update config %s", configSpec.Name)
}
case apiclient.IsErrNotFound(err):
// config does not exist, then we create a new one.
fmt.Fprintf(dockerCli.Out(), "Creating config %s\n", configSpec.Name)
if _, err := client.ConfigCreate(ctx, configSpec); err != nil {
errors.Wrapf(err, "failed to create config %s", configSpec.Name)
return errors.Wrapf(err, "failed to create config %s", configSpec.Name)
}
default:
return err

View File

@ -1,3 +1,3 @@
FROM dockercore/golang-cross@sha256:2e843a0e4d82b6bab34d2cb7abe26d1a6cda23226ecc3869100c8db553603f9b
FROM dockercore/golang-cross:1.9.4@sha256:b8d43ef11ccaa15bec63a1f1fd0c28a0e729074aa62fcfa51f0a5888f3571315
ENV DISABLE_WARN_OUTSIDE_CONTAINER=1
WORKDIR /go/src/github.com/docker/cli

View File

@ -1,5 +1,5 @@
FROM golang:1.9.2-alpine3.6
FROM golang:1.9.4-alpine3.6
RUN apk add -U git make bash coreutils ca-certificates

View File

@ -1,4 +1,4 @@
FROM golang:1.9.2-alpine3.6
FROM golang:1.9.4-alpine3.6
RUN apk add -U git

View File

@ -87,7 +87,7 @@ RUN apt-get update && apt-get install -y \
# will need updating, to avoid errors. Ping #docker-maintainers on IRC
# with a heads-up.
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" \
| tar -xzC /usr/local

View File

@ -73,7 +73,7 @@ RUN apt-get update && apt-get install -y \
# Install Go
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz" \
| tar -xzC /usr/local

View File

@ -63,7 +63,7 @@ RUN apt-get update && apt-get install -y \
# Install Go
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" \
| tar -xzC /usr/local
ENV PATH /go/bin:/usr/local/go/bin:$PATH

View File

@ -1,5 +1,5 @@
## Step 1: Build tests
FROM golang:1.9.2-alpine3.6 as builder
FROM golang:1.9.4-alpine3.6 as builder
RUN apk add --update \
bash \

View File

@ -64,7 +64,7 @@ RUN apt-get update && apt-get install -y \
# Install Go
# NOTE: official ppc64le go binaries weren't available until go 1.6.4 and 1.7.4
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" \
| tar -xzC /usr/local

View File

@ -58,7 +58,7 @@ RUN apt-get update && apt-get install -y \
--no-install-recommends
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" \
| tar -xzC /usr/local

View File

@ -40,7 +40,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
# will need updating, to avoid errors. Ping #docker-maintainers on IRC
# with a heads-up.
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" \
| tar -xzC /usr/local
ENV PATH /go/bin:/usr/local/go/bin:$PATH

View File

@ -161,7 +161,7 @@ SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPref
# Environment variable notes:
# - GO_VERSION must be consistent with 'Dockerfile' used by Linux.
# - FROM_DOCKERFILE is used for detection of building within a container.
ENV GO_VERSION=1.9.2 `
ENV GO_VERSION=1.9.4 `
GIT_VERSION=2.11.1 `
GOPATH=C:\go `
FROM_DOCKERFILE=1

View File

@ -177,6 +177,13 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
// return the network. Skipped using isMatchingScope because it is true if the scope
// is not set which would be case if the client API v1.30
if strings.HasPrefix(nwk.ID, term) || (netconst.SwarmScope == scope) {
// If we have a previous match "backend", return it, we need verbose when enabled
// ex: overlay/partial_ID or name/swarm_scope
if nwv, ok := listByPartialID[nwk.ID]; ok {
nwk = nwv
} else if nwv, ok := listByFullName[nwk.ID]; ok {
nwk = nwv
}
return httputils.WriteJSON(w, http.StatusOK, nwk)
}
}

View File

@ -2788,6 +2788,8 @@ definitions:
- "shutdown"
- "failed"
- "rejected"
- "remove"
- "orphaned"
Task:
type: "object"

View File

@ -36,6 +36,10 @@ const (
TaskStateFailed TaskState = "failed"
// TaskStateRejected REJECTED
TaskStateRejected TaskState = "rejected"
// TaskStateRemove REMOVE
TaskStateRemove TaskState = "remove"
// TaskStateOrphaned ORPHANED
TaskStateOrphaned TaskState = "orphaned"
)
// Task represents a task.

View File

@ -11,7 +11,6 @@ import (
"os"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/docker/docker/api/types"
@ -23,10 +22,8 @@ import (
"github.com/docker/docker/pkg/containerfs"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/symlink"
"github.com/docker/docker/pkg/system"
"github.com/docker/go-connections/nat"
lcUser "github.com/opencontainers/runc/libcontainer/user"
"github.com/pkg/errors"
)
@ -216,82 +213,6 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
return b.exportImage(state, imageMount, runConfigWithCommentCmd)
}
func parseChownFlag(chown, ctrRootPath string, idMappings *idtools.IDMappings) (idtools.IDPair, error) {
var userStr, grpStr string
parts := strings.Split(chown, ":")
if len(parts) > 2 {
return idtools.IDPair{}, errors.New("invalid chown string format: " + chown)
}
if len(parts) == 1 {
// if no group specified, use the user spec as group as well
userStr, grpStr = parts[0], parts[0]
} else {
userStr, grpStr = parts[0], parts[1]
}
passwdPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "passwd"), ctrRootPath)
if err != nil {
return idtools.IDPair{}, errors.Wrapf(err, "can't resolve /etc/passwd path in container rootfs")
}
groupPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "group"), ctrRootPath)
if err != nil {
return idtools.IDPair{}, errors.Wrapf(err, "can't resolve /etc/group path in container rootfs")
}
uid, err := lookupUser(userStr, passwdPath)
if err != nil {
return idtools.IDPair{}, errors.Wrapf(err, "can't find uid for user "+userStr)
}
gid, err := lookupGroup(grpStr, groupPath)
if err != nil {
return idtools.IDPair{}, errors.Wrapf(err, "can't find gid for group "+grpStr)
}
// convert as necessary because of user namespaces
chownPair, err := idMappings.ToHost(idtools.IDPair{UID: uid, GID: gid})
if err != nil {
return idtools.IDPair{}, errors.Wrapf(err, "unable to convert uid/gid to host mapping")
}
return chownPair, nil
}
func lookupUser(userStr, filepath string) (int, error) {
// if the string is actually a uid integer, parse to int and return
// as we don't need to translate with the help of files
uid, err := strconv.Atoi(userStr)
if err == nil {
return uid, nil
}
users, err := lcUser.ParsePasswdFileFilter(filepath, func(u lcUser.User) bool {
return u.Name == userStr
})
if err != nil {
return 0, err
}
if len(users) == 0 {
return 0, errors.New("no such user: " + userStr)
}
return users[0].Uid, nil
}
func lookupGroup(groupStr, filepath string) (int, error) {
// if the string is actually a gid integer, parse to int and return
// as we don't need to translate with the help of files
gid, err := strconv.Atoi(groupStr)
if err == nil {
return gid, nil
}
groups, err := lcUser.ParseGroupFileFilter(filepath, func(g lcUser.Group) bool {
return g.Name == groupStr
})
if err != nil {
return 0, err
}
if len(groups) == 0 {
return 0, errors.New("no such group: " + groupStr)
}
return groups[0].Gid, nil
}
func createDestInfo(workingDir string, inst copyInstruction, imageMount *imageMount, platform string) (copyInfo, error) {
// Twiddle the destination when it's a relative path - meaning, make it
// relative to the WORKINGDIR

View File

@ -0,0 +1,88 @@
package dockerfile
import (
"path/filepath"
"strconv"
"strings"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/symlink"
lcUser "github.com/opencontainers/runc/libcontainer/user"
"github.com/pkg/errors"
)
func parseChownFlag(chown, ctrRootPath string, idMappings *idtools.IDMappings) (idtools.IDPair, error) {
var userStr, grpStr string
parts := strings.Split(chown, ":")
if len(parts) > 2 {
return idtools.IDPair{}, errors.New("invalid chown string format: " + chown)
}
if len(parts) == 1 {
// if no group specified, use the user spec as group as well
userStr, grpStr = parts[0], parts[0]
} else {
userStr, grpStr = parts[0], parts[1]
}
passwdPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "passwd"), ctrRootPath)
if err != nil {
return idtools.IDPair{}, errors.Wrapf(err, "can't resolve /etc/passwd path in container rootfs")
}
groupPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "group"), ctrRootPath)
if err != nil {
return idtools.IDPair{}, errors.Wrapf(err, "can't resolve /etc/group path in container rootfs")
}
uid, err := lookupUser(userStr, passwdPath)
if err != nil {
return idtools.IDPair{}, errors.Wrapf(err, "can't find uid for user "+userStr)
}
gid, err := lookupGroup(grpStr, groupPath)
if err != nil {
return idtools.IDPair{}, errors.Wrapf(err, "can't find gid for group "+grpStr)
}
// convert as necessary because of user namespaces
chownPair, err := idMappings.ToHost(idtools.IDPair{UID: uid, GID: gid})
if err != nil {
return idtools.IDPair{}, errors.Wrapf(err, "unable to convert uid/gid to host mapping")
}
return chownPair, nil
}
func lookupUser(userStr, filepath string) (int, error) {
// if the string is actually a uid integer, parse to int and return
// as we don't need to translate with the help of files
uid, err := strconv.Atoi(userStr)
if err == nil {
return uid, nil
}
users, err := lcUser.ParsePasswdFileFilter(filepath, func(u lcUser.User) bool {
return u.Name == userStr
})
if err != nil {
return 0, err
}
if len(users) == 0 {
return 0, errors.New("no such user: " + userStr)
}
return users[0].Uid, nil
}
func lookupGroup(groupStr, filepath string) (int, error) {
// if the string is actually a gid integer, parse to int and return
// as we don't need to translate with the help of files
gid, err := strconv.Atoi(groupStr)
if err == nil {
return gid, nil
}
groups, err := lcUser.ParseGroupFileFilter(filepath, func(g lcUser.Group) bool {
return g.Name == groupStr
})
if err != nil {
return 0, err
}
if len(groups) == 0 {
return 0, errors.New("no such group: " + groupStr)
}
return groups[0].Gid, nil
}

View File

@ -0,0 +1,138 @@
package dockerfile
import (
"os"
"path/filepath"
"testing"
"github.com/docker/docker/pkg/idtools"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestChownFlagParsing(t *testing.T) {
testFiles := map[string]string{
"passwd": `root:x:0:0::/bin:/bin/false
bin:x:1:1::/bin:/bin/false
wwwwww:x:21:33::/bin:/bin/false
unicorn:x:1001:1002::/bin:/bin/false
`,
"group": `root:x:0:
bin:x:1:
wwwwww:x:33:
unicorn:x:1002:
somegrp:x:5555:
othergrp:x:6666:
`,
}
// test mappings for validating use of maps
idMaps := []idtools.IDMap{
{
ContainerID: 0,
HostID: 100000,
Size: 65536,
},
}
remapped := idtools.NewIDMappingsFromMaps(idMaps, idMaps)
unmapped := &idtools.IDMappings{}
contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test")
defer cleanup()
if err := os.Mkdir(filepath.Join(contextDir, "etc"), 0755); err != nil {
t.Fatalf("error creating test directory: %v", err)
}
for filename, content := range testFiles {
createTestTempFile(t, filepath.Join(contextDir, "etc"), filename, content, 0644)
}
// positive tests
for _, testcase := range []struct {
name string
chownStr string
idMapping *idtools.IDMappings
expected idtools.IDPair
}{
{
name: "UIDNoMap",
chownStr: "1",
idMapping: unmapped,
expected: idtools.IDPair{UID: 1, GID: 1},
},
{
name: "UIDGIDNoMap",
chownStr: "0:1",
idMapping: unmapped,
expected: idtools.IDPair{UID: 0, GID: 1},
},
{
name: "UIDWithMap",
chownStr: "0",
idMapping: remapped,
expected: idtools.IDPair{UID: 100000, GID: 100000},
},
{
name: "UIDGIDWithMap",
chownStr: "1:33",
idMapping: remapped,
expected: idtools.IDPair{UID: 100001, GID: 100033},
},
{
name: "UserNoMap",
chownStr: "bin:5555",
idMapping: unmapped,
expected: idtools.IDPair{UID: 1, GID: 5555},
},
{
name: "GroupWithMap",
chownStr: "0:unicorn",
idMapping: remapped,
expected: idtools.IDPair{UID: 100000, GID: 101002},
},
{
name: "UserOnlyWithMap",
chownStr: "unicorn",
idMapping: remapped,
expected: idtools.IDPair{UID: 101001, GID: 101002},
},
} {
t.Run(testcase.name, func(t *testing.T) {
idPair, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
require.NoError(t, err, "Failed to parse chown flag: %q", testcase.chownStr)
assert.Equal(t, testcase.expected, idPair, "chown flag mapping failure")
})
}
// error tests
for _, testcase := range []struct {
name string
chownStr string
idMapping *idtools.IDMappings
descr string
}{
{
name: "BadChownFlagFormat",
chownStr: "bob:1:555",
idMapping: unmapped,
descr: "invalid chown string format: bob:1:555",
},
{
name: "UserNoExist",
chownStr: "bob",
idMapping: unmapped,
descr: "can't find uid for user bob: no such user: bob",
},
{
name: "GroupNoExist",
chownStr: "root:bob",
idMapping: unmapped,
descr: "can't find gid for group bob: no such group: bob",
},
} {
t.Run(testcase.name, func(t *testing.T) {
_, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
assert.EqualError(t, err, testcase.descr, "Expected error string doesn't match")
})
}
}

View File

@ -2,8 +2,6 @@ package dockerfile
import (
"fmt"
"os"
"path/filepath"
"runtime"
"testing"
@ -13,7 +11,6 @@ import (
"github.com/docker/docker/builder"
"github.com/docker/docker/builder/remotecontext"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/go-connections/nat"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -171,130 +168,3 @@ func TestDeepCopyRunConfig(t *testing.T) {
copy.Shell[0] = "sh"
assert.Equal(t, fullMutableRunConfig(), runConfig)
}
func TestChownFlagParsing(t *testing.T) {
testFiles := map[string]string{
"passwd": `root:x:0:0::/bin:/bin/false
bin:x:1:1::/bin:/bin/false
wwwwww:x:21:33::/bin:/bin/false
unicorn:x:1001:1002::/bin:/bin/false
`,
"group": `root:x:0:
bin:x:1:
wwwwww:x:33:
unicorn:x:1002:
somegrp:x:5555:
othergrp:x:6666:
`,
}
// test mappings for validating use of maps
idMaps := []idtools.IDMap{
{
ContainerID: 0,
HostID: 100000,
Size: 65536,
},
}
remapped := idtools.NewIDMappingsFromMaps(idMaps, idMaps)
unmapped := &idtools.IDMappings{}
contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test")
defer cleanup()
if err := os.Mkdir(filepath.Join(contextDir, "etc"), 0755); err != nil {
t.Fatalf("error creating test directory: %v", err)
}
for filename, content := range testFiles {
createTestTempFile(t, filepath.Join(contextDir, "etc"), filename, content, 0644)
}
// positive tests
for _, testcase := range []struct {
name string
chownStr string
idMapping *idtools.IDMappings
expected idtools.IDPair
}{
{
name: "UIDNoMap",
chownStr: "1",
idMapping: unmapped,
expected: idtools.IDPair{UID: 1, GID: 1},
},
{
name: "UIDGIDNoMap",
chownStr: "0:1",
idMapping: unmapped,
expected: idtools.IDPair{UID: 0, GID: 1},
},
{
name: "UIDWithMap",
chownStr: "0",
idMapping: remapped,
expected: idtools.IDPair{UID: 100000, GID: 100000},
},
{
name: "UIDGIDWithMap",
chownStr: "1:33",
idMapping: remapped,
expected: idtools.IDPair{UID: 100001, GID: 100033},
},
{
name: "UserNoMap",
chownStr: "bin:5555",
idMapping: unmapped,
expected: idtools.IDPair{UID: 1, GID: 5555},
},
{
name: "GroupWithMap",
chownStr: "0:unicorn",
idMapping: remapped,
expected: idtools.IDPair{UID: 100000, GID: 101002},
},
{
name: "UserOnlyWithMap",
chownStr: "unicorn",
idMapping: remapped,
expected: idtools.IDPair{UID: 101001, GID: 101002},
},
} {
t.Run(testcase.name, func(t *testing.T) {
idPair, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
require.NoError(t, err, "Failed to parse chown flag: %q", testcase.chownStr)
assert.Equal(t, testcase.expected, idPair, "chown flag mapping failure")
})
}
// error tests
for _, testcase := range []struct {
name string
chownStr string
idMapping *idtools.IDMappings
descr string
}{
{
name: "BadChownFlagFormat",
chownStr: "bob:1:555",
idMapping: unmapped,
descr: "invalid chown string format: bob:1:555",
},
{
name: "UserNoExist",
chownStr: "bob",
idMapping: unmapped,
descr: "can't find uid for user bob: no such user: bob",
},
{
name: "GroupNoExist",
chownStr: "root:bob",
idMapping: unmapped,
descr: "can't find gid for group bob: no such group: bob",
},
} {
t.Run(testcase.name, func(t *testing.T) {
_, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
assert.EqualError(t, err, testcase.descr, "Expected error string doesn't match")
})
}
}

View File

@ -0,0 +1,7 @@
package dockerfile
import "github.com/docker/docker/pkg/idtools"
func parseChownFlag(chown, ctrRootPath string, idMappings *idtools.IDMappings) (idtools.IDPair, error) {
return idMappings.RootPair(), nil
}

View File

@ -67,7 +67,7 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
flags.StringVar(&conf.MetricsAddress, "metrics-addr", "", "Set default address and port to serve the metrics api on")
flags.Var(opts.NewListOptsRef(&conf.NodeGenericResources, opts.ValidateSingleGenericResource), "node-generic-resource", "Advertise user-defined resource")
flags.Var(opts.NewNamedListOptsRef("node-generic-resources", &conf.NodeGenericResources, opts.ValidateSingleGenericResource), "node-generic-resource", "Advertise user-defined resource")
flags.IntVar(&conf.NetworkControlPlaneMTU, "network-control-plane-mtu", config.DefaultNetworkMtu, "Network Control plane MTU")

View File

@ -61,6 +61,22 @@ func TestLoadDaemonCliConfigWithConflicts(t *testing.T) {
testutil.ErrorContains(t, err, "as a flag and in the configuration file: labels")
}
func TestLoadDaemonCliWithConflictingNodeGenericResources(t *testing.T) {
tempFile := fs.NewFile(t, "config", fs.WithContent(`{"node-generic-resources": ["foo=bar", "bar=baz"]}`))
defer tempFile.Remove()
configFile := tempFile.Path()
opts := defaultOptions(configFile)
flags := opts.flags
assert.NoError(t, flags.Set("config-file", configFile))
assert.NoError(t, flags.Set("node-generic-resource", "r1=bar"))
assert.NoError(t, flags.Set("node-generic-resource", "r2=baz"))
_, err := loadDaemonCliConfig(opts)
testutil.ErrorContains(t, err, "as a flag and in the configuration file: node-generic-resources")
}
func TestLoadDaemonCliWithConflictingLabels(t *testing.T) {
opts := defaultOptions("")
flags := opts.flags

View File

@ -1,18 +1,5 @@
package main
import (
"sync/atomic"
_ "github.com/docker/docker/autogen/winresources/dockerd"
)
//go:cgo_import_dynamic main.dummy CommandLineToArgvW%2 "shell32.dll"
var dummy uintptr
func init() {
// Ensure that this import is not removed by the linker. This is used to
// ensure that shell32.dll is loaded by the system loader, preventing
// go#15286 from triggering on Nano Server TP5.
atomic.LoadUintptr(&dummy)
}

View File

@ -7,7 +7,7 @@ FROM aarch64/debian:jessie
RUN echo deb http://ftp.debian.org/debian jessie-backports main > /etc/apt/sources.list.d/backports.list
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev libseccomp-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM aarch64/debian:stretch
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-dev libseccomp-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM aarch64/ubuntu:trusty
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM aarch64/ubuntu:xenial
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-dev libseccomp-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -10,7 +10,7 @@ RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -10,7 +10,7 @@ RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -12,7 +12,7 @@ RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list.d
RUN apt-get update && apt-get install -y -t wheezy-backports btrfs-tools --no-install-recommends && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y apparmor bash-completion build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM ubuntu:trusty
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM ubuntu:xenial
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM ubuntu:yakkety
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM ubuntu:zesty
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -10,7 +10,7 @@ RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -10,7 +10,7 @@ RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
# GOARM is the ARM architecture version which is unrelated to the above Golang version
ENV GOARM 6
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local

View File

@ -6,7 +6,7 @@ FROM armhf/ubuntu:trusty
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM armhf/ubuntu:xenial
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM armhf/ubuntu:yakkety
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM ppc64le/ubuntu:trusty
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM ppc64le/ubuntu:xenial
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libseccomp-dev libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM ppc64le/ubuntu:yakkety
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libseccomp-dev libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM s390x/ubuntu:xenial
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config libsystemd-dev vim-common --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -6,7 +6,7 @@ FROM s390x/ubuntu:yakkety
RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config libsystemd-dev vim-common --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -7,7 +7,7 @@ FROM amazonlinux:latest
RUN yum groupinstall -y "Development Tools"
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel tar git cmake vim-common
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -8,7 +8,7 @@ RUN yum groupinstall -y "Development Tools"
RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -8,7 +8,7 @@ RUN dnf -y upgrade
RUN dnf install -y @development-tools fedora-packager
RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -8,7 +8,7 @@ RUN dnf -y upgrade
RUN dnf install -y @development-tools fedora-packager
RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -7,7 +7,7 @@ FROM opensuse:13.2
RUN zypper --non-interactive install ca-certificates* curl gzip rpm-build
RUN zypper --non-interactive install libbtrfs-devel device-mapper-devel glibc-static libselinux-devel pkg-config selinux-policy selinux-policy-devel systemd-devel tar git cmake vim systemd-rpm-macros
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -10,7 +10,7 @@ RUN yum install -y kernel-uek-devel-4.1.12-32.el6uek
RUN yum groupinstall -y "Development Tools"
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel pkgconfig selinux-policy selinux-policy-devel tar git cmake vim-common
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -7,7 +7,7 @@ FROM oraclelinux:7
RUN yum groupinstall -y "Development Tools"
RUN yum install -y --enablerepo=ol7_optional_latest btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -7,7 +7,7 @@ FROM photon:1.0
RUN tdnf install -y wget curl ca-certificates gzip make rpm-build sed gcc linux-api-headers glibc-devel binutils libseccomp elfutils
RUN tdnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkg-config selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -9,7 +9,7 @@ RUN yum groupinstall --skip-broken -y "Development Tools"
RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim-common
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -8,7 +8,7 @@ RUN yum groupinstall -y "Development Tools"
RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim-common
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -8,7 +8,7 @@ RUN dnf -y upgrade
RUN dnf install -y @development-tools fedora-packager
RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim-common
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -9,7 +9,7 @@ RUN zypper addrepo -n ppc64le-updates -f https://download.opensuse.org/ports/upd
RUN zypper --non-interactive install ca-certificates* curl gzip rpm-build
RUN zypper --non-interactive install libbtrfs-devel device-mapper-devel glibc-static libselinux-devel pkg-config selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -8,7 +8,7 @@ FROM sinenomine/clefos-base-s390x
RUN touch /var/lib/rpm/* && yum groupinstall -y "Development Tools"
RUN touch /var/lib/rpm/* && yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim-common
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -9,7 +9,7 @@ RUN zypper ar https://download.opensuse.org/ports/zsystems/tumbleweed/repo/oss/
RUN zypper --non-interactive install ca-certificates* curl gzip rpm-build
RUN zypper --non-interactive install libbtrfs-devel device-mapper-devel glibc-static libselinux-devel pkg-config selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim systemd-rpm-macros
ENV GO_VERSION 1.9.2
ENV GO_VERSION 1.9.4
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin

View File

@ -22,6 +22,9 @@ func (daemon *Daemon) ContainerChanges(name string) ([]archive.Change, error) {
container.Lock()
defer container.Unlock()
if container.RWLayer == nil {
return nil, errors.New("RWLayer of container " + name + " is unexpectedly nil")
}
c, err := container.RWLayer.Changes()
if err != nil {
return nil, err

View File

@ -168,6 +168,7 @@ func BasicNetworkFromGRPC(n swarmapi.Network) basictypes.NetworkResource {
Ingress: IsIngressNetwork(&n),
Labels: n.Spec.Annotations.Labels,
}
nr.Created, _ = gogotypes.TimestampFromProto(n.Meta.CreatedAt)
if n.Spec.GetNetwork() != "" {
nr.ConfigFrom = networktypes.ConfigReference{

View File

@ -0,0 +1,34 @@
package convert
import (
"testing"
"time"
swarmapi "github.com/docker/swarmkit/api"
gogotypes "github.com/gogo/protobuf/types"
)
func TestNetworkConvertBasicNetworkFromGRPCCreatedAt(t *testing.T) {
expected, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Jan 10, 2018 at 7:54pm (PST)")
if err != nil {
t.Fatal(err)
}
createdAt, err := gogotypes.TimestampProto(expected)
if err != nil {
t.Fatal(err)
}
nw := swarmapi.Network{
Meta: swarmapi.Meta{
Version: swarmapi.Version{
Index: 1,
},
CreatedAt: createdAt,
},
}
n := BasicNetworkFromGRPC(nw)
if !n.Created.Equal(expected) {
t.Fatalf("expected time %s; received %s", expected, n.Created)
}
}

View File

@ -146,6 +146,11 @@ func (e *executor) Configure(ctx context.Context, node *api.Node) error {
attachments[na.Network.ID] = na.Addresses[0]
}
if (ingressNA == nil) && (node.Attachment != nil) {
ingressNA = node.Attachment
attachments[ingressNA.Network.ID] = ingressNA.Addresses[0]
}
if ingressNA == nil {
e.backend.ReleaseIngress()
return e.backend.GetAttachmentStore().ResetAttachments(attachments)

View File

@ -19,6 +19,7 @@ import (
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/pkg/truncindex"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/volume"
"github.com/docker/go-connections/nat"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
@ -293,6 +294,14 @@ func (daemon *Daemon) verifyContainerSettings(platform string, hostConfig *conta
return nil, errors.Errorf("can't create 'AutoRemove' container with restart policy")
}
// Validate mounts; check if host directories still exist
parser := volume.NewParser(platform)
for _, cfg := range hostConfig.Mounts {
if err := parser.ValidateMountConfig(&cfg); err != nil {
return nil, err
}
}
for _, extraHost := range hostConfig.ExtraHosts {
if _, err := opts.ValidateExtraHost(extraHost); err != nil {
return nil, err

View File

@ -1056,6 +1056,9 @@ func (daemon *Daemon) Shutdown() error {
// Mount sets container.BaseFS
// (is it not set coming in? why is it unset?)
func (daemon *Daemon) Mount(container *container.Container) error {
if container.RWLayer == nil {
return errors.New("RWLayer of container " + container.ID + " is unexpectedly nil")
}
dir, err := container.RWLayer.Mount(container.GetMountLabel())
if err != nil {
return err
@ -1078,6 +1081,9 @@ func (daemon *Daemon) Mount(container *container.Container) error {
// Unmount unsets the container base filesystem
func (daemon *Daemon) Unmount(container *container.Container) error {
if container.RWLayer == nil {
return errors.New("RWLayer of container " + container.ID + " is unexpectedly nil")
}
if err := container.RWLayer.Unmount(); err != nil {
logrus.Errorf("Error unmounting container %s: %s", container.ID, err)
return err

View File

@ -10,6 +10,7 @@ import (
"github.com/docker/docker/pkg/fileutils"
"github.com/docker/docker/pkg/mount"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@ -67,9 +68,29 @@ func (daemon *Daemon) cleanupMountsFromReaderByID(reader io.Reader, id string, u
return nil
}
// cleanupMounts umounts shm/mqueue mounts for old containers
// cleanupMounts umounts used by container resources and the daemon root mount
func (daemon *Daemon) cleanupMounts() error {
return daemon.cleanupMountsByID("")
if err := daemon.cleanupMountsByID(""); err != nil {
return err
}
infos, err := mount.GetMounts()
if err != nil {
return errors.Wrap(err, "error reading mount table for cleanup")
}
info := getMountInfo(infos, daemon.root)
// `info.Root` here is the root mountpoint of the passed in path (`daemon.root`).
// The ony cases that need to be cleaned up is when the daemon has performed a
// `mount --bind /daemon/root /daemon/root && mount --make-shared /daemon/root`
// This is only done when the daemon is started up and `/daemon/root` is not
// already on a shared mountpoint.
if !shouldUnmountRoot(daemon.root, info) {
return nil
}
logrus.WithField("mountpoint", daemon.root).Debug("unmounting daemon root")
return mount.Unmount(daemon.root)
}
func getCleanPatterns(id string) (regexps []*regexp.Regexp) {
@ -91,3 +112,16 @@ func getCleanPatterns(id string) (regexps []*regexp.Regexp) {
func getRealPath(path string) (string, error) {
return fileutils.ReadSymlinkedDirectory(path)
}
func shouldUnmountRoot(root string, info *mount.Info) bool {
if info == nil {
return false
}
if info.Mountpoint != root {
return false
}
if !strings.HasSuffix(root, info.Root) {
return false
}
return hasMountinfoOption(info.Optional, sharedPropagationOption)
}

View File

@ -10,6 +10,7 @@ import (
"github.com/docker/docker/container"
"github.com/docker/docker/oci"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/mount"
"github.com/stretchr/testify/assert"
)
@ -164,3 +165,66 @@ func TestValidateContainerIsolationLinux(t *testing.T) {
_, err := d.verifyContainerSettings("linux", &containertypes.HostConfig{Isolation: containertypes.IsolationHyperV}, nil, false)
assert.EqualError(t, err, "invalid isolation 'hyperv' on linux")
}
func TestShouldUnmountRoot(t *testing.T) {
for _, test := range []struct {
desc string
root string
info *mount.Info
expect bool
}{
{
desc: "root is at /",
root: "/docker",
info: &mount.Info{Root: "/docker", Mountpoint: "/docker"},
expect: true,
},
{
desc: "not a mountpoint",
root: "/docker",
info: nil,
expect: false,
},
{
desc: "root is at in a submount from `/`",
root: "/foo/docker",
info: &mount.Info{Root: "/docker", Mountpoint: "/foo/docker"},
expect: true,
},
{
desc: "root is mounted in from a parent mount namespace same root dir", // dind is an example of this
root: "/docker",
info: &mount.Info{Root: "/docker/volumes/1234657/_data", Mountpoint: "/docker"},
expect: false,
},
{
desc: "root is mounted in from a parent mount namespace different root dir",
root: "/foo/bar",
info: &mount.Info{Root: "/docker/volumes/1234657/_data", Mountpoint: "/foo/bar"},
expect: false,
},
} {
t.Run(test.desc, func(t *testing.T) {
for _, options := range []struct {
desc string
Optional string
expect bool
}{
{desc: "shared", Optional: "shared:", expect: true},
{desc: "slave", Optional: "slave:", expect: false},
{desc: "private", Optional: "private:", expect: false},
} {
t.Run(options.desc, func(t *testing.T) {
expect := options.expect
if expect {
expect = test.expect
}
if test.info != nil {
test.info.Optional = options.Optional
}
assert.Equal(t, expect, shouldUnmountRoot(test.root, test.info))
})
}
})
}
}

View File

@ -29,6 +29,7 @@ import (
"github.com/docker/docker/pkg/containerfs"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/pkg/parsers/kernel"
"github.com/docker/docker/pkg/sysinfo"
@ -1228,6 +1229,12 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPa
}
}
}
if err := ensureSharedOrSlave(config.Root); err != nil {
if err := mount.MakeShared(config.Root); err != nil {
logrus.WithError(err).WithField("dir", config.Root).Warn("Could not set daemon root propagation to shared, this is not generally critical but may cause some functionality to not work or fallback to less desirable behavior")
}
}
return nil
}

View File

@ -124,6 +124,7 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
container.SetRemovalError(e)
return e
}
container.RWLayer = nil
}
if err := system.EnsureRemoveAll(container.Root); err != nil {

View File

@ -136,10 +136,6 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
return nil, err
}
if err := mountpk.MakePrivate(root); err != nil {
return nil, err
}
// Populate the dir structure
for _, p := range paths {
if err := idtools.MkdirAllAndChown(path.Join(root, p), 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
@ -610,7 +606,7 @@ func (a *Driver) Cleanup() error {
logrus.Debugf("aufs error unmounting %s: %s", m, err)
}
}
return mountpk.Unmount(a.root)
return mountpk.RecursiveUnmount(a.root)
}
func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err error) {

View File

@ -29,7 +29,6 @@ import (
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/pkg/containerfs"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/pkg/system"
"github.com/docker/go-units"
@ -77,10 +76,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
return nil, err
}
if err := mount.MakePrivate(home); err != nil {
return nil, err
}
opt, userDiskQuota, err := parseOptions(options)
if err != nil {
return nil, err
@ -167,7 +162,7 @@ func (d *Driver) Cleanup() error {
return err
}
return mount.Unmount(d.home)
return nil
}
func free(p *C.char) {

View File

@ -42,10 +42,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
return nil, err
}
if err := mount.MakePrivate(home); err != nil {
return nil, err
}
d := &Driver{
DeviceSet: deviceSet,
home: home,
@ -127,7 +123,7 @@ func (d *Driver) GetMetadata(id string) (map[string]string, error) {
func (d *Driver) Cleanup() error {
err := d.DeviceSet.Shutdown(d.home)
if err2 := mount.Unmount(d.home); err == nil {
if err2 := mount.RecursiveUnmount(d.home); err == nil {
err = err2
}

View File

@ -164,10 +164,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
return nil, err
}
if err := mount.MakePrivate(home); err != nil {
return nil, err
}
d := &Driver{
home: home,
uidMaps: uidMaps,
@ -248,7 +244,7 @@ func (d *Driver) GetMetadata(id string) (map[string]string, error) {
// is being shutdown. For now, we just have to unmount the bind mounted
// we had created.
func (d *Driver) Cleanup() error {
return mount.Unmount(d.home)
return mount.RecursiveUnmount(d.home)
}
// CreateReadWrite creates a layer that is writable for use as a container

View File

@ -200,10 +200,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
return nil, err
}
if err := mount.MakePrivate(home); err != nil {
return nil, err
}
d := &Driver{
home: home,
uidMaps: uidMaps,
@ -334,7 +330,7 @@ func (d *Driver) GetMetadata(id string) (map[string]string, error) {
// is being shutdown. For now, we just have to unmount the bind mounted
// we had created.
func (d *Driver) Cleanup() error {
return mount.Unmount(d.home)
return mount.RecursiveUnmount(d.home)
}
// CreateReadWrite creates a layer that is writable for use as a container

View File

@ -350,11 +350,17 @@ func makeBackingFsDev(home string) (string, error) {
backingFsBlockDev := path.Join(home, "backingFsBlockDev")
// Re-create just in case someone copied the home directory over to a new device
unix.Unlink(backingFsBlockDev)
if err := unix.Mknod(backingFsBlockDev, unix.S_IFBLK|0600, int(stat.Dev)); err != nil {
err := unix.Mknod(backingFsBlockDev, unix.S_IFBLK|0600, int(stat.Dev))
switch err {
case nil:
return backingFsBlockDev, nil
case unix.ENOSYS:
return "", ErrQuotaNotSupported
default:
return "", fmt.Errorf("Failed to mknod %s: %v", backingFsBlockDev, err)
}
return backingFsBlockDev, nil
}
func hasQuotaSupport(backingFsBlockDev string) (bool, error) {

View File

@ -35,9 +35,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
return nil, err
}
if err := setupDriverQuota(d); err != nil {
return nil, err
}
setupDriverQuota(d)
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
}

View File

@ -2,20 +2,21 @@
package vfs
import "github.com/docker/docker/daemon/graphdriver/quota"
import (
"github.com/docker/docker/daemon/graphdriver/quota"
"github.com/sirupsen/logrus"
)
type driverQuota struct {
quotaCtl *quota.Control
}
func setupDriverQuota(driver *Driver) error {
func setupDriverQuota(driver *Driver) {
if quotaCtl, err := quota.NewControl(driver.home); err == nil {
driver.quotaCtl = quotaCtl
} else if err != quota.ErrQuotaNotSupported {
return err
logrus.Warnf("Unable to setup quota: %v\n", err)
}
return nil
}
func (d *Driver) setupQuota(dir string, size uint64) error {

View File

@ -108,9 +108,6 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
return nil, fmt.Errorf("Failed to create '%s': %v", base, err)
}
if err := mount.MakePrivate(base); err != nil {
return nil, err
}
d := &Driver{
dataset: rootDataset,
options: options,
@ -181,7 +178,8 @@ func (d *Driver) String() string {
return "zfs"
}
// Cleanup is used to implement graphdriver.ProtoDriver. There is no cleanup required for this driver.
// Cleanup is called on daemon shutdown, it is a no-op for ZFS.
// TODO(@cpuguy83): Walk layer tree and check mounts?
func (d *Driver) Cleanup() error {
return nil
}

View File

@ -80,6 +80,7 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container
execConfig.Tty = false
execConfig.Privileged = false
execConfig.User = cntr.Config.User
execConfig.WorkingDir = cntr.Config.WorkingDir
linkedEnv, err := d.setupLinkedContainers(cntr)
if err != nil {

View File

@ -1,6 +1,7 @@
package daemon
import (
"errors"
"fmt"
"time"
@ -183,14 +184,24 @@ func (daemon *Daemon) getInspectData(container *container.Container) (*types.Con
contJSONBase.GraphDriver.Name = container.Driver
if container.RWLayer == nil {
if container.Dead {
return contJSONBase, nil
}
return nil, systemError{errors.New("RWLayer of container " + container.ID + " is unexpectedly nil")}
}
graphDriverData, err := container.RWLayer.Metadata()
// If container is marked as Dead, the container's graphdriver metadata
// could have been removed, it will cause error if we try to get the metadata,
// we can ignore the error if the container is dead.
if err != nil && !container.Dead {
return nil, systemError{err}
if err != nil {
if !container.Dead {
return nil, systemError{err}
}
} else {
contJSONBase.GraphDriver.Data = graphDriverData
}
contJSONBase.GraphDriver.Data = graphDriverData
return contJSONBase, nil
}

View File

@ -0,0 +1,33 @@
package daemon // import "github.com/docker/docker/daemon"
import (
"testing"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/config"
"github.com/docker/docker/daemon/exec"
"github.com/stretchr/testify/assert"
)
func TestGetInspectData(t *testing.T) {
c := &container.Container{
ID: "inspect-me",
HostConfig: &containertypes.HostConfig{},
State: container.NewState(),
ExecCommands: exec.NewStore(),
}
d := &Daemon{
linkIndex: newLinkIndex(),
configStore: &config.Config{},
}
_, err := d.getInspectData(c)
assert.Error(t, err)
c.Dead = true
_, err = d.getInspectData(c)
assert.NoError(t, err)
}

View File

@ -95,6 +95,17 @@ func init() {
}
}
// eventBatch holds the events that are batched for submission and the
// associated data about it.
//
// Warning: this type is not threadsafe and must not be used
// concurrently. This type is expected to be consumed in a single go
// routine and never concurrently.
type eventBatch struct {
batch []wrappedEvent
bytes int
}
// New creates an awslogs logger using the configuration passed in on the
// context. Supported context configuration variables are awslogs-region,
// awslogs-group, awslogs-stream, awslogs-create-group, awslogs-multiline-pattern
@ -389,32 +400,32 @@ var newTicker = func(freq time.Duration) *time.Ticker {
// Logs, the processEvents method is called. If a multiline pattern is not
// configured, log events are submitted to the processEvents method immediately.
func (l *logStream) collectBatch() {
timer := newTicker(batchPublishFrequency)
var events []wrappedEvent
ticker := newTicker(batchPublishFrequency)
var eventBuffer []byte
var eventBufferTimestamp int64
var batch = newEventBatch()
for {
select {
case t := <-timer.C:
case t := <-ticker.C:
// If event buffer is older than batch publish frequency flush the event buffer
if eventBufferTimestamp > 0 && len(eventBuffer) > 0 {
eventBufferAge := t.UnixNano()/int64(time.Millisecond) - eventBufferTimestamp
eventBufferExpired := eventBufferAge > int64(batchPublishFrequency)/int64(time.Millisecond)
eventBufferNegative := eventBufferAge < 0
if eventBufferExpired || eventBufferNegative {
events = l.processEvent(events, eventBuffer, eventBufferTimestamp)
l.processEvent(batch, eventBuffer, eventBufferTimestamp)
eventBuffer = eventBuffer[:0]
}
}
l.publishBatch(events)
events = events[:0]
l.publishBatch(batch)
batch.reset()
case msg, more := <-l.messages:
if !more {
// Flush event buffer and release resources
events = l.processEvent(events, eventBuffer, eventBufferTimestamp)
l.processEvent(batch, eventBuffer, eventBufferTimestamp)
eventBuffer = eventBuffer[:0]
l.publishBatch(events)
events = events[:0]
l.publishBatch(batch)
batch.reset()
return
}
if eventBufferTimestamp == 0 {
@ -425,7 +436,7 @@ func (l *logStream) collectBatch() {
if l.multilinePattern.Match(unprocessedLine) || len(eventBuffer)+len(unprocessedLine) > maximumBytesPerEvent {
// This is a new log event or we will exceed max bytes per event
// so flush the current eventBuffer to events and reset timestamp
events = l.processEvent(events, eventBuffer, eventBufferTimestamp)
l.processEvent(batch, eventBuffer, eventBufferTimestamp)
eventBufferTimestamp = msg.Timestamp.UnixNano() / int64(time.Millisecond)
eventBuffer = eventBuffer[:0]
}
@ -434,7 +445,7 @@ func (l *logStream) collectBatch() {
eventBuffer = append(eventBuffer, processedLine...)
logger.PutMessage(msg)
} else {
events = l.processEvent(events, unprocessedLine, msg.Timestamp.UnixNano()/int64(time.Millisecond))
l.processEvent(batch, unprocessedLine, msg.Timestamp.UnixNano()/int64(time.Millisecond))
logger.PutMessage(msg)
}
}
@ -450,8 +461,7 @@ func (l *logStream) collectBatch() {
// bytes per event (defined in maximumBytesPerEvent). There is a fixed per-event
// byte overhead (defined in perEventBytes) which is accounted for in split- and
// batch-calculations.
func (l *logStream) processEvent(events []wrappedEvent, unprocessedLine []byte, timestamp int64) []wrappedEvent {
bytes := 0
func (l *logStream) processEvent(batch *eventBatch, unprocessedLine []byte, timestamp int64) {
for len(unprocessedLine) > 0 {
// Split line length so it does not exceed the maximum
lineBytes := len(unprocessedLine)
@ -459,38 +469,33 @@ func (l *logStream) processEvent(events []wrappedEvent, unprocessedLine []byte,
lineBytes = maximumBytesPerEvent
}
line := unprocessedLine[:lineBytes]
unprocessedLine = unprocessedLine[lineBytes:]
if (len(events) >= maximumLogEventsPerPut) || (bytes+lineBytes+perEventBytes > maximumBytesPerPut) {
// Publish an existing batch if it's already over the maximum number of events or if adding this
// event would push it over the maximum number of total bytes.
l.publishBatch(events)
events = events[:0]
bytes = 0
}
events = append(events, wrappedEvent{
event := wrappedEvent{
inputLogEvent: &cloudwatchlogs.InputLogEvent{
Message: aws.String(string(line)),
Timestamp: aws.Int64(timestamp),
},
insertOrder: len(events),
})
bytes += (lineBytes + perEventBytes)
insertOrder: batch.count(),
}
added := batch.add(event, lineBytes)
if added {
unprocessedLine = unprocessedLine[lineBytes:]
} else {
l.publishBatch(batch)
batch.reset()
}
}
return events
}
// publishBatch calls PutLogEvents for a given set of InputLogEvents,
// accounting for sequencing requirements (each request must reference the
// sequence token returned by the previous request).
func (l *logStream) publishBatch(events []wrappedEvent) {
if len(events) == 0 {
func (l *logStream) publishBatch(batch *eventBatch) {
if batch.isEmpty() {
return
}
// events in a batch must be sorted by timestamp
// see http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html
sort.Sort(byTimestamp(events))
cwEvents := unwrapEvents(events)
cwEvents := unwrapEvents(batch.events())
nextSequenceToken, err := l.putLogEvents(cwEvents, l.sequenceToken)
@ -615,3 +620,70 @@ func unwrapEvents(events []wrappedEvent) []*cloudwatchlogs.InputLogEvent {
}
return cwEvents
}
func newEventBatch() *eventBatch {
return &eventBatch{
batch: make([]wrappedEvent, 0),
bytes: 0,
}
}
// events returns a slice of wrappedEvents sorted in order of their
// timestamps and then by their insertion order (see `byTimestamp`).
//
// Warning: this method is not threadsafe and must not be used
// concurrently.
func (b *eventBatch) events() []wrappedEvent {
sort.Sort(byTimestamp(b.batch))
return b.batch
}
// add adds an event to the batch of events accounting for the
// necessary overhead for an event to be logged. An error will be
// returned if the event cannot be added to the batch due to service
// limits.
//
// Warning: this method is not threadsafe and must not be used
// concurrently.
func (b *eventBatch) add(event wrappedEvent, size int) bool {
addBytes := size + perEventBytes
// verify we are still within service limits
switch {
case len(b.batch)+1 > maximumLogEventsPerPut:
return false
case b.bytes+addBytes > maximumBytesPerPut:
return false
}
b.bytes += addBytes
b.batch = append(b.batch, event)
return true
}
// count is the number of batched events. Warning: this method
// is not threadsafe and must not be used concurrently.
func (b *eventBatch) count() int {
return len(b.batch)
}
// size is the total number of bytes that the batch represents.
//
// Warning: this method is not threadsafe and must not be used
// concurrently.
func (b *eventBatch) size() int {
return b.bytes
}
func (b *eventBatch) isEmpty() bool {
zeroEvents := b.count() == 0
zeroSize := b.size() == 0
return zeroEvents && zeroSize
}
// reset prepares the batch for reuse.
func (b *eventBatch) reset() {
b.bytes = 0
b.batch = b.batch[:0]
}

View File

@ -49,6 +49,15 @@ func (l *logStream) logGenerator(lineCount int, multilineCount int) {
}
}
func testEventBatch(events []wrappedEvent) *eventBatch {
batch := newEventBatch()
for _, event := range events {
eventlen := len([]byte(*event.inputLogEvent.Message))
batch.add(event, eventlen)
}
return batch
}
func TestNewAWSLogsClientUserAgentHandler(t *testing.T) {
info := logger.Info{
Config: map[string]string{
@ -212,7 +221,7 @@ func TestPublishBatchSuccess(t *testing.T) {
},
}
stream.publishBatch(events)
stream.publishBatch(testEventBatch(events))
if stream.sequenceToken == nil {
t.Fatal("Expected non-nil sequenceToken")
}
@ -257,7 +266,7 @@ func TestPublishBatchError(t *testing.T) {
},
}
stream.publishBatch(events)
stream.publishBatch(testEventBatch(events))
if stream.sequenceToken == nil {
t.Fatal("Expected non-nil sequenceToken")
}
@ -291,7 +300,7 @@ func TestPublishBatchInvalidSeqSuccess(t *testing.T) {
},
}
stream.publishBatch(events)
stream.publishBatch(testEventBatch(events))
if stream.sequenceToken == nil {
t.Fatal("Expected non-nil sequenceToken")
}
@ -354,7 +363,7 @@ func TestPublishBatchAlreadyAccepted(t *testing.T) {
},
}
stream.publishBatch(events)
stream.publishBatch(testEventBatch(events))
if stream.sequenceToken == nil {
t.Fatal("Expected non-nil sequenceToken")
}
@ -859,7 +868,8 @@ func TestCollectBatchMaxEvents(t *testing.T) {
}
func TestCollectBatchMaxTotalBytes(t *testing.T) {
mockClient := newMockClientBuffered(1)
expectedPuts := 2
mockClient := newMockClientBuffered(expectedPuts)
stream := &logStream{
client: mockClient,
logGroupName: groupName,
@ -867,11 +877,14 @@ func TestCollectBatchMaxTotalBytes(t *testing.T) {
sequenceToken: aws.String(sequenceToken),
messages: make(chan *logger.Message),
}
mockClient.putLogEventsResult <- &putLogEventsResult{
successResult: &cloudwatchlogs.PutLogEventsOutput{
NextSequenceToken: aws.String(nextSequenceToken),
},
for i := 0; i < expectedPuts; i++ {
mockClient.putLogEventsResult <- &putLogEventsResult{
successResult: &cloudwatchlogs.PutLogEventsOutput{
NextSequenceToken: aws.String(nextSequenceToken),
},
}
}
var ticks = make(chan time.Time)
newTicker = func(_ time.Duration) *time.Ticker {
return &time.Ticker{
@ -881,32 +894,57 @@ func TestCollectBatchMaxTotalBytes(t *testing.T) {
go stream.collectBatch()
longline := strings.Repeat("A", maximumBytesPerPut)
numPayloads := maximumBytesPerPut / (maximumBytesPerEvent + perEventBytes)
// maxline is the maximum line that could be submitted after
// accounting for its overhead.
maxline := strings.Repeat("A", maximumBytesPerPut-(perEventBytes*numPayloads))
// This will be split and batched up to the `maximumBytesPerPut'
// (+/- `maximumBytesPerEvent'). This /should/ be aligned, but
// should also tolerate an offset within that range.
stream.Log(&logger.Message{
Line: []byte(longline + "B"),
Line: []byte(maxline[:len(maxline)/2]),
Timestamp: time.Time{},
})
stream.Log(&logger.Message{
Line: []byte(maxline[len(maxline)/2:]),
Timestamp: time.Time{},
})
stream.Log(&logger.Message{
Line: []byte("B"),
Timestamp: time.Time{},
})
// no ticks
// no ticks, guarantee batch by size (and chan close)
stream.Close()
argument := <-mockClient.putLogEventsArgument
if argument == nil {
t.Fatal("Expected non-nil PutLogEventsInput")
}
bytes := 0
// Should total to the maximum allowed bytes.
eventBytes := 0
for _, event := range argument.LogEvents {
bytes += len(*event.Message)
eventBytes += len(*event.Message)
}
if bytes > maximumBytesPerPut {
t.Errorf("Expected <= %d bytes but was %d", maximumBytesPerPut, bytes)
eventsOverhead := len(argument.LogEvents) * perEventBytes
payloadTotal := eventBytes + eventsOverhead
// lowestMaxBatch allows the payload to be offset if the messages
// don't lend themselves to align with the maximum event size.
lowestMaxBatch := maximumBytesPerPut - maximumBytesPerEvent
if payloadTotal > maximumBytesPerPut {
t.Errorf("Expected <= %d bytes but was %d", maximumBytesPerPut, payloadTotal)
}
if payloadTotal < lowestMaxBatch {
t.Errorf("Batch to be no less than %d but was %d", lowestMaxBatch, payloadTotal)
}
argument = <-mockClient.putLogEventsArgument
if len(argument.LogEvents) != 1 {
t.Errorf("Expected LogEvents to contain 1 elements, but contains %d", len(argument.LogEvents))
}
message := *argument.LogEvents[0].Message
message := *argument.LogEvents[len(argument.LogEvents)-1].Message
if message[len(message)-1:] != "B" {
t.Errorf("Expected message to be %s but was %s", "B", message[len(message)-1:])
}

View File

@ -1,6 +1,10 @@
package awslogs
import "github.com/aws/aws-sdk-go/service/cloudwatchlogs"
import (
"fmt"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
)
type mockcwlogsclient struct {
createLogGroupArgument chan *cloudwatchlogs.CreateLogGroupInput
@ -67,7 +71,30 @@ func (m *mockcwlogsclient) PutLogEvents(input *cloudwatchlogs.PutLogEventsInput)
LogGroupName: input.LogGroupName,
LogStreamName: input.LogStreamName,
}
// Intended mock output
output := <-m.putLogEventsResult
// Checked enforced limits in mock
totalBytes := 0
for _, evt := range events {
if evt.Message == nil {
continue
}
eventBytes := len([]byte(*evt.Message))
if eventBytes > maximumBytesPerEvent {
// exceeded per event message size limits
return nil, fmt.Errorf("maximum bytes per event exceeded: Event too large %d, max allowed: %d", eventBytes, maximumBytesPerEvent)
}
// total event bytes including overhead
totalBytes += eventBytes + perEventBytes
}
if totalBytes > maximumBytesPerPut {
// exceeded per put maximum size limit
return nil, fmt.Errorf("maximum bytes per put exceeded: Upload too large %d, max allowed: %d", totalBytes, maximumBytesPerPut)
}
return output.successResult, output.errorResult
}

View File

@ -215,6 +215,7 @@ func New(info logger.Info) (logger.Logger, error) {
transport := &http.Transport{
TLSClientConfig: tlsConfig,
Proxy: http.ProxyFromEnvironment,
}
client := &http.Client{
Transport: transport,

View File

@ -4,12 +4,14 @@ import (
"compress/gzip"
"context"
"fmt"
"net/http"
"os"
"runtime"
"testing"
"time"
"github.com/docker/docker/daemon/logger"
"github.com/gotestyourself/gotestyourself/env"
"github.com/stretchr/testify/require"
)
@ -82,6 +84,36 @@ func TestNewMissedToken(t *testing.T) {
}
}
func TestNewWithProxy(t *testing.T) {
proxy := "http://proxy.testing:8888"
reset := env.Patch(t, "HTTP_PROXY", proxy)
defer reset()
// must not be localhost
splunkURL := "http://example.com:12345"
logger, err := New(logger.Info{
Config: map[string]string{
splunkURLKey: splunkURL,
splunkTokenKey: "token",
splunkVerifyConnectionKey: "false",
},
ContainerID: "containeriid",
})
require.NoError(t, err)
splunkLogger := logger.(*splunkLoggerInline)
proxyFunc := splunkLogger.transport.Proxy
require.NotNil(t, proxyFunc)
req, err := http.NewRequest("GET", splunkURL, nil)
require.NoError(t, err)
proxyURL, err := proxyFunc(req)
require.NoError(t, err)
require.NotNil(t, proxyURL)
require.Equal(t, proxy, proxyURL.String())
}
// Test default settings
func TestDefault(t *testing.T) {
hec := NewHTTPEventCollectorMock(t)

View File

@ -24,6 +24,7 @@ import (
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/opencontainers/runc/libcontainer/user"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@ -419,52 +420,46 @@ func getSourceMount(source string) (string, string, error) {
return "", "", fmt.Errorf("Could not find source mount of %s", source)
}
const (
sharedPropagationOption = "shared:"
slavePropagationOption = "master:"
)
// hasMountinfoOption checks if any of the passed any of the given option values
// are set in the passed in option string.
func hasMountinfoOption(opts string, vals ...string) bool {
for _, opt := range strings.Split(opts, " ") {
for _, val := range vals {
if strings.HasPrefix(opt, val) {
return true
}
}
}
return false
}
// Ensure mount point on which path is mounted, is shared.
func ensureShared(path string) error {
sharedMount := false
sourceMount, optionalOpts, err := getSourceMount(path)
if err != nil {
return err
}
// Make sure source mount point is shared.
optsSplit := strings.Split(optionalOpts, " ")
for _, opt := range optsSplit {
if strings.HasPrefix(opt, "shared:") {
sharedMount = true
break
}
}
if !sharedMount {
return fmt.Errorf("path %s is mounted on %s but it is not a shared mount", path, sourceMount)
if !hasMountinfoOption(optionalOpts, sharedPropagationOption) {
return errors.Errorf("path %s is mounted on %s but it is not a shared mount", path, sourceMount)
}
return nil
}
// Ensure mount point on which path is mounted, is either shared or slave.
func ensureSharedOrSlave(path string) error {
sharedMount := false
slaveMount := false
sourceMount, optionalOpts, err := getSourceMount(path)
if err != nil {
return err
}
// Make sure source mount point is shared.
optsSplit := strings.Split(optionalOpts, " ")
for _, opt := range optsSplit {
if strings.HasPrefix(opt, "shared:") {
sharedMount = true
break
} else if strings.HasPrefix(opt, "master:") {
slaveMount = true
break
}
}
if !sharedMount && !slaveMount {
return fmt.Errorf("path %s is mounted on %s but it is not a shared or slave mount", path, sourceMount)
if !hasMountinfoOption(optionalOpts, sharedPropagationOption, slavePropagationOption) {
return errors.Errorf("path %s is mounted on %s but it is not a shared or slave mount", path, sourceMount)
}
return nil
}
@ -604,7 +599,8 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c
//
// For private volumes any root propagation value should work.
pFlag := mountPropagationMap[m.Propagation]
if pFlag == mount.SHARED || pFlag == mount.RSHARED {
switch pFlag {
case mount.SHARED, mount.RSHARED:
if err := ensureShared(m.Source); err != nil {
return err
}
@ -612,13 +608,34 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c
if rootpg != mount.SHARED && rootpg != mount.RSHARED {
s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.SHARED]
}
} else if pFlag == mount.SLAVE || pFlag == mount.RSLAVE {
case mount.SLAVE, mount.RSLAVE:
var fallback bool
if err := ensureSharedOrSlave(m.Source); err != nil {
return err
// For backwards compatability purposes, treat mounts from the daemon root
// as special since we automatically add rslave propagation to these mounts
// when the user did not set anything, so we should fallback to the old
// behavior which is to use private propagation which is normally the
// default.
if !strings.HasPrefix(m.Source, daemon.root) && !strings.HasPrefix(daemon.root, m.Source) {
return err
}
cm, ok := c.MountPoints[m.Destination]
if !ok {
return err
}
if cm.Spec.BindOptions != nil && cm.Spec.BindOptions.Propagation != "" {
// This means the user explicitly set a propagation, do not fallback in that case.
return err
}
fallback = true
logrus.WithField("container", c.ID).WithField("source", m.Source).Warn("Falling back to default propagation for bind source in daemon root")
}
rootpg := mountPropagationMap[s.Linux.RootfsPropagation]
if rootpg != mount.SHARED && rootpg != mount.RSHARED && rootpg != mount.SLAVE && rootpg != mount.RSLAVE {
s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.RSLAVE]
if !fallback {
rootpg := mountPropagationMap[s.Linux.RootfsPropagation]
if rootpg != mount.SHARED && rootpg != mount.RSHARED && rootpg != mount.SLAVE && rootpg != mount.RSLAVE {
s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.RSLAVE]
}
}
}

View File

@ -1,6 +1,7 @@
package daemon
import (
"errors"
"fmt"
"io/ioutil"
"path/filepath"
@ -145,6 +146,9 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
// Reverse order, expecting parent most first
s.Windows.LayerFolders = append([]string{layerPath}, s.Windows.LayerFolders...)
}
if c.RWLayer == nil {
return nil, errors.New("RWLayer of container " + c.ID + " is unexpectedly nil")
}
m, err := c.RWLayer.Metadata()
if err != nil {
return nil, fmt.Errorf("failed to get layer metadata - %s", err)

View File

@ -10,6 +10,7 @@ import (
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
mounttypes "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/container"
"github.com/docker/docker/volume"
@ -145,6 +146,13 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
if err != nil {
return err
}
needsSlavePropagation, err := daemon.validateBindDaemonRoot(bind.Spec)
if err != nil {
return err
}
if needsSlavePropagation {
bind.Propagation = mount.PropagationRSlave
}
// #10618
_, tmpfsExists := hostConfig.Tmpfs[bind.Destination]
@ -177,6 +185,13 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
if err != nil {
return validationError{err}
}
needsSlavePropagation, err := daemon.validateBindDaemonRoot(mp.Spec)
if err != nil {
return err
}
if needsSlavePropagation {
mp.Propagation = mount.PropagationRSlave
}
if binds[mp.Destination] {
return duplicateMountPointError(cfg.Target)

View File

@ -0,0 +1,35 @@
package daemon
import (
"strings"
"github.com/docker/docker/api/types/mount"
"github.com/pkg/errors"
)
// validateBindDaemonRoot ensures that if a given mountpoint's source is within
// the daemon root path, that the propagation is setup to prevent a container
// from holding private refereneces to a mount within the daemon root, which
// can cause issues when the daemon attempts to remove the mountpoint.
func (daemon *Daemon) validateBindDaemonRoot(m mount.Mount) (bool, error) {
if m.Type != mount.TypeBind {
return false, nil
}
// check if the source is within the daemon root, or if the daemon root is within the source
if !strings.HasPrefix(m.Source, daemon.root) && !strings.HasPrefix(daemon.root, m.Source) {
return false, nil
}
if m.BindOptions == nil {
return true, nil
}
switch m.BindOptions.Propagation {
case mount.PropagationRSlave, mount.PropagationRShared, "":
return m.BindOptions.Propagation == "", nil
default:
}
return false, validationError{errors.Errorf(`invalid mount config: must use either propagation mode "rslave" or "rshared" when mount source is within the daemon root, daemon root: %q, bind mount source: %q, propagation: %q`, daemon.root, m.Source, m.BindOptions.Propagation)}
}

View File

@ -0,0 +1,56 @@
package daemon
import (
"path/filepath"
"testing"
"github.com/docker/docker/api/types/mount"
)
func TestBindDaemonRoot(t *testing.T) {
t.Parallel()
d := &Daemon{root: "/a/b/c/daemon"}
for _, test := range []struct {
desc string
opts *mount.BindOptions
needsProp bool
err bool
}{
{desc: "nil propagation settings", opts: nil, needsProp: true, err: false},
{desc: "empty propagation settings", opts: &mount.BindOptions{}, needsProp: true, err: false},
{desc: "private propagation", opts: &mount.BindOptions{Propagation: mount.PropagationPrivate}, err: true},
{desc: "rprivate propagation", opts: &mount.BindOptions{Propagation: mount.PropagationRPrivate}, err: true},
{desc: "slave propagation", opts: &mount.BindOptions{Propagation: mount.PropagationSlave}, err: true},
{desc: "rslave propagation", opts: &mount.BindOptions{Propagation: mount.PropagationRSlave}, err: false, needsProp: false},
{desc: "shared propagation", opts: &mount.BindOptions{Propagation: mount.PropagationShared}, err: true},
{desc: "rshared propagation", opts: &mount.BindOptions{Propagation: mount.PropagationRSlave}, err: false, needsProp: false},
} {
t.Run(test.desc, func(t *testing.T) {
test := test
for desc, source := range map[string]string{
"source is root": d.root,
"source is subpath": filepath.Join(d.root, "a", "b"),
"source is parent": filepath.Dir(d.root),
"source is /": "/",
} {
t.Run(desc, func(t *testing.T) {
mount := mount.Mount{
Type: mount.TypeBind,
Source: source,
BindOptions: test.opts,
}
needsProp, err := d.validateBindDaemonRoot(mount)
if (err != nil) != test.err {
t.Fatalf("expected err=%v, got: %v", test.err, err)
}
if test.err {
return
}
if test.needsProp != needsProp {
t.Fatalf("expected needsProp=%v, got: %v", test.needsProp, needsProp)
}
})
}
})
}
}

View File

@ -5,6 +5,7 @@ package daemon
import (
"sort"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/container"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/volume"
@ -46,3 +47,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
func setBindModeIfNull(bind *volume.MountPoint) {
return
}
func (daemon *Daemon) validateBindDaemonRoot(m mount.Mount) (bool, error) {
return false, nil
}

View File

@ -3,8 +3,8 @@
TOMLV_COMMIT=9baf8a8a9f2ed20a8e54160840c492f937eeaf9a
# When updating RUNC_COMMIT, also update runc in vendor.conf accordingly
RUNC_COMMIT=b2567b37d7b75eb4cf325b77297b140ea686ce8f
CONTAINERD_COMMIT=89623f28b87a6004d4b785663257362d1658a729 # v1.0.0
RUNC_COMMIT=9f9c96235cc97674e935002fc3d78361b696a69e
CONTAINERD_COMMIT=9b55aab90508bd389d7654c4baf173a981477d55 # v1.0.1
TINI_COMMIT=949e6facb77383876aeff8a6944dde66b3089574
LIBNETWORK_COMMIT=7b2b1feb1de4817d522cc372af149ff48d25028e
VNDR_COMMIT=a6e196d8b4b0cbbdc29aebdb20c59ac6926bb384

View File

@ -36,7 +36,6 @@ import (
"github.com/docker/libnetwork/types"
"github.com/go-check/check"
"github.com/gotestyourself/gotestyourself/icmd"
libcontainerUser "github.com/opencontainers/runc/libcontainer/user"
"golang.org/x/net/context"
)
@ -751,7 +750,7 @@ func (s *DockerSuite) TestRunUserByIDBig(c *check.C) {
if err == nil {
c.Fatal("No error, but must be.", out)
}
if !strings.Contains(strings.ToUpper(out), strings.ToUpper(libcontainerUser.ErrRange.Error())) {
if !strings.Contains(strings.ToLower(out), "uids and gids must be in range") {
c.Fatalf("expected error about uids range, got %s", out)
}
}
@ -764,7 +763,7 @@ func (s *DockerSuite) TestRunUserByIDNegative(c *check.C) {
if err == nil {
c.Fatal("No error, but must be.", out)
}
if !strings.Contains(strings.ToUpper(out), strings.ToUpper(libcontainerUser.ErrRange.Error())) {
if !strings.Contains(strings.ToLower(out), "uids and gids must be in range") {
c.Fatalf("expected error about uids range, got %s", out)
}
}

View File

@ -0,0 +1,61 @@
package container
import (
"context"
"testing"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/docker/client"
"github.com/docker/docker/integration/util/request"
"github.com/gotestyourself/gotestyourself/poll"
"github.com/stretchr/testify/require"
)
// TestHealthCheckWorkdir verifies that health-checks inherit the containers'
// working-dir.
func TestHealthCheckWorkdir(t *testing.T) {
defer setupTest(t)()
ctx := context.Background()
client := request.NewAPIClient(t)
c, err := client.ContainerCreate(ctx,
&container.Config{
Image: "busybox",
Tty: true,
WorkingDir: "/foo",
Cmd: strslice.StrSlice([]string{"top"}),
Healthcheck: &container.HealthConfig{
Test: []string{"CMD-SHELL", "if [ \"$PWD\" = \"/foo\" ]; then exit 0; else exit 1; fi;"},
Interval: 50 * time.Millisecond,
Retries: 3,
},
},
&container.HostConfig{},
&network.NetworkingConfig{},
"healthtest",
)
require.NoError(t, err)
err = client.ContainerStart(ctx, c.ID, types.ContainerStartOptions{})
require.NoError(t, err)
poll.WaitOn(t, pollForHealthStatus(ctx, client, c.ID, types.Healthy), poll.WithDelay(100*time.Millisecond))
}
func pollForHealthStatus(ctx context.Context, client client.APIClient, containerID string, healthStatus string) func(log poll.LogT) poll.Result {
return func(log poll.LogT) poll.Result {
inspect, err := client.ContainerInspect(ctx, containerID)
switch {
case err != nil:
return poll.Error(err)
case inspect.State.Health.Status == healthStatus:
return poll.Success()
default:
return poll.Continue("waiting for container to become %s", healthStatus)
}
}
}

View File

@ -0,0 +1,139 @@
package container
import (
"context"
"fmt"
"path/filepath"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/integration/util/request"
)
func TestMountDaemonRoot(t *testing.T) {
t.Parallel()
client := request.NewAPIClient(t)
ctx := context.Background()
info, err := client.Info(ctx)
if err != nil {
t.Fatal(err)
}
for _, test := range []struct {
desc string
propagation mount.Propagation
expected mount.Propagation
}{
{
desc: "default",
propagation: "",
expected: mount.PropagationRSlave,
},
{
desc: "private",
propagation: mount.PropagationPrivate,
},
{
desc: "rprivate",
propagation: mount.PropagationRPrivate,
},
{
desc: "slave",
propagation: mount.PropagationSlave,
},
{
desc: "rslave",
propagation: mount.PropagationRSlave,
expected: mount.PropagationRSlave,
},
{
desc: "shared",
propagation: mount.PropagationShared,
},
{
desc: "rshared",
propagation: mount.PropagationRShared,
expected: mount.PropagationRShared,
},
} {
t.Run(test.desc, func(t *testing.T) {
test := test
t.Parallel()
propagationSpec := fmt.Sprintf(":%s", test.propagation)
if test.propagation == "" {
propagationSpec = ""
}
bindSpecRoot := info.DockerRootDir + ":" + "/foo" + propagationSpec
bindSpecSub := filepath.Join(info.DockerRootDir, "containers") + ":/foo" + propagationSpec
for name, hc := range map[string]*container.HostConfig{
"bind root": {Binds: []string{bindSpecRoot}},
"bind subpath": {Binds: []string{bindSpecSub}},
"mount root": {
Mounts: []mount.Mount{
{
Type: mount.TypeBind,
Source: info.DockerRootDir,
Target: "/foo",
BindOptions: &mount.BindOptions{Propagation: test.propagation},
},
},
},
"mount subpath": {
Mounts: []mount.Mount{
{
Type: mount.TypeBind,
Source: filepath.Join(info.DockerRootDir, "containers"),
Target: "/foo",
BindOptions: &mount.BindOptions{Propagation: test.propagation},
},
},
},
} {
t.Run(name, func(t *testing.T) {
hc := hc
t.Parallel()
c, err := client.ContainerCreate(ctx, &container.Config{
Image: "busybox",
Cmd: []string{"true"},
}, hc, nil, "")
if err != nil {
if test.expected != "" {
t.Fatal(err)
}
// expected an error, so this is ok and should not continue
return
}
if test.expected == "" {
t.Fatal("expected create to fail")
}
defer func() {
if err := client.ContainerRemove(ctx, c.ID, types.ContainerRemoveOptions{Force: true}); err != nil {
panic(err)
}
}()
inspect, err := client.ContainerInspect(ctx, c.ID)
if err != nil {
t.Fatal(err)
}
if len(inspect.Mounts) != 1 {
t.Fatalf("unexpected number of mounts: %+v", inspect.Mounts)
}
m := inspect.Mounts[0]
if m.Propagation != test.expected {
t.Fatalf("got unexpected propagation mode, expected %q, got: %v", test.expected, m.Propagation)
}
})
}
})
}
}

View File

@ -0,0 +1,221 @@
package network
import (
"fmt"
"runtime"
"testing"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"github.com/docker/docker/integration-cli/daemon"
"github.com/docker/docker/integration-cli/request"
"github.com/gotestyourself/gotestyourself/poll"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
)
const defaultSwarmPort = 2477
const dockerdBinary = "dockerd"
func TestInspectNetwork(t *testing.T) {
defer setupTest(t)()
d := newSwarm(t)
defer d.Stop(t)
client, err := request.NewClientForHost(d.Sock())
require.NoError(t, err)
overlayName := "overlay1"
networkCreate := types.NetworkCreate{
CheckDuplicate: true,
Driver: "overlay",
}
netResp, err := client.NetworkCreate(context.Background(), overlayName, networkCreate)
require.NoError(t, err)
overlayID := netResp.ID
var instances uint64 = 4
serviceName := "TestService"
serviceSpec := swarmServiceSpec(serviceName, instances)
serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarm.NetworkAttachmentConfig{Target: overlayName})
serviceResp, err := client.ServiceCreate(context.Background(), serviceSpec, types.ServiceCreateOptions{
QueryRegistry: false,
})
require.NoError(t, err)
pollSettings := func(config *poll.Settings) {
if runtime.GOARCH == "arm" {
config.Timeout = 30 * time.Second
config.Delay = 100 * time.Millisecond
}
}
serviceID := serviceResp.ID
poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, instances), pollSettings)
_, _, err = client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
require.NoError(t, err)
// Test inspect verbose with full NetworkID
networkVerbose, err := client.NetworkInspect(context.Background(), overlayID, types.NetworkInspectOptions{
Verbose: true,
})
require.NoError(t, err)
require.True(t, validNetworkVerbose(networkVerbose, serviceName, instances))
// Test inspect verbose with partial NetworkID
networkVerbose, err = client.NetworkInspect(context.Background(), overlayID[0:11], types.NetworkInspectOptions{
Verbose: true,
})
require.NoError(t, err)
require.True(t, validNetworkVerbose(networkVerbose, serviceName, instances))
// Test inspect verbose with Network name and swarm scope
networkVerbose, err = client.NetworkInspect(context.Background(), overlayName, types.NetworkInspectOptions{
Verbose: true,
Scope: "swarm",
})
require.NoError(t, err)
require.True(t, validNetworkVerbose(networkVerbose, serviceName, instances))
err = client.ServiceRemove(context.Background(), serviceID)
require.NoError(t, err)
poll.WaitOn(t, serviceIsRemoved(client, serviceID), pollSettings)
poll.WaitOn(t, noTasks(client), pollSettings)
serviceResp, err = client.ServiceCreate(context.Background(), serviceSpec, types.ServiceCreateOptions{
QueryRegistry: false,
})
require.NoError(t, err)
serviceID2 := serviceResp.ID
poll.WaitOn(t, serviceRunningTasksCount(client, serviceID2, instances), pollSettings)
err = client.ServiceRemove(context.Background(), serviceID2)
require.NoError(t, err)
poll.WaitOn(t, serviceIsRemoved(client, serviceID2), pollSettings)
poll.WaitOn(t, noTasks(client), pollSettings)
err = client.NetworkRemove(context.Background(), overlayID)
require.NoError(t, err)
poll.WaitOn(t, networkIsRemoved(client, overlayID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
}
func newSwarm(t *testing.T) *daemon.Swarm {
d := &daemon.Swarm{
Daemon: daemon.New(t, "", dockerdBinary, daemon.Config{
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
}),
// TODO: better method of finding an unused port
Port: defaultSwarmPort,
}
// TODO: move to a NewSwarm constructor
d.ListenAddr = fmt.Sprintf("0.0.0.0:%d", d.Port)
// avoid networking conflicts
args := []string{"--iptables=false", "--swarm-default-advertise-addr=lo"}
d.StartWithBusybox(t, args...)
require.NoError(t, d.Init(swarm.InitRequest{}))
return d
}
func swarmServiceSpec(name string, replicas uint64) swarm.ServiceSpec {
return swarm.ServiceSpec{
Annotations: swarm.Annotations{
Name: name,
},
TaskTemplate: swarm.TaskSpec{
ContainerSpec: &swarm.ContainerSpec{
Image: "busybox:latest",
Command: []string{"/bin/top"},
},
},
Mode: swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{
Replicas: &replicas,
},
},
}
}
func serviceRunningTasksCount(client client.ServiceAPIClient, serviceID string, instances uint64) func(log poll.LogT) poll.Result {
return func(log poll.LogT) poll.Result {
filter := filters.NewArgs()
filter.Add("service", serviceID)
tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
Filters: filter,
})
switch {
case err != nil:
return poll.Error(err)
case len(tasks) == int(instances):
for _, task := range tasks {
if task.Status.State != swarm.TaskStateRunning {
return poll.Continue("waiting for tasks to enter run state")
}
}
return poll.Success()
default:
return poll.Continue("task count at %d waiting for %d", len(tasks), instances)
}
}
}
func networkIsRemoved(client client.NetworkAPIClient, networkID string) func(log poll.LogT) poll.Result {
return func(log poll.LogT) poll.Result {
_, err := client.NetworkInspect(context.Background(), networkID, types.NetworkInspectOptions{})
if err == nil {
return poll.Continue("waiting for network %s to be removed", networkID)
}
return poll.Success()
}
}
func serviceIsRemoved(client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
return func(log poll.LogT) poll.Result {
filter := filters.NewArgs()
filter.Add("service", serviceID)
_, err := client.TaskList(context.Background(), types.TaskListOptions{
Filters: filter,
})
if err == nil {
return poll.Continue("waiting for service %s to be deleted", serviceID)
}
return poll.Success()
}
}
func noTasks(client client.ServiceAPIClient) func(log poll.LogT) poll.Result {
return func(log poll.LogT) poll.Result {
filter := filters.NewArgs()
tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
Filters: filter,
})
switch {
case err != nil:
return poll.Error(err)
case len(tasks) == 0:
return poll.Success()
default:
return poll.Continue("task count at %d waiting for 0", len(tasks))
}
}
}
// Check to see if Service and Tasks info are part of the inspect verbose response
func validNetworkVerbose(network types.NetworkResource, service string, instances uint64) bool {
if service, ok := network.Services[service]; ok {
if len(service.Tasks) == int(instances) {
return true
}
}
return false
}

View File

@ -113,8 +113,21 @@ type client struct {
containers map[string]*container
}
func (c *client) setRemote(remote *containerd.Client) {
c.Lock()
c.remote = remote
c.Unlock()
}
func (c *client) getRemote() *containerd.Client {
c.RLock()
remote := c.remote
c.RUnlock()
return remote
}
func (c *client) Version(ctx context.Context) (containerd.Version, error) {
return c.remote.Version(ctx)
return c.getRemote().Version(ctx)
}
func (c *client) Restore(ctx context.Context, id string, attachStdio StdioCallback) (alive bool, pid int, err error) {
@ -188,7 +201,7 @@ func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, run
c.logger.WithField("bundle", bdir).WithField("root", ociSpec.Root.Path).Debug("bundle dir created")
cdCtr, err := c.remote.NewContainer(ctx, id,
cdCtr, err := c.getRemote().NewContainer(ctx, id,
containerd.WithSpec(ociSpec),
// TODO(mlaventure): when containerd support lcow, revisit runtime value
containerd.WithRuntime(fmt.Sprintf("io.containerd.runtime.v1.%s", runtime.GOOS), runtimeOptions))
@ -231,7 +244,7 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin
// remove the checkpoint when we're done
defer func() {
if cp != nil {
err := c.remote.ContentStore().Delete(context.Background(), cp.Digest)
err := c.getRemote().ContentStore().Delete(context.Background(), cp.Digest)
if err != nil {
c.logger.WithError(err).WithFields(logrus.Fields{
"ref": checkpointDir,
@ -262,8 +275,9 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin
func(_ context.Context, _ *containerd.Client, info *containerd.TaskInfo) error {
info.Checkpoint = cp
info.Options = &runctypes.CreateOptions{
IoUid: uint32(uid),
IoGid: uint32(gid),
IoUid: uint32(uid),
IoGid: uint32(gid),
NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
}
return nil
})
@ -533,14 +547,14 @@ func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDi
}
// Whatever happens, delete the checkpoint from containerd
defer func() {
err := c.remote.ImageService().Delete(context.Background(), img.Name())
err := c.getRemote().ImageService().Delete(context.Background(), img.Name())
if err != nil {
c.logger.WithError(err).WithField("digest", img.Target().Digest).
Warnf("failed to delete checkpoint image")
}
}()
b, err := content.ReadBlob(ctx, c.remote.ContentStore(), img.Target().Digest)
b, err := content.ReadBlob(ctx, c.getRemote().ContentStore(), img.Target().Digest)
if err != nil {
return wrapSystemError(errors.Wrapf(err, "failed to retrieve checkpoint data"))
}
@ -560,7 +574,7 @@ func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDi
return wrapSystemError(errors.Wrapf(err, "invalid checkpoint"))
}
rat, err := c.remote.ContentStore().ReaderAt(ctx, cpDesc.Digest)
rat, err := c.getRemote().ContentStore().ReaderAt(ctx, cpDesc.Digest)
if err != nil {
return wrapSystemError(errors.Wrapf(err, "failed to get checkpoint reader"))
}
@ -713,16 +727,19 @@ func (c *client) processEventStream(ctx context.Context) {
}
}()
eventStream, err = c.remote.EventService().Subscribe(ctx, &eventsapi.SubscribeRequest{
eventStream, err = c.getRemote().EventService().Subscribe(ctx, &eventsapi.SubscribeRequest{
Filters: []string{
"namespace==" + c.namespace,
"topic~=/tasks/",
// Filter on both namespace *and* topic. To create an "and" filter,
// this must be a single, comma-separated string
"namespace==" + c.namespace + ",topic~=|^/tasks/|",
},
}, grpc.FailFast(false))
if err != nil {
return
}
c.logger.WithField("namespace", c.namespace).Debug("processing event stream")
var oomKilled bool
for {
ev, err = eventStream.Recv()
@ -826,7 +843,7 @@ func (c *client) processEventStream(ctx context.Context) {
}
func (c *client) writeContent(ctx context.Context, mediaType, ref string, r io.Reader) (*types.Descriptor, error) {
writer, err := c.remote.ContentStore().Writer(ctx, ref, 0, "")
writer, err := c.getRemote().ContentStore().Writer(ctx, ref, 0, "")
if err != nil {
return nil, err
}

Some files were not shown because too many files have changed in this diff Show More