Compare commits

...

238 Commits

Author SHA1 Message Date
Tibor Vass
91e29e815d Bump VERSION to v1.12.0-rc3
Signed-off-by: Tibor Vass <tibor@docker.com>
2016-07-01 16:56:08 -07:00
Tibor Vass
da6da096b8 Merge pull request #24247 from tiborvass/fix-rpm-for-fedora24
fix F24 rpm build
2016-07-01 16:52:57 -07:00
Antonio Murdaca
0500446b36 fix F24 rpm build
error: line 89: Invalid version (epoch must be unsigned integer):
%{epoch}:1.12.0-0.3.rc3.fc24: Requires(pre): docker-engine-selinux >=
%%{epoch}:1.12.0-0.3.rc3.fc24

Signed-off-by: Antonio Murdaca <runcom@redhat.com>
(cherry picked from commit 0c07e55e4c)
2016-07-01 16:51:47 -07:00
Tibor Vass
ac499dc4bb Merge pull request #24231 from tiborvass/cherry-pick-containerd-race-fix
Vendor containerd 1b3a81545ca79456086dc2aa424357be98b962ee
2016-07-01 13:30:13 -07:00
Kenfe-Mickael Laventure
5199e3960e Vendor containerd 1b3a81545ca79456086dc2aa424357be98b962ee
Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
(cherry picked from commit 2685c82c2e)
2016-07-01 12:01:28 -07:00
Tibor Vass
39bc769113 Merge pull request #23724 from tiborvass/cherry-picks-for-1.12.0-rc3
[WIP] Cherry picks for 1.12.0 rc3
2016-07-01 03:59:35 -07:00
Aaron Lehmann
8a18eac752 Add documentation for external CA features in API/CLI
Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
(cherry picked from commit fa147591ed)
2016-07-01 00:40:48 -07:00
Tonis Tiigi
f9f7abfffe Add support for external CAs
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 11085b2260)
2016-07-01 00:40:37 -07:00
Tonis Tiigi
4905c858db Update to new swarmkit/engine-api
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 9b65273836)
2016-07-01 00:40:37 -07:00
Tonis Tiigi
7d835ed934 Vendor updated swarmkit, libnetwork, engine-api
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 54255f53d8)
2016-07-01 00:40:36 -07:00
Nishant Totla
ae47b00c6c Adding a flag to specify sending of registry auth
Signed-off-by: Nishant Totla <nishanttotla@gmail.com>
(cherry picked from commit 538bac39d7)
2016-07-01 00:40:31 -07:00
Nishant Totla
d199f78d18 Updating header name and executor
Signed-off-by: Nishant Totla <nishanttotla@gmail.com>
(cherry picked from commit af5df117a8)
2016-07-01 00:39:51 -07:00
Nishant Totla
25c6d432ac Update engine-api vendored version
Signed-off-by: Nishant Totla <nishanttotla@gmail.com>
(cherry picked from commit e13a14340d)
2016-07-01 00:39:51 -07:00
Nishant Totla
c5ddd5bb7f Passing registry auth token for service create and update
Signed-off-by: Nishant Totla <nishanttotla@gmail.com>
(cherry picked from commit 93ec5cda55)
2016-07-01 00:39:51 -07:00
Tonis Tiigi
88cb79d400 Fix duplicate layers in exported tar
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 41bf7de73b)
2016-07-01 00:39:50 -07:00
Wen Cheng Ma
72a4697cda Fix docker stack link
Signed-off-by: Wen Cheng Ma <wenchma@cn.ibm.com>
(cherry picked from commit 93ed4b35fa)
2016-07-01 00:39:50 -07:00
Keith Hudgins
17939a3159 Added HPE volume plugin listing
Signed-off-by: Keith Hudgins <greenman@greenman.org>
(cherry picked from commit 87ef5b4254)
2016-07-01 00:39:50 -07:00
John Howard
ddae91f21a Windows: Skip RunCidFileCleanupIfEmpty on RS1
Signed-off-by: John Howard <jhoward@microsoft.com>
(cherry picked from commit 20424fccdd)
2016-07-01 00:39:50 -07:00
John Howard
9185a0a681 Windows: Skip TestBuildEmptyCmd on RS1
Signed-off-by: John Howard <jhoward@microsoft.com>
(cherry picked from commit 45da127442)
2016-07-01 00:39:50 -07:00
Derek McGowan
6234af6680 Set permission on atomic file write
Perform chmod before rename with the atomic file writer.
Ensure writeErr is set on short write and file is removed on write error.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
(cherry picked from commit 1cd7490281)
2016-07-01 00:39:50 -07:00
Vincent Demeester
1e30b67b4d Fix --filter=desired_state behaviour
Just like `docker service tasks`, we should add `desired_state` filters
only in case there is no provided filters.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
(cherry picked from commit 3c194bd2a4)
2016-06-30 17:12:46 -07:00
Stefan Scherer
06cbb86a29 Revert tasksmax workaround to avoid unsupported bins
Signed-off-by: Stefan Scherer <scherer_stefan@icloud.com>
(cherry picked from commit 6509cc4e63)
2016-06-30 17:12:46 -07:00
Akihiro Suda
4d4848ab68 zfs: call mount.MakePrivate
Fix #24008

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
(cherry picked from commit 5ec8441bb5)
2016-06-30 17:12:45 -07:00
allencloud
acc31b4448 fix comments and handle err
Signed-off-by: allencloud <allen.sun@daocloud.io>
(cherry picked from commit cc054f3195)
2016-06-30 17:12:45 -07:00
Yong Tang
b34706b152 Use HOSTNAME in the output of docker node ls
This fix tries to address an issue raised in #24090 where
the title field of `docker node ls` use NAME instead of
HOSTNAME. Yet the content of this field is actually
hostname.

The fix makes needed changes for the output of
`docker node ls`.

An additional test has been added to cover the change in
this fix.

This fix fixes #24090.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
(cherry picked from commit 4bc91ceeb7)
2016-06-30 17:12:45 -07:00
Antonio Murdaca
b375ccfee7 fix warn message typos
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
(cherry picked from commit a4143e6756)
2016-06-30 17:12:37 -07:00
allencloud
49981f8327 make cmd short short consistency and change docs
Signed-off-by: allencloud <allen.sun@daocloud.io>
(cherry picked from commit 184afb92bf)
2016-06-30 17:11:29 -07:00
Alexander Morozov
90c272a4f2 daemon/logger: fix races in channel close
it's actually not okay to do such trick from multiple goroutines

Signed-off-by: Alexander Morozov <lk4d4@docker.com>
(cherry picked from commit 378f0657f9)
2016-06-30 17:11:13 -07:00
Lei Jitang
bb09b3e5f1 Remove dm.no_warn_on_loop_devices in info warning
I think we doesn't provide dm.no_warn_on_loop_devices option
at all. I didn't found any code to handle this option.

Signed-off-by: Lei Jitang <leijitang@huawei.com>
(cherry picked from commit 2aa01e0fbc)
2016-06-30 16:58:15 -07:00
Madhu Venugopal
3657275ca0 Added missing flags for swarm-mode networks
Signed-off-by: Madhu Venugopal <madhu@docker.com>
(cherry picked from commit cf78863e9d)
2016-06-30 16:57:32 -07:00
Sebastiaan van Stijn
99cfbbc287 docs: add note that we no longer send container-id for exec
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 82876c0f5b)
2016-06-30 16:57:32 -07:00
Daniel Nephin
0d061f680a Fix mount opts error message.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit c8659d3cc5)
2016-06-30 16:57:31 -07:00
Tonis Tiigi
f492978638 Add test for force-new-cluster
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 1acb8ef825)
2016-06-30 16:57:31 -07:00
Tonis Tiigi
1f4e2f33ef Fix race on clearing swarm nodes on stop
On stop there were multiple places that marked
`cluster.node` nil. Now stop waits for the node to
set itself nil.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 1a8a473017)
2016-06-30 16:57:31 -07:00
Tonis Tiigi
7b70250d07 Switch node management tests to api types
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 22b34d6449)
2016-06-30 16:57:31 -07:00
Daniel Nephin
cd0ffa9359 Fix service update of Args
add a unit test

Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit 07b59ef210)
2016-06-30 16:57:31 -07:00
Vincent Demeester
ea03b75b20 Return an empty network list if nothing matches filter
Initializing the network list struct in order to return an empty list
instead of a nil object.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
(cherry picked from commit a7043ac5c4)
2016-06-30 16:57:31 -07:00
Adam Kunk
d83a27471b updates to fix install script for RHEL
fixing install script for rhel7

fixing install script for rhel7

Signed-off-by: Adam Kunk <adam.kunk@tiaa-cref.org>

updates to fix install script for RHEL

fixing install script for rhel7

fixing install script for rhel7

Signed-off-by: Adam Kunk <adam.kunk@tiaa-cref.org>
(cherry picked from commit 485c27e186)
2016-06-30 16:57:31 -07:00
Vincent Demeester
5c04eab617 Fixes /swarm/join endpoint documentation
The JSON payload given in the API documentation is wrong, fixing it.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
(cherry picked from commit 97c9491b0d)
2016-06-30 16:57:28 -07:00
Wonjun Kim
9ce196dc3c Add integration tests for swarm incompatible
Signed-off-by: Wonjun Kim <wonjun.kim@navercorp.com>
(cherry picked from commit d71789828f)
2016-06-30 16:57:21 -07:00
Wonjun Kim
bd572fcd8f Fix check code for --cluster-store and --cluster-advertise in config_unix.go
Signed-off-by: Wonjun Kim <wonjun.kim@navercorp.com>
(cherry picked from commit 4f671ae85e)
2016-06-30 16:57:21 -07:00
Anton Tiurin
712cbdbe1f Remove extra comma in Image Pull API examples
Signed-off-by: Anton Tiurin <noxiouz@yandex.ru>
(cherry picked from commit 307c39c187)
2016-06-30 16:57:08 -07:00
Harald Albers
4476697867 add documentation for desired_state filter
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit 36f57f7d02)
2016-06-30 16:47:53 -07:00
Anusha Ragunathan
43186c4304 Fix daemon tests.
Fix two test issues:
- pidof is not available in PATH on some Jenkins systems (rhel, centos)
  Use kill -0 instead.
- Cleanup after plugin test. This is a stop gap fix. The right way to
  fix this, is to shutdown the plugin on daemon shutdown path (except
  for the live-restore case). This will be done in a follow up PR.

Signed-off-by: Anusha Ragunathan <anusha@docker.com>
(cherry picked from commit 6d36431e23)
2016-06-30 16:47:53 -07:00
Charles Smith
79b488d5c4 remove margin from bottom of swarm mode docs
Signed-off-by: Charles Smith <charles.smith@docker.com>
(cherry picked from commit 0d6f59e85a)
2016-06-30 16:47:53 -07:00
Derek McGowan
07bd7b0128 overlay2: close read end of pipe on mount exec
Use StdinPipe to ensure pipe is properly closed after startup

Fixes #23686

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
(cherry picked from commit ad4b3e11fe)
2016-06-30 16:47:53 -07:00
Brian Goff
0f1439a065 Add ip_vs to check-config script
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 9e14002ced)
2016-06-30 16:47:52 -07:00
Akihiro Suda
cab4e108c8 update storagedriver/zfs-driver.md for Ubuntu 16.04 LTS
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
(cherry picked from commit ddf9b6f666)
2016-06-30 16:47:52 -07:00
danf
a55b4462d4 Fix Malformed history layer - missing Sprintf in append of shell in Builder run config
Signed-off-by: Dan Feldman <danf@jfrog.com>
(cherry picked from commit 29b6a69660)
2016-06-30 16:47:52 -07:00
Victoria Bialas
246d503dea added another alias for getting started stuff
Signed-off-by: Victoria Bialas <victoria.bialas@docker.com>
(cherry picked from commit 4060eb02ef)
2016-06-30 16:47:52 -07:00
Michael Crosby
795390b033 Change root_maxkeys
Most modern distros have the limit for the maximum root keys at 1000000
but some do not.  Because we are creating a new key for each container
we need to bump this up as the older distros are having this limit at
200.

Using 1000000 as the limit because that is that most distros are setting
this to now.  If someone has this value configured over that we do not
change it.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
(cherry picked from commit ca3e4545aa)
2016-06-30 16:47:52 -07:00
Michael Crosby
4dc392b213 Update containerd to b93a33be39bc4ef0fb00bfcb79147
This updates containerd to b93a33be39bc4ef0fb00bfcb79147a28c33d9d43
fixing the start sync issues.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
(cherry picked from commit 7db7e04631)
2016-06-30 16:47:52 -07:00
Anusha Ragunathan
8ac21ed2d4 Add plugin restore tests.
Also live restore is stable now. So move experimental tests out to stable.

Signed-off-by: Anusha Ragunathan <anusha@docker.com>
(cherry picked from commit 006d58d7e6)
2016-06-30 16:47:51 -07:00
Sebastiaan van Stijn
6ed3aaf3b6 deprecate '-h' shorthand and remove '-help'
This deprecates the shorthand '-h', because we don't mention it as an option,
and it conflicts with the 'docker create -h/--hostname' option, so cannot be
used for all commands consistently.

This also removes the (single-dash) '-help' option. The single-dash variant was
marked "deprecated" in version 1.5.0 (basically, since it was added in
a2b529ead2), but still kept around to
prevent '-help' being treated as '-h -e -l -p', causing confusing
warnings.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 6add695084)
2016-06-30 16:47:51 -07:00
allencloud
db9b930abc add err handling, close fd
Signed-off-by: allencloud <allen.sun@daocloud.io>
(cherry picked from commit 2281ce7e98)
2016-06-30 16:47:51 -07:00
Sebastiaan van Stijn
63ce6b60d5 Fix installation script for RHEL
the previous fix changed the lsb_dist variable
too early. We only normalize to "centos"
for the repository-location, so changing
it just before that.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 73d5f0648e)
2016-06-30 16:47:51 -07:00
Kenfe-Mickael Laventure
7a5e247d01 Use "docker-runc" as alias for the default runtime
This also moves the variable holding the default runtime name from the
engine-api repository into docker repository

Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
(cherry picked from commit 69af7d0d13)
2016-06-30 16:47:51 -07:00
Harald Albers
795560dba2 bash completion enhancements for docker {swarm,node,service}
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit e3339a75d3)
2016-06-30 16:47:51 -07:00
Dong Chen
6661a20564 Block service mode change.
Signed-off-by: Dong Chen <dongluo.chen@docker.com>
(cherry picked from commit 72f7cebfb0)
2016-06-30 16:47:50 -07:00
allencloud
a3b8c1a80e uppercase output in node inspect to keep consistency
Signed-off-by: allencloud <allen.sun@daocloud.io>
(cherry picked from commit 89582e1a34)
2016-06-30 16:47:50 -07:00
Alex Coventry
460418f035 Clarify role of dockerhost variable
Signed-off-by: Alex Coventry <alx@empirical.com>
(cherry picked from commit 8aba9fd3ec)
2016-06-30 16:47:50 -07:00
Tadej Janež
ea4d01f22f Use 'default.target' in systemd unit file example
Previously, 'local.target' was used which is not going to work
out-of-the-box for lots of users.
The 'default.target' is the default unit systemd starts at bootup and
hence what most users would be expected to use.

Signed-off-by: Tadej Janež <tadej.j@nez.si>
(cherry picked from commit 0a8c46d5e5)
2016-06-30 16:47:50 -07:00
Tonis Tiigi
e5fb8b1fb6 Disable iptables in swarm test daemons
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit caaf92f7b4)
2016-06-30 16:47:50 -07:00
Vincent Demeester
0762cce09a Update experimental docs dsb -> dab
Fixing bundle extension in experimental documentation.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
(cherry picked from commit 8af760c910)
2016-06-30 16:47:50 -07:00
Aanand Prasad
b84206157a Update bundle extension
It's now .dab, for Distributed Application Bundle

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
(cherry picked from commit 61e2d4240b)
2016-06-30 16:47:49 -07:00
Vincent Demeester
8084f80259 No need for container.Lock if rename same name
During the renaming of a container, no need to call `container.Lock()`
if `oldName == newName`.

This is a follow-up from #23360 (commit 88d1ee6c11)

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
(cherry picked from commit 7e1ec8d2bd)
2016-06-30 16:47:49 -07:00
Aaron Lehmann
77e3708bf7 Fix "sanbox" typos
Correcting "sanbox" to "sandbox".

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
(cherry picked from commit fa04558ac1)
2016-06-30 16:47:49 -07:00
Brian Goff
5ff054ef51 Fix panic due to nil bind options
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 4c2e1a9cb0)
2016-06-30 16:47:49 -07:00
allencloud
f463560ead fix error message and typos in swarm cluster
Signed-off-by: allencloud <allen.sun@daocloud.io>

(cherry picked from commit 39bc10c36d)
2016-06-30 16:47:49 -07:00
Alexander Morozov
d985acae68 cli: fix network create usage
Signed-off-by: Alexander Morozov <lk4d4@docker.com>
(cherry picked from commit 159e7341ad)
2016-06-30 16:47:49 -07:00
Aaron Lehmann
72bb109a68 Remove log warning on task update
This warning appears in the course of normal use of swarm mode. Since
it's meant more as an internal TODO than something which should be
exposed to a user, remove the log message.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
(cherry picked from commit 39c93cfb47)
2016-06-30 16:47:48 -07:00
Tonis Tiigi
1fdce39622 Add better error messages for unavailable swarm commands
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 803733120a)
2016-06-30 16:47:48 -07:00
Adam Avilla
5c1a76f8da Fixing typo on service log output.
Signed-off-by: Adam Avilla <aavilla@yp.com>
(cherry picked from commit 7cc6fe5758)
2016-06-30 16:47:48 -07:00
Charles Smith
b6b262d8bb correct output description - only one task is running on manager
Signed-off-by: Charles Smith <charles.smith@docker.com>
(cherry picked from commit 5f219806fc)
2016-06-30 16:47:48 -07:00
Sainath Grandhi
eebd6ac4df docker rename fix to address the issue of renaming with the same name issue #23319
Signed-off-by: Sainath Grandhi <sainath.grandhi@intel.com>
(cherry picked from commit 3e8c16ef6d)
2016-06-30 16:47:48 -07:00
Alexander Morozov
0ec119e727 pkg/pools: avoid copy of sync.Pool
Signed-off-by: Alexander Morozov <lk4d4@docker.com>
(cherry picked from commit ba3af336eb)
2016-06-30 16:47:48 -07:00
Tonis Tiigi
de5fd9d641 Increase test timeouts for node state changes
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 2e5da44341)
2016-06-30 16:47:47 -07:00
Tonis Tiigi
e546ffb37a Fix error reporting on executor wait
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit c895a76f10)
2016-06-30 16:47:47 -07:00
Alvaro Saurin
c2b195d3c2 Make sure we are trying to unmount a mounted /etc/resolv.conf
Signed-off-by: Alvaro Saurin <alvaro.saurin@gmail.com>
(cherry picked from commit 41cc7c4d9d)
2016-06-30 16:47:47 -07:00
Alvaro Saurin
3e6c39e2ee Make sure we compare strings with the same capitalization
Signed-off-by: Alvaro Saurin <alvaro.saurin@gmail.com>
(cherry picked from commit ded347e744)
2016-06-30 16:47:47 -07:00
Akihiro Suda
71dbab2235 update go-md2man to v1.0.5
Due to the issue of go-md2man, a numbered list in `man docker login` was not rendered correctly.
a8f937e113

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
(cherry picked from commit cfe16e0d5b)
2016-06-30 16:47:47 -07:00
Tonis Tiigi
80405bef73 Do not show tasks from down nodes as active in ls
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 5d4401d6d7)
2016-06-30 16:47:47 -07:00
John Howard
f3da720d8b Windows: Ensure frozen cope with prefix
Signed-off-by: John Howard <jhoward@microsoft.com>
(cherry picked from commit b820ead426)
2016-06-30 16:47:46 -07:00
Kenfe-Mickael Laventure
1641f5e308 Fix the value of the Components value in apt {In,}Release files
Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
(cherry picked from commit 63b0713014)
2016-06-30 16:47:46 -07:00
Derek McGowan
d99f4cc83d Fix overlay2 ignoring whiteout files
Currently when overlay creates a whiteout file then the overlay2 layer is archived,
the correct tar header will be created for the whiteout file, but the tar logic will then attempt to open the file causing a failure.
When tar encounters such failures the file is skipped and excluded for the archive, causing the whiteout to be ignored.
By skipping the copy of empty files, no open attempt will be made on whiteout files.

Fixes #23863

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
(cherry picked from commit bd13c53f8d)
2016-06-30 16:47:46 -07:00
Serhat Gülçiçek
f4de46d2ac Fix error for env variables example in docker reference - 2
The reason why the issue occurs is because sh parses the first argument after -c as the whole script to execute.
Everything after isn't executed as one might expect.

When working on the 'fix' I found out the same fix is also done in commit 2af7c5cfe2, except only for one occurrence.

Signed-off-by: Serhat Gülçiçek <serhat+signoff@equil.nl>
(cherry picked from commit 7a30fa7af6)
2016-06-30 16:47:46 -07:00
Sven Dowideit
eaf5b17592 Removing some url links that can be resolved using src markdown links
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
(cherry picked from commit 01f9cbc366)
2016-06-30 16:47:46 -07:00
David M. Karr (fullname at gmail.com)
6de8049ab5 Update work-with-networks.md
The value of the Subnet and Gateway properties didn't match the command-line argument.

Signed-off-by: David M. Karr <davidmichaelkarr@gmail.com>
(cherry picked from commit a54c3fbb8a)
2016-06-30 16:47:46 -07:00
Alessandro Boch
f312883f39 Add modules for secure overlay network to check-config.sh
Signed-off-by: Alessandro Boch <aboch@docker.com>
(cherry picked from commit c355e059cc)
2016-06-30 16:47:45 -07:00
Alessandro Boch
ada93c7182 Vendoring libnetwork ed311d0
Signed-off-by: Alessandro Boch <aboch@docker.com>
(cherry picked from commit e26c513fa8)
2016-06-30 16:47:45 -07:00
Alessandro Boch
e928358dfb Vendoring vishvananda/netlink 734d02c
Signed-off-by: Alessandro Boch <aboch@docker.com>
(cherry picked from commit 58b8b8fa15)
2016-06-30 16:47:45 -07:00
Antonio Murdaca
ae0648fca8 contrib: builder: rpm: add Fedora 24
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
(cherry picked from commit 0b83f27328)
2016-06-30 16:47:45 -07:00
orkaa
d4cdc5172c For the lulz! (evalulate -> evaluate)
Signed-off-by: orkaa <orkica@gmail.com>
(cherry picked from commit 1bdbfa5770)
2016-06-30 16:47:45 -07:00
Steve Durrheimer
790501e51f Add zsh completion for 'load' and 'save' image events
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
(cherry picked from commit e2f1f699b3)
2016-06-30 16:47:45 -07:00
Steve Durrheimer
7b643a09d5 Add zsh completion for 'docker {create,run,network connect} --link-local-ip'
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
(cherry picked from commit 704d9b6864)
2016-06-30 16:47:44 -07:00
Steve Durrheimer
c6721ada67 Add zsh completion for 'docker run --storage-opt size='
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
(cherry picked from commit 857e7d6ae4)
2016-06-30 16:47:44 -07:00
Steve Durrheimer
3714e9b393 Re-Add zsh completion for '-c' alias to '--cpu-shares'
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
(cherry picked from commit 4c23ac0ae5)
2016-06-30 16:47:44 -07:00
Steve Durrheimer
27d64ec8ad Add zsh completion for 'docker daemon --storage-driver (overlay2|vfs)'
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
(cherry picked from commit 8e582a2573)
2016-06-30 16:47:44 -07:00
Tonis Tiigi
43014ea54b Fix opts tests after default port fix
The code for default port was already there but
it didn’t work because split function errored out
before. This should be the desired behavior that
matches daemon listen address with swarm listen
address.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 0a4a0d9800)
2016-06-30 16:47:44 -07:00
Tonis Tiigi
3d06cd4910 Unify swarm init and update options
Add api side validation and defaults for init and
join requests.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit fb3eb1c27e)
2016-06-30 16:47:44 -07:00
Stefan J. Wernli
8691607ade Fixing file handle leak for "docker logs"
If "docker logs" was used on an offline container, the logger is leaked, leaving it up to the finalizer to close the file handle, which could block removal of the container.  Further, the json file logger could leak an open handle if the logs are read without follow due to an early return without a close.  This change addresses both cases.

Signed-off-by: Stefan J. Wernli <swernli@microsoft.com>
(cherry picked from commit 54f11b84d2)
2016-06-30 16:47:44 -07:00
Kai Qiang Wu(Kennan)
db4153cc90 Fix the missing 'ls'
Signed-off-by: Kai Qiang Wu(Kennan) <wkq5325@gmail.com>
(cherry picked from commit cbd240581f)
2016-06-30 16:47:43 -07:00
Shishir Mahajan
c984a6cdc8 Man page fix: Mention supported drivers for --storage-opt size option in docker create/run
Signed-off-by: Shishir Mahajan <shishir.mahajan@redhat.com>
(cherry picked from commit dc3fdfbe5b)
2016-06-30 16:47:43 -07:00
Eric Barch
060b6c4363 Add missing words in the swarm docs
Signed-off-by: Eric Barch <barch@tomesoftware.com>
(cherry picked from commit db158e9182)
2016-06-30 16:47:43 -07:00
bin liu
6a5a150722 fix some typos
Signed-off-by: bin liu <liubin0329@gmail.com>
(cherry picked from commit 950073aabb)
2016-06-30 16:47:43 -07:00
nick
62d50a3f13 Fix misspell typos
Signed-off-by: nick <nicholasrusso@icloud.com>
(cherry picked from commit 7135afa79b)
2016-06-30 16:47:43 -07:00
Aaron Lehmann
56d53adab1 Fix parallel push of the same image to different registries
Layer uploads are deduplicated by a "key" made up of the layer DiffID
and the repository name. The repository name being used to form this key
was a remote version of the name that didn't include the name of the
registry. Consequently, pushes of the same layer in a repository with
the same remote name to different registries would wrongly be
deduplicated.

Correct the key by using the full name of the repository, which includes
the registry hostname as well as the image's name.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
(cherry picked from commit 1333ef3ca3)
2016-06-30 16:47:43 -07:00
Ken Cochrane
8f677417d1 Fixed issue #23787 install script not working for RHEL7 host
Signed-off-by: Ken Cochrane <kencochrane@gmail.com>
(cherry picked from commit 0e84474672)
2016-06-30 16:47:42 -07:00
Sven Dowideit
3d5903a45f Now that the Docker4 links are online, we can use them
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
(cherry picked from commit 587d2f9d81)
2016-06-30 16:47:42 -07:00
Akihiro Suda
ba77356d98 Add overlay2 description to overlayfs-driver.md
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
(cherry picked from commit 8625adbd67)
2016-06-30 16:47:42 -07:00
Harald Albers
5604fb2362 bash completion for -c alias to --cpu-shares
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit 79296b2770)
2016-06-30 16:47:42 -07:00
Harald Albers
b23562ea20 bash completion for docker daemon --storage-opt btrfs.min_space
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit f693b99870)
2016-06-30 16:47:42 -07:00
Harald Albers
0afeae16de bash completion for docker {run,create} --storage-opt
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit 7d2ffa00c0)
2016-06-30 16:47:42 -07:00
Harald Albers
749f94e885 bash completion for load and save image events
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit 5334520bf0)
2016-06-30 16:47:41 -07:00
Harald Albers
5188629256 bash completion for docker daemon --storage-driver olverlay2
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit d96eeff194)
2016-06-30 16:47:41 -07:00
Harald Albers
9117c0dd41 bash completion for docker {run,create,network connect} --link-local-ip
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit 98483f57ed)
2016-06-30 16:47:41 -07:00
Steve Durrheimer
7d72ca329c Add zsh completion for 'docker daemon --live-restore'
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
(cherry picked from commit 8036fc794a)
2016-06-30 16:47:41 -07:00
Kenfe-Mickael Laventure
df987c84de Fix missing container runtime on upgrade
Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
(cherry picked from commit d7ceda4e37)
2016-06-30 16:47:41 -07:00
Shishir Mahajan
16274ba940 PR 19367 doc change: Mention supported drivers for --storage-opt size option in docker create/run.
Signed-off-by: Shishir Mahajan <shishir.mahajan@redhat.com>
(cherry picked from commit fe6416d04f)
2016-06-30 16:47:41 -07:00
Kevin Jing Qiu
c7d35daad8 Fix some doc typos and spacings
Signed-off-by: Kevin Jing Qiu <kevin@idempotent.ca>
(cherry picked from commit 3b2ee9a704)
2016-06-30 16:47:40 -07:00
Sebastiaan van Stijn
1b053efb5a Sync API changes from 1.25 -> 1.24
commit 79e1d3877a
updated the v1.25 API with changes in SwarmKit,
but these changes should apply to v1.24.

This updates the 1.24 API with the same changes.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 93095b1699)
2016-06-30 16:47:40 -07:00
Charles Smith
803b4d30a6 clarify swarm concept
Signed-off-by: Charles Smith <charles.smith@docker.com>
(cherry picked from commit e029631713)
2016-06-30 16:47:40 -07:00
Lei Jitang
c51fced060 Fix link doesn't work sometimes when container start with multiple networks
If there is multiple networks to connect to on container starting,
the order of these networks is random because we "range a map". But
the defautl network "bridge" should be connected first since only
"bridge" support link and we should have do some settings on sandbox
creation, and only the first connect will setting the sandbox.

Signed-off-by: Lei Jitang <leijitang@huawei.com>
(cherry picked from commit 57c0a653e3)
2016-06-30 16:47:40 -07:00
Derek McGowan
aa123b73d1 Add tests for rmi
Add integration test for removing by image id with tag and digest reference to the same repository.
Add integration test to ensure only tag to other repository remains after deleting tag with accompanying digest reference.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
(cherry picked from commit 5cff374b14)
2016-06-30 16:47:39 -07:00
Derek McGowan
611bbed2df Update rmi logic for canonical references
Updates the rmi code to treat canonical references as related to tagged references from the same repository during deletion.
Canonical references with a different repository name will be treated as separate references.
Updates the remove by ID logic to still remove an image if there is a single tag reference and only canonical references to the same repository remaining.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
(cherry picked from commit a281be1c11)
2016-06-30 16:47:39 -07:00
Daniel Nephin
8a93ff1ffb Remove dead code from node update.
Also share mode code between update commands
and use flag constants

Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit cacaeab9db)
2016-06-30 16:47:39 -07:00
Daniel Nephin
0641b8ba9c Use flag constants for swarm flag.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit c08a50dbd1)
2016-06-30 16:47:39 -07:00
Daniel Nephin
7b0bb3f92c add more flag constants to service update.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit 9d1f3373b3)
2016-06-30 16:47:39 -07:00
Charles Smith
3f276b58a5 update docker swarm cli
Signed-off-by: Charles Smith <charles.smith@docker.com>
(cherry picked from commit 78ebfaff1a)
2016-06-30 16:47:39 -07:00
Harald Albers
75193e58ce fix bash completion for docker {swarm,node} subcommands
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit e65f036e13)
2016-06-30 16:47:38 -07:00
Harald Albers
a4e3415235 correct sort order in new bash completions
The completion for the new `docker service`, `docker swarm` and
`docker node` command families were partly added in non-alphabetical
order.

Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit 19753ec84d)
2016-06-30 16:47:38 -07:00
Mike Goelzer
b8b938c61c Add bash completion for docker service scale
Signed-off-by: Mike Goelzer <mike.goelzer@docker.com>

Conflicts:
	contrib/completion/bash/docker

(cherry picked from commit 5cf73a47f5)
2016-06-30 16:47:38 -07:00
Sebastiaan van Stijn
2899866c63 Fix markdown formatting for experimental
- don't indent code-blocks that use fences
- fixed some formatting
- wrapped long lines to 80-chars
- removed stray empty lines

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit a92f6551c9)
2016-06-30 16:47:38 -07:00
Michael Friis
9157831d16 bring dab into title and intro
Signed-off-by: Michael Friis <friism@gmail.com>
(cherry picked from commit 6defef5619)
2016-06-30 16:47:38 -07:00
Michael Friis
cabc0df64d expand stack doc
Signed-off-by: Michael Friis <friism@gmail.com>
(cherry picked from commit cd2237c2bc)
2016-06-30 16:47:38 -07:00
Zhang Wei
c40debc362 Make --help information consistent
Other docker command always print "[OPTIONS]" right after `docker
COMMAND`, but `build` and `push` has inconsistent help message.

This commit will fix help information format.

Signed-off-by: Zhang Wei <zhangwei555@huawei.com>
(cherry picked from commit 0e53976336)
2016-06-30 16:47:37 -07:00
Charles Smith
1784366694 add links to swarm and service commands to swarm mode overview
Signed-off-by: Charles Smith <charles.smith@docker.com>
(cherry picked from commit c13c560196)
2016-06-30 16:47:37 -07:00
Tonis Tiigi
8a994d4724 Don’t try to restore swarm from incomplete state
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit ded1d9af38)
2016-06-30 16:47:37 -07:00
Yong Tang
abb6e38208 Fix reference link error in Jenkins docs failure
This fix fixes one of the Jenkins docs failure:
https://jenkins.dockerproject.org/job/docs-docker-pr/9754/

There are 7 errors. This fix addresses one:
`* link error: (in page engine/userguide/storagedriver/device-mapper-driver.md) ../../reference/commandline/dockerd/#storage-driver-options`

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
(cherry picked from commit 1cb7fb4d2e)
2016-06-30 16:47:37 -07:00
Yong Tang
92ffbd2b52 Fix docker start error with renamed container
This fix tries to fix the issue raised in #23716 where `docker start`
causes an error of `No such container:` if the container has been
renamed before `docker start` returns.

The issue is that `docker start` use container name passed at the
beginning to check for exit code at the end of the `docker start`.

This fix addresses the issue by always use container's `ID` to get
the information during `docker start`.

Additional integration tests have been added to cover this fix.

This fix fixes #23716.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
(cherry picked from commit 6e86733b47)
2016-06-30 16:47:37 -07:00
Jana Radhakrishnan
2b45db42b2 Vendoring @ab62dd6bf06bf0
Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
(cherry picked from commit 663159e9ac)
2016-06-30 16:47:37 -07:00
Charles Chan
fe2a9bea38 Update help output to match Docker 1.11.
* Also touch up headings.

Signed-off-by: Charles Chan <charleswhchan@users.noreply.github.com>
(cherry picked from commit 17ae6539f6)
2016-06-18 20:58:25 -07:00
Charles Smith
dd39dbe79c update cli commands output for rc1, revise key concepts
Signed-off-by: Charles Smith <charles.smith@docker.com>
(cherry picked from commit 9499d5fd52)
2016-06-18 20:58:25 -07:00
Sven Dowideit
e8e1fbb72f bad url
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
(cherry picked from commit c2495dbe6d)
2016-06-18 20:58:25 -07:00
Sven Dowideit
d546db33ff href links are not converted from file.md by hugo
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
(cherry picked from commit ad1819ca1d)
2016-06-18 20:58:24 -07:00
Daniel Nephin
3c35da6029 Add tests for AutoAcceptOption
Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit c544649874)
2016-06-18 20:58:24 -07:00
Daniel Nephin
e090e2dbd4 Support a listen addr without a port, and add tests.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit 595e79b805)
2016-06-18 20:58:24 -07:00
Victor Vieux
6b2cd6e843 accept uppercase endpoint mode
Signed-off-by: Victor Vieux <vieux@docker.com>
(cherry picked from commit 8a0c5f1578)
2016-06-18 20:58:24 -07:00
Jana Radhakrishnan
0728f28cbe Vendoring swarmkit @3f135f206179e
Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
(cherry picked from commit 64a567d24c)
2016-06-18 20:58:24 -07:00
tomwbarlow@gmail.com
9e1d592de3 Change log.printf service creation msg to debug
Signed-off-by: tomwbarlow@gmail.com <tomwbarlow@gmail.com>
(cherry picked from commit eed8d14ed1)
2016-06-18 20:58:24 -07:00
Arnaud Porterie (icecrime)
f1f2461e09 Change docker service update semantics
Change `docker service update` to replace attributes of the target
service rather than augment them. One particular occurrence where the
previous behavior proved problematic is when trying to update a port
mapping: the merge semantics provided no way of removing published
ports, but strictly of adding more.

The utility merge* functions where renamed accordingly to update*.

Signed-off-by: Arnaud Porterie (icecrime) <arnaud.porterie@docker.com>
(cherry picked from commit 1f8ab93b44)
2016-06-18 20:58:23 -07:00
Harald Albers
ffb4daf0fb fix bash completion for docker service subcommands
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit 42f3b1f4ad)
2016-06-18 20:58:23 -07:00
Sebastiaan van Stijn
d5c89ec65e Add initial "service" docs
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit f565bc7ec9)
2016-06-18 20:58:23 -07:00
Victoria Bialas
d1c91b41e1 install mac and win pared down, link to d4mac, d4win, getting started improved, added menu.md's
fixing links to d4mac and d4win

updates per @Sven and @theJeztah, fixing links, typos, menu definitions, etc.

updates per Sven's comments in the PR

removed duplicate line in tutorials menu.md

Signed-off-by: Victoria Bialas <victoria.bialas@docker.com>
(cherry picked from commit 5bc730eda7)
2016-06-18 20:58:23 -07:00
Tibor Vass
906eacd586 Bump VERSION to v1.12.0-rc2
Signed-off-by: Tibor Vass <tibor@docker.com>
2016-06-17 16:28:45 -04:00
Tibor Vass
02772750e1 Merge pull request #23594 from tiborvass/changelog-fix
v1.12.0: some fixes to CHANGELOG
2016-06-17 13:25:47 -07:00
Tibor Vass
2ba4108c95 Merge pull request #23661 from tiborvass/cherry-picks-for-1.12.0-rc2
Cherry picks for 1.12.0 rc2
2016-06-17 13:20:10 -07:00
Tibor Vass
11daa3e417 v1.12.0: some fixes to CHANGELOG
Signed-off-by: Tibor Vass <tibor@docker.com>
2016-06-17 13:18:49 -07:00
Arnaud Porterie (icecrime)
944a8f16c7 Improve docker service inspect --pretty
Remove capitalization in placement, and remove spurious `\t`.

Signed-off-by: Arnaud Porterie (icecrime) <arnaud.porterie@docker.com>
(cherry picked from commit 3c60b7b984)
2016-06-17 13:09:13 -07:00
Tibor Vass
70bd46293a plugins: fix name handling for legacy plugins
Signed-off-by: Tibor Vass <tibor@docker.com>
(cherry picked from commit 8ba17b4823)
2016-06-17 13:09:13 -07:00
Vincent Demeester
6eaac7be89 Update plugin command with defaulttag
This way, you don't have to specify the ":latest" tag for some command
and not for others

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
(cherry picked from commit cb321e82db)
2016-06-17 13:09:13 -07:00
Tibor Vass
2ae7330140 plugins: fix usage for plugin commands
Signed-off-by: Tibor Vass <tibor@docker.com>
(cherry picked from commit 65ed9daf70)
2016-06-17 13:09:12 -07:00
Tibor Vass
1cfd620124 plugins: remove automatic mounting of a state dir
Signed-off-by: Tibor Vass <tibor@docker.com>
(cherry picked from commit 15ff9de658)
2016-06-17 13:09:12 -07:00
Tibor Vass
f24e5d79bc plugins: install should not automatically accept all permissions
Signed-off-by: Tibor Vass <tibor@docker.com>
(cherry picked from commit 4b70d4561e)
2016-06-17 13:09:12 -07:00
Jana Radhakrishnan
a7c925cba4 Vendoring libnetwork @13be89d1cf79
Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
(cherry picked from commit 70e2585d54)
2016-06-17 13:09:12 -07:00
Tonis Tiigi
bbc214fa9b Add newline to promote/demote message
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 3386e3570a)
2016-06-17 13:09:12 -07:00
Jana Radhakrishnan
8fdc925338 Check if DriverState is valid before using in convert
Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
(cherry picked from commit 4fea81154b)
2016-06-17 13:09:12 -07:00
Anil Madhavapeddy
4b2883fac6 docker swarm: more consistent CLI help
This changes `docker swarm help` to be consistent with capitals
and removes full stops.

Before:

```
Commands:
  init        Initialize a Swarm.
  join        Join a Swarm as a node and/or manager.
  update      update the Swarm.
  leave       Leave a Swarm.
  inspect     Inspect the Swarm
```

After:

```
Commands:
  init        Initialize a Swarm
  join        Join a Swarm as a node and/or manager
  update      Update the Swarm
  leave       Leave a Swarm
  inspect     Inspect the Swarm
```

Signed-off-by: Anil Madhavapeddy <anil@docker.com>
(cherry picked from commit 0ec78739ac)
2016-06-17 13:09:12 -07:00
Ke Xu
05c32e7f08 fix #23017, add zsh completion for dockerd
Signed-off-by: Ke Xu <leonhartx.k@gmail.com>
(cherry picked from commit bd1fc1e5c2)
2016-06-17 13:09:11 -07:00
Anusha Ragunathan
82608cd4ce Add disable flag for plugin Install.
Signed-off-by: Anusha Ragunathan <anusha@docker.com>
(cherry picked from commit 22e781e8e3)
2016-06-17 13:09:11 -07:00
Harald Albers
fcdd4d4a52 bash completion for docker swarm update --cert-expiry
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit dc2fc75d42)
2016-06-17 13:09:11 -07:00
Justin Cormack
445f4f2f3e Missing line feed after accepting node
Avoids
```
root@swarmatorium:~# docker node accept 3dnh6k13o44np9fwl83e7gh80
Node 3dnh6k13o44np9fwl83e7gh80 accepted in the swarm.root@swarmatorium:~#
```

Signed-off-by: Justin Cormack <justin.cormack@docker.com>
(cherry picked from commit c34d752419)
2016-06-17 13:09:11 -07:00
Justin Cormack
b7a1f1a2d1 fix invalid typo in error message
Signed-off-by: Justin Cormack <justin.cormack@docker.com>
(cherry picked from commit 2322ccb8ff)
2016-06-17 13:09:11 -07:00
Charles Smith
aa1f241894 update output for node commands, minor edits
Signed-off-by: Charles Smith <charles.smith@docker.com>
(cherry picked from commit 3b2132c2c7)
2016-06-16 23:36:59 -07:00
Daniel Nephin
fb364d86fa Add some tests for bundlefile and improve the error messages for LoadFile
Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit c0ea589c1b)
2016-06-16 23:36:59 -07:00
Victor Vieux
d747fbc95c improve some messages in the node subcmds
Signed-off-by: Victor Vieux <vieux@docker.com>
(cherry picked from commit eb962235fb)
2016-06-16 23:36:58 -07:00
Tonis Tiigi
0efe76c62b Add integration test for parsing swarm update options
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit a933ac3c27)
2016-06-16 23:36:58 -07:00
Tonis Tiigi
ab6abb799b Add cert-expiry to swarm update
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 7d8d51aa9d)
2016-06-16 23:36:58 -07:00
Brian Goff
9647e4d6cc Implement plugin restore after daemon restart
This ensures that:

- The in-memory plugin store is populated with all the plugins
- Plugins which were active before daemon restart are active after.
  This utilizes the liverestore feature when available, otherwise it
  manually starts the plugin.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit dfd9187305)
2016-06-16 23:36:58 -07:00
Brian Goff
be82ff5c7f Fix removing plugins
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 5e156fd3d4)
2016-06-16 23:36:58 -07:00
Tonis Tiigi
efc10a92ef Fix removing containers on leaving from pending state
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 826f6f0703)
2016-06-16 23:36:58 -07:00
Victor Vieux
3635938b00 add some more fields in docker service inspect -p
Signed-off-by: Victor Vieux <vieux@docker.com>
(cherry picked from commit 4c9e21b674)
2016-06-16 23:36:58 -07:00
Tonis Tiigi
0cac3c4c23 Return membership status on join without timeout
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 1973cee0cd)
2016-06-16 23:36:57 -07:00
Tonis Tiigi
01c5b208e8 Update daemon to new swarmkit
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit a83bba467a)
2016-06-16 23:36:57 -07:00
Tonis Tiigi
528f3ab668 Update swarmkit to 310f1119
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 2783568284)
2016-06-16 23:36:57 -07:00
Derek McGowan
56b253fb3c Do not show empty tags for digest references in output
When a repository has a tag and digests, show tag for each digest value.
Do not duplicate rows for the same image name with both tag and digest.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
(cherry picked from commit 79eada3814)
2016-06-16 23:36:57 -07:00
Sebastiaan van Stijn
b3387e96ed fix "overlay" -> "overlay2" in error message
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 2d4b285a75)
2016-06-16 23:36:57 -07:00
Vincent Demeester
42f445565d Update ServiceInspectWithRaw
Signed-off-by: Vincent Demeester <vincent@sbr.pm>
(cherry picked from commit 4a70cb5571)
2016-06-16 23:36:57 -07:00
Vincent Demeester
d9d24b6605 Update docker plugin install code…
… for more consistency (api side).

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
(cherry picked from commit 2c82337b04)
2016-06-16 23:36:57 -07:00
Vincent Demeester
7444bb2c25 Bump engine-api to c57d0447ea1ae71f6dad83c8d8a1215a89869a0c
Signed-off-by: Vincent Demeester <vincent@sbr.pm>
(cherry picked from commit 58529a1553)
2016-06-16 23:36:56 -07:00
Sebastiaan van Stijn
fdb5324595 Update docker info output example
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 07e1c62bf4)
2016-06-16 23:36:56 -07:00
Madhu Venugopal
ed01cfc6db Vendoring Libnetwork caf22bd9a6a53dfe91b0266274155bc69235e8ed
* fixes https://github.com/docker/docker/issues/23622
* fixes a memory leak issue with bulk sync
* fixes external DNS resolution issue after live restore

Signed-off-by: Madhu Venugopal <madhu@docker.com>
(cherry picked from commit 7f2f6ed0d6)
2016-06-16 23:36:56 -07:00
Justin Cormack
213adac2ea Add the seccomp build tag for s390x now runc updated
This was waiting for runc bump see https://github.com/docker/docker/issues/23171
runc was bumped in https://github.com/docker/docker/pull/23603

Fixes #23171

Signed-off-by: Justin Cormack <justin.cormack@docker.com>
(cherry picked from commit f8d970169a)
2016-06-16 23:36:56 -07:00
Lei Jitang
83fbaa3cb4 Fix restore active sandbox
we store the active sandbox after daemon.containerd.Restore, but there
is a chance the `Restore` will set the container to exit see
(https://github.com/docker/docker/blob/master/libcontainerd/client_linux.go#L469).
so we should check if the container is really running before add it to
activesandbox.

Signed-off-by: Lei Jitang <leijitang@huawei.com>
(cherry picked from commit 78f3094518)
2016-06-16 23:36:56 -07:00
Yong Tang
1ad7b517fa Add security info to docker info
The security infomation has already been added to `GET /info` in #21172.
However, it is not part of the output of `docker info` yet.

This fix adds the security information to `docker info`.

Additional tests has been added to cover changes.

This fix fixes #23500. This fix is related to #20909, #21172.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
(cherry picked from commit eee20b564f)
2016-06-16 23:36:56 -07:00
Stephen J Day
6c3d080e83 api/client/service: fix minor spelling error in service inspect
Signed-off-by: Stephen J Day <stephen.day@docker.com>
(cherry picked from commit 920e65ccbc)
2016-06-16 23:36:55 -07:00
Kai Qiang Wu(Kennan)
82453c84ba Add ecryptfs check for overlay2
We added docs about ecryptfs check but not in code side.
Also refactor code to make it clean.

Signed-off-by: Kai Qiang Wu(Kennan) <wkqwu@cn.ibm.com>
(cherry picked from commit 136323b043)
2016-06-16 23:36:55 -07:00
Sebastiaan van Stijn
c73e56fd7f docs: move "advisory" to general metadata
the advisory option should not be
below "menu"

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit aadd88c306)
2016-06-16 23:36:55 -07:00
Sainath Grandhi
1e5ee1dd37 Fixes #23596-returns empty string for NetworkID in response to /containers/json
Signed-off-by: Sainath Grandhi <sainath.grandhi@intel.com>
(cherry picked from commit ad85d29906)
2016-06-16 23:36:55 -07:00
Stephen J Day
c3015a22cb api/client/service: list running services over replicas
To provide users a view of service status, list the number of running
task over the requested number of replicas.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
(cherry picked from commit b86cb293ec)
2016-06-16 23:36:55 -07:00
Tonis Tiigi
01d26abd5c Add api tests for secret update
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit aed7667bee)
2016-06-16 23:36:55 -07:00
Victor Vieux
7f4bca0f90 use same hash for same secret
Signed-off-by: Victor Vieux <vieux@docker.com>
(cherry picked from commit a579ce8ed3)
2016-06-16 23:36:55 -07:00
Victor Vieux
7cc76facba fix docker swarm init/update --secret
Signed-off-by: Victor Vieux <vieux@docker.com>
(cherry picked from commit 085895342c)
2016-06-16 23:36:54 -07:00
Michael Crosby
e491dbb38a Set systemd KillMode
Change the kill mode to process so that systemd does not kill container
processes when the daemon is shutdown but only the docker daemon

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
(cherry picked from commit db435f526a)
2016-06-16 23:36:54 -07:00
Sebastiaan van Stijn
dd5573bc60 Update release script installation instructions
Instructions for installing the static binaries
has changed, so updated the instructions.

The comment on top already requires the steps
to be executed as root, so removing the 'sudo'

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit e233c8dd72)
2016-06-16 23:36:54 -07:00
Daniel Nephin
bb8996d62b Fix String() for some option types
and add unit tests for them.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit c26e7d8c91)
2016-06-16 23:36:54 -07:00
Adrian Moisey
2401c0223d Fix URL to issue
Signed-off-by: Adrian Moisey <adrian@changeover.za.net>
(cherry picked from commit 88d4da7f42)
2016-06-16 23:36:54 -07:00
Sven Dowideit
af3b1c7370 advisory can't be in the [menu] section
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
(cherry picked from commit bc033cb706)
2016-06-16 23:36:54 -07:00
Thomas Leonard
979ad07925 Remove out-of-date health test
The test was waiting for the container to exit after failing its
healthcheck. However, we no longer automatically terminate containers,
so this waited instead for the container to time-out by itself.

Signed-off-by: Thomas Leonard <thomas.leonard@docker.com>
(cherry picked from commit 18a59bb869)
2016-06-16 23:36:53 -07:00
Anusha Ragunathan
3e44703cae Add basic integration tests for plugins.
Signed-off-by: Anusha Ragunathan <anusha@docker.com>
(cherry picked from commit a2d48c9e4e)
2016-06-16 23:36:53 -07:00
Anusha Ragunathan
c82f23095a Add accept-permissions flag for install.
Signed-off-by: Anusha Ragunathan <anusha@docker.com>
(cherry picked from commit ec4857da48)
2016-06-16 23:36:53 -07:00
Arnaud Porterie (icecrime)
187b6607a4 Service --update-parallelism defauls to 0
The `--update-parallelism` flaag should default to 0, which is
interpreted by the backend as unlimited. In other words, by default all
services should update simultaneously.

Signed-off-by: Arnaud Porterie (icecrime) <arnaud.porterie@docker.com>
(cherry picked from commit f22d0174f3)
2016-06-16 23:36:53 -07:00
Tom Barlow
5e41ec703d Change 'invald' to 'invalid' in volume opts errors
Signed-off-by: Tom Barlow <tomwbarlow@gmail.com>
(cherry picked from commit bf988fc6ff)
2016-06-16 23:36:53 -07:00
Justin Cormack
ad28216987 Move mlock back into the default ungated seccomp profile
Do not gate with CAP_IPC_LOCK as unprivileged use is now
allowed in Linux. This returns it to how it was in 1.11.

Fixes #23587

Signed-off-by: Justin Cormack <justin.cormack@docker.com>
(cherry picked from commit bdf01cf5de)
2016-06-16 23:36:53 -07:00
Anusha Ragunathan
d2a9560e71 Avoid back and forth conversion between strings and bytes.
Signed-off-by: Anusha Ragunathan <anusha@docker.com>
(cherry picked from commit d6d448aab8)
2016-06-16 23:36:53 -07:00
Daniel Nephin
41d72e28c3 Change SCALE to REPLICAS.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
(cherry picked from commit 4df0349948)
2016-06-16 23:36:52 -07:00
Kenfe-Mickael Laventure
e324ec639b Fix TestRunWithRuntime* on arm
Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
(cherry picked from commit bd6317031b)
2016-06-16 23:36:52 -07:00
Michael Crosby
cdb04519e2 Update libnetwork to 96d45528599c32354230480a1ebc0
This fixes an issue with hanging tests due to store.

Fixes #23560

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
(cherry picked from commit e927df7b4b)
2016-06-16 23:36:52 -07:00
Victoria Bialas
38305cb676 fixing links and merge conflicts
updated link to faqs/troubleshoot to point to new location /toolbox

fixed docker-for-mac link error

Signed-off-by: Victoria Bialas <victoria.bialas@docker.com>
(cherry picked from commit 1d80f8ca19)
2016-06-16 23:36:52 -07:00
Shoubhik Bose
2b36087597 Fixes #23376 Broken URL for Centos yum repo for docker changed to now have a hardcoded centos version 7
Signed-off-by: Shoubhik Bose <sbose78@gmail.com>
(cherry picked from commit d3fad80cc7)
2016-06-16 23:36:52 -07:00
Sven Dowideit
7af9b80f23 fix links for #23505
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
(cherry picked from commit fa94eb4604)
2016-06-16 23:36:52 -07:00
Madhu Venugopal
60a86590aa Use service alias and configure container's --net-alias
Signed-off-by: Madhu Venugopal <madhu@docker.com>
(cherry picked from commit 07e39e9e72)
2016-06-16 23:36:51 -07:00
Madhu Venugopal
588b76c2a1 Vendoring swarmkit 682e0b69be208176d6055cba855a5e9cf15c7cb4
Signed-off-by: Madhu Venugopal <madhu@docker.com>
(cherry picked from commit 5af8e2a9c0)
2016-06-16 23:36:51 -07:00
Madhu Venugopal
f1d5c3374c Vendoring libnetwork 0d517a9e4e5cbdb889b3257eebd2351addcd46d4
Signed-off-by: Madhu Venugopal <madhu@docker.com>
(cherry picked from commit f2f97f768e)
2016-06-16 23:36:51 -07:00
Tonis Tiigi
ca16a4c81d Fix hanging integration tests
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 22f827abab)
2016-06-16 23:36:51 -07:00
Antonio Murdaca
034d555d30 daemon: allow tmpfs to trump over VOLUME(s)
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
(cherry picked from commit 756f6cef4a)
2016-06-16 23:36:51 -07:00
Akihiro Suda
ce6211d252 Update experimental/README.md
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
(cherry picked from commit fec98ecca1)
2016-06-16 23:36:51 -07:00
Victoria Bialas
92ff142da2 surfacing Learn by example topics to top level of Docker Engine docs
fixing links after moving surfacing tutorials

fixing more links for the newly located tutorials

WIP: merging 3 getting started tutorials into one to cover all platforms, added new files

fixing broken images and links in 3-in-1 getting started, re-wrote story flow, linked to Toolbox, d4mac,d4win

Signed-off-by: Victoria Bialas <victoria.bialas@docker.com>
(cherry picked from commit 0254c12e1e)
2016-06-16 23:36:50 -07:00
Drew Erny
c816f2c905 Remove the node leader column, show leader as status.
Removes the leader column from node ls and shows whether a node is the
leader in the manager status column instead.

Signed-off-by: Drew Erny <drew.erny@docker.com>
(cherry picked from commit 4104c1dc13)
2016-06-16 23:36:50 -07:00
Michael Crosby
be67aae778 Merge pull request #23627 from mlaventure/update-runc-on-1.12
Update runc on 1.12
2016-06-16 14:30:54 -07:00
Kenfe-Mickael Laventure
301f3d776f Revert build-{deb,rpm} to cloning from opencontainers repository
Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
2016-06-16 09:06:53 -07:00
Kenfe-Mickael Laventure
9eb6e049bd Vendor in runc cc29e3dded8e27ba8f65738f40d251c885030a28
Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
(cherry picked from commit b675124cf5)
2016-06-16 08:46:23 -07:00
Tibor Vass
1f136c1f85 bump VERSION to v1.12.0-rc1
Signed-off-by: Tibor Vass <tibor@docker.com>
2016-06-15 00:36:32 -04:00
Michael Crosby
c293f64dc0 Merge pull request #23564 from tiborvass/fix-changelog
v1.12.0: fixed date in CHANGELOG to be in YYYY-MM-DD form
2016-06-14 21:35:39 -07:00
Tibor Vass
adbf43fe10 v1.12.0: fixed date in CHANGELOG to be in YYYY-MM-DD form
Signed-off-by: Tibor Vass <tibor@docker.com>
2016-06-14 21:33:17 -07:00
Arnaud Porterie
aab402731f Merge pull request #23563 from icecrime/i_trust_crosbymichael
Use runc from crosbymichael's fork
2016-06-15 04:14:13 +00:00
Arnaud Porterie (icecrime)
e2b7f648f4 Use runc from crosbymichael's fork
Signed-off-by: Arnaud Porterie (icecrime) <arnaud.porterie@docker.com>
2016-06-14 21:10:38 -07:00
Tibor Vass
427738d81f v1.12.0: first draft of CHANGELOG additions
Signed-off-by: Tibor Vass <tibor@docker.com>
2016-06-14 20:12:57 -07:00
399 changed files with 12224 additions and 4539 deletions

View File

@@ -5,6 +5,133 @@ 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.
## 1.12.0 (2016-07-14)
### Builder
+ New `HEALTHCHECK` Dockerfile instruction to support user-defined healthchecks [#23218](https://github.com/docker/docker/pull/23218)
+ New `SHELL` Dockerfile instruction to specify the default shell when using the shell form for commands in a Dockerfile [#22489](https://github.com/docker/docker/pull/22489)
+ Add `#escape=` Dockerfile directive to support platform-specific parsing of file paths in Dockerfile [#22268](https://github.com/docker/docker/pull/22268)
+ Add support for comments in `.dockerignore` [#23111](https://github.com/docker/docker/pull/23111)
* Support for UTF-8 in Dockerfiles [#23372](https://github.com/docker/docker/pull/23372)
* Skip UTF-8 BOM bytes from `Dockerfile` and `.dockerignore` if exist [#23234](https://github.com/docker/docker/pull/23234)
* Windows: support for `ARG` to match Linux [#22508](https://github.com/docker/docker/pull/22508)
- Fix error message when building using a daemon with the bridge network disabled [#22932](https://github.com/docker/docker/pull/22932)
### Contrib
* Enable seccomp for Centos 7 and Oracle Linux 7 [#22344](https://github.com/docker/docker/pull/22344)
- Remove MountFlags in systemd unit to allow shared mount propagation [#22806](https://github.com/docker/docker/pull/22806)
### Distribution
+ Add `--max-concurrent-downloads` and `--max-concurrent-uploads` daemon flags useful for situations where network connections don't support multiple downloads/uploads [#22445](https://github.com/docker/docker/pull/22445)
* Registry operations now honor the `ALL_PROXY` environment variable [#22316](https://github.com/docker/docker/pull/22316)
* Provide more information to the user on `docker load` [#23377](https://github.com/docker/docker/pull/23377)
### Logging
+ Syslog logging driver now supports DGRAM sockets [#21613](https://github.com/docker/docker/pull/21613)
+ Add `--details` option to `docker logs` to also display log tags [#21889](https://github.com/docker/docker/pull/21889)
+ Enable syslog logger to have access to env and labels [#21724](https://github.com/docker/docker/pull/21724)
+ An additional syslog-format option `rfc5424micro` to allow microsecond resolution in syslog timestamp [#21844](https://github.com/docker/docker/pull/21844)
* Inherit the daemon log options when creating containers [#21153](https://github.com/docker/docker/pull/21153)
* Remove `docker/` prefix from log messages tag and replace it with `{{.DaemonName}}` so that users have the option of changing the prefix [#22384](https://github.com/docker/docker/pull/22384)
### Networking
+ Built-in Virtual-IP based internal and ingress load-balancing using IPVS [#23361](https://github.com/docker/docker/pull/23361)
+ Secured multi-host overlay networking using encrypted control-plane and Data-plane [#23361](https://github.com/docker/docker/pull/23361)
+ MacVlan driver is out of experimental [#23524](https://github.com/docker/docker/pull/23524)
+ Add `driver` filter to `network ls` [#22319](https://github.com/docker/docker/pull/22319)
+ Adding `network` filter to `docker ps --filter` [#23300](https://github.com/docker/docker/pull/23300)
+ Add `--link-local-ip` flag to `create`, `run` and `network connect` to specify a container's link-local address [#23415](https://github.com/docker/docker/pull/23415)
+ Add network label filter support [#21495](https://github.com/docker/docker/pull/21495)
* Removed dependency on external KV-Store for Overlay networking in Swarm-Mode [#23361](https://github.com/docker/docker/pull/23361)
* Add container's short-id as default network alias [#21901](https://github.com/docker/docker/pull/21901)
* `run` options `--dns` and `--net=host` are no longer mutually exclusive [#22408](https://github.com/docker/docker/pull/22408)
- Fix DNS issue when renaming containers with generated names [#22716](https://github.com/docker/docker/pull/22716)
- Allow both `network inspect -f {{.Id}}` and `network inspect -f {{.ID}}` to address inconsistency with inspect output [#23226](https://github.com/docker/docker/pull/23226)
### Plugins (experimental)
+ New `plugin` command to manager plugins with `install`, `enable`, `disable`, `rm`, `inspect`, `set` subcommands [#23446](https://github.com/docker/docker/pull/23446)
### Remote API (v1.24) & Client
+ Split the binary into two: `docker` (client) and `dockerd` (daemon) [#20639](https://github.com/docker/docker/pull/20639)
+ Add `before` and `since` filters to `docker images --filter` [#22908](https://github.com/docker/docker/pull/22908)
+ Add `--limit` option to `docker search` [#23107](https://github.com/docker/docker/pull/23107)
+ Add `--filter` option to `docker search` [#22369](https://github.com/docker/docker/pull/22369)
+ Add security options to `docker info` output [#21172](https://github.com/docker/docker/pull/21172) [#23520](https://github.com/docker/docker/pull/23520)
+ Add insecure registries to `docker info` output [#20410](https://github.com/docker/docker/pull/20410)
+ Extend Docker authorization with TLS user information [#21556](https://github.com/docker/docker/pull/21556)
+ devicemapper: expose Mininum Thin Pool Free Space through `docker info` [#21945](https://github.com/docker/docker/pull/21945)
* API now returns a JSON object when an error occurs making it more consistent [#22880](https://github.com/docker/docker/pull/22880)
- Prevent `docker run -i --restart` from hanging on exit [#22777](https://github.com/docker/docker/pull/22777)
- Fix API/CLI discrepancy on hostname validation [#21641](https://github.com/docker/docker/pull/21641)
- Fix discrepancy in the format of sizes in `stats` from HumanSize to BytesSize [#21773](https://github.com/docker/docker/pull/21773)
- authz: when request is denied return forbbiden exit code (403) [#22448](https://github.com/docker/docker/pull/22448)
### Runtime
+ Add `--live-restore` daemon flag to keep containers running when daemon shuts down, and regain control on startup [#23213](https://github.com/docker/docker/pull/23213)
+ Ability to add OCI-compatible runtimes (via `--add-runtime` daemon flag) and select one with `--runtime` on `create` and `run` [#22983](https://github.com/docker/docker/pull/22983)
+ New `overlay2` graphdriver for Linux 4.0+ with multiple lower directory support [#22126](https://github.com/docker/docker/pull/22126)
+ New load/save image events [#22137](https://github.com/docker/docker/pull/22137)
+ Add support for reloading daemon configuration through systemd [#22446](https://github.com/docker/docker/pull/22446)
+ Add disk quota support for btrfs [#19651](https://github.com/docker/docker/pull/19651)
+ Add disk quota support for zfs [#21946](https://github.com/docker/docker/pull/21946)
+ Add support for `docker run --pid=container:<id>` [#22481](https://github.com/docker/docker/pull/22481)
+ Align default seccomp profile with selected capabilities [#22554](https://github.com/docker/docker/pull/22554)
+ Add a `daemon reload` event when the daemon reloads its configuration [#22590](https://github.com/docker/docker/pull/22590)
+ Add `trace` capability in the pprof profiler to show execution traces in binary form [#22715](https://github.com/docker/docker/pull/22715)
+ Add a `detach` event [#22898](https://github.com/docker/docker/pull/22898)
+ Add support for setting sysctls with `--sysctl` [#19265](https://github.com/docker/docker/pull/19265)
+ Add `--storage-opt` flag to `create` and `run` allowing to set `size` on devicemapper [#19367](https://github.com/docker/docker/pull/19367)
* Undeprecate the `-c` short alias of `--cpu-shares` on `run`, `build`, `create`, `update` [#22621](https://github.com/docker/docker/pull/22621)
* Prevent from using aufs and overlay graphdrivers on an eCryptfs mount [#23121](https://github.com/docker/docker/pull/23121)
- Fix issues with tmpfs mount ordering [#22329](https://github.com/docker/docker/pull/22329)
- Created containers are no longer listed on `docker ps -a -f exited=0` [#21947](https://github.com/docker/docker/pull/21947)
- Fix an issue where containers are stuck in a "Removal In Progress" state [#22423](https://github.com/docker/docker/pull/22423)
- Fix bug that was returning an HTTP 500 instead of a 400 when not specifying a command on run/create [#22762](https://github.com/docker/docker/pull/22762)
- Fix bug with `--detach-keys` whereby input matching a prefix of the detach key was not preserved [#22943](https://github.com/docker/docker/pull/22943)
- SELinux labeling is now disabled when using `--privileged` mode [#22993](https://github.com/docker/docker/pull/22993)
- If volume-mounted into a container, `/etc/hosts`, `/etc/resolv.conf`, `/etc/hostname` are no longer SELinux-relabeled [#22993](https://github.com/docker/docker/pull/22993)
- Fix inconsistency in `--tmpfs` behavior regarding mount options [#22438](https://github.com/docker/docker/pull/22438)
- Fix an issue where daemon hangs at startup [#23148](https://github.com/docker/docker/pull/23148)
- Ignore SIGPIPE events to prevent journald restarts to crash docker in some cases [#22460](https://github.com/docker/docker/pull/22460)
- Containers are not removed from stats list on error [#20835](https://github.com/docker/docker/pull/20835)
- Fix `on-failure` restart policy when daemon restarts [#20853](https://github.com/docker/docker/pull/20853)
- Fix an issue with `stats` when a container is using another container's network [#21904](https://github.com/docker/docker/pull/21904)
### Swarm Mode
+ New `swarm` command to manage swarms with `init`, `join`, `leave`, `update` subcommands [#23361](https://github.com/docker/docker/pull/23361)
+ New `service` command to manage swarm-wide services with `create`, `inspect`, `update`, `remove`, `tasks` subcommands [#23361](https://github.com/docker/docker/pull/23361)
+ New `node` command to manage nodes with `accept`, `promote`, `demote`, `inspect`, `update`, `tasks`, `ls` and `rm` subcommands [#23361](https://github.com/docker/docker/pull/23361)
+ (experimental) New `stack` and `deploy` commands to manage and deploy multi-service applications [#23522](https://github.com/docker/docker/pull/23522)
### Volume
+ Add support for local and global volume scopes (analogous to network scopes) [#22077](https://github.com/docker/docker/pull/22077)
+ Allow volume drivers to provide a `Status` field [#21006](https://github.com/docker/docker/pull/21006)
+ Add name/driver filter support for volume [#21361](https://github.com/docker/docker/pull/21361)
* Mount/Unmount operations now receives an opaque ID to allow volume drivers to differentiate between two callers [#21015](https://github.com/docker/docker/pull/21015)
- Fix issue preventing to remove a volume in a corner case [#22103](https://github.com/docker/docker/pull/22103)
- Windows: Enable auto-creation of host-path to match Linux [#22094](https://github.com/docker/docker/pull/22094)
### DEPRECATION
* Environment variables `DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE` and `DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE` have been renamed
to `DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE` and `DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE` respectively [#22574](https://github.com/docker/docker/pull/22574)
* Remove deprecated `syslog-tag`, `gelf-tag`, `fluentd-tag` log option in favor of the more generic `tag` one [#22620](https://github.com/docker/docker/pull/22620)
* Remove deprecated feature of passing HostConfig at API container start [#22570](https://github.com/docker/docker/pull/22570)
* Remove deprecated `-f`/`--force` flag on docker tag [#23090](https://github.com/docker/docker/pull/23090)
* Remove deprecated `/containers/<id|name>/copy` endpoint [#22149](https://github.com/docker/docker/pull/22149)
* Remove deprecated `docker ps` flags `--since` and `--before` [#22138](https://github.com/docker/docker/pull/22138)
* Deprecate the old 3-args form of `docker import` [#23273](https://github.com/docker/docker/pull/23273)
## 1.11.2 (2016-05-31)
### Networking
@@ -116,7 +243,7 @@ be found.
### Misc
+ When saving linked images together with `docker save` a subsequent `docker load` will correctly restore their parent/child relationship ([#21385](https://github.com/docker/docker/pull/c))
+ When saving linked images together with `docker save` a subsequent `docker load` will correctly restore their parent/child relationship ([#21385](https://github.com/docker/docker/pull/21385))
+ Support for building the Docker cli for OpenBSD was added ([#21325](https://github.com/docker/docker/pull/21325))
+ Labels can now be applied at network, volume and image creation ([#21270](https://github.com/docker/docker/pull/21270))
* The `dockremap` is now created as a system user ([#21266](https://github.com/docker/docker/pull/21266))

View File

@@ -217,7 +217,7 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
# Download man page generator
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone --depth 1 -b v1.0.4 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
&& git clone --depth 1 -b v1.0.5 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
&& git clone --depth 1 -b v1.4 https://github.com/russross/blackfriday.git "$GOPATH/src/github.com/russross/blackfriday" \
&& go get -v -d github.com/cpuguy83/go-md2man \
&& go build -v -o /usr/local/bin/go-md2man github.com/cpuguy83/go-md2man \
@@ -233,10 +233,10 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install runc
ENV RUNC_COMMIT 5ce88a95f6cf218ba7f3309562f95464a968e890
ENV RUNC_COMMIT cc29e3dded8e27ba8f65738f40d251c885030a28
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
&& git checkout -q "$RUNC_COMMIT" \
&& make static BUILDTAGS="seccomp apparmor selinux" \
@@ -244,7 +244,7 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install containerd
ENV CONTAINERD_COMMIT 860f3a94940894ac0a106eff4bd1616a67407ee2
ENV CONTAINERD_COMMIT 1b3a81545ca79456086dc2aa424357be98b962ee
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \

View File

@@ -164,7 +164,7 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
# Download man page generator
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone --depth 1 -b v1.0.4 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
&& git clone --depth 1 -b v1.0.5 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
&& git clone --depth 1 -b v1.4 https://github.com/russross/blackfriday.git "$GOPATH/src/github.com/russross/blackfriday" \
&& go get -v -d github.com/cpuguy83/go-md2man \
&& go build -v -o /usr/local/bin/go-md2man github.com/cpuguy83/go-md2man \
@@ -180,10 +180,10 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install runc
ENV RUNC_COMMIT 5ce88a95f6cf218ba7f3309562f95464a968e890
ENV RUNC_COMMIT cc29e3dded8e27ba8f65738f40d251c885030a28
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
&& git checkout -q "$RUNC_COMMIT" \
&& make static BUILDTAGS="seccomp apparmor selinux" \
@@ -191,7 +191,7 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install containerd
ENV CONTAINERD_COMMIT 860f3a94940894ac0a106eff4bd1616a67407ee2
ENV CONTAINERD_COMMIT 1b3a81545ca79456086dc2aa424357be98b962ee
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \

View File

@@ -173,7 +173,7 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
# Download man page generator
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone --depth 1 -b v1.0.4 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
&& git clone --depth 1 -b v1.0.5 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
&& git clone --depth 1 -b v1.4 https://github.com/russross/blackfriday.git "$GOPATH/src/github.com/russross/blackfriday" \
&& go get -v -d github.com/cpuguy83/go-md2man \
&& go build -v -o /usr/local/bin/go-md2man github.com/cpuguy83/go-md2man \
@@ -189,10 +189,10 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install runc
ENV RUNC_COMMIT 5ce88a95f6cf218ba7f3309562f95464a968e890
ENV RUNC_COMMIT cc29e3dded8e27ba8f65738f40d251c885030a28
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
&& git checkout -q "$RUNC_COMMIT" \
&& make static BUILDTAGS="seccomp apparmor selinux" \
@@ -200,7 +200,7 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install containerd
ENV CONTAINERD_COMMIT 860f3a94940894ac0a106eff4bd1616a67407ee2
ENV CONTAINERD_COMMIT 1b3a81545ca79456086dc2aa424357be98b962ee
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \

View File

@@ -74,10 +74,10 @@ WORKDIR /go/src/github.com/docker/docker
ENV DOCKER_BUILDTAGS apparmor seccomp selinux
# Install runc
ENV RUNC_COMMIT 5ce88a95f6cf218ba7f3309562f95464a968e890
ENV RUNC_COMMIT cc29e3dded8e27ba8f65738f40d251c885030a28
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
&& git checkout -q "$RUNC_COMMIT" \
&& make static BUILDTAGS="seccomp apparmor selinux" \
@@ -85,7 +85,7 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install containerd
ENV CONTAINERD_COMMIT 860f3a94940894ac0a106eff4bd1616a67407ee2
ENV CONTAINERD_COMMIT 1b3a81545ca79456086dc2aa424357be98b962ee
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \

View File

@@ -188,7 +188,7 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
# Download man page generator
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone --depth 1 -b v1.0.4 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
&& git clone --depth 1 -b v1.0.5 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
&& git clone --depth 1 -b v1.4 https://github.com/russross/blackfriday.git "$GOPATH/src/github.com/russross/blackfriday" \
&& go get -v -d github.com/cpuguy83/go-md2man \
&& go build -v -o /usr/local/bin/go-md2man github.com/cpuguy83/go-md2man \
@@ -204,10 +204,10 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install runc
ENV RUNC_COMMIT 5ce88a95f6cf218ba7f3309562f95464a968e890
ENV RUNC_COMMIT cc29e3dded8e27ba8f65738f40d251c885030a28
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
&& git checkout -q "$RUNC_COMMIT" \
&& make static BUILDTAGS="apparmor seccomp selinux" \
@@ -215,7 +215,7 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install containerd
ENV CONTAINERD_COMMIT 860f3a94940894ac0a106eff4bd1616a67407ee2
ENV CONTAINERD_COMMIT 1b3a81545ca79456086dc2aa424357be98b962ee
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \

View File

@@ -161,7 +161,7 @@ RUN useradd --create-home --gid docker unprivilegeduser
VOLUME /var/lib/docker
WORKDIR /go/src/github.com/docker/docker
ENV DOCKER_BUILDTAGS apparmor selinux
ENV DOCKER_BUILDTAGS apparmor selinux seccomp
# Let us use a .bashrc file
RUN ln -sfv $PWD/.bashrc ~/.bashrc
@@ -181,7 +181,7 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
# Download man page generator
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone --depth 1 -b v1.0.4 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
&& git clone --depth 1 -b v1.0.5 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
&& git clone --depth 1 -b v1.4 https://github.com/russross/blackfriday.git "$GOPATH/src/github.com/russross/blackfriday" \
&& go get -v -d github.com/cpuguy83/go-md2man \
&& go build -v -o /usr/local/bin/go-md2man github.com/cpuguy83/go-md2man \
@@ -197,10 +197,10 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install runc
ENV RUNC_COMMIT 5ce88a95f6cf218ba7f3309562f95464a968e890
ENV RUNC_COMMIT cc29e3dded8e27ba8f65738f40d251c885030a28
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
&& git checkout -q "$RUNC_COMMIT" \
&& make static BUILDTAGS="seccomp apparmor selinux" \
@@ -208,7 +208,7 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install containerd
ENV CONTAINERD_COMMIT 860f3a94940894ac0a106eff4bd1616a67407ee2
ENV CONTAINERD_COMMIT 1b3a81545ca79456086dc2aa424357be98b962ee
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \

View File

@@ -57,10 +57,10 @@ ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
ENV CGO_LDFLAGS -L/lib
# Install runc
ENV RUNC_COMMIT 5ce88a95f6cf218ba7f3309562f95464a968e890
ENV RUNC_COMMIT cc29e3dded8e27ba8f65738f40d251c885030a28
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
&& git checkout -q "$RUNC_COMMIT" \
&& make static BUILDTAGS="seccomp apparmor selinux" \
@@ -68,7 +68,7 @@ RUN set -x \
&& rm -rf "$GOPATH"
# Install containerd
ENV CONTAINERD_COMMIT 860f3a94940894ac0a106eff4bd1616a67407ee2
ENV CONTAINERD_COMMIT 1b3a81545ca79456086dc2aa424357be98b962ee
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \

View File

@@ -1 +1 @@
1.12.0-dev
1.12.0-rc3

View File

@@ -4,8 +4,8 @@ package bundlefile
import (
"encoding/json"
"fmt"
"io"
"os"
)
// Bundlefile stores the contents of a bundlefile
@@ -34,19 +34,28 @@ type Port struct {
}
// LoadFile loads a bundlefile from a path to the file
func LoadFile(path string) (*Bundlefile, error) {
reader, err := os.Open(path)
if err != nil {
return nil, err
}
func LoadFile(reader io.Reader) (*Bundlefile, error) {
bundlefile := &Bundlefile{}
if err := json.NewDecoder(reader).Decode(bundlefile); err != nil {
decoder := json.NewDecoder(reader)
if err := decoder.Decode(bundlefile); err != nil {
switch jsonErr := err.(type) {
case *json.SyntaxError:
return nil, fmt.Errorf(
"JSON syntax error at byte %v: %s",
jsonErr.Offset,
jsonErr.Error())
case *json.UnmarshalTypeError:
return nil, fmt.Errorf(
"Unexpected type at byte %v. Expected %s but received %s.",
jsonErr.Offset,
jsonErr.Type,
jsonErr.Value)
}
return nil, err
}
return bundlefile, err
return bundlefile, nil
}
// Print writes the contents of the bundlefile to the output writer

View File

@@ -0,0 +1,79 @@
// +build experimental
package bundlefile
import (
"bytes"
"strings"
"testing"
"github.com/docker/docker/pkg/testutil/assert"
)
func TestLoadFileV01Success(t *testing.T) {
reader := strings.NewReader(`{
"Version": "0.1",
"Services": {
"redis": {
"Image": "redis@sha256:4b24131101fa0117bcaa18ac37055fffd9176aa1a240392bb8ea85e0be50f2ce",
"Networks": ["default"]
},
"web": {
"Image": "dockercloud/hello-world@sha256:fe79a2cfbd17eefc344fb8419420808df95a1e22d93b7f621a7399fd1e9dca1d",
"Networks": ["default"],
"User": "web"
}
}
}`)
bundle, err := LoadFile(reader)
assert.NilError(t, err)
assert.Equal(t, bundle.Version, "0.1")
assert.Equal(t, len(bundle.Services), 2)
}
func TestLoadFileSyntaxError(t *testing.T) {
reader := strings.NewReader(`{
"Version": "0.1",
"Services": unquoted string
}`)
_, err := LoadFile(reader)
assert.Error(t, err, "syntax error at byte 37: invalid character 'u'")
}
func TestLoadFileTypeError(t *testing.T) {
reader := strings.NewReader(`{
"Version": "0.1",
"Services": {
"web": {
"Image": "redis",
"Networks": "none"
}
}
}`)
_, err := LoadFile(reader)
assert.Error(t, err, "Unexpected type at byte 94. Expected []string but received string")
}
func TestPrint(t *testing.T) {
var buffer bytes.Buffer
bundle := &Bundlefile{
Version: "0.1",
Services: map[string]Service{
"web": {
Image: "image",
Command: []string{"echo", "something"},
},
},
}
assert.NilError(t, Print(&buffer, bundle))
output := buffer.String()
assert.Contains(t, output, "\"Image\": \"image\"")
assert.Contains(t, output,
`"Command": [
"echo",
"something"
]`)
}

View File

@@ -15,7 +15,7 @@ type diffOptions struct {
container string
}
// NewDiffCommand creats a new cobra.Command for `docker diff`
// NewDiffCommand creates a new cobra.Command for `docker diff`
func NewDiffCommand(dockerCli *client.DockerCli) *cobra.Command {
var opts diffOptions

View File

@@ -18,7 +18,7 @@ type restartOptions struct {
containers []string
}
// NewRestartCommand creats a new cobra.Command for `docker restart`
// NewRestartCommand creates a new cobra.Command for `docker restart`
func NewRestartCommand(dockerCli *client.DockerCli) *cobra.Command {
var opts restartOptions
@@ -41,7 +41,8 @@ func runRestart(dockerCli *client.DockerCli, opts *restartOptions) error {
ctx := context.Background()
var errs []string
for _, name := range opts.containers {
if err := dockerCli.Client().ContainerRestart(ctx, name, time.Duration(opts.nSeconds)*time.Second); err != nil {
timeout := time.Duration(opts.nSeconds) * time.Second
if err := dockerCli.Client().ContainerRestart(ctx, name, &timeout); err != nil {
errs = append(errs, err.Error())
} else {
fmt.Fprintf(dockerCli.Out(), "%s\n", name)

View File

@@ -20,7 +20,7 @@ type rmOptions struct {
containers []string
}
// NewRmCommand creats a new cobra.Command for `docker rm`
// NewRmCommand creates a new cobra.Command for `docker rm`
func NewRmCommand(dockerCli *client.DockerCli) *cobra.Command {
var opts rmOptions

View File

@@ -63,8 +63,9 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error {
return err
}
// We always use c.ID instead of container to maintain consistency during `docker start`
if !c.Config.Tty {
sigc := dockerCli.ForwardAllSignals(ctx, container)
sigc := dockerCli.ForwardAllSignals(ctx, c.ID)
defer signal.StopCatch(sigc)
}
@@ -86,7 +87,7 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error {
in = dockerCli.In()
}
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, container, options)
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, c.ID, options)
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
// ContainerAttach return an ErrPersistEOF (connection closed)
// means server met an error and put it in Hijacked connection
@@ -103,7 +104,7 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error {
})
// 3. Start the container.
if err := dockerCli.Client().ContainerStart(ctx, container, types.ContainerStartOptions{}); err != nil {
if err := dockerCli.Client().ContainerStart(ctx, c.ID, types.ContainerStartOptions{}); err != nil {
cancelFun()
<-cErr
return err
@@ -111,14 +112,14 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error {
// 4. Wait for attachment to break.
if c.Config.Tty && dockerCli.IsTerminalOut() {
if err := dockerCli.MonitorTtySize(ctx, container, false); err != nil {
if err := dockerCli.MonitorTtySize(ctx, c.ID, false); err != nil {
fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err)
}
}
if attchErr := <-cErr; attchErr != nil {
return attchErr
}
_, status, err := getExitCode(dockerCli, ctx, container)
_, status, err := getExitCode(dockerCli, ctx, c.ID)
if err != nil {
return err
}

View File

@@ -43,7 +43,8 @@ func runStop(dockerCli *client.DockerCli, opts *stopOptions) error {
var errs []string
for _, container := range opts.containers {
if err := dockerCli.Client().ContainerStop(ctx, container, time.Duration(opts.time)*time.Second); err != nil {
timeout := time.Duration(opts.time) * time.Second
if err := dockerCli.Client().ContainerStop(ctx, container, &timeout); err != nil {
errs = append(errs, err.Error())
} else {
fmt.Fprintf(dockerCli.Out(), "%s\n", container)

View File

@@ -18,7 +18,7 @@ type topOptions struct {
args []string
}
// NewTopCommand creats a new cobra.Command for `docker top`
// NewTopCommand creates a new cobra.Command for `docker top`
func NewTopCommand(dockerCli *client.DockerCli) *cobra.Command {
var opts topOptions

View File

@@ -15,7 +15,7 @@ type waitOptions struct {
containers []string
}
// NewWaitCommand creats a new cobra.Command for `docker wait`
// NewWaitCommand creates a new cobra.Command for `docker wait`
func NewWaitCommand(dockerCli *client.DockerCli) *cobra.Command {
var opts waitOptions

View File

@@ -155,6 +155,10 @@ func (ctx ContainerContext) Write() {
ctx.postformat(tmpl, &containerContext{})
}
func isDangling(image types.Image) bool {
return len(image.RepoTags) == 1 && image.RepoTags[0] == "<none>:<none>" && len(image.RepoDigests) == 1 && image.RepoDigests[0] == "<none>@<none>"
}
func (ctx ImageContext) Write() {
switch ctx.Format {
case tableFormatKey:
@@ -200,42 +204,98 @@ virtual_size: {{.Size}}
}
for _, image := range ctx.Images {
images := []*imageContext{}
if isDangling(image) {
images = append(images, &imageContext{
trunc: ctx.Trunc,
i: image,
repo: "<none>",
tag: "<none>",
digest: "<none>",
})
} else {
repoTags := map[string][]string{}
repoDigests := map[string][]string{}
repoTags := image.RepoTags
repoDigests := image.RepoDigests
if len(repoTags) == 1 && repoTags[0] == "<none>:<none>" && len(repoDigests) == 1 && repoDigests[0] == "<none>@<none>" {
// dangling image - clear out either repoTags or repoDigests so we only show it once below
repoDigests = []string{}
}
// combine the tags and digests lists
tagsAndDigests := append(repoTags, repoDigests...)
for _, repoAndRef := range tagsAndDigests {
repo := "<none>"
tag := "<none>"
digest := "<none>"
if !strings.HasPrefix(repoAndRef, "<none>") {
ref, err := reference.ParseNamed(repoAndRef)
for _, refString := range append(image.RepoTags) {
ref, err := reference.ParseNamed(refString)
if err != nil {
continue
}
repo = ref.Name()
switch x := ref.(type) {
case reference.Canonical:
digest = x.Digest().String()
case reference.NamedTagged:
tag = x.Tag()
if nt, ok := ref.(reference.NamedTagged); ok {
repoTags[ref.Name()] = append(repoTags[ref.Name()], nt.Tag())
}
}
imageCtx := &imageContext{
trunc: ctx.Trunc,
i: image,
repo: repo,
tag: tag,
digest: digest,
for _, refString := range append(image.RepoDigests) {
ref, err := reference.ParseNamed(refString)
if err != nil {
continue
}
if c, ok := ref.(reference.Canonical); ok {
repoDigests[ref.Name()] = append(repoDigests[ref.Name()], c.Digest().String())
}
}
for repo, tags := range repoTags {
digests := repoDigests[repo]
// Do not display digests as their own row
delete(repoDigests, repo)
if !ctx.Digest {
// Ignore digest references, just show tag once
digests = nil
}
for _, tag := range tags {
if len(digests) == 0 {
images = append(images, &imageContext{
trunc: ctx.Trunc,
i: image,
repo: repo,
tag: tag,
digest: "<none>",
})
continue
}
// Display the digests for each tag
for _, dgst := range digests {
images = append(images, &imageContext{
trunc: ctx.Trunc,
i: image,
repo: repo,
tag: tag,
digest: dgst,
})
}
}
}
// Show rows for remaining digest only references
for repo, digests := range repoDigests {
// If digests are displayed, show row per digest
if ctx.Digest {
for _, dgst := range digests {
images = append(images, &imageContext{
trunc: ctx.Trunc,
i: image,
repo: repo,
tag: "<none>",
digest: dgst,
})
}
} else {
images = append(images, &imageContext{
trunc: ctx.Trunc,
i: image,
repo: repo,
tag: "<none>",
})
}
}
}
for _, imageCtx := range images {
err = ctx.contextFormat(tmpl, imageCtx)
if err != nil {
return

View File

@@ -301,7 +301,6 @@ func TestImageContextWrite(t *testing.T) {
},
`REPOSITORY TAG IMAGE ID CREATED SIZE
image tag1 imageID1 24 hours ago 0 B
image <none> imageID1 24 hours ago 0 B
image tag2 imageID2 24 hours ago 0 B
<none> <none> imageID3 24 hours ago 0 B
`,
@@ -312,7 +311,7 @@ image tag2 imageID2 24 hours ago
Format: "table {{.Repository}}",
},
},
"REPOSITORY\nimage\nimage\nimage\n<none>\n",
"REPOSITORY\nimage\nimage\n<none>\n",
},
{
ImageContext{
@@ -322,7 +321,6 @@ image tag2 imageID2 24 hours ago
Digest: true,
},
`REPOSITORY DIGEST
image <none>
image sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
image <none>
<none> <none>
@@ -335,7 +333,7 @@ image <none>
Quiet: true,
},
},
"REPOSITORY\nimage\nimage\nimage\n<none>\n",
"REPOSITORY\nimage\nimage\n<none>\n",
},
{
ImageContext{
@@ -344,7 +342,7 @@ image <none>
Quiet: true,
},
},
"imageID1\nimageID1\nimageID2\nimageID3\n",
"imageID1\nimageID2\nimageID3\n",
},
{
ImageContext{
@@ -355,8 +353,7 @@ image <none>
Digest: true,
},
`REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
image tag1 <none> imageID1 24 hours ago 0 B
image <none> sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf imageID1 24 hours ago 0 B
image tag1 sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf imageID1 24 hours ago 0 B
image tag2 <none> imageID2 24 hours ago 0 B
<none> <none> <none> imageID3 24 hours ago 0 B
`,
@@ -369,7 +366,7 @@ image tag2 <none>
},
Digest: true,
},
"imageID1\nimageID1\nimageID2\nimageID3\n",
"imageID1\nimageID2\nimageID3\n",
},
// Raw Format
{
@@ -384,12 +381,6 @@ image_id: imageID1
created_at: %s
virtual_size: 0 B
repository: image
tag: <none>
image_id: imageID1
created_at: %s
virtual_size: 0 B
repository: image
tag: tag2
image_id: imageID2
@@ -402,7 +393,7 @@ image_id: imageID3
created_at: %s
virtual_size: 0 B
`, expectedTime, expectedTime, expectedTime, expectedTime),
`, expectedTime, expectedTime, expectedTime),
},
{
ImageContext{
@@ -413,13 +404,6 @@ virtual_size: 0 B
},
fmt.Sprintf(`repository: image
tag: tag1
digest: <none>
image_id: imageID1
created_at: %s
virtual_size: 0 B
repository: image
tag: <none>
digest: sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
image_id: imageID1
created_at: %s
@@ -439,7 +423,7 @@ image_id: imageID3
created_at: %s
virtual_size: 0 B
`, expectedTime, expectedTime, expectedTime, expectedTime),
`, expectedTime, expectedTime, expectedTime),
},
{
ImageContext{
@@ -449,7 +433,6 @@ virtual_size: 0 B
},
},
`image_id: imageID1
image_id: imageID1
image_id: imageID2
image_id: imageID3
`,
@@ -461,7 +444,7 @@ image_id: imageID3
Format: "{{.Repository}}",
},
},
"image\nimage\nimage\n<none>\n",
"image\nimage\n<none>\n",
},
{
ImageContext{
@@ -470,7 +453,7 @@ image_id: imageID3
},
Digest: true,
},
"image\nimage\nimage\n<none>\n",
"image\nimage\n<none>\n",
},
}

View File

@@ -28,7 +28,7 @@ func New(client client.APIClient, noResolve bool) *IDResolver {
func (r *IDResolver) get(ctx context.Context, t interface{}, id string) (string, error) {
switch t.(type) {
case swarm.Node:
node, err := r.client.NodeInspect(ctx, id)
node, _, err := r.client.NodeInspectWithRaw(ctx, id)
if err != nil {
return id, nil
}
@@ -40,7 +40,7 @@ func (r *IDResolver) get(ctx context.Context, t interface{}, id string) (string,
}
return id, nil
case swarm.Service:
service, err := r.client.ServiceInspect(ctx, id)
service, _, err := r.client.ServiceInspectWithRaw(ctx, id)
if err != nil {
return id, nil
}

View File

@@ -67,7 +67,7 @@ func NewBuildCommand(dockerCli *client.DockerCli) *cobra.Command {
}
cmd := &cobra.Command{
Use: "build PATH | URL | -",
Use: "build [OPTIONS] PATH | URL | -",
Short: "Build an image from a Dockerfile",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {

View File

@@ -14,7 +14,7 @@ import (
// NewPushCommand creates a new `docker push` command
func NewPushCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "push NAME[:TAG]",
Use: "push [OPTIONS] NAME[:TAG]",
Short: "Push an image or a repository to a registry",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {

View File

@@ -41,7 +41,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
// print a warning if devicemapper is using a loopback file
if pair[0] == "Data loop file" {
fmt.Fprintln(cli.err, " WARNING: Usage of loopback devices is strongly discouraged for production use. Either use `--storage-opt dm.thinpooldev` or use `--storage-opt dm.no_warn_on_loop_devices=true` to suppress this warning.")
fmt.Fprintln(cli.err, " WARNING: Usage of loopback devices is strongly discouraged for production use. Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.")
}
}
@@ -94,6 +94,10 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
fmt.Fprintf(cli.out, "Default Runtime: %s\n", info.DefaultRuntime)
}
fmt.Fprintf(cli.out, "Security Options:")
ioutils.FprintfIfNotEmpty(cli.out, " %s", strings.Join(info.SecurityOptions, " "))
fmt.Fprintf(cli.out, "\n")
ioutils.FprintfIfNotEmpty(cli.out, "Kernel Version: %s\n", info.KernelVersion)
ioutils.FprintfIfNotEmpty(cli.out, "Operating System: %s\n", info.OperatingSystem)
ioutils.FprintfIfNotEmpty(cli.out, "OSType: %s\n", info.OSType)

View File

@@ -86,10 +86,10 @@ func (cli *DockerCli) inspectAll(ctx context.Context, getSize bool) inspect.GetR
}
return nil, nil, err
}
return i, rawImage, err
return i, rawImage, nil
}
return nil, nil, err
}
return c, rawContainer, err
return c, rawContainer, nil
}
}

View File

@@ -28,7 +28,7 @@ func newConnectCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "connect [OPTIONS] NETWORK CONTAINER",
Short: "Connects a container to a network",
Short: "Connect a container to a network",
Args: cli.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
opts.network = args[0]

View File

@@ -40,7 +40,7 @@ func newCreateCommand(dockerCli *client.DockerCli) *cobra.Command {
}
cmd := &cobra.Command{
Use: "create",
Use: "create [OPTIONS] NETWORK",
Short: "Create a network",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {

View File

@@ -19,7 +19,7 @@ func newDisconnectCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "disconnect [OPTIONS] NETWORK CONTAINER",
Short: "Disconnects container from a network",
Short: "Disconnect a container from a network",
Args: cli.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
opts.network = args[0]

View File

@@ -19,7 +19,7 @@ func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "inspect [OPTIONS] NETWORK [NETWORK...]",
Short: "Displays detailed information on one or more networks",
Short: "Display detailed information on one or more networks",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.names = args

View File

@@ -7,34 +7,25 @@ import (
"github.com/docker/docker/cli"
"github.com/docker/engine-api/types/swarm"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func newAcceptCommand(dockerCli *client.DockerCli) *cobra.Command {
var flags *pflag.FlagSet
cmd := &cobra.Command{
return &cobra.Command{
Use: "accept NODE [NODE...]",
Short: "Accept a node in the swarm",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runAccept(dockerCli, flags, args)
return runAccept(dockerCli, args)
},
}
flags = cmd.Flags()
return cmd
}
func runAccept(dockerCli *client.DockerCli, flags *pflag.FlagSet, args []string) error {
for _, id := range args {
if err := runUpdate(dockerCli, id, func(node *swarm.Node) {
node.Spec.Membership = swarm.NodeMembershipAccepted
}); err != nil {
return err
}
fmt.Println(id, "attempting to accept a node in the swarm.")
func runAccept(dockerCli *client.DockerCli, nodes []string) error {
accept := func(node *swarm.Node) {
node.Spec.Membership = swarm.NodeMembershipAccepted
}
return nil
success := func(nodeID string) {
fmt.Fprintf(dockerCli.Out(), "Node %s accepted in the swarm.\n", nodeID)
}
return updateNodes(dockerCli, nodes, accept, success)
}

View File

@@ -7,34 +7,25 @@ import (
"github.com/docker/docker/cli"
"github.com/docker/engine-api/types/swarm"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func newDemoteCommand(dockerCli *client.DockerCli) *cobra.Command {
var flags *pflag.FlagSet
cmd := &cobra.Command{
return &cobra.Command{
Use: "demote NODE [NODE...]",
Short: "Demote a node from manager in the swarm",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runDemote(dockerCli, flags, args)
return runDemote(dockerCli, args)
},
}
flags = cmd.Flags()
return cmd
}
func runDemote(dockerCli *client.DockerCli, flags *pflag.FlagSet, args []string) error {
for _, id := range args {
if err := runUpdate(dockerCli, id, func(node *swarm.Node) {
node.Spec.Role = swarm.NodeRoleWorker
}); err != nil {
return err
}
fmt.Println(id, "attempting to demote a manager in the swarm.")
func runDemote(dockerCli *client.DockerCli, nodes []string) error {
demote := func(node *swarm.Node) {
node.Spec.Role = swarm.NodeRoleWorker
}
return nil
success := func(nodeID string) {
fmt.Fprintf(dockerCli.Out(), "Manager %s demoted in the swarm.\n", nodeID)
}
return updateNodes(dockerCli, nodes, demote, success)
}

View File

@@ -27,7 +27,7 @@ func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "inspect [OPTIONS] self|NODE [NODE...]",
Short: "Inspect a node in the swarm",
Short: "Display detailed information on one or more nodes",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.nodeIds = args
@@ -49,7 +49,7 @@ func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error {
if err != nil {
return nil, nil, err
}
node, err := client.NodeInspect(ctx, nodeRef)
node, _, err := client.NodeInspectWithRaw(ctx, nodeRef)
return node, nil, err
}
@@ -96,7 +96,7 @@ func printNode(out io.Writer, node swarm.Node) {
if node.ManagerStatus != nil {
fmt.Fprintln(out, "Manager Status:")
fmt.Fprintf(out, " Address:\t\t%s\n", node.ManagerStatus.Addr)
fmt.Fprintf(out, " Raft status:\t\t%s\n", client.PrettyPrint(node.ManagerStatus.Reachability))
fmt.Fprintf(out, " Raft Status:\t\t%s\n", client.PrettyPrint(node.ManagerStatus.Reachability))
leader := "No"
if node.ManagerStatus.Leader {
leader = "Yes"

View File

@@ -16,7 +16,7 @@ import (
)
const (
listItemFmt = "%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
listItemFmt = "%s\t%s\t%s\t%s\t%s\t%s\n"
)
type listOptions struct {
@@ -74,24 +74,19 @@ func printTable(out io.Writer, nodes []swarm.Node, info types.Info) {
// Ignore flushing errors
defer writer.Flush()
fmt.Fprintf(writer, listItemFmt, "ID", "NAME", "MEMBERSHIP", "STATUS", "AVAILABILITY", "MANAGER STATUS", "LEADER")
fmt.Fprintf(writer, listItemFmt, "ID", "HOSTNAME", "MEMBERSHIP", "STATUS", "AVAILABILITY", "MANAGER STATUS")
for _, node := range nodes {
name := node.Spec.Name
name := node.Description.Hostname
availability := string(node.Spec.Availability)
membership := string(node.Spec.Membership)
if name == "" {
name = node.Description.Hostname
}
leader := ""
if node.ManagerStatus != nil && node.ManagerStatus.Leader {
leader = "Yes"
}
reachability := ""
if node.ManagerStatus != nil {
reachability = string(node.ManagerStatus.Reachability)
if node.ManagerStatus.Leader {
reachability = "Leader"
} else {
reachability = string(node.ManagerStatus.Reachability)
}
}
ID := node.ID
@@ -107,8 +102,7 @@ func printTable(out io.Writer, nodes []swarm.Node, info types.Info) {
client.PrettyPrint(membership),
client.PrettyPrint(string(node.Status.State)),
client.PrettyPrint(availability),
client.PrettyPrint(reachability),
leader)
client.PrettyPrint(reachability))
}
}

View File

@@ -7,34 +7,25 @@ import (
"github.com/docker/docker/cli"
"github.com/docker/engine-api/types/swarm"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func newPromoteCommand(dockerCli *client.DockerCli) *cobra.Command {
var flags *pflag.FlagSet
cmd := &cobra.Command{
return &cobra.Command{
Use: "promote NODE [NODE...]",
Short: "Promote a node to a manager in the swarm",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runPromote(dockerCli, flags, args)
return runPromote(dockerCli, args)
},
}
flags = cmd.Flags()
return cmd
}
func runPromote(dockerCli *client.DockerCli, flags *pflag.FlagSet, args []string) error {
for _, id := range args {
if err := runUpdate(dockerCli, id, func(node *swarm.Node) {
node.Spec.Role = swarm.NodeRoleManager
}); err != nil {
return err
}
fmt.Println(id, "attempting to promote a node to a manager in the swarm.")
func runPromote(dockerCli *client.DockerCli, nodes []string) error {
promote := func(node *swarm.Node) {
node.Spec.Role = swarm.NodeRoleManager
}
return nil
success := func(nodeID string) {
fmt.Fprintf(dockerCli.Out(), "Node %s promoted to a manager in the swarm.\n", nodeID)
}
return updateNodes(dockerCli, nodes, promote, success)
}

View File

@@ -48,14 +48,14 @@ func runTasks(dockerCli *client.DockerCli, opts tasksOptions) error {
if err != nil {
return nil
}
node, err := client.NodeInspect(ctx, nodeRef)
node, _, err := client.NodeInspectWithRaw(ctx, nodeRef)
if err != nil {
return err
}
filter := opts.filter.Value()
filter.Add("node", node.ID)
if !opts.all {
if !opts.all && !filter.Include("desired_state") {
filter.Add("desired_state", string(swarm.TaskStateRunning))
filter.Add("desired_state", string(swarm.TaskStateAccepted))

View File

@@ -5,7 +5,6 @@ import (
"github.com/docker/docker/api/client"
"github.com/docker/docker/cli"
runconfigopts "github.com/docker/docker/runconfig/opts"
"github.com/docker/engine-api/types/swarm"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@@ -14,87 +13,71 @@ import (
func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command {
var opts nodeOptions
var flags *pflag.FlagSet
cmd := &cobra.Command{
Use: "update [OPTIONS] NODE",
Short: "Update a node",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runUpdate(dockerCli, args[0], mergeNodeUpdate(flags))
return runUpdate(dockerCli, cmd.Flags(), args[0])
},
}
flags = cmd.Flags()
flags.StringVar(&opts.role, "role", "", "Role of the node (worker/manager)")
flags.StringVar(&opts.membership, "membership", "", "Membership of the node (accepted/rejected)")
flags.StringVar(&opts.availability, "availability", "", "Availability of the node (active/pause/drain)")
flags := cmd.Flags()
flags.StringVar(&opts.role, flagRole, "", "Role of the node (worker/manager)")
flags.StringVar(&opts.membership, flagMembership, "", "Membership of the node (accepted/rejected)")
flags.StringVar(&opts.availability, flagAvailability, "", "Availability of the node (active/pause/drain)")
return cmd
}
func runUpdate(dockerCli *client.DockerCli, nodeID string, mergeNode func(node *swarm.Node)) error {
func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, nodeID string) error {
success := func(_ string) {
fmt.Fprintln(dockerCli.Out(), nodeID)
}
return updateNodes(dockerCli, []string{nodeID}, mergeNodeUpdate(flags), success)
}
func updateNodes(dockerCli *client.DockerCli, nodes []string, mergeNode func(node *swarm.Node), success func(nodeID string)) error {
client := dockerCli.Client()
ctx := context.Background()
node, err := client.NodeInspect(ctx, nodeID)
if err != nil {
return err
}
for _, nodeID := range nodes {
node, _, err := client.NodeInspectWithRaw(ctx, nodeID)
if err != nil {
return err
}
mergeNode(&node)
err = client.NodeUpdate(ctx, node.ID, node.Version, node.Spec)
if err != nil {
return err
mergeNode(&node)
err = client.NodeUpdate(ctx, node.ID, node.Version, node.Spec)
if err != nil {
return err
}
success(nodeID)
}
fmt.Fprintf(dockerCli.Out(), "%s\n", nodeID)
return nil
}
func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) {
return func(node *swarm.Node) {
mergeString := func(flag string, field *string) {
if flags.Changed(flag) {
*field, _ = flags.GetString(flag)
}
}
mergeRole := func(flag string, field *swarm.NodeRole) {
if flags.Changed(flag) {
str, _ := flags.GetString(flag)
*field = swarm.NodeRole(str)
}
}
mergeMembership := func(flag string, field *swarm.NodeMembership) {
if flags.Changed(flag) {
str, _ := flags.GetString(flag)
*field = swarm.NodeMembership(str)
}
}
mergeAvailability := func(flag string, field *swarm.NodeAvailability) {
if flags.Changed(flag) {
str, _ := flags.GetString(flag)
*field = swarm.NodeAvailability(str)
}
}
mergeLabels := func(flag string, field *map[string]string) {
if flags.Changed(flag) {
values, _ := flags.GetStringSlice(flag)
for key, value := range runconfigopts.ConvertKVStringsToMap(values) {
(*field)[key] = value
}
}
}
spec := &node.Spec
mergeString("name", &spec.Name)
// TODO: setting labels is not working
mergeLabels("label", &spec.Labels)
mergeRole("role", &spec.Role)
mergeMembership("membership", &spec.Membership)
mergeAvailability("availability", &spec.Availability)
if flags.Changed(flagRole) {
str, _ := flags.GetString(flagRole)
spec.Role = swarm.NodeRole(str)
}
if flags.Changed(flagMembership) {
str, _ := flags.GetString(flagMembership)
spec.Membership = swarm.NodeMembership(str)
}
if flags.Changed(flagAvailability) {
str, _ := flags.GetString(flagAvailability)
spec.Availability = swarm.NodeAvailability(str)
}
}
}
const (
flagRole = "role"
flagMembership = "membership"
flagAvailability = "availability"
)

View File

@@ -3,21 +3,39 @@
package plugin
import (
"fmt"
"github.com/docker/docker/api/client"
"github.com/docker/docker/cli"
"github.com/docker/docker/reference"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)
func newDisableCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "disable",
Use: "disable PLUGIN",
Short: "Disable a plugin",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return dockerCli.Client().PluginDisable(context.Background(), args[0])
return runDisable(dockerCli, args[0])
},
}
return cmd
}
func runDisable(dockerCli *client.DockerCli, name string) error {
named, err := reference.ParseNamed(name) // FIXME: validate
if err != nil {
return err
}
if reference.IsNameOnly(named) {
named = reference.WithDefaultTag(named)
}
ref, ok := named.(reference.NamedTagged)
if !ok {
return fmt.Errorf("invalid name: %s", named.String())
}
return dockerCli.Client().PluginDisable(context.Background(), ref.String())
}

View File

@@ -3,21 +3,39 @@
package plugin
import (
"fmt"
"github.com/docker/docker/api/client"
"github.com/docker/docker/cli"
"github.com/docker/docker/reference"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)
func newEnableCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "enable",
Use: "enable PLUGIN",
Short: "Enable a plugin",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return dockerCli.Client().PluginEnable(context.Background(), args[0])
return runEnable(dockerCli, args[0])
},
}
return cmd
}
func runEnable(dockerCli *client.DockerCli, name string) error {
named, err := reference.ParseNamed(name) // FIXME: validate
if err != nil {
return err
}
if reference.IsNameOnly(named) {
named = reference.WithDefaultTag(named)
}
ref, ok := named.(reference.NamedTagged)
if !ok {
return fmt.Errorf("invalid name: %s", named.String())
}
return dockerCli.Client().PluginEnable(context.Background(), ref.String())
}

View File

@@ -4,16 +4,18 @@ package plugin
import (
"encoding/json"
"fmt"
"github.com/docker/docker/api/client"
"github.com/docker/docker/cli"
"github.com/docker/docker/reference"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)
func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "inspect",
Use: "inspect PLUGIN",
Short: "Inspect a plugin",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
@@ -25,7 +27,18 @@ func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
}
func runInspect(dockerCli *client.DockerCli, name string) error {
p, err := dockerCli.Client().PluginInspect(context.Background(), name)
named, err := reference.ParseNamed(name) // FIXME: validate
if err != nil {
return err
}
if reference.IsNameOnly(named) {
named = reference.WithDefaultTag(named)
}
ref, ok := named.(reference.NamedTagged)
if !ok {
return fmt.Errorf("invalid name: %s", named.String())
}
p, err := dockerCli.Client().PluginInspect(context.Background(), ref.String())
if err != nil {
return err
}

View File

@@ -3,35 +3,52 @@
package plugin
import (
"bufio"
"fmt"
"strings"
"github.com/docker/docker/api/client"
"github.com/docker/docker/cli"
"github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/docker/engine-api/types"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)
type pluginOptions struct {
name string
grantPerms bool
disable bool
}
func newInstallCommand(dockerCli *client.DockerCli) *cobra.Command {
var options pluginOptions
cmd := &cobra.Command{
Use: "install",
Use: "install PLUGIN",
Short: "Install a plugin",
Args: cli.RequiresMinArgs(1), // TODO: allow for set args
RunE: func(cmd *cobra.Command, args []string) error {
return runInstall(dockerCli, args[0], args[1:])
options.name = args[0]
return runInstall(dockerCli, options)
},
}
flags := cmd.Flags()
flags.BoolVar(&options.grantPerms, "grant-all-permissions", false, "grant all permissions necessary to run the plugin")
flags.BoolVar(&options.disable, "disable", false, "do not enable the plugin on install")
return cmd
}
func runInstall(dockerCli *client.DockerCli, name string, args []string) error {
named, err := reference.ParseNamed(name) // FIXME: validate
func runInstall(dockerCli *client.DockerCli, opts pluginOptions) error {
named, err := reference.ParseNamed(opts.name) // FIXME: validate
if err != nil {
return err
}
named = reference.WithDefaultTag(named)
if reference.IsNameOnly(named) {
named = reference.WithDefaultTag(named)
}
ref, ok := named.(reference.NamedTagged)
if !ok {
return fmt.Errorf("invalid name: %s", named.String())
@@ -40,12 +57,44 @@ func runInstall(dockerCli *client.DockerCli, name string, args []string) error {
ctx := context.Background()
repoInfo, err := registry.ParseRepositoryInfo(named)
if err != nil {
return err
}
authConfig := dockerCli.ResolveAuthConfig(ctx, repoInfo.Index)
encodedAuth, err := client.EncodeAuthToBase64(authConfig)
if err != nil {
return err
}
// TODO: pass acceptAllPermissions and noEnable flag
return dockerCli.Client().PluginInstall(ctx, ref.String(), encodedAuth, false, false, dockerCli.In(), dockerCli.Out())
registryAuthFunc := dockerCli.RegistryAuthenticationPrivilegedFunc(repoInfo.Index, "plugin install")
options := types.PluginInstallOptions{
RegistryAuth: encodedAuth,
Disabled: opts.disable,
AcceptAllPermissions: opts.grantPerms,
AcceptPermissionsFunc: acceptPrivileges(dockerCli, opts.name),
// TODO: Rename PrivilegeFunc, it has nothing to do with privileges
PrivilegeFunc: registryAuthFunc,
}
return dockerCli.Client().PluginInstall(ctx, ref.String(), options)
}
func acceptPrivileges(dockerCli *client.DockerCli, name string) func(privileges types.PluginPrivileges) (bool, error) {
return func(privileges types.PluginPrivileges) (bool, error) {
fmt.Fprintf(dockerCli.Out(), "Plugin %q is requesting the following privileges:\n", name)
for _, privilege := range privileges {
fmt.Fprintf(dockerCli.Out(), " - %s: %v\n", privilege.Name, privilege.Value)
}
fmt.Fprint(dockerCli.Out(), "Do you grant the above permissions? [y/N] ")
reader := bufio.NewReader(dockerCli.In())
line, _, err := reader.ReadLine()
if err != nil {
return false, err
}
return strings.ToLower(string(line)) == "y", nil
}
}

View File

@@ -16,7 +16,7 @@ import (
func newPushCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "push",
Use: "push PLUGIN",
Short: "Push a plugin",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
@@ -31,7 +31,9 @@ func runPush(dockerCli *client.DockerCli, name string) error {
if err != nil {
return err
}
named = reference.WithDefaultTag(named)
if reference.IsNameOnly(named) {
named = reference.WithDefaultTag(named)
}
ref, ok := named.(reference.NamedTagged)
if !ok {
return fmt.Errorf("invalid name: %s", named.String())
@@ -40,6 +42,9 @@ func runPush(dockerCli *client.DockerCli, name string) error {
ctx := context.Background()
repoInfo, err := registry.ParseRepositoryInfo(named)
if err != nil {
return err
}
authConfig := dockerCli.ResolveAuthConfig(ctx, repoInfo.Index)
encodedAuth, err := client.EncodeAuthToBase64(authConfig)

View File

@@ -7,13 +7,14 @@ import (
"github.com/docker/docker/api/client"
"github.com/docker/docker/cli"
"github.com/docker/docker/reference"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)
func newRemoveCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "rm",
Use: "rm PLUGIN",
Short: "Remove a plugin",
Aliases: []string{"remove"},
Args: cli.RequiresMinArgs(1),
@@ -28,8 +29,19 @@ func newRemoveCommand(dockerCli *client.DockerCli) *cobra.Command {
func runRemove(dockerCli *client.DockerCli, names []string) error {
var errs cli.Errors
for _, name := range names {
named, err := reference.ParseNamed(name) // FIXME: validate
if err != nil {
return err
}
if reference.IsNameOnly(named) {
named = reference.WithDefaultTag(named)
}
ref, ok := named.(reference.NamedTagged)
if !ok {
return fmt.Errorf("invalid name: %s", named.String())
}
// TODO: pass names to api instead of making multiple api calls
if err := dockerCli.Client().PluginRemove(context.Background(), name); err != nil {
if err := dockerCli.Client().PluginRemove(context.Background(), ref.String()); err != nil {
errs = append(errs, err)
continue
}

View File

@@ -3,16 +3,19 @@
package plugin
import (
"fmt"
"golang.org/x/net/context"
"github.com/docker/docker/api/client"
"github.com/docker/docker/cli"
"github.com/docker/docker/reference"
"github.com/spf13/cobra"
)
func newSetCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "set",
Use: "set PLUGIN key1=value1 [key2=value2...]",
Short: "Change settings for a plugin",
Args: cli.RequiresMinArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
@@ -24,5 +27,16 @@ func newSetCommand(dockerCli *client.DockerCli) *cobra.Command {
}
func runSet(dockerCli *client.DockerCli, name string, args []string) error {
return dockerCli.Client().PluginSet(context.Background(), name, args)
named, err := reference.ParseNamed(name) // FIXME: validate
if err != nil {
return err
}
if reference.IsNameOnly(named) {
named = reference.WithDefaultTag(named)
}
ref, ok := named.(reference.NamedTagged)
if !ok {
return fmt.Errorf("invalid name: %s", named.String())
}
return dockerCli.Client().PluginSet(context.Background(), ref.String(), args)
}

View File

@@ -13,6 +13,7 @@ import (
"golang.org/x/net/context"
"github.com/docker/docker/pkg/term"
"github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/docker/engine-api/types"
registrytypes "github.com/docker/engine-api/types/registry"
@@ -42,7 +43,7 @@ func EncodeAuthToBase64(authConfig types.AuthConfig) (string, error) {
return base64.URLEncoding.EncodeToString(buf), nil
}
// RegistryAuthenticationPrivilegedFunc return a RequestPrivilegeFunc from the specified registry index info
// RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info
// for the given command.
func (cli *DockerCli) RegistryAuthenticationPrivilegedFunc(index *registrytypes.IndexInfo, cmdName string) types.RequestPrivilegeFunc {
return func() (string, error) {
@@ -103,14 +104,14 @@ func (cli *DockerCli) ConfigureAuth(flUser, flPassword, serverAddress string, is
// will hit this if you attempt docker login from mintty where stdin
// is a pipe, not a character based console.
if flPassword == "" && !cli.isTerminalIn {
return authconfig, fmt.Errorf("Error: Cannot perform an interactive logon from a non TTY device")
return authconfig, fmt.Errorf("Error: Cannot perform an interactive login from a non TTY device")
}
authconfig.Username = strings.TrimSpace(authconfig.Username)
if flUser = strings.TrimSpace(flUser); flUser == "" {
if isDefaultRegistry {
// if this is a defauly registry (docker hub), then display the following message.
// if this is a default registry (docker hub), then display the following message.
fmt.Fprintln(cli.out, "Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.")
}
cli.promptWithDefault("Username", authconfig.Username)
@@ -148,6 +149,34 @@ func (cli *DockerCli) ConfigureAuth(flUser, flPassword, serverAddress string, is
return authconfig, nil
}
// resolveAuthConfigFromImage retrieves that AuthConfig using the image string
func (cli *DockerCli) resolveAuthConfigFromImage(ctx context.Context, image string) (types.AuthConfig, error) {
registryRef, err := reference.ParseNamed(image)
if err != nil {
return types.AuthConfig{}, err
}
repoInfo, err := registry.ParseRepositoryInfo(registryRef)
if err != nil {
return types.AuthConfig{}, err
}
authConfig := cli.ResolveAuthConfig(ctx, repoInfo.Index)
return authConfig, nil
}
// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete image
func (cli *DockerCli) RetrieveAuthTokenFromImage(ctx context.Context, image string) (string, error) {
// Retrieve encoded auth token from the image reference
authConfig, err := cli.resolveAuthConfigFromImage(ctx, image)
if err != nil {
return "", err
}
encodedAuth, err := EncodeAuthToBase64(authConfig)
if err != nil {
return "", err
}
return encodedAuth, nil
}
func readInput(in io.Reader, out io.Writer) string {
reader := bufio.NewReader(in)
line, _, err := reader.ReadLine()

View File

@@ -24,20 +24,35 @@ func newCreateCommand(dockerCli *client.DockerCli) *cobra.Command {
return runCreate(dockerCli, opts)
},
}
flags := cmd.Flags()
flags.StringVar(&opts.mode, flagMode, "replicated", "Service mode (replicated or global)")
addServiceFlags(cmd, opts)
cmd.Flags().SetInterspersed(false)
return cmd
}
func runCreate(dockerCli *client.DockerCli, opts *serviceOptions) error {
client := dockerCli.Client()
apiClient := dockerCli.Client()
headers := map[string][]string{}
service, err := opts.ToService()
if err != nil {
return err
}
response, err := client.ServiceCreate(context.Background(), service)
ctx := context.Background()
// only send auth if flag was set
if opts.registryAuth {
// Retrieve encoded auth token from the image reference
encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, opts.image)
if err != nil {
return err
}
headers["X-Registry-Auth"] = []string{encodedAuth}
}
response, err := apiClient.ServiceCreate(ctx, service, headers)
if err != nil {
return err
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/docker/docker/pkg/ioutils"
apiclient "github.com/docker/engine-api/client"
"github.com/docker/engine-api/types/swarm"
"github.com/docker/go-units"
"github.com/spf13/cobra"
)
@@ -27,7 +28,7 @@ func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "inspect [OPTIONS] SERVICE [SERVICE...]",
Short: "Inspect a service",
Short: "Display detailed information on one or more services",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.refs = args
@@ -50,7 +51,7 @@ func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error {
ctx := context.Background()
getRef := func(ref string) (interface{}, []byte, error) {
service, err := client.ServiceInspect(ctx, ref)
service, _, err := client.ServiceInspectWithRaw(ctx, ref)
if err == nil || !apiclient.IsErrServiceNotFound(err) {
return service, nil, err
}
@@ -93,22 +94,61 @@ func printService(out io.Writer, service swarm.Service) {
}
if service.Spec.Mode.Global != nil {
fmt.Fprintln(out, "Mode:\t\tGLOBAL")
fmt.Fprintln(out, "Mode:\t\tGlobal")
} else {
fmt.Fprintln(out, "Mode:\t\tREPLICATED")
fmt.Fprintln(out, "Mode:\t\tReplicated")
if service.Spec.Mode.Replicated.Replicas != nil {
fmt.Fprintf(out, " Replicas:\t\t%d\n", *service.Spec.Mode.Replicated.Replicas)
fmt.Fprintf(out, " Replicas:\t%d\n", *service.Spec.Mode.Replicated.Replicas)
}
}
fmt.Fprintln(out, "Placement:")
fmt.Fprintln(out, " Strategy:\tSPREAD")
fmt.Fprintf(out, "UpateConfig:\n")
fmt.Fprintln(out, " Strategy:\tSpread")
if service.Spec.TaskTemplate.Placement != nil && len(service.Spec.TaskTemplate.Placement.Constraints) > 0 {
ioutils.FprintfIfNotEmpty(out, " Constraints\t: %s\n", strings.Join(service.Spec.TaskTemplate.Placement.Constraints, ", "))
}
fmt.Fprintf(out, "UpdateConfig:\n")
fmt.Fprintf(out, " Parallelism:\t%d\n", service.Spec.UpdateConfig.Parallelism)
if service.Spec.UpdateConfig.Delay.Nanoseconds() > 0 {
fmt.Fprintf(out, " Delay:\t\t%s\n", service.Spec.UpdateConfig.Delay)
}
fmt.Fprintf(out, "ContainerSpec:\n")
printContainerSpec(out, service.Spec.TaskTemplate.ContainerSpec)
if service.Spec.TaskTemplate.Resources != nil {
fmt.Fprintln(out, "Resources:")
printResources := func(out io.Writer, r *swarm.Resources) {
if r.NanoCPUs != 0 {
fmt.Fprintf(out, " CPU:\t\t%g\n", float64(r.NanoCPUs)/1e9)
}
if r.MemoryBytes != 0 {
fmt.Fprintf(out, " Memory:\t\t%s\n", units.BytesSize(float64(r.MemoryBytes)))
}
}
if service.Spec.TaskTemplate.Resources.Reservations != nil {
fmt.Fprintln(out, "Reservations:")
printResources(out, service.Spec.TaskTemplate.Resources.Reservations)
}
if service.Spec.TaskTemplate.Resources.Limits != nil {
fmt.Fprintln(out, "Limits:")
printResources(out, service.Spec.TaskTemplate.Resources.Limits)
}
}
if len(service.Spec.Networks) > 0 {
fmt.Fprintf(out, "Networks:")
for _, n := range service.Spec.Networks {
fmt.Fprintf(out, " %s", n.Target)
}
}
if len(service.Endpoint.Ports) > 0 {
fmt.Fprintln(out, "Ports:")
for _, port := range service.Endpoint.Ports {
fmt.Fprintf(out, " Name = %s\n", port.Name)
fmt.Fprintf(out, " Protocol = %s\n", port.Protocol)
fmt.Fprintf(out, " TargetPort = %d\n", port.TargetPort)
fmt.Fprintf(out, " PublishedPort = %d\n", port.PublishedPort)
}
}
}
func printContainerSpec(out io.Writer, containerSpec swarm.ContainerSpec) {
@@ -117,11 +157,20 @@ func printContainerSpec(out io.Writer, containerSpec swarm.ContainerSpec) {
fmt.Fprintf(out, " Command:\t%s\n", strings.Join(containerSpec.Command, " "))
}
if len(containerSpec.Args) > 0 {
fmt.Fprintf(out, " Args:\t%s\n", strings.Join(containerSpec.Args, " "))
fmt.Fprintf(out, " Args:\t\t%s\n", strings.Join(containerSpec.Args, " "))
}
if len(containerSpec.Env) > 0 {
fmt.Fprintf(out, " Env:\t\t%s\n", strings.Join(containerSpec.Env, " "))
}
ioutils.FprintfIfNotEmpty(out, " Dir\t\t%s\n", containerSpec.Dir)
ioutils.FprintfIfNotEmpty(out, " User\t\t%s\n", containerSpec.User)
if len(containerSpec.Mounts) > 0 {
fmt.Fprintln(out, " Mounts:")
for _, v := range containerSpec.Mounts {
fmt.Fprintf(out, " Target = %s\n", v.Target)
fmt.Fprintf(out, " Source = %s\n", v.Source)
fmt.Fprintf(out, " Writable = %v\n", v.Writable)
fmt.Fprintf(out, " Type = %v\n", v.Type)
}
}
}

View File

@@ -6,15 +6,15 @@ import (
"strings"
"text/tabwriter"
"golang.org/x/net/context"
"github.com/docker/docker/api/client"
"github.com/docker/docker/cli"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/swarm"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)
const (
@@ -47,11 +47,10 @@ func newListCommand(dockerCli *client.DockerCli) *cobra.Command {
}
func runList(dockerCli *client.DockerCli, opts listOptions) error {
ctx := context.Background()
client := dockerCli.Client()
services, err := client.ServiceList(
context.Background(),
types.ServiceListOptions{Filter: opts.filter.Value()})
services, err := client.ServiceList(ctx, types.ServiceListOptions{Filter: opts.filter.Value()})
if err != nil {
return err
}
@@ -60,31 +59,59 @@ func runList(dockerCli *client.DockerCli, opts listOptions) error {
if opts.quiet {
printQuiet(out, services)
} else {
printTable(out, services)
taskFilter := filters.NewArgs()
for _, service := range services {
taskFilter.Add("service", service.ID)
}
tasks, err := client.TaskList(ctx, types.TaskListOptions{Filter: taskFilter})
if err != nil {
return err
}
nodes, err := client.NodeList(ctx, types.NodeListOptions{})
if err != nil {
return err
}
activeNodes := make(map[string]struct{})
for _, n := range nodes {
if n.Status.State == swarm.NodeStateReady {
activeNodes[n.ID] = struct{}{}
}
}
running := map[string]int{}
for _, task := range tasks {
if _, nodeActive := activeNodes[task.NodeID]; nodeActive && task.Status.State == "running" {
running[task.ServiceID]++
}
}
printTable(out, services, running)
}
return nil
}
func printTable(out io.Writer, services []swarm.Service) {
func printTable(out io.Writer, services []swarm.Service, running map[string]int) {
writer := tabwriter.NewWriter(out, 0, 4, 2, ' ', 0)
// Ignore flushing errors
defer writer.Flush()
fmt.Fprintf(writer, listItemFmt, "ID", "NAME", "SCALE", "IMAGE", "COMMAND")
fmt.Fprintf(writer, listItemFmt, "ID", "NAME", "REPLICAS", "IMAGE", "COMMAND")
for _, service := range services {
scale := ""
replicas := ""
if service.Spec.Mode.Replicated != nil && service.Spec.Mode.Replicated.Replicas != nil {
scale = fmt.Sprintf("%d", *service.Spec.Mode.Replicated.Replicas)
replicas = fmt.Sprintf("%d/%d", running[service.ID], *service.Spec.Mode.Replicated.Replicas)
} else if service.Spec.Mode.Global != nil {
scale = "global"
replicas = "global"
}
fmt.Fprintf(
writer,
listItemFmt,
stringid.TruncateID(service.ID),
service.Spec.Name,
scale,
replicas,
service.Spec.TaskTemplate.ContainerSpec.Image,
strings.Join(service.Spec.TaskTemplate.ContainerSpec.Args, " "))
}

View File

@@ -28,7 +28,7 @@ type int64Value interface {
type memBytes int64
func (m *memBytes) String() string {
return strconv.FormatInt(m.Value(), 10)
return units.BytesSize(float64(m.Value()))
}
func (m *memBytes) Set(value string) error {
@@ -48,7 +48,7 @@ func (m *memBytes) Value() int64 {
type nanoCPUs int64
func (c *nanoCPUs) String() string {
return strconv.FormatInt(c.Value(), 10)
return big.NewRat(c.Value(), 1e9).FloatString(3)
}
func (c *nanoCPUs) Set(value string) error {
@@ -160,6 +160,13 @@ func (m *MountOpt) Set(value string) error {
return mount.VolumeOptions
}
bindOptions := func() *swarm.BindOptions {
if mount.BindOptions == nil {
mount.BindOptions = new(swarm.BindOptions)
}
return mount.BindOptions
}
setValueOnMap := func(target map[string]string, value string) {
parts := strings.SplitN(value, "=", 2)
if len(parts) == 1 {
@@ -177,7 +184,7 @@ func (m *MountOpt) Set(value string) error {
}
if len(parts) != 2 {
return fmt.Errorf("invald field '%s' must be a key=value pair", field)
return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
}
key, value := parts[0], parts[1]
@@ -191,14 +198,14 @@ func (m *MountOpt) Set(value string) error {
case "writable":
mount.Writable, err = strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("invald value for writable: %s", err.Error())
return fmt.Errorf("invalid value for writable: %s", value)
}
case "bind-propagation":
mount.BindOptions.Propagation = swarm.MountPropagation(strings.ToUpper(value))
bindOptions().Propagation = swarm.MountPropagation(strings.ToUpper(value))
case "volume-populate":
volumeOptions().Populate, err = strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("invald value for populate: %s", err.Error())
return fmt.Errorf("invalid value for populate: %s", value)
}
case "volume-label":
setValueOnMap(volumeOptions().Labels, value)
@@ -210,7 +217,7 @@ func (m *MountOpt) Set(value string) error {
}
setValueOnMap(volumeOptions().DriverConfig.Options, value)
default:
return fmt.Errorf("unexpected key '%s' in '%s'", key, value)
return fmt.Errorf("unexpected key '%s' in '%s'", key, field)
}
}
@@ -235,7 +242,8 @@ func (m *MountOpt) Type() string {
func (m *MountOpt) String() string {
mounts := []string{}
for _, mount := range m.values {
mounts = append(mounts, fmt.Sprintf("%v", mount))
repr := fmt.Sprintf("%s %s %s", mount.Type, mount.Source, mount.Target)
mounts = append(mounts, repr)
}
return strings.Join(mounts, ", ")
}
@@ -309,7 +317,7 @@ func (e *endpointOptions) ToEndpointSpec() *swarm.EndpointSpec {
}
return &swarm.EndpointSpec{
Mode: swarm.ResolutionMode(e.mode),
Mode: swarm.ResolutionMode(strings.ToLower(e.mode)),
Ports: portConfigs,
}
}
@@ -365,6 +373,8 @@ type serviceOptions struct {
update updateOptions
networks []string
endpoint endpointOptions
registryAuth bool
}
func newServiceOptions() *serviceOptions {
@@ -428,7 +438,7 @@ func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) {
return service, nil
}
// addServiceFlags adds all flags that are common to both `create` and `update.
// addServiceFlags adds all flags that are common to both `create` and `update`.
// Any flags that are not common are added separately in the individual command
func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) {
flags := cmd.Flags()
@@ -437,51 +447,55 @@ func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) {
flags.VarP(&opts.env, "env", "e", "Set environment variables")
flags.StringVarP(&opts.workdir, "workdir", "w", "", "Working directory inside the container")
flags.StringVarP(&opts.user, "user", "u", "", "Username or UID")
flags.StringVarP(&opts.user, flagUser, "u", "", "Username or UID")
flags.VarP(&opts.mounts, flagMount, "m", "Attach a mount to the service")
flags.Var(&opts.resources.limitCPU, flagLimitCPU, "Limit CPUs")
flags.Var(&opts.resources.limitMemBytes, flagLimitMemory, "Limit Memory")
flags.Var(&opts.resources.resCPU, flagReserveCPU, "Reserve CPUs")
flags.Var(&opts.resources.resMemBytes, flagReserveMemory, "Reserve Memory")
flags.Var(&opts.stopGrace, "stop-grace-period", "Time to wait before force killing a container")
flags.Var(&opts.stopGrace, flagStopGracePeriod, "Time to wait before force killing a container")
flags.StringVar(&opts.mode, flagMode, "replicated", "Service mode (replicated or global)")
flags.Var(&opts.replicas, flagReplicas, "Number of tasks")
flags.StringVar(&opts.restartPolicy.condition, flagRestartCondition, "", "Restart when condition is met (none, on_failure, or any)")
flags.Var(&opts.restartPolicy.delay, flagRestartDelay, "Delay between restart attempts")
flags.Var(&opts.restartPolicy.maxAttempts, flagRestartMaxAttempts, "Maximum number of restarts before giving up")
flags.Var(&opts.restartPolicy.window, flagRestartWindow, "Window used to evalulate the restart policy")
flags.Var(&opts.restartPolicy.window, flagRestartWindow, "Window used to evaluate the restart policy")
flags.StringSliceVar(&opts.constraints, flagConstraint, []string{}, "Placement constraints")
flags.Uint64Var(&opts.update.parallelism, flagUpdateParallelism, 1, "Maximum number of tasks updated simultaneously")
flags.Uint64Var(&opts.update.parallelism, flagUpdateParallelism, 0, "Maximum number of tasks updated simultaneously")
flags.DurationVar(&opts.update.delay, flagUpdateDelay, time.Duration(0), "Delay between updates")
flags.StringSliceVar(&opts.networks, flagNetwork, []string{}, "Network attachments")
flags.StringVar(&opts.endpoint.mode, flagEndpointMode, "", "Endpoint mode(Valid values: VIP, DNSRR)")
flags.StringVar(&opts.endpoint.mode, flagEndpointMode, "", "Endpoint mode(Valid values: vip, dnsrr)")
flags.VarP(&opts.endpoint.ports, flagPublish, "p", "Publish a port as a node port")
flags.BoolVar(&opts.registryAuth, flagRegistryAuth, false, "Send registry authentication details to Swarm agents")
}
const (
flagConstraint = "constraint"
flagName = "name"
flagEndpointMode = "endpoint-mode"
flagLabel = "label"
flagLimitCPU = "limit-cpu"
flagLimitMemory = "limit-memory"
flagMode = "mode"
flagMount = "mount"
flagName = "name"
flagNetwork = "network"
flagPublish = "publish"
flagReplicas = "replicas"
flagReserveCPU = "reserve-cpu"
flagReserveMemory = "reserve-memory"
flagMount = "mount"
flagMode = "mode"
flagReplicas = "replicas"
flagPublish = "publish"
flagNetwork = "network"
flagRestartCondition = "restart-condition"
flagRestartDelay = "restart-delay"
flagRestartMaxAttempts = "restart-max-attempts"
flagRestartWindow = "restart-window"
flagEndpointMode = "endpoint-mode"
flagUpdateParallelism = "update-parallelism"
flagStopGracePeriod = "stop-grace-period"
flagUpdateDelay = "update-delay"
flagUpdateParallelism = "update-parallelism"
flagUser = "user"
flagRegistryAuth = "registry-auth"
)

View File

@@ -0,0 +1,115 @@
package service
import (
"testing"
"time"
"github.com/docker/docker/pkg/testutil/assert"
"github.com/docker/engine-api/types/swarm"
)
func TestMemBytesString(t *testing.T) {
var mem memBytes = 1048576
assert.Equal(t, mem.String(), "1 MiB")
}
func TestMemBytesSetAndValue(t *testing.T) {
var mem memBytes
assert.NilError(t, mem.Set("5kb"))
assert.Equal(t, mem.Value(), int64(5120))
}
func TestNanoCPUsString(t *testing.T) {
var cpus nanoCPUs = 6100000000
assert.Equal(t, cpus.String(), "6.100")
}
func TestNanoCPUsSetAndValue(t *testing.T) {
var cpus nanoCPUs
assert.NilError(t, cpus.Set("0.35"))
assert.Equal(t, cpus.Value(), int64(350000000))
}
func TestDurationOptString(t *testing.T) {
dur := time.Duration(300 * 10e8)
duration := DurationOpt{value: &dur}
assert.Equal(t, duration.String(), "5m0s")
}
func TestDurationOptSetAndValue(t *testing.T) {
var duration DurationOpt
assert.NilError(t, duration.Set("300s"))
assert.Equal(t, *duration.Value(), time.Duration(300*10e8))
}
func TestUint64OptString(t *testing.T) {
value := uint64(2345678)
opt := Uint64Opt{value: &value}
assert.Equal(t, opt.String(), "2345678")
opt = Uint64Opt{}
assert.Equal(t, opt.String(), "none")
}
func TestUint64OptSetAndValue(t *testing.T) {
var opt Uint64Opt
assert.NilError(t, opt.Set("14445"))
assert.Equal(t, *opt.Value(), uint64(14445))
}
func TestMountOptString(t *testing.T) {
mount := MountOpt{
values: []swarm.Mount{
{
Type: swarm.MountType("BIND"),
Source: "/home/path",
Target: "/target",
},
{
Type: swarm.MountType("VOLUME"),
Source: "foo",
Target: "/target/foo",
},
},
}
expected := "BIND /home/path /target, VOLUME foo /target/foo"
assert.Equal(t, mount.String(), expected)
}
func TestMountOptSetNoError(t *testing.T) {
var mount MountOpt
assert.NilError(t, mount.Set("type=bind,target=/target,source=/foo"))
mounts := mount.Value()
assert.Equal(t, len(mounts), 1)
assert.Equal(t, mounts[0], swarm.Mount{
Type: swarm.MountType("BIND"),
Source: "/foo",
Target: "/target",
})
}
func TestMountOptSetErrorNoType(t *testing.T) {
var mount MountOpt
assert.Error(t, mount.Set("target=/target,source=/foo"), "type is required")
}
func TestMountOptSetErrorNoTarget(t *testing.T) {
var mount MountOpt
assert.Error(t, mount.Set("type=VOLUME,source=/foo"), "target is required")
}
func TestMountOptSetErrorInvalidKey(t *testing.T) {
var mount MountOpt
assert.Error(t, mount.Set("type=VOLUME,bogus=foo"), "unexpected key 'bogus'")
}
func TestMountOptSetErrorInvalidField(t *testing.T) {
var mount MountOpt
assert.Error(t, mount.Set("type=VOLUME,bogus"), "invalid field 'bogus'")
}
func TestMountOptSetErrorInvalidWritable(t *testing.T) {
var mount MountOpt
assert.Error(t, mount.Set("type=VOLUME,writable=yes"), "invalid value for writable: yes")
}

View File

@@ -14,7 +14,7 @@ import (
func newScaleCommand(dockerCli *client.DockerCli) *cobra.Command {
return &cobra.Command{
Use: "scale SERVICE=SCALE [SERVICE=SCALE...]",
Use: "scale SERVICE=REPLICAS [SERVICE=REPLICAS...]",
Short: "Scale one or multiple services",
Args: scaleArgs,
RunE: func(cmd *cobra.Command, args []string) error {
@@ -61,7 +61,8 @@ func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string
client := dockerCli.Client()
ctx := context.Background()
service, err := client.ServiceInspect(ctx, serviceID)
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID)
if err != nil {
return err
}
@@ -76,7 +77,7 @@ func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string
}
serviceMode.Replicated.Replicas = &uintScale
err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec)
err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, nil)
if err != nil {
return err
}

View File

@@ -44,7 +44,7 @@ func runTasks(dockerCli *client.DockerCli, opts tasksOptions) error {
client := dockerCli.Client()
ctx := context.Background()
service, err := client.ServiceInspect(ctx, opts.serviceID)
service, _, err := client.ServiceInspectWithRaw(ctx, opts.serviceID)
if err != nil {
return err
}

View File

@@ -18,18 +18,17 @@ import (
func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command {
opts := newServiceOptions()
var flags *pflag.FlagSet
cmd := &cobra.Command{
Use: "update [OPTIONS] SERVICE",
Short: "Update a service",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runUpdate(dockerCli, flags, args[0])
return runUpdate(dockerCli, cmd.Flags(), args[0])
},
}
flags = cmd.Flags()
flags := cmd.Flags()
flags.String("image", "", "Service image tag")
flags.StringSlice("command", []string{}, "Service command")
flags.StringSlice("arg", []string{}, "Service command args")
@@ -38,19 +37,37 @@ func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command {
}
func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID string) error {
client := dockerCli.Client()
apiClient := dockerCli.Client()
ctx := context.Background()
headers := map[string][]string{}
service, err := client.ServiceInspect(ctx, serviceID)
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID)
if err != nil {
return err
}
err = mergeService(&service.Spec, flags)
err = updateService(flags, &service.Spec)
if err != nil {
return err
}
err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec)
// only send auth if flag was set
sendAuth, err := flags.GetBool(flagRegistryAuth)
if err != nil {
return err
}
if sendAuth {
// Retrieve encoded auth token from the image reference
// This would be the old image if it didn't change in this update
image := service.Spec.TaskTemplate.ContainerSpec.Image
encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, image)
if err != nil {
return err
}
headers["X-Registry-Auth"] = []string{encodedAuth}
}
err = apiClient.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, headers)
if err != nil {
return err
}
@@ -59,52 +76,52 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID stri
return nil
}
func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
func updateService(flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
mergeString := func(flag string, field *string) {
updateString := func(flag string, field *string) {
if flags.Changed(flag) {
*field, _ = flags.GetString(flag)
}
}
mergeListOpts := func(flag string, field *[]string) {
updateListOpts := func(flag string, field *[]string) {
if flags.Changed(flag) {
value := flags.Lookup(flag).Value.(*opts.ListOpts)
*field = value.GetAll()
}
}
mergeSlice := func(flag string, field *[]string) {
updateSlice := func(flag string, field *[]string) {
if flags.Changed(flag) {
*field, _ = flags.GetStringSlice(flag)
}
}
mergeInt64Value := func(flag string, field *int64) {
updateInt64Value := func(flag string, field *int64) {
if flags.Changed(flag) {
*field = flags.Lookup(flag).Value.(int64Value).Value()
}
}
mergeDuration := func(flag string, field *time.Duration) {
updateDuration := func(flag string, field *time.Duration) {
if flags.Changed(flag) {
*field, _ = flags.GetDuration(flag)
}
}
mergeDurationOpt := func(flag string, field *time.Duration) {
updateDurationOpt := func(flag string, field *time.Duration) {
if flags.Changed(flag) {
*field = *flags.Lookup(flag).Value.(*DurationOpt).Value()
}
}
mergeUint64 := func(flag string, field *uint64) {
updateUint64 := func(flag string, field *uint64) {
if flags.Changed(flag) {
*field, _ = flags.GetUint64(flag)
}
}
mergeUint64Opt := func(flag string, field *uint64) {
updateUint64Opt := func(flag string, field *uint64) {
if flags.Changed(flag) {
*field = *flags.Lookup(flag).Value.(*Uint64Opt).Value()
}
@@ -112,37 +129,39 @@ func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
cspec := &spec.TaskTemplate.ContainerSpec
task := &spec.TaskTemplate
mergeString(flagName, &spec.Name)
mergeLabels(flags, &spec.Labels)
mergeString("image", &cspec.Image)
mergeSlice("command", &cspec.Command)
mergeSlice("arg", &cspec.Command)
mergeListOpts("env", &cspec.Env)
mergeString("workdir", &cspec.Dir)
mergeString("user", &cspec.User)
mergeMounts(flags, &cspec.Mounts)
if flags.Changed(flagLimitCPU) || flags.Changed(flagLimitMemory) {
taskResources := func() *swarm.ResourceRequirements {
if task.Resources == nil {
task.Resources = &swarm.ResourceRequirements{}
}
task.Resources.Limits = &swarm.Resources{}
mergeInt64Value(flagLimitCPU, &task.Resources.Limits.NanoCPUs)
mergeInt64Value(flagLimitMemory, &task.Resources.Limits.MemoryBytes)
return task.Resources
}
updateString(flagName, &spec.Name)
updateLabels(flags, &spec.Labels)
updateString("image", &cspec.Image)
updateSlice("command", &cspec.Command)
updateSlice("arg", &cspec.Args)
updateListOpts("env", &cspec.Env)
updateString("workdir", &cspec.Dir)
updateString(flagUser, &cspec.User)
updateMounts(flags, &cspec.Mounts)
if flags.Changed(flagLimitCPU) || flags.Changed(flagLimitMemory) {
taskResources().Limits = &swarm.Resources{}
updateInt64Value(flagLimitCPU, &task.Resources.Limits.NanoCPUs)
updateInt64Value(flagLimitMemory, &task.Resources.Limits.MemoryBytes)
}
if flags.Changed(flagReserveCPU) || flags.Changed(flagReserveMemory) {
if task.Resources == nil {
task.Resources = &swarm.ResourceRequirements{}
}
task.Resources.Reservations = &swarm.Resources{}
mergeInt64Value(flagReserveCPU, &task.Resources.Reservations.NanoCPUs)
mergeInt64Value(flagReserveMemory, &task.Resources.Reservations.MemoryBytes)
taskResources().Reservations = &swarm.Resources{}
updateInt64Value(flagReserveCPU, &task.Resources.Reservations.NanoCPUs)
updateInt64Value(flagReserveMemory, &task.Resources.Reservations.MemoryBytes)
}
mergeDurationOpt("stop-grace-period", cspec.StopGracePeriod)
updateDurationOpt(flagStopGracePeriod, cspec.StopGracePeriod)
if flags.Changed(flagRestartCondition) || flags.Changed(flagRestartDelay) || flags.Changed(flagRestartMaxAttempts) || flags.Changed(flagRestartWindow) {
if anyChanged(flags, flagRestartCondition, flagRestartDelay, flagRestartMaxAttempts, flagRestartWindow) {
if task.RestartPolicy == nil {
task.RestartPolicy = &swarm.RestartPolicy{}
}
@@ -151,29 +170,29 @@ func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
value, _ := flags.GetString(flagRestartCondition)
task.RestartPolicy.Condition = swarm.RestartPolicyCondition(value)
}
mergeDurationOpt(flagRestartDelay, task.RestartPolicy.Delay)
mergeUint64Opt(flagRestartMaxAttempts, task.RestartPolicy.MaxAttempts)
mergeDurationOpt((flagRestartWindow), task.RestartPolicy.Window)
updateDurationOpt(flagRestartDelay, task.RestartPolicy.Delay)
updateUint64Opt(flagRestartMaxAttempts, task.RestartPolicy.MaxAttempts)
updateDurationOpt((flagRestartWindow), task.RestartPolicy.Window)
}
if flags.Changed(flagConstraint) {
task.Placement = &swarm.Placement{}
mergeSlice(flagConstraint, &task.Placement.Constraints)
updateSlice(flagConstraint, &task.Placement.Constraints)
}
if err := mergeMode(flags, &spec.Mode); err != nil {
if err := updateReplicas(flags, &spec.Mode); err != nil {
return err
}
if flags.Changed(flagUpdateParallelism) || flags.Changed(flagUpdateDelay) {
if anyChanged(flags, flagUpdateParallelism, flagUpdateDelay) {
if spec.UpdateConfig == nil {
spec.UpdateConfig = &swarm.UpdateConfig{}
}
mergeUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism)
mergeDuration(flagUpdateDelay, &spec.UpdateConfig.Delay)
updateUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism)
updateDuration(flagUpdateDelay, &spec.UpdateConfig.Delay)
}
mergeNetworks(flags, &spec.Networks)
updateNetworks(flags, &spec.Networks)
if flags.Changed(flagEndpointMode) {
value, _ := flags.GetString(flagEndpointMode)
spec.EndpointSpec.Mode = swarm.ResolutionMode(value)
@@ -183,38 +202,45 @@ func mergeService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error {
if spec.EndpointSpec == nil {
spec.EndpointSpec = &swarm.EndpointSpec{}
}
mergePorts(flags, &spec.EndpointSpec.Ports)
updatePorts(flags, &spec.EndpointSpec.Ports)
}
return nil
}
func mergeLabels(flags *pflag.FlagSet, field *map[string]string) {
func updateLabels(flags *pflag.FlagSet, field *map[string]string) {
if !flags.Changed(flagLabel) {
return
}
if *field == nil {
*field = make(map[string]string)
}
values := flags.Lookup(flagLabel).Value.(*opts.ListOpts).GetAll()
localLabels := map[string]string{}
for key, value := range runconfigopts.ConvertKVStringsToMap(values) {
(*field)[key] = value
localLabels[key] = value
}
*field = localLabels
}
func anyChanged(flags *pflag.FlagSet, fields ...string) bool {
for _, flag := range fields {
if flags.Changed(flag) {
return true
}
}
return false
}
// TODO: should this override by destination path, or does swarm handle that?
func mergeMounts(flags *pflag.FlagSet, mounts *[]swarm.Mount) {
func updateMounts(flags *pflag.FlagSet, mounts *[]swarm.Mount) {
if !flags.Changed(flagMount) {
return
}
values := flags.Lookup(flagMount).Value.(*MountOpt).Value()
*mounts = append(*mounts, values...)
*mounts = flags.Lookup(flagMount).Value.(*MountOpt).Value()
}
// TODO: should this override by name, or does swarm handle that?
func mergePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) {
func updatePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) {
if !flags.Changed(flagPublish) {
return
}
@@ -222,55 +248,34 @@ func mergePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) {
values := flags.Lookup(flagPublish).Value.(*opts.ListOpts).GetAll()
ports, portBindings, _ := nat.ParsePortSpecs(values)
var localPortConfig []swarm.PortConfig
for port := range ports {
*portConfig = append(*portConfig, convertPortToPortConfig(port, portBindings)...)
localPortConfig = append(localPortConfig, convertPortToPortConfig(port, portBindings)...)
}
*portConfig = localPortConfig
}
func mergeNetworks(flags *pflag.FlagSet, attachments *[]swarm.NetworkAttachmentConfig) {
func updateNetworks(flags *pflag.FlagSet, attachments *[]swarm.NetworkAttachmentConfig) {
if !flags.Changed(flagNetwork) {
return
}
networks, _ := flags.GetStringSlice(flagNetwork)
var localAttachments []swarm.NetworkAttachmentConfig
for _, network := range networks {
*attachments = append(*attachments, swarm.NetworkAttachmentConfig{Target: network})
localAttachments = append(localAttachments, swarm.NetworkAttachmentConfig{Target: network})
}
*attachments = localAttachments
}
func mergeMode(flags *pflag.FlagSet, serviceMode *swarm.ServiceMode) error {
if !flags.Changed(flagMode) && !flags.Changed(flagReplicas) {
func updateReplicas(flags *pflag.FlagSet, serviceMode *swarm.ServiceMode) error {
if !flags.Changed(flagReplicas) {
return nil
}
var mode string
if flags.Changed(flagMode) {
mode, _ = flags.GetString(flagMode)
}
if !(mode == "replicated" || serviceMode.Replicated != nil) && flags.Changed(flagReplicas) {
if serviceMode.Replicated == nil {
return fmt.Errorf("replicas can only be used with replicated mode")
}
if mode == "global" {
serviceMode.Replicated = nil
serviceMode.Global = &swarm.GlobalService{}
return nil
}
if flags.Changed(flagReplicas) {
replicas := flags.Lookup(flagReplicas).Value.(*Uint64Opt).Value()
serviceMode.Replicated = &swarm.ReplicatedService{Replicas: replicas}
serviceMode.Global = nil
return nil
}
if mode == "replicated" {
if serviceMode.Replicated != nil {
return nil
}
serviceMode.Replicated = &swarm.ReplicatedService{Replicas: &DefaultReplicas}
serviceMode.Global = nil
}
serviceMode.Replicated.Replicas = flags.Lookup(flagReplicas).Value.(*Uint64Opt).Value()
return nil
}

View File

@@ -0,0 +1,26 @@
package service
import (
"testing"
"github.com/docker/docker/pkg/testutil/assert"
"github.com/docker/engine-api/types/swarm"
)
func TestUpdateServiceCommandAndArgs(t *testing.T) {
flags := newUpdateCommand(nil).Flags()
flags.Set("command", "the")
flags.Set("command", "new")
flags.Set("command", "command")
flags.Set("arg", "the")
flags.Set("arg", "new args")
spec := &swarm.ServiceSpec{}
cspec := &spec.TaskTemplate.ContainerSpec
cspec.Command = []string{"old", "command"}
cspec.Args = []string{"old", "args"}
updateService(flags, spec)
assert.EqualStringSlice(t, cspec.Command, []string{"the", "new", "command"})
assert.EqualStringSlice(t, cspec.Args, []string{"the", "new args"})
}

View File

@@ -7,12 +7,12 @@ import (
"github.com/spf13/cobra"
)
// NewStackCommand returns nocommand
// NewStackCommand returns no command
func NewStackCommand(dockerCli *client.DockerCli) *cobra.Command {
return &cobra.Command{}
}
// NewTopLevelDeployCommand return no command
// NewTopLevelDeployCommand returns no command
func NewTopLevelDeployCommand(dockerCli *client.DockerCli) *cobra.Command {
return &cobra.Command{}
}

View File

@@ -31,7 +31,7 @@ func newDeployCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "deploy [OPTIONS] STACK",
Aliases: []string{"up"},
Short: "Create and update a stack",
Short: "Create and update a stack from a Distributed Application Bundle (DAB)",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.namespace = args[0]
@@ -184,18 +184,21 @@ func deployServices(
if service, exists := existingServiceMap[name]; exists {
fmt.Fprintf(out, "Updating service %s (id: %s)\n", name, service.ID)
// TODO(nishanttotla): Pass headers with X-Registry-Auth
if err := apiClient.ServiceUpdate(
ctx,
service.ID,
service.Version,
serviceSpec,
nil,
); err != nil {
return err
}
} else {
fmt.Fprintf(out, "Creating service %s\n", name)
if _, err := apiClient.ServiceCreate(ctx, serviceSpec); err != nil {
// TODO(nishanttotla): Pass headers with X-Registry-Auth
if _, err := apiClient.ServiceCreate(ctx, serviceSpec, nil); err != nil {
return err
}
}

View File

@@ -15,11 +15,11 @@ func addBundlefileFlag(opt *string, flags *pflag.FlagSet) {
flags.StringVarP(
opt,
"bundle", "f", "",
"Path to a bundle (Default: STACK.dsb)")
"Path to a Distributed Application Bundle file (Default: STACK.dab)")
}
func loadBundlefile(stderr io.Writer, namespace string, path string) (*bundlefile.Bundlefile, error) {
defaultPath := fmt.Sprintf("%s.dsb", namespace)
defaultPath := fmt.Sprintf("%s.dab", namespace)
if path == "" {
path = defaultPath
@@ -31,7 +31,12 @@ func loadBundlefile(stderr io.Writer, namespace string, path string) (*bundlefil
}
fmt.Fprintf(stderr, "Loading bundle from %s\n", path)
bundle, err := bundlefile.LoadFile(path)
reader, err := os.Open(path)
if err != nil {
return nil, err
}
bundle, err := bundlefile.LoadFile(reader)
if err != nil {
return nil, fmt.Errorf("Error reading %s: %v\n", path, err)
}

View File

@@ -9,49 +9,49 @@ import (
"github.com/docker/docker/cli"
"github.com/docker/engine-api/types/swarm"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
type initOptions struct {
swarmOptions
listenAddr NodeAddrOption
autoAccept AutoAcceptOption
forceNewCluster bool
secret string
}
func newInitCommand(dockerCli *client.DockerCli) *cobra.Command {
opts := initOptions{
listenAddr: NewNodeAddrOption(),
autoAccept: NewAutoAcceptOption(),
listenAddr: NewListenAddrOption(),
swarmOptions: swarmOptions{
autoAccept: NewAutoAcceptOption(),
},
}
cmd := &cobra.Command{
Use: "init",
Short: "Initialize a Swarm.",
Short: "Initialize a Swarm",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runInit(dockerCli, opts)
return runInit(dockerCli, cmd.Flags(), opts)
},
}
flags := cmd.Flags()
flags.Var(&opts.listenAddr, "listen-addr", "Listen address")
flags.Var(&opts.autoAccept, "auto-accept", "Auto acceptance policy (worker, manager, or none)")
flags.StringVar(&opts.secret, "secret", "", "Set secret value needed to accept nodes into cluster")
flags.BoolVar(&opts.forceNewCluster, "force-new-cluster", false, "Force create a new cluster from current state.")
addSwarmFlags(flags, &opts.swarmOptions)
return cmd
}
func runInit(dockerCli *client.DockerCli, opts initOptions) error {
func runInit(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts initOptions) error {
client := dockerCli.Client()
ctx := context.Background()
req := swarm.InitRequest{
ListenAddr: opts.listenAddr.String(),
ForceNewCluster: opts.forceNewCluster,
Spec: opts.swarmOptions.ToSpec(),
}
req.Spec.AcceptancePolicy.Policies = opts.autoAccept.Policies(opts.secret)
nodeID, err := client.SwarmInit(ctx, req)
if err != nil {
return err

View File

@@ -20,12 +20,12 @@ type joinOptions struct {
func newJoinCommand(dockerCli *client.DockerCli) *cobra.Command {
opts := joinOptions{
listenAddr: NodeAddrOption{addr: defaultListenAddr},
listenAddr: NewListenAddrOption(),
}
cmd := &cobra.Command{
Use: "join [OPTIONS] HOST:PORT",
Short: "Join a Swarm as a node and/or manager.",
Short: "Join a Swarm as a node and/or manager",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.remote = args[0]
@@ -34,9 +34,9 @@ func newJoinCommand(dockerCli *client.DockerCli) *cobra.Command {
}
flags := cmd.Flags()
flags.Var(&opts.listenAddr, "listen-addr", "Listen address")
flags.Var(&opts.listenAddr, flagListenAddr, "Listen address")
flags.BoolVar(&opts.manager, "manager", false, "Try joining as a manager.")
flags.StringVar(&opts.secret, "secret", "", "Secret for node acceptance")
flags.StringVar(&opts.secret, flagSecret, "", "Secret for node acceptance")
flags.StringVar(&opts.CACertHash, "ca-hash", "", "Hash of the Root Certificate Authority certificate used for trusted join")
return cmd
}

View File

@@ -19,7 +19,7 @@ func newLeaveCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "leave",
Short: "Leave a Swarm.",
Short: "Leave a Swarm",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runLeave(dockerCli, opts)
@@ -39,6 +39,6 @@ func runLeave(dockerCli *client.DockerCli, opts leaveOptions) error {
return err
}
fmt.Fprintln(dockerCli.Out(), "Node left the default swarm.")
fmt.Fprintln(dockerCli.Out(), "Node left the swarm.")
return nil
}

View File

@@ -1,10 +1,15 @@
package swarm
import (
"encoding/csv"
"errors"
"fmt"
"strings"
"time"
"github.com/docker/docker/opts"
"github.com/docker/engine-api/types/swarm"
"github.com/spf13/pflag"
)
const (
@@ -13,6 +18,14 @@ const (
WORKER = "WORKER"
// MANAGER constant for manager name
MANAGER = "MANAGER"
flagAutoAccept = "auto-accept"
flagCertExpiry = "cert-expiry"
flagDispatcherHeartbeat = "dispatcher-heartbeat"
flagListenAddr = "listen-addr"
flagSecret = "secret"
flagTaskHistoryLimit = "task-history-limit"
flagExternalCA = "external-ca"
)
var (
@@ -22,6 +35,15 @@ var (
}
)
type swarmOptions struct {
autoAccept AutoAcceptOption
secret string
taskHistoryLimit int64
dispatcherHeartbeat time.Duration
nodeCertExpiry time.Duration
externalCA ExternalCAOption
}
// NodeAddrOption is a pflag.Value for listen and remote addresses
type NodeAddrOption struct {
addr string
@@ -29,21 +51,16 @@ type NodeAddrOption struct {
// String prints the representation of this flag
func (a *NodeAddrOption) String() string {
return a.addr
return a.Value()
}
// Set the value for this flag
func (a *NodeAddrOption) Set(value string) error {
if !strings.Contains(value, ":") {
return fmt.Errorf("Invalud url, a host and port are required")
addr, err := opts.ParseTCPAddr(value, a.addr)
if err != nil {
return err
}
parts := strings.Split(value, ":")
if len(parts) != 2 {
return fmt.Errorf("Invalud url, too many colons")
}
a.addr = value
a.addr = addr
return nil
}
@@ -52,9 +69,19 @@ func (a *NodeAddrOption) Type() string {
return "node-addr"
}
// Value returns the value of this option as addr:port
func (a *NodeAddrOption) Value() string {
return strings.TrimPrefix(a.addr, "tcp://")
}
// NewNodeAddrOption returns a new node address option
func NewNodeAddrOption() NodeAddrOption {
return NodeAddrOption{addr: defaultListenAddr}
func NewNodeAddrOption(addr string) NodeAddrOption {
return NodeAddrOption{addr}
}
// NewListenAddrOption returns a NodeAddrOption with default values
func NewListenAddrOption() NodeAddrOption {
return NewNodeAddrOption(defaultListenAddr)
}
// AutoAcceptOption is a value type for auto-accept policy
@@ -65,10 +92,10 @@ type AutoAcceptOption struct {
// String prints a string representation of this option
func (o *AutoAcceptOption) String() string {
keys := []string{}
for key := range o.values {
keys = append(keys, key)
for key, value := range o.values {
keys = append(keys, fmt.Sprintf("%s=%v", strings.ToLower(key), value))
}
return strings.Join(keys, " ")
return strings.Join(keys, ", ")
}
// Set sets a new value on this option
@@ -102,7 +129,7 @@ func (o *AutoAcceptOption) Type() string {
}
// Policies returns a representation of this option for the api
func (o *AutoAcceptOption) Policies(secret string) []swarm.Policy {
func (o *AutoAcceptOption) Policies(secret *string) []swarm.Policy {
policies := []swarm.Policy{}
for _, p := range defaultPolicies {
if len(o.values) != 0 {
@@ -118,3 +145,115 @@ func (o *AutoAcceptOption) Policies(secret string) []swarm.Policy {
func NewAutoAcceptOption() AutoAcceptOption {
return AutoAcceptOption{values: make(map[string]bool)}
}
// ExternalCAOption is a Value type for parsing external CA specifications.
type ExternalCAOption struct {
values []*swarm.ExternalCA
}
// Set parses an external CA option.
func (m *ExternalCAOption) Set(value string) error {
parsed, err := parseExternalCA(value)
if err != nil {
return err
}
m.values = append(m.values, parsed)
return nil
}
// Type returns the type of this option.
func (m *ExternalCAOption) Type() string {
return "external-ca"
}
// String returns a string repr of this option.
func (m *ExternalCAOption) String() string {
externalCAs := []string{}
for _, externalCA := range m.values {
repr := fmt.Sprintf("%s: %s", externalCA.Protocol, externalCA.URL)
externalCAs = append(externalCAs, repr)
}
return strings.Join(externalCAs, ", ")
}
// Value returns the external CAs
func (m *ExternalCAOption) Value() []*swarm.ExternalCA {
return m.values
}
// parseExternalCA parses an external CA specification from the command line,
// such as protocol=cfssl,url=https://example.com.
func parseExternalCA(caSpec string) (*swarm.ExternalCA, error) {
csvReader := csv.NewReader(strings.NewReader(caSpec))
fields, err := csvReader.Read()
if err != nil {
return nil, err
}
externalCA := swarm.ExternalCA{
Options: make(map[string]string),
}
var (
hasProtocol bool
hasURL bool
)
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid field '%s' must be a key=value pair", field)
}
key, value := parts[0], parts[1]
switch strings.ToLower(key) {
case "protocol":
hasProtocol = true
if strings.ToLower(value) == string(swarm.ExternalCAProtocolCFSSL) {
externalCA.Protocol = swarm.ExternalCAProtocolCFSSL
} else {
return nil, fmt.Errorf("unrecognized external CA protocol %s", value)
}
case "url":
hasURL = true
externalCA.URL = value
default:
externalCA.Options[key] = value
}
}
if !hasProtocol {
return nil, errors.New("the external-ca option needs a protocol= parameter")
}
if !hasURL {
return nil, errors.New("the external-ca option needs a url= parameter")
}
return &externalCA, nil
}
func addSwarmFlags(flags *pflag.FlagSet, opts *swarmOptions) {
flags.Var(&opts.autoAccept, flagAutoAccept, "Auto acceptance policy (worker, manager or none)")
flags.StringVar(&opts.secret, flagSecret, "", "Set secret value needed to accept nodes into cluster")
flags.Int64Var(&opts.taskHistoryLimit, flagTaskHistoryLimit, 10, "Task history retention limit")
flags.DurationVar(&opts.dispatcherHeartbeat, flagDispatcherHeartbeat, time.Duration(5*time.Second), "Dispatcher heartbeat period")
flags.DurationVar(&opts.nodeCertExpiry, flagCertExpiry, time.Duration(90*24*time.Hour), "Validity period for node certificates")
flags.Var(&opts.externalCA, flagExternalCA, "Specifications of one or more certificate signing endpoints")
}
func (opts *swarmOptions) ToSpec() swarm.Spec {
spec := swarm.Spec{}
if opts.secret != "" {
spec.AcceptancePolicy.Policies = opts.autoAccept.Policies(&opts.secret)
} else {
spec.AcceptancePolicy.Policies = opts.autoAccept.Policies(nil)
}
spec.Orchestration.TaskHistoryRetentionLimit = opts.taskHistoryLimit
spec.Dispatcher.HeartbeatPeriod = uint64(opts.dispatcherHeartbeat.Nanoseconds())
spec.CAConfig.NodeCertExpiry = opts.nodeCertExpiry
spec.CAConfig.ExternalCAs = opts.externalCA.Value()
return spec
}

View File

@@ -0,0 +1,120 @@
package swarm
import (
"testing"
"github.com/docker/docker/pkg/testutil/assert"
"github.com/docker/engine-api/types/swarm"
)
func TestNodeAddrOptionSetHostAndPort(t *testing.T) {
opt := NewNodeAddrOption("old:123")
addr := "newhost:5555"
assert.NilError(t, opt.Set(addr))
assert.Equal(t, opt.Value(), addr)
}
func TestNodeAddrOptionSetHostOnly(t *testing.T) {
opt := NewListenAddrOption()
assert.NilError(t, opt.Set("newhost"))
assert.Equal(t, opt.Value(), "newhost:2377")
}
func TestNodeAddrOptionSetHostOnlyIPv6(t *testing.T) {
opt := NewListenAddrOption()
assert.NilError(t, opt.Set("::1"))
assert.Equal(t, opt.Value(), "[::1]:2377")
}
func TestNodeAddrOptionSetPortOnly(t *testing.T) {
opt := NewListenAddrOption()
assert.NilError(t, opt.Set(":4545"))
assert.Equal(t, opt.Value(), "0.0.0.0:4545")
}
func TestNodeAddrOptionSetInvalidFormat(t *testing.T) {
opt := NewListenAddrOption()
assert.Error(t, opt.Set("http://localhost:4545"), "Invalid")
}
func TestAutoAcceptOptionSetWorker(t *testing.T) {
opt := NewAutoAcceptOption()
assert.NilError(t, opt.Set("worker"))
assert.Equal(t, opt.values[WORKER], true)
}
func TestAutoAcceptOptionSetManager(t *testing.T) {
opt := NewAutoAcceptOption()
assert.NilError(t, opt.Set("manager"))
assert.Equal(t, opt.values[MANAGER], true)
}
func TestAutoAcceptOptionSetInvalid(t *testing.T) {
opt := NewAutoAcceptOption()
assert.Error(t, opt.Set("bogus"), "must be one of")
}
func TestAutoAcceptOptionSetNone(t *testing.T) {
opt := NewAutoAcceptOption()
assert.NilError(t, opt.Set("none"))
assert.Equal(t, opt.values[MANAGER], false)
assert.Equal(t, opt.values[WORKER], false)
}
func TestAutoAcceptOptionSetConflict(t *testing.T) {
opt := NewAutoAcceptOption()
assert.NilError(t, opt.Set("manager"))
assert.Error(t, opt.Set("none"), "value NONE is incompatible with MANAGER")
opt = NewAutoAcceptOption()
assert.NilError(t, opt.Set("none"))
assert.Error(t, opt.Set("worker"), "value NONE is incompatible with WORKER")
}
func TestAutoAcceptOptionPoliciesDefault(t *testing.T) {
opt := NewAutoAcceptOption()
secret := "thesecret"
policies := opt.Policies(&secret)
assert.Equal(t, len(policies), 2)
assert.Equal(t, policies[0], swarm.Policy{
Role: WORKER,
Autoaccept: true,
Secret: &secret,
})
assert.Equal(t, policies[1], swarm.Policy{
Role: MANAGER,
Autoaccept: false,
Secret: &secret,
})
}
func TestAutoAcceptOptionPoliciesWithManager(t *testing.T) {
opt := NewAutoAcceptOption()
secret := "thesecret"
assert.NilError(t, opt.Set("manager"))
policies := opt.Policies(&secret)
assert.Equal(t, len(policies), 2)
assert.Equal(t, policies[0], swarm.Policy{
Role: WORKER,
Autoaccept: false,
Secret: &secret,
})
assert.Equal(t, policies[1], swarm.Policy{
Role: MANAGER,
Autoaccept: true,
Secret: &secret,
})
}
func TestAutoAcceptOptionString(t *testing.T) {
opt := NewAutoAcceptOption()
assert.NilError(t, opt.Set("manager"))
assert.NilError(t, opt.Set("worker"))
repr := opt.String()
assert.Contains(t, repr, "worker=true")
assert.Contains(t, repr, "manager=true")
}

View File

@@ -2,7 +2,6 @@ package swarm
import (
"fmt"
"time"
"golang.org/x/net/context"
@@ -13,35 +12,23 @@ import (
"github.com/spf13/pflag"
)
type updateOptions struct {
autoAccept AutoAcceptOption
secret string
taskHistoryLimit int64
dispatcherHeartbeat time.Duration
}
func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command {
opts := updateOptions{autoAccept: NewAutoAcceptOption()}
var flags *pflag.FlagSet
opts := swarmOptions{autoAccept: NewAutoAcceptOption()}
cmd := &cobra.Command{
Use: "update",
Short: "update the Swarm.",
Short: "Update the Swarm",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runUpdate(dockerCli, flags, opts)
return runUpdate(dockerCli, cmd.Flags(), opts)
},
}
flags = cmd.Flags()
flags.Var(&opts.autoAccept, "auto-accept", "Auto acceptance policy (worker, manager or none)")
flags.StringVar(&opts.secret, "secret", "", "Set secret value needed to accept nodes into cluster")
flags.Int64Var(&opts.taskHistoryLimit, "task-history-limit", 10, "Task history retention limit")
flags.DurationVar(&opts.dispatcherHeartbeat, "dispatcher-heartbeat", time.Duration(5*time.Second), "Dispatcher heartbeat period")
addSwarmFlags(cmd.Flags(), &opts)
return cmd
}
func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts updateOptions) error {
func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts swarmOptions) error {
client := dockerCli.Client()
ctx := context.Background()
@@ -54,6 +41,7 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts updateOpt
if err != nil {
return err
}
err = client.SwarmUpdate(ctx, swarm.Version, swarm.Spec)
if err != nil {
return err
@@ -66,31 +54,41 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts updateOpt
func mergeSwarm(swarm *swarm.Swarm, flags *pflag.FlagSet) error {
spec := &swarm.Spec
if flags.Changed("auto-accept") {
value := flags.Lookup("auto-accept").Value.(*AutoAcceptOption)
if len(spec.AcceptancePolicy.Policies) > 0 {
spec.AcceptancePolicy.Policies = value.Policies(spec.AcceptancePolicy.Policies[0].Secret)
} else {
spec.AcceptancePolicy.Policies = value.Policies("")
}
if flags.Changed(flagAutoAccept) {
value := flags.Lookup(flagAutoAccept).Value.(*AutoAcceptOption)
spec.AcceptancePolicy.Policies = value.Policies(nil)
}
if flags.Changed("secret") {
secret, _ := flags.GetString("secret")
for _, policy := range spec.AcceptancePolicy.Policies {
policy.Secret = secret
}
var psecret *string
if flags.Changed(flagSecret) {
secret, _ := flags.GetString(flagSecret)
psecret = &secret
}
if flags.Changed("task-history-limit") {
spec.Orchestration.TaskHistoryRetentionLimit, _ = flags.GetInt64("task-history-limit")
for i := range spec.AcceptancePolicy.Policies {
spec.AcceptancePolicy.Policies[i].Secret = psecret
}
if flags.Changed("dispatcher-heartbeat") {
if v, err := flags.GetDuration("dispatcher-heartbeat"); err == nil {
if flags.Changed(flagTaskHistoryLimit) {
spec.Orchestration.TaskHistoryRetentionLimit, _ = flags.GetInt64(flagTaskHistoryLimit)
}
if flags.Changed(flagDispatcherHeartbeat) {
if v, err := flags.GetDuration(flagDispatcherHeartbeat); err == nil {
spec.Dispatcher.HeartbeatPeriod = uint64(v.Nanoseconds())
}
}
if flags.Changed(flagCertExpiry) {
if v, err := flags.GetDuration(flagCertExpiry); err == nil {
spec.CAConfig.NodeCertExpiry = v
}
}
if flags.Changed(flagExternalCA) {
value := flags.Lookup(flagExternalCA).Value.(*ExternalCAOption)
spec.CAConfig.ExternalCAs = value.Value()
}
return nil
}

View File

@@ -134,7 +134,7 @@ func CopyToFile(outfile string, r io.Reader) error {
return nil
}
// ForwardAllSignals forwards signals to the contianer
// ForwardAllSignals forwards signals to the container
// TODO: this can be unexported again once all container commands are under
// api/client/container
func (cli *DockerCli) ForwardAllSignals(ctx context.Context, cid string) chan os.Signal {

View File

@@ -19,7 +19,7 @@ func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "inspect [OPTIONS] VOLUME [VOLUME...]",
Short: "Return low-level information on a volume",
Short: "Display detailed information on one or more volumes",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.names = args

View File

@@ -53,7 +53,7 @@ func filterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.N
return nil, err
}
var displayNet []types.NetworkResource
displayNet := []types.NetworkResource{}
for _, nw := range nws {
if filter.Include("driver") {
if !filter.ExactMatch("driver", nw.Driver) {

View File

@@ -14,8 +14,8 @@ type Backend interface {
Update(uint64, types.Spec) error
GetServices(basictypes.ServiceListOptions) ([]types.Service, error)
GetService(string) (types.Service, error)
CreateService(types.ServiceSpec) (string, error)
UpdateService(string, uint64, types.ServiceSpec) error
CreateService(types.ServiceSpec, string) (string, error)
UpdateService(string, uint64, types.ServiceSpec, string) error
RemoveService(string) error
GetNodes(basictypes.NodeListOptions) ([]types.Node, error)
GetNode(string) (types.Node, error)

View File

@@ -107,9 +107,12 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter,
return err
}
id, err := sr.backend.CreateService(service)
// Get returns "" if the header does not exist
encodedAuth := r.Header.Get("X-Registry-Auth")
id, err := sr.backend.CreateService(service, encodedAuth)
if err != nil {
logrus.Errorf("Error reating service %s: %v", id, err)
logrus.Errorf("Error creating service %s: %v", id, err)
return err
}
@@ -130,7 +133,10 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter,
return fmt.Errorf("Invalid service version '%s': %s", rawVersion, err.Error())
}
if err := sr.backend.UpdateService(vars["id"], version, service); err != nil {
// Get returns "" if the header does not exist
encodedAuth := r.Header.Get("X-Registry-Auth")
if err := sr.backend.UpdateService(vars["id"], version, service, encodedAuth); err != nil {
logrus.Errorf("Error updating service %s: %v", vars["id"], err)
return err
}

View File

@@ -172,7 +172,7 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowLocalD
}
cmd := b.runConfig.Cmd
b.runConfig.Cmd = strslice.StrSlice(append(getShell(b.runConfig), "#(nop) %s %s in %s ", cmdName, srcHash, dest))
b.runConfig.Cmd = strslice.StrSlice(append(getShell(b.runConfig), fmt.Sprintf("#(nop) %s %s in %s ", cmdName, srcHash, dest)))
defer func(cmd strslice.StrSlice) { b.runConfig.Cmd = cmd }(cmd)
if hit, err := b.probeCache(); err != nil {

View File

@@ -1,21 +1,20 @@
package cli
import "bytes"
import "strings"
// Errors is a list of errors.
// Useful in a loop if you don't want to return the error right away and you want to display after the loop,
// all the errors that happened during the loop.
type Errors []error
func (errs Errors) Error() string {
if len(errs) < 1 {
func (errList Errors) Error() string {
if len(errList) < 1 {
return ""
}
var buf bytes.Buffer
buf.WriteString(errs[0].Error())
for _, err := range errs[1:] {
buf.WriteString(", ")
buf.WriteString(err.Error())
out := make([]string, len(errList))
for i := range errList {
out[i] = errList[i].Error()
}
return buf.String()
return strings.Join(out, ", ")
}

View File

@@ -10,5 +10,5 @@ import (
)
func pluginInit(config *daemon.Config, remote libcontainerd.Remote, rs registry.Service) error {
return plugin.Init(config.Root, config.ExecRoot, remote, rs)
return plugin.Init(config.Root, config.ExecRoot, remote, rs, config.LiveRestore)
}

View File

@@ -823,7 +823,7 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epC
})
}
createOptions = append(createOptions, libnetwork.CreateOptionService(svcCfg.Name, svcCfg.ID, net.ParseIP(vip), portConfigs))
createOptions = append(createOptions, libnetwork.CreateOptionService(svcCfg.Name, svcCfg.ID, net.ParseIP(vip), portConfigs, svcCfg.Aliases[n.ID()]))
}
if !containertypes.NetworkMode(n.Name()).IsUserDefined() {

View File

@@ -54,7 +54,8 @@ func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog fun
// TmpfsMounts returns the list of tmpfs mounts
func (container *Container) TmpfsMounts() []Mount {
return nil
var mounts []Mount
return mounts
}
// UpdateContainer updates configuration of a container

View File

@@ -0,0 +1,18 @@
#
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/rpm/amd64/generate.sh"!
#
FROM fedora:24
RUN dnf install -y @development-tools fedora-packager
RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git
ENV GO_VERSION 1.6.2
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin
ENV AUTO_GOPATH 1
ENV DOCKER_BUILDTAGS pkcs11 seccomp selinux
ENV RUNC_BUILDTAGS seccomp selinux

View File

@@ -232,6 +232,7 @@ flags=(
CGROUP_HUGETLB
NET_CLS_CGROUP $netprio
CFS_BANDWIDTH FAIR_GROUP_SCHED RT_GROUP_SCHED
IP_VS
)
check_flags "${flags[@]}"
@@ -249,6 +250,8 @@ echo '- Network Drivers:'
{
echo '- "'$(wrap_color 'overlay' blue)'":'
check_flags VXLAN | sed 's/^/ /'
echo ' Optional (for secure networks):'
check_flags XFRM_ALGO XFRM_USER | sed 's/^/ /'
} | sed 's/^/ /'
echo '- Storage Drivers:'

View File

@@ -693,7 +693,7 @@ _docker_build() {
--cgroup-parent
--cpuset-cpus
--cpuset-mems
--cpu-shares
--cpu-shares -c
--cpu-period
--cpu-quota
--file -f
@@ -942,10 +942,11 @@ _docker_daemon() {
return
;;
--storage-driver|-s)
COMPREPLY=( $( compgen -W "aufs btrfs devicemapper overlay vfs zfs" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
COMPREPLY=( $( compgen -W "aufs btrfs devicemapper overlay overlay2 vfs zfs" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
return
;;
--storage-opt)
local btrfs_options="btrfs.min_space"
local devicemapper_options="
dm.basesize
dm.blkdiscard
@@ -965,7 +966,10 @@ _docker_daemon() {
case $(__docker_value_of_option '--storage-driver|-s') in
'')
COMPREPLY=( $( compgen -W "$devicemapper_options $zfs_options" -S = -- "$cur" ) )
COMPREPLY=( $( compgen -W "$btrfs_options $devicemapper_options $zfs_options" -S = -- "$cur" ) )
;;
btrfs)
COMPREPLY=( $( compgen -W "$btrfs_options" -S = -- "$cur" ) )
;;
devicemapper)
COMPREPLY=( $( compgen -W "$devicemapper_options" -S = -- "$cur" ) )
@@ -1049,6 +1053,7 @@ _docker_events() {
export
import
kill
load
mount
oom
pause
@@ -1058,6 +1063,7 @@ _docker_events() {
rename
resize
restart
save
start
stop
tag
@@ -1355,6 +1361,7 @@ _docker_network_connect() {
--ip
--ip6
--link
--link-local-ip
"
local boolean_options="
@@ -1528,11 +1535,12 @@ _docker_network() {
_docker_service() {
local subcommands="
create
tasks
inspect
ls list
rm remove
scale
tasks
update
ls
rm
"
__docker_subcommands "$subcommands" && return
@@ -1547,43 +1555,47 @@ _docker_service() {
}
_docker_service_create() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --constraint --endpoint-ingress --endpoint-mode --env --label --limit-cpu --limit-memory --mode --name --network --publish --reserve-cpu --reserve-memory --restart-condition --restart-delay --restart-max-attempts --restart-window --replicas --stop-grace-period --update-delay --update-parallelism --user --volume --workdir" -- "$cur" ) )
;;
esac
}
_docker_service_update() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--arg --command --constraint --endpoint-ingress --endpoint-mode --env --help --image --label --limit-cpu --limit-memory --mode --name --network --publish --reserve-cpu --reserve-memory --restart-condition--restart-delay --restart-max-attempts --restart-window --replicas --stop-grace-period --update-delay --update-parallelism --user --volume --workdir" -- "$cur" ) )
;;
*)
__docker_complete_services
esac
_docker_service_update
}
_docker_service_inspect() {
case "$prev" in
--format|-f)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --format --pretty" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--format -f --help --pretty -p" -- "$cur" ) )
;;
*)
__docker_complete_services
esac
}
_docker_service_tasks() {
_docker_service_list() {
_docker_service_ls
}
_docker_service_ls() {
case "$prev" in
--format|-f)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --all --filter --no-resolve" -- "$cur" ) )
COMPREPLY=( $( compgen -W "-f --filter --help --quiet -q" -- "$cur" ) )
;;
*)
__docker_complete_services
esac
}
_docker_service_remove() {
_docker_service_rm
}
_docker_service_rm() {
case "$cur" in
-*)
@@ -1594,21 +1606,131 @@ _docker_service_rm() {
esac
}
_docker_service_ls() {
_docker_service_scale() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
COMPREPLY=( $(compgen -S "=" -W "$(__docker_services $1)" -- "$cur") )
__docker_nospace
;;
esac
}
_docker_service_tasks() {
case "$prev" in
--format|-f)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--all -a --filter -f --help --no-resolve -n" -- "$cur" ) )
;;
*)
__docker_complete_services
esac
}
_docker_service_update() {
local $subcommand="${words[$subcommand_pos]}"
local options_with_args="
--constraint
--endpoint-mode
--env -e
--label -l
--limit-cpu
--limit-memory
--mode
--mount -m
--name
--network
--publish -p
--replicas
--reserve-cpu
--reserve-memory
--restart-condition
--restart-delay
--restart-max-attempts
--restart-window
--stop-grace-period
--update-delay
--update-parallelism
--user -u
--workdir -w
"
local boolean_options="
--help
"
if [ "$subcommand" = "update" ] ; then
options_with_args="$options_with_args
--arg
--command
--image
"
case "$prev" in
--image)
__docker_complete_image_repos_and_tags
return
;;
esac
fi
case "$prev" in
--endpoint-mode)
COMPREPLY=( $( compgen -W "DNSRR VIP" -- "$cur" ) )
return
;;
--env|-e)
COMPREPLY=( $( compgen -e -S = -- "$cur" ) )
__docker_nospace
return
;;
--mode)
COMPREPLY=( $( compgen -W "global replicated" -- "$cur" ) )
return
;;
--network)
__docker_complete_networks
return
;;
--restart-condition)
COMPREPLY=( $( compgen -W "any none on_failure" -- "$cur" ) )
return
;;
--user|-u)
__docker_complete_user_group
return
;;
$(__docker_to_extglob "$options_with_args") )
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
;;
*)
if [ "$subcommand" = "update" ] ; then
__docker_complete_services
fi
esac
}
_docker_swarm() {
local subcommands="
init
join
update
leave
inspect
join
leave
update
"
__docker_subcommands "$subcommands" && return
@@ -1623,14 +1745,44 @@ _docker_swarm() {
}
_docker_swarm_init() {
case "$prev" in
--auto-accept)
COMPREPLY=( $( compgen -W "manager none worker" -- "$cur" ) )
return
;;
--listen-addr|--secret)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --auto-accept --force-new-cluster --secret" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--auto-accept --force-new-cluster --help --listen-addr --secret" -- "$cur" ) )
;;
esac
}
_docker_swarm_inspect() {
case "$prev" in
--format|-f)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) )
;;
esac
}
_docker_swarm_join() {
case "$prev" in
--ca-hash|--listen-addr|--secret)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--ca-hash --help --listen-addr --manager --secret" -- "$cur" ) )
@@ -1638,14 +1790,6 @@ _docker_swarm_join() {
esac
}
_docker_swarm_update() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--auto-accept --dispatcher-heartbeat --help --secret --task-history-limit" -- "$cur" ) )
;;
esac
}
_docker_swarm_leave() {
case "$cur" in
-*)
@@ -1654,10 +1798,20 @@ _docker_swarm_leave() {
esac
}
_docker_swarm_inspect() {
_docker_swarm_update() {
case "$prev" in
--auto-accept)
COMPREPLY=( $( compgen -W "manager none worker" -- "$cur" ) )
return
;;
--cert-expiry|--dispatcher-heartbeat|--secret|--task-history-limit)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--format --help" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--auto-accept --cert-expiry --dispatcher-heartbeat --help --secret --task-history-limit" -- "$cur" ) )
;;
esac
}
@@ -1667,9 +1821,9 @@ _docker_node() {
accept
demote
inspect
ls
ls list
promote
rm
rm remove
tasks
update
"
@@ -1695,20 +1849,46 @@ _docker_node_accept() {
esac
}
_docker_node_inspect() {
_docker_node_demote() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --format --pretty" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
__docker_complete_manager_nodes
esac
}
_docker_node_inspect() {
case "$prev" in
--format|-f)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--format -f --help --pretty -p" -- "$cur" ) )
;;
*)
__docker_complete_nodes
esac
}
_docker_node_list() {
_docker_node_ls
}
_docker_node_ls() {
case "$prev" in
--filter|-f)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --filter --quiet" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--filter -f --help --quiet -q" -- "$cur" ) )
;;
esac
}
@@ -1723,14 +1903,8 @@ _docker_node_promote() {
esac
}
_docker_node_demote() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
__docker_complete_manager_nodes
esac
_docker_node_remove() {
_docker_node_rm
}
_docker_node_rm() {
@@ -1744,9 +1918,15 @@ _docker_node_rm() {
}
_docker_node_tasks() {
case "$prev" in
--filter|-f)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --no-resolve --filter --all" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--all -a --filter -f --help --no-resolve -n" -- "$cur" ) )
;;
*)
__docker_complete_nodes_plus_self
@@ -1754,9 +1934,24 @@ _docker_node_tasks() {
}
_docker_node_update() {
case "$prev" in
--availability)
COMPREPLY=( $( compgen -W "active drain pause" -- "$cur" ) )
return
;;
--membership)
COMPREPLY=( $( compgen -W "accepted rejected" -- "$cur" ) )
return
;;
--role)
COMPREPLY=( $( compgen -W "manager worker" -- "$cur" ) )
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --availability --membership --role" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--availability --help --membership --role" -- "$cur" ) )
;;
*)
__docker_complete_nodes
@@ -1964,7 +2159,7 @@ _docker_run() {
--cpu-quota
--cpuset-cpus
--cpuset-mems
--cpu-shares
--cpu-shares -c
--device
--device-read-bps
--device-read-iops
@@ -1987,6 +2182,7 @@ _docker_run() {
--label-file
--label -l
--link
--link-local-ip
--log-driver
--log-opt
--mac-address
@@ -2006,6 +2202,7 @@ _docker_run() {
--security-opt
--shm-size
--stop-signal
--storage-opt
--tmpfs
--sysctl
--ulimit
@@ -2192,6 +2389,11 @@ _docker_run() {
fi
return
;;
--storage-opt)
COMPREPLY=( $( compgen -W "size" -S = -- "$cur") )
__docker_nospace
return
;;
--user|-u)
__docker_complete_user_group
return
@@ -2359,7 +2561,7 @@ _docker_update() {
--cpu-quota
--cpuset-cpus
--cpuset-mems
--cpu-shares
--cpu-shares -c
--kernel-memory
--memory -m
--memory-reservation

View File

@@ -1,4 +1,4 @@
#compdef docker
#compdef docker dockerd
#
# zsh completion for docker (http://docker.com)
#
@@ -415,8 +415,8 @@ __docker_complete_events_filter() {
(event)
local -a event_opts
event_opts=('attach' 'commit' 'connect' 'copy' 'create' 'delete' 'destroy' 'detach' 'die' 'disconnect' 'exec_create' 'exec_detach'
'exec_start' 'export' 'import' 'kill' 'mount' 'oom' 'pause' 'pull' 'push' 'reload' 'rename' 'resize' 'restart' 'start' 'stop' 'tag'
'top' 'unmount' 'unpause' 'untag' 'update')
'exec_start' 'export' 'import' 'kill' 'load' 'mount' 'oom' 'pause' 'pull' 'push' 'reload' 'rename' 'resize' 'restart' 'save' 'start'
'stop' 'tag' 'top' 'unmount' 'unpause' 'untag' 'update')
_describe -t event-filter-opts "event filter options" event_opts && ret=0
;;
(image)
@@ -538,7 +538,7 @@ __docker_networks_names() {
__docker_network_commands() {
local -a _docker_network_subcommands
_docker_network_subcommands=(
"connect:Connects a container to a network"
"connect:Connect a container to a network"
"create:Creates a new network with a name specified by the user"
"disconnect:Disconnects a container from a network"
"inspect:Displays detailed information on a network"
@@ -563,6 +563,7 @@ __docker_network_subcommand() {
"($help)--ip=[Container IPv4 address]:IPv4: " \
"($help)--ip6=[Container IPv6 address]:IPv6: " \
"($help)*--link=[Add a link to another container]:link:->link" \
"($help)*--link-local-ip=[Add a link-local address for the container]:IPv4/IPv6: " \
"($help -)1:network:__docker_networks" \
"($help -)2:containers:__docker_containers" && ret=0
@@ -693,7 +694,7 @@ __docker_volume_commands() {
local -a _docker_volume_subcommands
_docker_volume_subcommands=(
"create:Create a volume"
"inspect:Return low-level information on a volume"
"inspect:Display detailed information on one or more volumes"
"ls:List volumes"
"rm:Remove a volume"
)
@@ -786,7 +787,7 @@ __docker_subcommand() {
"($help)--userns=[Container user namespace]:user namespace:(host)"
)
opts_build_create_run_update=(
"($help)--cpu-shares=[CPU shares (relative weight)]:CPU shares:(0 10 100 200 500 800 1000)"
"($help -c --cpu-shares)"{-c=,--cpu-shares=}"[CPU shares (relative weight)]:CPU shares:(0 10 100 200 500 800 1000)"
"($help)--cpu-period=[Limit the CPU CFS (Completely Fair Scheduler) period]:CPU period: "
"($help)--cpu-quota=[Limit the CPU CFS (Completely Fair Scheduler) quota]:CPU quota: "
"($help)--cpuset-cpus=[CPUs in which to allow execution]:CPUs: "
@@ -820,6 +821,7 @@ __docker_subcommand() {
"($help)--ip6=[Container IPv6 address]:IPv6: "
"($help)--ipc=[IPC namespace to use]:IPC namespace: "
"($help)*--link=[Add link to another container]:link:->link"
"($help)*--link-local-ip=[Add a link-local address for the container]:IPv4/IPv6: "
"($help)*"{-l=,--label=}"[Container metadata]:label: "
"($help)--log-driver=[Default driver for container logs]:Logging driver:(awslogs etwlogs fluentd gcplogs gelf journald json-file none splunk syslog)"
"($help)*--log-opt=[Log driver specific options]:log driver options:__docker_log_options"
@@ -965,6 +967,7 @@ __docker_subcommand() {
"($help)--ipv6[Enable IPv6 networking]" \
"($help -l --log-level)"{-l=,--log-level=}"[Logging level]:level:(debug info warn error fatal)" \
"($help)*--label=[Key=value labels]:label: " \
"($help)--live-restore[Enable live restore of docker when containers are still running]" \
"($help)--log-driver=[Default driver for container logs]:Logging driver:(awslogs etwlogs fluentd gcplogs gelf journald json-file none splunk syslog)" \
"($help)*--log-opt=[Log driver specific options]:log driver options:__docker_log_options" \
"($help)--max-concurrent-downloads[Set the max concurrent downloads for each pull]" \
@@ -973,7 +976,7 @@ __docker_subcommand() {
"($help -p --pidfile)"{-p=,--pidfile=}"[Path to use for daemon PID file]:PID file:_files" \
"($help)--raw-logs[Full timestamps without ANSI coloring]" \
"($help)*--registry-mirror=[Preferred Docker registry mirror]:registry mirror: " \
"($help -s --storage-driver)"{-s=,--storage-driver=}"[Storage driver to use]:driver:(aufs devicemapper btrfs zfs overlay)" \
"($help -s --storage-driver)"{-s=,--storage-driver=}"[Storage driver to use]:driver:(aufs btrfs devicemapper overlay overlay2 vfs zfs)" \
"($help)--selinux-enabled[Enable selinux support]" \
"($help)*--storage-opt=[Storage driver options]:storage driver options: " \
"($help)--tls[Use TLS]" \
@@ -1250,6 +1253,7 @@ __docker_subcommand() {
"($help)--rm[Remove intermediate containers when it exits]" \
"($help)--sig-proxy[Proxy all received signals to the process (non-TTY mode only)]" \
"($help)--stop-signal=[Signal to kill a container]:signal:_signals" \
"($help)--storage-opt=[Set storage driver options per container]:storage options:->storage-opt" \
"($help -): :__docker_images" \
"($help -):command: _command_names -e" \
"($help -)*::arguments: _normal" && ret=0
@@ -1262,6 +1266,14 @@ __docker_subcommand() {
__docker_runningcontainers -qS ":" && ret=0
fi
;;
(storage-opt)
if compset -P "*="; then
_message "value" && ret=0
else
opts=('size')
_describe -t filter-opts "storage options" opts -qS "=" && ret=0
fi
;;
esac
;;
@@ -1410,6 +1422,13 @@ _docker() {
return ret
}
_dockerd() {
integer ret=1
words[1]='daemon'
__docker_subcommand && ret=0
return ret
}
_docker "$@"
# Local Variables:

View File

@@ -20,6 +20,8 @@ LimitCORE=infinity
TimeoutStartSec=0
# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes
# kill only the docker process, not all processes in the cgroup
KillMode=process
[Install]
WantedBy=multi-user.target

View File

@@ -13,10 +13,12 @@ import (
"google.golang.org/grpc"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/digest"
"github.com/docker/docker/daemon/cluster/convert"
executorpkg "github.com/docker/docker/daemon/cluster/executor"
"github.com/docker/docker/daemon/cluster/executor/container"
"github.com/docker/docker/errors"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/runconfig"
apitypes "github.com/docker/engine-api/types"
@@ -28,25 +30,46 @@ import (
const swarmDirName = "swarm"
const controlSocket = "control.sock"
const swarmConnectTimeout = 10 * time.Second
const swarmConnectTimeout = 20 * time.Second
const stateFile = "docker-state.json"
const defaultAddr = "0.0.0.0:2377"
const (
initialReconnectDelay = 100 * time.Millisecond
maxReconnectDelay = 10 * time.Second
maxReconnectDelay = 30 * time.Second
)
// ErrNoManager is returned then a manager-only function is called on non-manager
var ErrNoManager = fmt.Errorf("this node is not participating as a Swarm manager")
// ErrNoSwarm is returned on leaving a cluster that was never initialized
var ErrNoSwarm = fmt.Errorf("this node is not part of Swarm")
var ErrNoSwarm = fmt.Errorf("This node is not part of Swarm")
// ErrSwarmExists is returned on initialize or join request for a cluster that has already been activated
var ErrSwarmExists = fmt.Errorf("this node is already part of a Swarm")
var ErrSwarmExists = fmt.Errorf("This node is already part of a Swarm cluster. Use \"docker swarm leave\" to leave this cluster and join another one.")
// ErrPendingSwarmExists is returned on initialize or join request for a cluster that is already processing a similar request but has not succeeded yet.
var ErrPendingSwarmExists = fmt.Errorf("This node is processing an existing join request that has not succeeded yet. Use \"docker swarm leave\" to cancel the current request.")
// ErrSwarmJoinTimeoutReached is returned when cluster join could not complete before timeout was reached.
var ErrSwarmJoinTimeoutReached = fmt.Errorf("timeout reached before node was joined")
var ErrSwarmJoinTimeoutReached = fmt.Errorf("Timeout was reached before node was joined. Attempt to join the cluster will continue in the background. Use \"docker info\" command to see the current Swarm status of your node.")
// defaultSpec contains some sane defaults if cluster options are missing on init
var defaultSpec = types.Spec{
Raft: types.RaftConfig{
SnapshotInterval: 10000,
KeepOldSnapshots: 0,
LogEntriesForSlowFollowers: 500,
HeartbeatTick: 1,
ElectionTick: 3,
},
CAConfig: types.CAConfig{
NodeCertExpiry: 90 * 24 * time.Hour,
},
Dispatcher: types.DispatcherConfig{
HeartbeatPeriod: uint64((5 * time.Second).Nanoseconds()),
},
Orchestration: types.OrchestrationConfig{
TaskHistoryRetentionLimit: 10,
},
}
type state struct {
ListenAddr string
@@ -59,22 +82,27 @@ type Config struct {
Backend executorpkg.Backend
}
// Cluster provides capabilities to pariticipate in a cluster as worker or a
// manager and a worker.
// Cluster provides capabilities to participate in a cluster as a worker or a
// manager.
type Cluster struct {
sync.RWMutex
root string
config Config
configEvent chan struct{} // todo: make this array and goroutine safe
node *swarmagent.Node
*node
root string
config Config
configEvent chan struct{} // todo: make this array and goroutine safe
listenAddr string
stop bool
err error
cancelDelay func()
}
type node struct {
*swarmagent.Node
done chan struct{}
ready bool
conn *grpc.ClientConn
client swarmapi.ControlClient
ready bool
listenAddr string
err error
reconnectDelay time.Duration
stop bool
cancelDelay func()
}
// New creates a new Cluster instance using provided config.
@@ -84,13 +112,12 @@ func New(config Config) (*Cluster, error) {
return nil, err
}
c := &Cluster{
root: root,
config: config,
configEvent: make(chan struct{}, 10),
reconnectDelay: initialReconnectDelay,
root: root,
config: config,
configEvent: make(chan struct{}, 10),
}
dt, err := ioutil.ReadFile(filepath.Join(root, stateFile))
st, err := c.loadState()
if err != nil {
if os.IsNotExist(err) {
return c, nil
@@ -98,12 +125,7 @@ func New(config Config) (*Cluster, error) {
return nil, err
}
var st state
if err := json.Unmarshal(dt, &st); err != nil {
return nil, err
}
n, ctx, err := c.startNewNode(false, st.ListenAddr, "", "", "", false)
n, err := c.startNewNode(false, st.ListenAddr, "", "", "", false)
if err != nil {
return nil, err
}
@@ -111,16 +133,33 @@ func New(config Config) (*Cluster, error) {
select {
case <-time.After(swarmConnectTimeout):
logrus.Errorf("swarm component could not be started before timeout was reached")
case <-n.Ready(context.Background()):
case <-ctx.Done():
case <-n.Ready():
case <-n.done:
return nil, fmt.Errorf("swarm component could not be started: %v", c.err)
}
if ctx.Err() != nil {
return nil, fmt.Errorf("swarm component could not be started")
}
go c.reconnectOnFailure(ctx)
go c.reconnectOnFailure(n)
return c, nil
}
func (c *Cluster) loadState() (*state, error) {
dt, err := ioutil.ReadFile(filepath.Join(c.root, stateFile))
if err != nil {
return nil, err
}
// missing certificate means no actual state to restore from
if _, err := os.Stat(filepath.Join(c.root, "certificates/swarm-node.crt")); err != nil {
if os.IsNotExist(err) {
c.clearState()
}
return nil, err
}
var st state
if err := json.Unmarshal(dt, &st); err != nil {
return nil, err
}
return &st, nil
}
func (c *Cluster) saveState() error {
dt, err := json.Marshal(state{ListenAddr: c.listenAddr})
if err != nil {
@@ -129,20 +168,20 @@ func (c *Cluster) saveState() error {
return ioutils.AtomicWriteFile(filepath.Join(c.root, stateFile), dt, 0600)
}
func (c *Cluster) reconnectOnFailure(ctx context.Context) {
func (c *Cluster) reconnectOnFailure(n *node) {
for {
<-ctx.Done()
<-n.done
c.Lock()
if c.stop || c.node != nil {
c.Unlock()
return
}
c.reconnectDelay *= 2
if c.reconnectDelay > maxReconnectDelay {
c.reconnectDelay = maxReconnectDelay
n.reconnectDelay *= 2
if n.reconnectDelay > maxReconnectDelay {
n.reconnectDelay = maxReconnectDelay
}
logrus.Warnf("Restarting swarm in %.2f seconds", c.reconnectDelay.Seconds())
delayCtx, cancel := context.WithTimeout(context.Background(), c.reconnectDelay)
logrus.Warnf("Restarting swarm in %.2f seconds", n.reconnectDelay.Seconds())
delayCtx, cancel := context.WithTimeout(context.Background(), n.reconnectDelay)
c.cancelDelay = cancel
c.Unlock()
<-delayCtx.Done()
@@ -155,22 +194,23 @@ func (c *Cluster) reconnectOnFailure(ctx context.Context) {
return
}
var err error
_, ctx, err = c.startNewNode(false, c.listenAddr, c.getRemoteAddress(), "", "", false)
n, err = c.startNewNode(false, c.listenAddr, c.getRemoteAddress(), "", "", false)
if err != nil {
c.err = err
ctx = delayCtx
close(n.done)
}
c.Unlock()
}
}
func (c *Cluster) startNewNode(forceNewCluster bool, listenAddr, joinAddr, secret, cahash string, ismanager bool) (*swarmagent.Node, context.Context, error) {
func (c *Cluster) startNewNode(forceNewCluster bool, listenAddr, joinAddr, secret, cahash string, ismanager bool) (*node, error) {
if err := c.config.Backend.IsSwarmCompatible(); err != nil {
return nil, nil, err
return nil, err
}
c.node = nil
c.cancelDelay = nil
node, err := swarmagent.NewNode(&swarmagent.NodeConfig{
c.stop = false
n, err := swarmagent.NewNode(&swarmagent.NodeConfig{
Hostname: c.config.Name,
ForceNewCluster: forceNewCluster,
ListenControlAPI: filepath.Join(c.root, controlSocket),
@@ -185,87 +225,85 @@ func (c *Cluster) startNewNode(forceNewCluster bool, listenAddr, joinAddr, secre
IsManager: ismanager,
})
if err != nil {
return nil, nil, err
return nil, err
}
ctx, cancel := context.WithCancel(context.Background())
if err := node.Start(ctx); err != nil {
return nil, nil, err
ctx := context.Background()
if err := n.Start(ctx); err != nil {
return nil, err
}
node := &node{
Node: n,
done: make(chan struct{}),
reconnectDelay: initialReconnectDelay,
}
c.node = node
c.listenAddr = listenAddr
c.saveState()
c.config.Backend.SetClusterProvider(c)
go func() {
err := node.Err(ctx)
err := n.Err(ctx)
if err != nil {
logrus.Errorf("cluster exited with error: %v", err)
}
c.Lock()
c.conn = nil
c.client = nil
c.node = nil
c.ready = false
c.err = err
c.Unlock()
cancel()
close(node.done)
}()
go func() {
select {
case <-node.Ready(context.Background()):
case <-n.Ready():
c.Lock()
c.reconnectDelay = initialReconnectDelay
c.Unlock()
case <-ctx.Done():
}
if ctx.Err() == nil {
c.Lock()
c.ready = true
node.ready = true
c.err = nil
c.Unlock()
case <-ctx.Done():
}
c.configEvent <- struct{}{}
}()
go func() {
for conn := range node.ListenControlSocket(ctx) {
for conn := range n.ListenControlSocket(ctx) {
c.Lock()
if c.conn != conn {
c.client = swarmapi.NewControlClient(conn)
if node.conn != conn {
if conn == nil {
node.client = nil
} else {
node.client = swarmapi.NewControlClient(conn)
}
}
if c.conn != nil {
c.client = nil
}
c.conn = conn
node.conn = conn
c.Unlock()
c.configEvent <- struct{}{}
}
}()
return node, ctx, nil
return node, nil
}
// Init initializes new cluster from user provided request.
func (c *Cluster) Init(req types.InitRequest) (string, error) {
c.Lock()
if c.node != nil {
c.Unlock()
if node := c.node; node != nil {
if !req.ForceNewCluster {
return "", ErrSwarmExists
c.Unlock()
return "", errSwarmExists(node)
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := c.node.Stop(ctx); err != nil && !strings.Contains(err.Error(), "context canceled") {
if err := c.stopNode(); err != nil {
c.Unlock()
return "", err
}
c.Lock()
c.node = nil
c.conn = nil
c.ready = false
}
if err := validateAndSanitizeInitRequest(&req); err != nil {
c.Unlock()
return "", err
}
// todo: check current state existing
n, ctx, err := c.startNewNode(req.ForceNewCluster, req.ListenAddr, "", "", "", false)
n, err := c.startNewNode(req.ForceNewCluster, req.ListenAddr, "", "", "", false)
if err != nil {
c.Unlock()
return "", err
@@ -273,71 +311,88 @@ func (c *Cluster) Init(req types.InitRequest) (string, error) {
c.Unlock()
select {
case <-n.Ready(context.Background()):
if err := initAcceptancePolicy(n, req.Spec.AcceptancePolicy); err != nil {
case <-n.Ready():
if err := initClusterSpec(n, req.Spec); err != nil {
return "", err
}
go c.reconnectOnFailure(ctx)
go c.reconnectOnFailure(n)
return n.NodeID(), nil
case <-ctx.Done():
case <-n.done:
c.RLock()
defer c.RUnlock()
if c.err != nil {
if !req.ForceNewCluster { // if failure on first attempt don't keep state
if err := c.clearState(); err != nil {
return "", err
}
if !req.ForceNewCluster { // if failure on first attempt don't keep state
if err := c.clearState(); err != nil {
return "", err
}
return "", c.err
}
return "", ctx.Err()
return "", c.err
}
}
// Join makes current Cluster part of an existing swarm cluster.
func (c *Cluster) Join(req types.JoinRequest) error {
c.Lock()
if c.node != nil {
if node := c.node; node != nil {
c.Unlock()
return ErrSwarmExists
return errSwarmExists(node)
}
if err := validateAndSanitizeJoinRequest(&req); err != nil {
c.Unlock()
return err
}
// todo: check current state existing
if len(req.RemoteAddrs) == 0 {
return fmt.Errorf("at least 1 RemoteAddr is required to join")
}
n, ctx, err := c.startNewNode(false, req.ListenAddr, req.RemoteAddrs[0], req.Secret, req.CACertHash, req.Manager)
n, err := c.startNewNode(false, req.ListenAddr, req.RemoteAddrs[0], req.Secret, req.CACertHash, req.Manager)
if err != nil {
c.Unlock()
return err
}
c.Unlock()
select {
case <-time.After(swarmConnectTimeout):
go c.reconnectOnFailure(ctx)
if nodeid := n.NodeID(); nodeid != "" {
return fmt.Errorf("Timeout reached before node was joined. Your cluster settings may be preventing this node from automatically joining. To accept this node into cluster run `docker node accept %v` in an existing cluster manager", nodeid)
}
return ErrSwarmJoinTimeoutReached
case <-n.Ready(context.Background()):
go c.reconnectOnFailure(ctx)
return nil
case <-ctx.Done():
c.RLock()
defer c.RUnlock()
if c.err != nil {
certificateRequested := n.CertificateRequested()
for {
select {
case <-certificateRequested:
if n.NodeMembership() == swarmapi.NodeMembershipPending {
return fmt.Errorf("Your node is in the process of joining the cluster but needs to be accepted by existing cluster member.\nTo accept this node into cluster run \"docker node accept %v\" in an existing cluster manager. Use \"docker info\" command to see the current Swarm status of your node.", n.NodeID())
}
certificateRequested = nil
case <-time.After(swarmConnectTimeout):
// attempt to connect will continue in background, also reconnecting
go c.reconnectOnFailure(n)
return ErrSwarmJoinTimeoutReached
case <-n.Ready():
go c.reconnectOnFailure(n)
return nil
case <-n.done:
c.RLock()
defer c.RUnlock()
return c.err
}
return ctx.Err()
}
}
func (c *Cluster) cancelReconnect() {
// stopNode is a helper that stops the active c.node and waits until it has
// shut down. Call while keeping the cluster lock.
func (c *Cluster) stopNode() error {
if c.node == nil {
return nil
}
c.stop = true
if c.cancelDelay != nil {
c.cancelDelay()
c.cancelDelay = nil
}
node := c.node
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
// TODO: can't hold lock on stop because it calls back to network
c.Unlock()
defer c.Lock()
if err := node.Stop(ctx); err != nil && !strings.Contains(err.Error(), "context canceled") {
return err
}
<-node.done
return nil
}
// Leave shuts down Cluster and removes current state.
@@ -360,36 +415,29 @@ func (c *Cluster) Leave(force bool) error {
c.Unlock()
return fmt.Errorf(msg)
}
msg += fmt.Sprintf("Leaving cluster will leave you with %v managers out of %v. This means Raft quorum will be lost and your cluster will become inaccessible. ", reachable-1, reachable+unreachable)
msg += fmt.Sprintf("Leaving cluster will leave you with %v managers out of %v. This means Raft quorum will be lost and your cluster will become inaccessible. ", reachable-1, reachable+unreachable)
}
}
} else {
msg += "Doing so may lose the consenus of your cluster. "
msg += "Doing so may lose the consensus of your cluster. "
}
msg += "Only way to restore a cluster that has lost consensus is to reinitialize it with `--force-new-cluster`. Use `--force` to ignore this message."
c.Unlock()
return fmt.Errorf(msg)
}
c.cancelReconnect()
c.Unlock()
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := node.Stop(ctx); err != nil && !strings.Contains(err.Error(), "context canceled") {
if err := c.stopNode(); err != nil {
c.Unlock()
return err
}
nodeID := node.NodeID()
for _, id := range c.config.Backend.ListContainersForNode(nodeID) {
if err := c.config.Backend.ContainerRm(id, &apitypes.ContainerRmConfig{ForceRemove: true}); err != nil {
logrus.Errorf("error removing %v: %v", id, err)
c.Unlock()
if nodeID := node.NodeID(); nodeID != "" {
for _, id := range c.config.Backend.ListContainersForNode(nodeID) {
if err := c.config.Backend.ContainerRm(id, &apitypes.ContainerRmConfig{ForceRemove: true}); err != nil {
logrus.Errorf("error removing %v: %v", id, err)
}
}
}
c.Lock()
defer c.Unlock()
c.node = nil
c.conn = nil
c.ready = false
c.configEvent <- struct{}{}
// todo: cleanup optional?
if err := c.clearState(); err != nil {
@@ -399,6 +447,7 @@ func (c *Cluster) Leave(force bool) error {
}
func (c *Cluster) clearState() error {
// todo: backup this data instead of removing?
if err := os.RemoveAll(c.root); err != nil {
return err
}
@@ -414,13 +463,13 @@ func (c *Cluster) getRequestContext() context.Context { // TODO: not needed when
return ctx
}
// Inspect retrives the confuguration properties of managed swarm cluster.
// Inspect retrieves the configuration properties of a managed swarm cluster.
func (c *Cluster) Inspect() (types.Swarm, error) {
c.RLock()
defer c.RUnlock()
if !c.isActiveManager() {
return types.Swarm{}, ErrNoManager
return types.Swarm{}, c.errNoManager()
}
swarm, err := getSwarm(c.getRequestContext(), c.client)
@@ -441,15 +490,15 @@ func (c *Cluster) Update(version uint64, spec types.Spec) error {
defer c.RUnlock()
if !c.isActiveManager() {
return ErrNoManager
return c.errNoManager()
}
swarmSpec, err := convert.SwarmSpecToGRPC(spec)
swarm, err := getSwarm(c.getRequestContext(), c.client)
if err != nil {
return err
}
swarm, err := getSwarm(c.getRequestContext(), c.client)
swarmSpec, err := convert.SwarmSpecToGRPCandMerge(spec, &swarm.Spec)
if err != nil {
return err
}
@@ -467,32 +516,32 @@ func (c *Cluster) Update(version uint64, spec types.Spec) error {
return err
}
// IsManager returns true is Cluster is participating as a manager.
// IsManager returns true if Cluster is participating as a manager.
func (c *Cluster) IsManager() bool {
c.RLock()
defer c.RUnlock()
return c.isActiveManager()
}
// IsAgent returns true is Cluster is participating as a worker/agent.
// IsAgent returns true if Cluster is participating as a worker/agent.
func (c *Cluster) IsAgent() bool {
c.RLock()
defer c.RUnlock()
return c.ready
return c.node != nil && c.ready
}
// GetListenAddress returns the listening address for current maanger's
// GetListenAddress returns the listening address for current manager's
// consensus and dispatcher APIs.
func (c *Cluster) GetListenAddress() string {
c.RLock()
defer c.RUnlock()
if c.conn != nil {
if c.isActiveManager() {
return c.listenAddr
}
return ""
}
// GetRemoteAddress returns a known advertise address of a remote maanger if
// GetRemoteAddress returns a known advertise address of a remote manager if
// available.
// todo: change to array/connect with info
func (c *Cluster) GetRemoteAddress() string {
@@ -541,7 +590,6 @@ func (c *Cluster) Info() types.Info {
if c.err != nil {
info.Error = c.err.Error()
}
if c.isActiveManager() {
info.ControlAvailable = true
if r, err := c.client.ListNodes(c.getRequestContext(), &swarmapi.ListNodesRequest{}); err == nil {
@@ -570,7 +618,19 @@ func (c *Cluster) Info() types.Info {
// isActiveManager should not be called without a read lock
func (c *Cluster) isActiveManager() bool {
return c.conn != nil
return c.node != nil && c.conn != nil
}
// errNoManager returns error describing why manager commands can't be used.
// Call with read lock.
func (c *Cluster) errNoManager() error {
if c.node == nil {
return fmt.Errorf("This node is not a Swarm manager. Use \"docker swarm init\" or \"docker swarm join --manager\" to connect this node to Swarm and try again.")
}
if c.node.Manager() != nil {
return fmt.Errorf("This node is not a Swarm manager. Manager is being prepared or has trouble connecting to the cluster.")
}
return fmt.Errorf("This node is not a Swarm manager. Worker nodes can't be used to view or modify cluster state. Please run this command on a manager node or promote the current node to a manager.")
}
// GetServices returns all services of a managed swarm cluster.
@@ -579,7 +639,7 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
defer c.RUnlock()
if !c.isActiveManager() {
return nil, ErrNoManager
return nil, c.errNoManager()
}
filters, err := newListServicesFilters(options.Filter)
@@ -603,12 +663,12 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
}
// CreateService creates a new service in a managed swarm cluster.
func (c *Cluster) CreateService(s types.ServiceSpec) (string, error) {
func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string, error) {
c.RLock()
defer c.RUnlock()
if !c.isActiveManager() {
return "", ErrNoManager
return "", c.errNoManager()
}
ctx := c.getRequestContext()
@@ -622,6 +682,15 @@ func (c *Cluster) CreateService(s types.ServiceSpec) (string, error) {
if err != nil {
return "", err
}
if encodedAuth != "" {
ctnr := serviceSpec.Task.GetContainer()
if ctnr == nil {
return "", fmt.Errorf("service does not use container tasks")
}
ctnr.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth}
}
r, err := c.client.CreateService(ctx, &swarmapi.CreateServiceRequest{Spec: &serviceSpec})
if err != nil {
return "", err
@@ -630,13 +699,13 @@ func (c *Cluster) CreateService(s types.ServiceSpec) (string, error) {
return r.Service.ID, nil
}
// GetService returns a service based on a ID or name.
// GetService returns a service based on an ID or name.
func (c *Cluster) GetService(input string) (types.Service, error) {
c.RLock()
defer c.RUnlock()
if !c.isActiveManager() {
return types.Service{}, ErrNoManager
return types.Service{}, c.errNoManager()
}
service, err := getService(c.getRequestContext(), c.client, input)
@@ -647,12 +716,12 @@ func (c *Cluster) GetService(input string) (types.Service, error) {
}
// UpdateService updates existing service to match new properties.
func (c *Cluster) UpdateService(serviceID string, version uint64, spec types.ServiceSpec) error {
func (c *Cluster) UpdateService(serviceID string, version uint64, spec types.ServiceSpec, encodedAuth string) error {
c.RLock()
defer c.RUnlock()
if !c.isActiveManager() {
return ErrNoManager
return c.errNoManager()
}
serviceSpec, err := convert.ServiceSpecToGRPC(spec)
@@ -660,6 +729,26 @@ func (c *Cluster) UpdateService(serviceID string, version uint64, spec types.Ser
return err
}
if encodedAuth != "" {
ctnr := serviceSpec.Task.GetContainer()
if ctnr == nil {
return fmt.Errorf("service does not use container tasks")
}
ctnr.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth}
} else {
// this is needed because if the encodedAuth isn't being updated then we
// shouldn't lose it, and continue to use the one that was already present
currentService, err := getService(c.getRequestContext(), c.client, serviceID)
if err != nil {
return err
}
ctnr := currentService.Spec.Task.GetContainer()
if ctnr == nil {
return fmt.Errorf("service does not use container tasks")
}
serviceSpec.Task.GetContainer().PullOptions = ctnr.PullOptions
}
_, err = c.client.UpdateService(
c.getRequestContext(),
&swarmapi.UpdateServiceRequest{
@@ -679,7 +768,7 @@ func (c *Cluster) RemoveService(input string) error {
defer c.RUnlock()
if !c.isActiveManager() {
return ErrNoManager
return c.errNoManager()
}
service, err := getService(c.getRequestContext(), c.client, input)
@@ -699,7 +788,7 @@ func (c *Cluster) GetNodes(options apitypes.NodeListOptions) ([]types.Node, erro
defer c.RUnlock()
if !c.isActiveManager() {
return nil, ErrNoManager
return nil, c.errNoManager()
}
filters, err := newListNodesFilters(options.Filter)
@@ -721,13 +810,13 @@ func (c *Cluster) GetNodes(options apitypes.NodeListOptions) ([]types.Node, erro
return nodes, nil
}
// GetNode returns a node based on a ID or name.
// GetNode returns a node based on an ID or name.
func (c *Cluster) GetNode(input string) (types.Node, error) {
c.RLock()
defer c.RUnlock()
if !c.isActiveManager() {
return types.Node{}, ErrNoManager
return types.Node{}, c.errNoManager()
}
node, err := getNode(c.getRequestContext(), c.client, input)
@@ -743,7 +832,7 @@ func (c *Cluster) UpdateNode(nodeID string, version uint64, spec types.NodeSpec)
defer c.RUnlock()
if !c.isActiveManager() {
return ErrNoManager
return c.errNoManager()
}
nodeSpec, err := convert.NodeSpecToGRPC(spec)
@@ -770,7 +859,7 @@ func (c *Cluster) RemoveNode(input string) error {
defer c.RUnlock()
if !c.isActiveManager() {
return ErrNoManager
return c.errNoManager()
}
ctx := c.getRequestContext()
@@ -792,7 +881,7 @@ func (c *Cluster) GetTasks(options apitypes.TaskListOptions) ([]types.Task, erro
defer c.RUnlock()
if !c.isActiveManager() {
return nil, ErrNoManager
return nil, c.errNoManager()
}
filters, err := newListTasksFilters(options.Filter)
@@ -820,7 +909,7 @@ func (c *Cluster) GetTask(input string) (types.Task, error) {
defer c.RUnlock()
if !c.isActiveManager() {
return types.Task{}, ErrNoManager
return types.Task{}, c.errNoManager()
}
task, err := getTask(c.getRequestContext(), c.client, input)
@@ -830,13 +919,13 @@ func (c *Cluster) GetTask(input string) (types.Task, error) {
return convert.TaskFromGRPC(*task), nil
}
// GetNetwork returns a cluster network by ID.
// GetNetwork returns a cluster network by an ID.
func (c *Cluster) GetNetwork(input string) (apitypes.NetworkResource, error) {
c.RLock()
defer c.RUnlock()
if !c.isActiveManager() {
return apitypes.NetworkResource{}, ErrNoManager
return apitypes.NetworkResource{}, c.errNoManager()
}
network, err := getNetwork(c.getRequestContext(), c.client, input)
@@ -852,7 +941,7 @@ func (c *Cluster) GetNetworks() ([]apitypes.NetworkResource, error) {
defer c.RUnlock()
if !c.isActiveManager() {
return nil, ErrNoManager
return nil, c.errNoManager()
}
r, err := c.client.ListNetworks(c.getRequestContext(), &swarmapi.ListNetworksRequest{})
@@ -875,7 +964,7 @@ func (c *Cluster) CreateNetwork(s apitypes.NetworkCreateRequest) (string, error)
defer c.RUnlock()
if !c.isActiveManager() {
return "", ErrNoManager
return "", c.errNoManager()
}
if runconfig.IsPreDefinedNetwork(s.Name) {
@@ -898,7 +987,7 @@ func (c *Cluster) RemoveNetwork(input string) error {
defer c.RUnlock()
if !c.isActiveManager() {
return ErrNoManager
return c.errNoManager()
}
network, err := getNetwork(c.getRequestContext(), c.client, input)
@@ -918,7 +1007,7 @@ func populateNetworkID(ctx context.Context, c swarmapi.ControlClient, s *types.S
if err != nil {
return err
}
s.Networks[i] = types.NetworkAttachmentConfig{Target: apiNetwork.ID}
s.Networks[i].Target = apiNetwork.ID
}
return nil
}
@@ -958,7 +1047,7 @@ func (c *Cluster) Cleanup() {
c.Unlock()
return
}
defer c.Unlock()
if c.isActiveManager() {
active, reachable, unreachable, err := c.managerStats()
if err == nil {
@@ -968,18 +1057,7 @@ func (c *Cluster) Cleanup() {
}
}
}
c.cancelReconnect()
c.Unlock()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := node.Stop(ctx); err != nil {
logrus.Errorf("error cleaning up cluster: %v", err)
}
c.Lock()
c.node = nil
c.ready = false
c.conn = nil
c.Unlock()
c.stopNode()
}
func (c *Cluster) managerStats() (current bool, reachable int, unreachable int, err error) {
@@ -1004,7 +1082,84 @@ func (c *Cluster) managerStats() (current bool, reachable int, unreachable int,
return
}
func initAcceptancePolicy(node *swarmagent.Node, acceptancePolicy types.AcceptancePolicy) error {
func validateAndSanitizeInitRequest(req *types.InitRequest) error {
var err error
req.ListenAddr, err = validateAddr(req.ListenAddr)
if err != nil {
return fmt.Errorf("invalid ListenAddr %q: %v", req.ListenAddr, err)
}
spec := &req.Spec
// provide sane defaults instead of erroring
if spec.Name == "" {
spec.Name = "default"
}
if spec.Raft.SnapshotInterval == 0 {
spec.Raft.SnapshotInterval = defaultSpec.Raft.SnapshotInterval
}
if spec.Raft.LogEntriesForSlowFollowers == 0 {
spec.Raft.LogEntriesForSlowFollowers = defaultSpec.Raft.LogEntriesForSlowFollowers
}
if spec.Raft.ElectionTick == 0 {
spec.Raft.ElectionTick = defaultSpec.Raft.ElectionTick
}
if spec.Raft.HeartbeatTick == 0 {
spec.Raft.HeartbeatTick = defaultSpec.Raft.HeartbeatTick
}
if spec.Dispatcher.HeartbeatPeriod == 0 {
spec.Dispatcher.HeartbeatPeriod = defaultSpec.Dispatcher.HeartbeatPeriod
}
if spec.CAConfig.NodeCertExpiry == 0 {
spec.CAConfig.NodeCertExpiry = defaultSpec.CAConfig.NodeCertExpiry
}
if spec.Orchestration.TaskHistoryRetentionLimit == 0 {
spec.Orchestration.TaskHistoryRetentionLimit = defaultSpec.Orchestration.TaskHistoryRetentionLimit
}
return nil
}
func validateAndSanitizeJoinRequest(req *types.JoinRequest) error {
var err error
req.ListenAddr, err = validateAddr(req.ListenAddr)
if err != nil {
return fmt.Errorf("invalid ListenAddr %q: %v", req.ListenAddr, err)
}
if len(req.RemoteAddrs) == 0 {
return fmt.Errorf("at least 1 RemoteAddr is required to join")
}
for i := range req.RemoteAddrs {
req.RemoteAddrs[i], err = validateAddr(req.RemoteAddrs[i])
if err != nil {
return fmt.Errorf("invalid remoteAddr %q: %v", req.RemoteAddrs[i], err)
}
}
if req.CACertHash != "" {
if _, err := digest.ParseDigest(req.CACertHash); err != nil {
return fmt.Errorf("invalid CACertHash %q, %v", req.CACertHash, err)
}
}
return nil
}
func validateAddr(addr string) (string, error) {
if addr == "" {
return addr, fmt.Errorf("invalid empty address")
}
newaddr, err := opts.ParseTCPAddr(addr, defaultAddr)
if err != nil {
return addr, nil
}
return strings.TrimPrefix(newaddr, "tcp://"), nil
}
func errSwarmExists(node *node) error {
if node.NodeMembership() != swarmapi.NodeMembershipAccepted {
return ErrPendingSwarmExists
}
return ErrSwarmExists
}
func initClusterSpec(node *node, spec types.Spec) error {
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
for conn := range node.ListenControlSocket(ctx) {
if ctx.Err() != nil {
@@ -1028,15 +1183,14 @@ func initAcceptancePolicy(node *swarmagent.Node, acceptancePolicy types.Acceptan
cluster = lcr.Clusters[0]
break
}
spec := &cluster.Spec
if err := convert.SwarmSpecUpdateAcceptancePolicy(spec, acceptancePolicy); err != nil {
newspec, err := convert.SwarmSpecToGRPCandMerge(spec, &cluster.Spec)
if err != nil {
return fmt.Errorf("error updating cluster settings: %v", err)
}
_, err := client.UpdateCluster(ctx, &swarmapi.UpdateClusterRequest{
_, err = client.UpdateCluster(ctx, &swarmapi.UpdateClusterRequest{
ClusterID: cluster.ID,
ClusterVersion: &cluster.Meta.Version,
Spec: spec,
Spec: &newspec,
})
if err != nil {
return fmt.Errorf("error updating cluster settings: %v", err)

View File

@@ -148,17 +148,22 @@ func BasicNetworkFromGRPC(n swarmapi.Network) basictypes.NetworkResource {
}
}
return basictypes.NetworkResource{
nr := basictypes.NetworkResource{
ID: n.ID,
Name: n.Spec.Annotations.Name,
Scope: "swarm",
Driver: n.DriverState.Name,
EnableIPv6: spec.Ipv6Enabled,
IPAM: ipam,
Internal: spec.Internal,
Options: n.DriverState.Options,
Labels: n.Spec.Annotations.Labels,
}
if n.DriverState != nil {
nr.Driver = n.DriverState.Name
nr.Options = n.DriverState.Options
}
return nr
}
// BasicNetworkCreateToGRPC converts a NetworkCreateRequest to a grpc NetworkSpec.

View File

@@ -3,6 +3,7 @@ package convert
import (
"fmt"
"strings"
"time"
"golang.org/x/crypto/bcrypt"
@@ -26,14 +27,22 @@ func SwarmFromGRPC(c swarmapi.Cluster) types.Swarm {
HeartbeatTick: c.Spec.Raft.HeartbeatTick,
ElectionTick: c.Spec.Raft.ElectionTick,
},
Dispatcher: types.DispatcherConfig{
HeartbeatPeriod: c.Spec.Dispatcher.HeartbeatPeriod,
},
},
}
heartbeatPeriod, _ := ptypes.Duration(c.Spec.Dispatcher.HeartbeatPeriod)
swarm.Spec.Dispatcher.HeartbeatPeriod = uint64(heartbeatPeriod)
swarm.Spec.CAConfig.NodeCertExpiry, _ = ptypes.Duration(c.Spec.CAConfig.NodeCertExpiry)
for _, ca := range c.Spec.CAConfig.ExternalCAs {
swarm.Spec.CAConfig.ExternalCAs = append(swarm.Spec.CAConfig.ExternalCAs, &types.ExternalCA{
Protocol: types.ExternalCAProtocol(strings.ToLower(ca.Protocol.String())),
URL: ca.URL,
Options: ca.Options,
})
}
// Meta
swarm.Version.Index = c.Meta.Version.Index
swarm.CreatedAt, _ = ptypes.Timestamp(c.Meta.CreatedAt)
@@ -49,7 +58,8 @@ func SwarmFromGRPC(c swarmapi.Cluster) types.Swarm {
Autoaccept: policy.Autoaccept,
}
if policy.Secret != nil {
p.Secret = string(policy.Secret.Data)
secret := string(policy.Secret.Data)
p.Secret = &secret
}
swarm.Spec.AcceptancePolicy.Policies = append(swarm.Spec.AcceptancePolicy.Policies, p)
}
@@ -57,8 +67,8 @@ func SwarmFromGRPC(c swarmapi.Cluster) types.Swarm {
return swarm
}
// SwarmSpecToGRPC converts a Spec to a grpc ClusterSpec.
func SwarmSpecToGRPC(s types.Spec) (swarmapi.ClusterSpec, error) {
// SwarmSpecToGRPCandMerge converts a Spec to a grpc ClusterSpec and merge AcceptancePolicy from an existing grpc ClusterSpec if provided.
func SwarmSpecToGRPCandMerge(s types.Spec, existingSpec *swarmapi.ClusterSpec) (swarmapi.ClusterSpec, error) {
spec := swarmapi.ClusterSpec{
Annotations: swarmapi.Annotations{
Name: s.Name,
@@ -75,22 +85,37 @@ func SwarmSpecToGRPC(s types.Spec) (swarmapi.ClusterSpec, error) {
ElectionTick: s.Raft.ElectionTick,
},
Dispatcher: swarmapi.DispatcherConfig{
HeartbeatPeriod: s.Dispatcher.HeartbeatPeriod,
HeartbeatPeriod: ptypes.DurationProto(time.Duration(s.Dispatcher.HeartbeatPeriod)),
},
CAConfig: swarmapi.CAConfig{
NodeCertExpiry: ptypes.DurationProto(s.CAConfig.NodeCertExpiry),
},
}
if err := SwarmSpecUpdateAcceptancePolicy(&spec, s.AcceptancePolicy); err != nil {
for _, ca := range s.CAConfig.ExternalCAs {
protocol, ok := swarmapi.ExternalCA_CAProtocol_value[strings.ToUpper(string(ca.Protocol))]
if !ok {
return swarmapi.ClusterSpec{}, fmt.Errorf("invalid protocol: %q", ca.Protocol)
}
spec.CAConfig.ExternalCAs = append(spec.CAConfig.ExternalCAs, &swarmapi.ExternalCA{
Protocol: swarmapi.ExternalCA_CAProtocol(protocol),
URL: ca.URL,
Options: ca.Options,
})
}
if err := SwarmSpecUpdateAcceptancePolicy(&spec, s.AcceptancePolicy, existingSpec); err != nil {
return swarmapi.ClusterSpec{}, err
}
return spec, nil
}
// SwarmSpecUpdateAcceptancePolicy updates a grpc ClusterSpec using AcceptancePolicy.
func SwarmSpecUpdateAcceptancePolicy(spec *swarmapi.ClusterSpec, acceptancePolicy types.AcceptancePolicy) error {
func SwarmSpecUpdateAcceptancePolicy(spec *swarmapi.ClusterSpec, acceptancePolicy types.AcceptancePolicy, oldSpec *swarmapi.ClusterSpec) error {
spec.AcceptancePolicy.Policies = nil
hashs := make(map[string][]byte)
for _, p := range acceptancePolicy.Policies {
role, ok := swarmapi.NodeRole_value[strings.ToUpper(string(p.Role))]
if !ok {
@@ -102,11 +127,24 @@ func SwarmSpecUpdateAcceptancePolicy(spec *swarmapi.ClusterSpec, acceptancePolic
Autoaccept: p.Autoaccept,
}
if p.Secret != "" {
hashPwd, _ := bcrypt.GenerateFromPassword([]byte(p.Secret), 0)
if p.Secret != nil {
if *p.Secret == "" { // if provided secret is empty, it means erase previous secret.
policy.Secret = nil
} else { // if provided secret is not empty, we generate a new one.
hashPwd, ok := hashs[*p.Secret]
if !ok {
hashPwd, _ = bcrypt.GenerateFromPassword([]byte(*p.Secret), 0)
hashs[*p.Secret] = hashPwd
}
policy.Secret = &swarmapi.AcceptancePolicy_RoleAdmissionPolicy_HashedSecret{
Data: hashPwd,
Alg: "bcrypt",
}
}
} else if oldSecret := getOldSecret(oldSpec, policy.Role); oldSecret != nil { // else use the old one.
policy.Secret = &swarmapi.AcceptancePolicy_RoleAdmissionPolicy_HashedSecret{
Data: hashPwd,
Alg: "bcrypt",
Data: oldSecret.Data,
Alg: oldSecret.Alg,
}
}
@@ -114,3 +152,15 @@ func SwarmSpecUpdateAcceptancePolicy(spec *swarmapi.ClusterSpec, acceptancePolic
}
return nil
}
func getOldSecret(oldSpec *swarmapi.ClusterSpec, role swarmapi.NodeRole) *swarmapi.AcceptancePolicy_RoleAdmissionPolicy_HashedSecret {
if oldSpec == nil {
return nil
}
for _, p := range oldSpec.AcceptancePolicy.Policies {
if p.Role == role {
return p.Secret
}
}
return nil
}

View File

@@ -38,8 +38,13 @@ func newContainerAdapter(b executorpkg.Backend, task *api.Task) (*containerAdapt
}
func (c *containerAdapter) pullImage(ctx context.Context) error {
spec := c.container.spec()
// if the image needs to be pulled, the auth config will be retrieved and updated
encodedAuthConfig := c.container.task.ServiceAnnotations.Labels[fmt.Sprintf("%v.registryauth", systemLabelPrefix)]
var encodedAuthConfig string
if spec.PullOptions != nil {
encodedAuthConfig = spec.PullOptions.RegistryAuth
}
authConfig := &types.AuthConfig{}
if encodedAuthConfig != "" {
@@ -64,7 +69,7 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
}
return err
}
// TOOD(stevvooe): Report this status somewhere.
// TODO(stevvooe): Report this status somewhere.
logrus.Debugln("pull progress", m)
}
// if the final stream object contained an error, return it
@@ -120,13 +125,12 @@ func (c *containerAdapter) create(ctx context.Context, backend executorpkg.Backe
return err
}
// Docker daemon currently doesnt support multiple networks in container create
// Docker daemon currently doesn't support multiple networks in container create
// Connect to all other networks
nc := c.container.connectNetworkingConfig()
if nc != nil {
for n, ep := range nc.EndpointsConfig {
logrus.Errorf("CONNECT %s : %v", n, ep.IPAMConfig.IPv4Address)
if err := backend.ConnectContainerToNetwork(cr.ID, n, ep); err != nil {
return err
}
@@ -158,7 +162,7 @@ func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, er
// events issues a call to the events API and returns a channel with all
// events. The stream of events can be shutdown by cancelling the context.
//
// A chan struct{} is returned that will be closed if the event procressing
// A chan struct{} is returned that will be closed if the event processing
// fails and needs to be restarted.
func (c *containerAdapter) wait(ctx context.Context) error {
return c.backend.ContainerWaitWithContext(ctx, c.container.name())

View File

@@ -3,11 +3,12 @@ package container
import (
"errors"
"fmt"
"log"
"net"
"strings"
"time"
"github.com/Sirupsen/logrus"
clustertypes "github.com/docker/docker/daemon/cluster/provider"
"github.com/docker/docker/reference"
"github.com/docker/engine-api/types"
@@ -18,7 +19,7 @@ import (
)
const (
// Explictly use the kernel's default setting for CPU quota of 100ms.
// Explicitly use the kernel's default setting for CPU quota of 100ms.
// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
cpuQuotaPeriod = 100 * time.Millisecond
@@ -116,8 +117,8 @@ func (c *containerConfig) config() *enginecontainer.Config {
// If Command is provided, we replace the whole invocation with Command
// by replacing Entrypoint and specifying Cmd. Args is ignored in this
// case.
config.Entrypoint = append(config.Entrypoint, c.spec().Command[0])
config.Cmd = append(config.Cmd, c.spec().Command[1:]...)
config.Entrypoint = append(config.Entrypoint, c.spec().Command...)
config.Cmd = append(config.Cmd, c.spec().Args...)
} else if len(c.spec().Args) > 0 {
// In this case, we assume the image has an Entrypoint and Args
// specifies the arguments for that entrypoint.
@@ -345,9 +346,10 @@ func (c *containerConfig) serviceConfig() *clustertypes.ServiceConfig {
return nil
}
log.Printf("Creating service config in agent for t = %+v", c.task)
logrus.Debugf("Creating service config in agent for t = %+v", c.task)
svcCfg := &clustertypes.ServiceConfig{
Name: c.task.ServiceAnnotations.Name,
Aliases: make(map[string][]string),
ID: c.task.ServiceID,
VirtualAddresses: make(map[string]*clustertypes.VirtualAddress),
}
@@ -357,6 +359,9 @@ func (c *containerConfig) serviceConfig() *clustertypes.ServiceConfig {
// We support only IPv4 virtual IP for now.
IPv4: c.virtualIP(na.Network.ID),
}
if len(na.Aliases) > 0 {
svcCfg.Aliases[na.Network.ID] = na.Aliases
}
}
if c.task.Endpoint != nil {
@@ -399,6 +404,9 @@ func (c *containerConfig) networkCreateRequest(name string) (clustertypes.Networ
Driver: na.Network.IPAM.Driver.Name,
},
Options: na.Network.DriverState.Options,
Labels: na.Network.Spec.Annotations.Labels,
Internal: na.Network.Spec.Internal,
EnableIPv6: na.Network.Spec.Ipv6Enabled,
CheckDuplicate: true,
}

View File

@@ -2,13 +2,13 @@ package container
import (
"fmt"
"strings"
executorpkg "github.com/docker/docker/daemon/cluster/executor"
"github.com/docker/engine-api/types"
"github.com/docker/swarmkit/agent/exec"
"github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/log"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
@@ -59,7 +59,6 @@ func (r *controller) ContainerStatus(ctx context.Context) (*api.ContainerStatus,
// Update tasks a recent task update and applies it to the container.
func (r *controller) Update(ctx context.Context, t *api.Task) error {
log.G(ctx).Warnf("task updates not yet supported")
// TODO(stevvooe): While assignment of tasks is idempotent, we do allow
// updates of metadata, such as labelling, as well as any other properties
// that make sense.
@@ -84,31 +83,32 @@ func (r *controller) Prepare(ctx context.Context) error {
return err
}
for {
if err := r.checkClosed(); err != nil {
return err
}
if err := r.adapter.create(ctx, r.backend); err != nil {
if isContainerCreateNameConflict(err) {
if _, err := r.adapter.inspect(ctx); err != nil {
return err
}
if err := r.adapter.pullImage(ctx); err != nil {
// NOTE(stevvooe): We always try to pull the image to make sure we have
// the most up to date version. This will return an error, but we only
// log it. If the image truly doesn't exist, the create below will
// error out.
//
// This gives us some nice behavior where we use up to date versions of
// mutable tags, but will still run if the old image is available but a
// registry is down.
//
// If you don't want this behavior, lock down your image to an
// immutable tag or digest.
log.G(ctx).WithError(err).Error("pulling image failed")
}
// container is already created. success!
return exec.ErrTaskPrepared
}
if !strings.Contains(err.Error(), "No such image") { // todo: better error detection
return err
}
if err := r.adapter.pullImage(ctx); err != nil {
if err := r.adapter.create(ctx, r.backend); err != nil {
if isContainerCreateNameConflict(err) {
if _, err := r.adapter.inspect(ctx); err != nil {
return err
}
continue // retry to create the container
// container is already created. success!
return exec.ErrTaskPrepared
}
break
return err
}
return nil
@@ -135,7 +135,7 @@ func (r *controller) Start(ctx context.Context) error {
}
if err := r.adapter.start(ctx); err != nil {
return err
return errors.Wrap(err, "starting container failed")
}
return nil
@@ -151,9 +151,6 @@ func (r *controller) Wait(pctx context.Context) error {
defer cancel()
err := r.adapter.wait(ctx)
if err != nil {
return err
}
if ctx.Err() != nil {
return ctx.Err()
}
@@ -165,6 +162,7 @@ func (r *controller) Wait(pctx context.Context) error {
if ec, ok := err.(exec.ExitCoder); ok {
ee.code = ec.ExitCode()
}
return ee
}
return nil
}

View File

@@ -13,7 +13,7 @@ type NetworkCreateResponse struct {
ID string `json:"Id"`
}
// VirtualAddress represents a virtual adress.
// VirtualAddress represents a virtual address.
type VirtualAddress struct {
IPv4 string
IPv6 string
@@ -31,6 +31,7 @@ type PortConfig struct {
type ServiceConfig struct {
ID string
Name string
Aliases map[string][]string
VirtualAddresses map[string]*VirtualAddress
ExposedPorts []*PortConfig
}

View File

@@ -14,7 +14,6 @@ import (
"github.com/docker/docker/pkg/discovery"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/registry"
"github.com/docker/engine-api/types"
"github.com/imdario/mergo"
)
@@ -27,6 +26,9 @@ const (
// maximum number of uploads that
// may take place at a time for each push.
defaultMaxConcurrentUploads = 5
// stockRuntimeName is the reserved name/alias used to represent the
// OCI runtime being shipped with the docker daemon package.
stockRuntimeName = "runc"
)
const (
@@ -426,12 +428,12 @@ func ValidateConfiguration(config *Config) error {
// validate that "default" runtime is not reset
if runtimes := config.GetAllRuntimes(); len(runtimes) > 0 {
if _, ok := runtimes[types.DefaultRuntimeName]; ok {
return fmt.Errorf("runtime name '%s' is reserved", types.DefaultRuntimeName)
if _, ok := runtimes[stockRuntimeName]; ok {
return fmt.Errorf("runtime name '%s' is reserved", stockRuntimeName)
}
}
if defaultRuntime := config.GetDefaultRuntimeName(); defaultRuntime != "" && defaultRuntime != types.DefaultRuntimeName {
if defaultRuntime := config.GetDefaultRuntimeName(); defaultRuntime != "" && defaultRuntime != stockRuntimeName {
runtimes := config.GetAllRuntimes()
if _, ok := runtimes[defaultRuntime]; !ok {
return fmt.Errorf("specified default runtime '%s' does not exist", defaultRuntime)

View File

@@ -88,8 +88,8 @@ func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) strin
cmd.StringVar(&config.ContainerdAddr, []string{"-containerd"}, "", usageFn("Path to containerd socket"))
cmd.BoolVar(&config.LiveRestore, []string{"-live-restore"}, false, usageFn("Enable live restore of docker when containers are still running"))
config.Runtimes = make(map[string]types.Runtime)
cmd.Var(runconfigopts.NewNamedRuntimeOpt("runtimes", &config.Runtimes), []string{"-add-runtime"}, usageFn("Register an additional OCI compatible runtime"))
cmd.StringVar(&config.DefaultRuntime, []string{"-default-runtime"}, types.DefaultRuntimeName, usageFn("Default OCI runtime to be used"))
cmd.Var(runconfigopts.NewNamedRuntimeOpt("runtimes", &config.Runtimes, stockRuntimeName), []string{"-add-runtime"}, usageFn("Register an additional OCI compatible runtime"))
cmd.StringVar(&config.DefaultRuntime, []string{"-default-runtime"}, stockRuntimeName, usageFn("Default OCI runtime to be used"))
config.attachExperimentalFlags(cmd, usageFn)
}
@@ -123,7 +123,7 @@ func (config *Config) GetAllRuntimes() map[string]types.Runtime {
}
func (config *Config) isSwarmCompatible() error {
if config.IsValueSet("cluster-store") || config.IsValueSet("cluster-advertise") {
if config.ClusterStore != "" || config.ClusterAdvertise != "" {
return fmt.Errorf("--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode")
}
if config.LiveRestore {

View File

@@ -50,7 +50,7 @@ func (config *Config) GetRuntime(name string) *types.Runtime {
// GetDefaultRuntimeName returns the current default runtime
func (config *Config) GetDefaultRuntimeName() string {
return types.DefaultRuntimeName
return stockRuntimeName
}
// GetAllRuntimes returns a copy of the runtimes map

View File

@@ -408,7 +408,21 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) error {
updateSettings = true
}
// always connect default network first since only default
// network mode support link and we need do some setting
// on sandbox initialize for link, but the sandbox only be initialized
// on first network connecting.
defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName()
if nConf, ok := container.NetworkSettings.Networks[defaultNetName]; ok {
if err := daemon.connectToNetwork(container, defaultNetName, nConf, updateSettings); err != nil {
return err
}
}
for n, nConf := range container.NetworkSettings.Networks {
if n == defaultNetName {
continue
}
if err := daemon.connectToNetwork(container, n, nConf, updateSettings); err != nil {
return err
}

View File

@@ -179,7 +179,7 @@ func (daemon *Daemon) restore() error {
logrus.Errorf("Failed to restore with containerd: %q", err)
return
}
if !c.HostConfig.NetworkMode.IsContainer() {
if !c.HostConfig.NetworkMode.IsContainer() && c.IsRunning() {
options, err := daemon.buildSandboxOptions(c)
if err != nil {
logrus.Warnf("Failed build sandbox option to restore container %s: %v", c.ID, err)
@@ -385,6 +385,11 @@ func (daemon *Daemon) IsSwarmCompatible() error {
func NewDaemon(config *Config, registryService registry.Service, containerdRemote libcontainerd.Remote) (daemon *Daemon, err error) {
setDefaultMtu(config)
// Ensure that we have a correct root key limit for launching containers.
if err := ModifyRootKeyLimit(); err != nil {
logrus.Warnf("unable to modify root key limit, number of containers could be limitied by this quota: %v", err)
}
// Ensure we have compatible and valid configuration options
if err := verifyDaemonSettings(config); err != nil {
return nil, err

View File

@@ -532,7 +532,7 @@ func (daemon *Daemon) platformReload(config *Config, attributes *map[string]stri
if config.IsValueSet("runtimes") {
daemon.configStore.Runtimes = config.Runtimes
// Always set the default one
daemon.configStore.Runtimes[types.DefaultRuntimeName] = types.Runtime{Path: DefaultRuntimeBinary}
daemon.configStore.Runtimes[stockRuntimeName] = types.Runtime{Path: DefaultRuntimeBinary}
}
if config.DefaultRuntime != "" {
@@ -574,12 +574,12 @@ func verifyDaemonSettings(config *Config) error {
}
if config.DefaultRuntime == "" {
config.DefaultRuntime = types.DefaultRuntimeName
config.DefaultRuntime = stockRuntimeName
}
if config.Runtimes == nil {
config.Runtimes = make(map[string]types.Runtime)
}
config.Runtimes[types.DefaultRuntimeName] = types.Runtime{Path: DefaultRuntimeBinary}
config.Runtimes[stockRuntimeName] = types.Runtime{Path: DefaultRuntimeBinary}
return nil
}

View File

@@ -197,6 +197,8 @@ func DriverTestDiffApply(t testing.TB, fileCount int, drivername string, driverO
defer PutDriver(t)
base := stringid.GenerateRandomID()
upper := stringid.GenerateRandomID()
deleteFile := "file-remove.txt"
deleteFileContent := []byte("This file should get removed in upper!")
if err := driver.Create(base, "", "", nil); err != nil {
t.Fatal(err)
@@ -206,6 +208,10 @@ func DriverTestDiffApply(t testing.TB, fileCount int, drivername string, driverO
t.Fatal(err)
}
if err := addFile(driver, base, deleteFile, deleteFileContent); err != nil {
t.Fatal(err)
}
if err := driver.Create(upper, base, "", nil); err != nil {
t.Fatal(err)
}
@@ -213,6 +219,11 @@ func DriverTestDiffApply(t testing.TB, fileCount int, drivername string, driverO
if err := addManyFiles(driver, upper, fileCount, 6); err != nil {
t.Fatal(err)
}
if err := removeFile(driver, upper, deleteFile); err != nil {
t.Fatal(err)
}
diffSize, err := driver.DiffSize(upper, "")
if err != nil {
t.Fatal(err)
@@ -227,6 +238,10 @@ func DriverTestDiffApply(t testing.TB, fileCount int, drivername string, driverO
t.Fatal(err)
}
if err := checkFile(driver, diff, deleteFile, deleteFileContent); err != nil {
t.Fatal(err)
}
arch, err := driver.Diff(upper, base)
if err != nil {
t.Fatal(err)
@@ -248,9 +263,14 @@ func DriverTestDiffApply(t testing.TB, fileCount int, drivername string, driverO
if applyDiffSize != diffSize {
t.Fatalf("Apply diff size different, got %d, expected %d", applyDiffSize, diffSize)
}
if err := checkManyFiles(driver, diff, fileCount, 6); err != nil {
t.Fatal(err)
}
if err := checkFileRemoved(driver, diff, deleteFile); err != nil {
t.Fatal(err)
}
}
// DriverTestChanges tests computed changes on a layer matches changes made

View File

@@ -78,6 +78,32 @@ func addFile(drv graphdriver.Driver, layer, filename string, content []byte) err
return ioutil.WriteFile(path.Join(root, filename), content, 0755)
}
func removeFile(drv graphdriver.Driver, layer, filename string) error {
root, err := drv.Get(layer, "")
if err != nil {
return err
}
defer drv.Put(layer)
return os.Remove(path.Join(root, filename))
}
func checkFileRemoved(drv graphdriver.Driver, layer, filename string) error {
root, err := drv.Get(layer, "")
if err != nil {
return err
}
defer drv.Put(layer)
if _, err := os.Stat(path.Join(root, filename)); err == nil {
return fmt.Errorf("file still exists: %s", path.Join(root, filename))
} else if !os.IsNotExist(err) {
return err
}
return nil
}
func addManyFiles(drv graphdriver.Driver, layer string, count int, seed int64) error {
root, err := drv.Get(layer, "")
if err != nil {

View File

@@ -32,12 +32,6 @@ type mountOptions struct {
}
func mountFrom(dir, device, target, mType, label string) error {
r, w, err := os.Pipe()
if err != nil {
return fmt.Errorf("mountfrom pipe failure: %v", err)
}
options := &mountOptions{
Device: device,
Target: target,
@@ -47,7 +41,10 @@ func mountFrom(dir, device, target, mType, label string) error {
}
cmd := reexec.Command("docker-mountfrom", dir)
cmd.Stdin = r
w, err := cmd.StdinPipe()
if err != nil {
return fmt.Errorf("mountfrom error on pipe creation: %v", err)
}
output := bytes.NewBuffer(nil)
cmd.Stdout = output

View File

@@ -114,19 +114,10 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
backingFs = fsName
}
// check if they are running over btrfs, aufs, zfs or overlay
// check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs
switch fsMagic {
case graphdriver.FsMagicBtrfs:
logrus.Error("'overlay' is not supported over btrfs.")
return nil, graphdriver.ErrIncompatibleFS
case graphdriver.FsMagicAufs:
logrus.Error("'overlay' is not supported over aufs.")
return nil, graphdriver.ErrIncompatibleFS
case graphdriver.FsMagicZfs:
logrus.Error("'overlay' is not supported over zfs.")
return nil, graphdriver.ErrIncompatibleFS
case graphdriver.FsMagicOverlay:
logrus.Error("'overlay' is not supported over overlay.")
case graphdriver.FsMagicBtrfs, graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs:
logrus.Errorf("'overlay2' is not supported over %s", backingFs)
return nil, graphdriver.ErrIncompatibleFS
}

View File

@@ -99,6 +99,9 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
return nil, fmt.Errorf("BUG: zfs get all -t filesystem -rHp '%s' should contain '%s'", options.fsName, options.fsName)
}
if err := mount.MakePrivate(base); err != nil {
return nil, err
}
d := &Driver{
dataset: rootDataset,
options: options,

View File

@@ -104,27 +104,34 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
repoRefs = daemon.referenceStore.References(imgID)
// If this is a tag reference and all the remaining references
// to this image are digest references, delete the remaining
// references so that they don't prevent removal of the image.
// If a tag reference was removed and the only remaining
// references to the same repository are digest references,
// then clean up those digest references.
if _, isCanonical := parsedRef.(reference.Canonical); !isCanonical {
foundTagRef := false
foundRepoTagRef := false
for _, repoRef := range repoRefs {
if _, repoRefIsCanonical := repoRef.(reference.Canonical); !repoRefIsCanonical {
foundTagRef = true
if _, repoRefIsCanonical := repoRef.(reference.Canonical); !repoRefIsCanonical && parsedRef.Name() == repoRef.Name() {
foundRepoTagRef = true
break
}
}
if !foundTagRef {
if !foundRepoTagRef {
// Remove canonical references from same repository
remainingRefs := []reference.Named{}
for _, repoRef := range repoRefs {
if _, err := daemon.removeImageRef(repoRef); err != nil {
return records, err
}
if _, repoRefIsCanonical := repoRef.(reference.Canonical); repoRefIsCanonical && parsedRef.Name() == repoRef.Name() {
if _, err := daemon.removeImageRef(repoRef); err != nil {
return records, err
}
untaggedRecord := types.ImageDelete{Untagged: repoRef.String()}
records = append(records, untaggedRecord)
untaggedRecord := types.ImageDelete{Untagged: repoRef.String()}
records = append(records, untaggedRecord)
} else {
remainingRefs = append(remainingRefs, repoRef)
}
}
repoRefs = []reference.Named{}
repoRefs = remainingRefs
}
}
@@ -135,11 +142,10 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
removedRepositoryRef = true
} else {
// If an ID reference was given AND there is exactly one
// repository reference to the image then we will want to
// remove that reference.
// FIXME: Is this the behavior we want?
if len(repoRefs) == 1 {
// If an ID reference was given AND there is at most one tag
// reference to the image AND all references are within one
// repository, then remove all references.
if isSingleReference(repoRefs) {
c := conflictHard
if !force {
c |= conflictSoft &^ conflictActiveReference
@@ -148,21 +154,48 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
return nil, conflict
}
parsedRef, err := daemon.removeImageRef(repoRefs[0])
if err != nil {
return nil, err
for _, repoRef := range repoRefs {
parsedRef, err := daemon.removeImageRef(repoRef)
if err != nil {
return nil, err
}
untaggedRecord := types.ImageDelete{Untagged: parsedRef.String()}
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
records = append(records, untaggedRecord)
}
untaggedRecord := types.ImageDelete{Untagged: parsedRef.String()}
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
records = append(records, untaggedRecord)
}
}
return records, daemon.imageDeleteHelper(imgID, &records, force, prune, removedRepositoryRef)
}
// isSingleReference returns true when all references are from one repository
// and there is at most one tag. Returns false for empty input.
func isSingleReference(repoRefs []reference.Named) bool {
if len(repoRefs) <= 1 {
return len(repoRefs) == 1
}
var singleRef reference.Named
canonicalRefs := map[string]struct{}{}
for _, repoRef := range repoRefs {
if _, isCanonical := repoRef.(reference.Canonical); isCanonical {
canonicalRefs[repoRef.Name()] = struct{}{}
} else if singleRef == nil {
singleRef = repoRef
} else {
return false
}
}
if singleRef == nil {
// Just use first canonical ref
singleRef = repoRefs[0]
}
_, ok := canonicalRefs[singleRef.Name()]
return len(canonicalRefs) == 1 && ok
}
// isImageIDPrefix returns whether the given possiblePrefix is a prefix of the
// given imageID.
func isImageIDPrefix(imageID, possiblePrefix string) bool {

59
daemon/keys.go Normal file
View File

@@ -0,0 +1,59 @@
// +build linux
package daemon
import (
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
)
const (
rootKeyFile = "/proc/sys/kernel/keys/root_maxkeys"
rootBytesFile = "/proc/sys/kernel/keys/root_maxbytes"
rootKeyLimit = 1000000
// it is standard configuration to allocate 25 bytes per key
rootKeyByteMultiplier = 25
)
// ModifyRootKeyLimit checks to see if the root key limit is set to
// at least 1000000 and changes it to that limit along with the maxbytes
// allocated to the keys at a 25 to 1 multiplier.
func ModifyRootKeyLimit() error {
value, err := readRootKeyLimit(rootKeyFile)
if err != nil {
return err
}
if value < rootKeyLimit {
return setRootKeyLimit(rootKeyLimit)
}
return nil
}
func setRootKeyLimit(limit int) error {
keys, err := os.OpenFile(rootKeyFile, os.O_WRONLY, 0)
if err != nil {
return err
}
defer keys.Close()
if _, err := fmt.Fprintf(keys, "%d", limit); err != nil {
return err
}
bytes, err := os.OpenFile(rootBytesFile, os.O_WRONLY, 0)
if err != nil {
return err
}
defer bytes.Close()
_, err = fmt.Fprintf(bytes, "%d", limit*rootKeyByteMultiplier)
return err
}
func readRootKeyLimit(path string) (int, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return -1, err
}
return strconv.Atoi(strings.Trim(string(data), "\n"))
}

View File

@@ -0,0 +1,8 @@
// +build !linux
package daemon
// ModifyRootKeyLimit is an noop on unsupported platforms.
func ModifyRootKeyLimit() error {
return nil
}

View File

@@ -465,6 +465,7 @@ func (daemon *Daemon) transformContainer(container *container.Container, ctx *li
GlobalIPv6Address: network.GlobalIPv6Address,
GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen,
MacAddress: network.MacAddress,
NetworkID: network.NetworkID,
}
if network.IPAMConfig != nil {
networks[name].IPAMConfig = &networktypes.EndpointIPAMConfig{

View File

@@ -14,10 +14,11 @@ import (
// Writes are concurrent, so you need implement some sync in your logger
type Copier struct {
// srcs is map of name -> reader pairs, for example "stdout", "stderr"
srcs map[string]io.Reader
dst Logger
copyJobs sync.WaitGroup
closed chan struct{}
srcs map[string]io.Reader
dst Logger
copyJobs sync.WaitGroup
closeOnce sync.Once
closed chan struct{}
}
// NewCopier creates a new Copier
@@ -74,9 +75,7 @@ func (c *Copier) Wait() {
// Close closes the copier
func (c *Copier) Close() {
select {
case <-c.closed:
default:
c.closeOnce.Do(func() {
close(c.closed)
}
})
}

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