mirror of
https://github.com/moby/moby.git
synced 2026-01-12 03:01:38 +00:00
Compare commits
260 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
301bfbdd21 | ||
|
|
5213a0a67e | ||
|
|
164ab2cfc9 | ||
|
|
fc438e4171 | ||
|
|
c4b72004fd | ||
|
|
b74db4d8e3 | ||
|
|
ad2a9f3d1e | ||
|
|
782e218be6 | ||
|
|
5d402c1c27 | ||
|
|
76f6c611f1 | ||
|
|
9aa451ec5f | ||
|
|
98638feaae | ||
|
|
0d8b909281 | ||
|
|
f646c041b8 | ||
|
|
683700619d | ||
|
|
650ddc1f81 | ||
|
|
af3b1fbcbc | ||
|
|
785e38d57b | ||
|
|
cc68a963e0 | ||
|
|
a6bbdf347a | ||
|
|
89b2a14932 | ||
|
|
5df3c258a1 | ||
|
|
d3bbe11b99 | ||
|
|
b6766eb676 | ||
|
|
25ccb74f4f | ||
|
|
e85cc97d65 | ||
|
|
b0e27887f2 | ||
|
|
fa6685ca85 | ||
|
|
6ccc50d04e | ||
|
|
d544b3ee99 | ||
|
|
a54af42edb | ||
|
|
7abc60a93b | ||
|
|
3bdc7244a8 | ||
|
|
fa29ecbceb | ||
|
|
c70c8f1ed9 | ||
|
|
4efcbf5784 | ||
|
|
9114864ace | ||
|
|
0626fa4555 | ||
|
|
d41cd45c3d | ||
|
|
a9f9a79d5c | ||
|
|
08f1f62f41 | ||
|
|
e47de0ba4d | ||
|
|
e3007c51a5 | ||
|
|
4971a81f75 | ||
|
|
b42d833cd8 | ||
|
|
281f0fa53b | ||
|
|
36ada7d158 | ||
|
|
307195b3e1 | ||
|
|
d1024638f9 | ||
|
|
9396307501 | ||
|
|
54a58f9886 | ||
|
|
ab595882e3 | ||
|
|
fcd432d110 | ||
|
|
77efb507b3 | ||
|
|
c23ad97de5 | ||
|
|
b28e6b7eda | ||
|
|
785665203d | ||
|
|
36a62de41f | ||
|
|
e7d0711142 | ||
|
|
6e916fca02 | ||
|
|
bfe58ad819 | ||
|
|
8d485949d6 | ||
|
|
7e83ae34dc | ||
|
|
ef76dd0761 | ||
|
|
b706ee90ca | ||
|
|
2f4b69229a | ||
|
|
b34165ae37 | ||
|
|
cef1a10f5e | ||
|
|
2c531b4fb7 | ||
|
|
8fc5559841 | ||
|
|
ea8b5e07b6 | ||
|
|
3cf8c53515 | ||
|
|
15af7564cf | ||
|
|
340c9a4619 | ||
|
|
3b2ed5b2df | ||
|
|
0b5c960bb0 | ||
|
|
14a2985f37 | ||
|
|
a1d16b4557 | ||
|
|
290e0ea54c | ||
|
|
5901eda4f7 | ||
|
|
c2bea5ba26 | ||
|
|
4703824f00 | ||
|
|
4bf3f74126 | ||
|
|
5b1136ba8d | ||
|
|
f264a73afd | ||
|
|
fc4f927588 | ||
|
|
84314e09ab | ||
|
|
895569df9d | ||
|
|
21223f8873 | ||
|
|
ac9b38b60d | ||
|
|
53b4fd1d81 | ||
|
|
ae4c7b1493 | ||
|
|
71c0acf1ca | ||
|
|
97a5055198 | ||
|
|
2d532738b5 | ||
|
|
3eaabe0257 | ||
|
|
b9f11557af | ||
|
|
ce9bc253f6 | ||
|
|
6bbe5722aa | ||
|
|
cde2df6db9 | ||
|
|
d9cf30d7de | ||
|
|
9af185e3d0 | ||
|
|
8d2798c37e | ||
|
|
4858230a07 | ||
|
|
4dc5990d75 | ||
|
|
365d80b3e1 | ||
|
|
2535db8678 | ||
|
|
54edfc41c6 | ||
|
|
4a6f2274be | ||
|
|
db08f19e36 | ||
|
|
af370ff997 | ||
|
|
645836f250 | ||
|
|
3d85e51ef4 | ||
|
|
5618fbf18c | ||
|
|
bf312aca9c | ||
|
|
4172802d68 | ||
|
|
fb06ddf4db | ||
|
|
d126e2fb72 | ||
|
|
3130169eb2 | ||
|
|
260a835cb4 | ||
|
|
d42b3f6765 | ||
|
|
134990dd07 | ||
|
|
aa7da70459 | ||
|
|
8ff913e45f | ||
|
|
642c4a7814 | ||
|
|
3522bdfc99 | ||
|
|
490c26524c | ||
|
|
7ae170ddab | ||
|
|
b4f0d68843 | ||
|
|
cbd50baf8b | ||
|
|
0b25417cce | ||
|
|
eb405d2b73 | ||
|
|
133773a4d0 | ||
|
|
9cdfce68f8 | ||
|
|
716b5b2547 | ||
|
|
52b8adea4d | ||
|
|
b1b86e9166 | ||
|
|
35d6def3aa | ||
|
|
2153d9ec9d | ||
|
|
b45be307a3 | ||
|
|
f75a21ef32 | ||
|
|
dae909fb3e | ||
|
|
99589731ac | ||
|
|
fd018754e2 | ||
|
|
a573ab1f81 | ||
|
|
c774c390b1 | ||
|
|
896f8b337e | ||
|
|
40ff845220 | ||
|
|
60be8487c1 | ||
|
|
5e5e07e106 | ||
|
|
9e4c6c75f5 | ||
|
|
5d1b0aecd0 | ||
|
|
f685fe1a99 | ||
|
|
9e62a2aad2 | ||
|
|
460806241c | ||
|
|
651cace5ee | ||
|
|
e941f698da | ||
|
|
cc5c9013d9 | ||
|
|
c17ee39d12 | ||
|
|
eeb30821ea | ||
|
|
38c206f97b | ||
|
|
346c5297b0 | ||
|
|
e66633c39e | ||
|
|
2f69842afa | ||
|
|
c5d179891f | ||
|
|
4d7d1736bd | ||
|
|
a5b5bdbbb4 | ||
|
|
9e3bfd5864 | ||
|
|
bab77d4991 | ||
|
|
4e1ff10d60 | ||
|
|
2936442f9d | ||
|
|
c5769cf53b | ||
|
|
b84d18ec21 | ||
|
|
2e92a84fa8 | ||
|
|
deb08a1012 | ||
|
|
4b21fdc96a | ||
|
|
1818ca9d75 | ||
|
|
9db0bd88f5 | ||
|
|
b98f05b4f4 | ||
|
|
ed2fcd9a2a | ||
|
|
1e67fdf3e4 | ||
|
|
2a60e5cac6 | ||
|
|
c319887dbb | ||
|
|
3fd08cc5e6 | ||
|
|
cd062fd3b3 | ||
|
|
55186eae32 | ||
|
|
76215b3268 | ||
|
|
f97f3e98fc | ||
|
|
7500c8cc72 | ||
|
|
c3eed8430c | ||
|
|
073d7841b4 | ||
|
|
8b0179c771 | ||
|
|
85d1517184 | ||
|
|
b59dced332 | ||
|
|
89ede3ae23 | ||
|
|
8facb73a8f | ||
|
|
c4fa814ecd | ||
|
|
e9279d57f7 | ||
|
|
01c531a72e | ||
|
|
a17e61c020 | ||
|
|
19b22712c0 | ||
|
|
e6629d4c10 | ||
|
|
0c598f34b6 | ||
|
|
e799da7e6a | ||
|
|
50552642ca | ||
|
|
474631498c | ||
|
|
08ccfd36e1 | ||
|
|
c3b3f8201a | ||
|
|
81a3a72727 | ||
|
|
70c594508f | ||
|
|
1fa9574e2b | ||
|
|
4a59dc5a41 | ||
|
|
c4b33d5334 | ||
|
|
197d61d01a | ||
|
|
abe8c11e36 | ||
|
|
08d09e7733 | ||
|
|
e23f622d38 | ||
|
|
73f7f515be | ||
|
|
501b0c387d | ||
|
|
cf1d012fda | ||
|
|
ad37aac45b | ||
|
|
f66010ad31 | ||
|
|
34fca93daf | ||
|
|
048db1da22 | ||
|
|
e768fc8468 | ||
|
|
31755449e1 | ||
|
|
fef9c5b432 | ||
|
|
519ac1252c | ||
|
|
89276c679e | ||
|
|
5a71ca6739 | ||
|
|
92c9bab6ab | ||
|
|
f04334ea04 | ||
|
|
ea799625bd | ||
|
|
3ef31215f4 | ||
|
|
4b03e857de | ||
|
|
c5e8051c81 | ||
|
|
6d324b4192 | ||
|
|
060330bf46 | ||
|
|
413155df6e | ||
|
|
48ce060e8c | ||
|
|
6106313b20 | ||
|
|
ae4f265053 | ||
|
|
4fc85b47fc | ||
|
|
3e890411bc | ||
|
|
1987d6e5df | ||
|
|
e4995d1517 | ||
|
|
6558158dc3 | ||
|
|
c985e2b84b | ||
|
|
6be088a3eb | ||
|
|
8c390f0987 | ||
|
|
d8ba21d07d | ||
|
|
b9d6c87592 | ||
|
|
03238022c8 | ||
|
|
1f8ea55c3d | ||
|
|
6fa49df0d9 | ||
|
|
32a5308237 | ||
|
|
76489af40f | ||
|
|
fad79467dd | ||
|
|
e651c1b2b9 | ||
|
|
b6f3c16ddc |
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -19,12 +19,5 @@ Please provide the following information:
|
||||
|
||||
**- How to verify it**
|
||||
|
||||
**- Description for the changelog**
|
||||
<!--
|
||||
Write a short (one line) summary that describes the changes in this
|
||||
pull request for inclusion in the changelog:
|
||||
-->
|
||||
|
||||
|
||||
**- A picture of a cute animal (not mandatory but encouraged)**
|
||||
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -7,13 +7,10 @@
|
||||
*.test
|
||||
.*.swp
|
||||
.DS_Store
|
||||
# a .bashrc may be added to customize the build environment
|
||||
.bashrc
|
||||
.gopath/
|
||||
autogen/
|
||||
bundles/
|
||||
cmd/dockerd/dockerd
|
||||
cmd/docker/docker
|
||||
docker/docker
|
||||
dockerversion/version_autogen.go
|
||||
docs/AWS_S3_BUCKET
|
||||
docs/GITCOMMIT
|
||||
|
||||
17
.mailmap
17
.mailmap
@@ -90,7 +90,6 @@ Sven Dowideit <SvenDowideit@home.org.au>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@fosiki.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@docker.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <¨SvenDowideit@home.org.au¨>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@home.org.au>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@users.noreply.github.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <sven@t440s.home.gateway>
|
||||
<alexl@redhat.com> <alexander.larsson@gmail.com>
|
||||
@@ -160,7 +159,6 @@ Deshi Xiao <dxiao@redhat.com> <xiaods@gmail.com>
|
||||
Doug Davis <dug@us.ibm.com> <duglin@users.noreply.github.com>
|
||||
Jacob Atzen <jacob@jacobatzen.dk> <jatzen@gmail.com>
|
||||
Jeff Nickoloff <jeff.nickoloff@gmail.com> <jeff@allingeek.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com> <jhowardmsft@users.noreply.github.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com> <john.howard@microsoft.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com> <jhoward@microsoft.com>
|
||||
@@ -236,19 +234,4 @@ Vishnu Kannan <vishnuk@google.com>
|
||||
xlgao-zju <xlgao@zju.edu.cn> xlgao <xlgao@zju.edu.cn>
|
||||
yuchangchun <yuchangchun1@huawei.com> y00277921 <yuchangchun1@huawei.com>
|
||||
<zij@case.edu> <zjaffee@us.ibm.com>
|
||||
<anujbahuguna.dev@gmail.com> <abahuguna@fiberlink.com>
|
||||
<eungjun.yi@navercorp.com> <semtlenori@gmail.com>
|
||||
<haosw@cn.ibm.com> <haoshuwei1989@163.com>
|
||||
Hao Shu Wei <haosw@cn.ibm.com>
|
||||
<matt.bentley@docker.com> <mbentley@mbentley.net>
|
||||
<MihaiBorob@gmail.com> <MihaiBorobocea@gmail.com>
|
||||
<redmond.martin@gmail.com> <xgithub@redmond5.com>
|
||||
<redmond.martin@gmail.com> <martin@tinychat.com>
|
||||
<srbrahma@us.ibm.com> <sbrahma@us.ibm.com>
|
||||
<suda.akihiro@lab.ntt.co.jp> <suda.kyoto@gmail.com>
|
||||
<thomas@gazagnaire.org> <thomas@gazagnaire.com>
|
||||
Shengbo Song <thomassong@tencent.com> mYmNeo <mymneo@163.com>
|
||||
Shengbo Song <thomassong@tencent.com>
|
||||
<sylvain@ascribe.io> <sylvain.bellemare@ezeep.com>
|
||||
Sylvain Bellemare <sylvain@ascribe.io>
|
||||
|
||||
|
||||
72
AUTHORS
72
AUTHORS
@@ -31,7 +31,6 @@ ajneu <ajneu@users.noreply.github.com>
|
||||
Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
|
||||
Al Tobey <al@ooyala.com>
|
||||
alambike <alambike@gmail.com>
|
||||
Alan Scherger <flyinprogrammer@gmail.com>
|
||||
Alan Thompson <cloojure@gmail.com>
|
||||
Albert Callarisa <shark234@gmail.com>
|
||||
Albert Zhang <zhgwenming@gmail.com>
|
||||
@@ -42,7 +41,6 @@ Alessandro Boch <aboch@docker.com>
|
||||
Alessio Biancalana <dottorblaster@gmail.com>
|
||||
Alex Chan <alex@alexwlchan.net>
|
||||
Alex Crawford <alex.crawford@coreos.com>
|
||||
Alex Ellis <alexellis2@gmail.com>
|
||||
Alex Gaynor <alex.gaynor@gmail.com>
|
||||
Alex Samorukov <samm@os2.kiev.ua>
|
||||
Alex Warhawk <ax.warhawk@gmail.com>
|
||||
@@ -58,7 +56,6 @@ Alexey Guskov <lexag@mail.ru>
|
||||
Alexey Kotlyarov <alexey@infoxchange.net.au>
|
||||
Alexey Shamrin <shamrin@gmail.com>
|
||||
Alexis THOMAS <fr.alexisthomas@gmail.com>
|
||||
Ali Dehghani <ali.dehghani.g@gmail.com>
|
||||
Allen Madsen <blatyo@gmail.com>
|
||||
Allen Sun <allen.sun@daocloud.io>
|
||||
almoehi <almoehi@users.noreply.github.com>
|
||||
@@ -161,7 +158,6 @@ bobby abbott <ttobbaybbob@gmail.com>
|
||||
boucher <rboucher@gmail.com>
|
||||
Bouke Haarsma <bouke@webatoom.nl>
|
||||
Boyd Hemphill <boyd@feedmagnet.com>
|
||||
boynux <boynux@gmail.com>
|
||||
Bradley Cicenas <bradley.cicenas@gmail.com>
|
||||
Bradley Wright <brad@intranation.com>
|
||||
Brandon Liu <bdon@bdon.org>
|
||||
@@ -169,7 +165,6 @@ Brandon Philips <brandon@ifup.org>
|
||||
Brandon Rhodes <brandon@rhodesmill.org>
|
||||
Brendan Dixon <brendand@microsoft.com>
|
||||
Brent Salisbury <brent.salisbury@docker.com>
|
||||
Brett Higgins <brhiggins@arbor.net>
|
||||
Brett Kochendorfer <brett.kochendorfer@gmail.com>
|
||||
Brian (bex) Exelbierd <bexelbie@redhat.com>
|
||||
Brian Bland <brian.bland@docker.com>
|
||||
@@ -181,7 +176,6 @@ Brian McCallister <brianm@skife.org>
|
||||
Brian Olsen <brian@maven-group.org>
|
||||
Brian Shumate <brian@couchbase.com>
|
||||
Brian Torres-Gil <brian@dralth.com>
|
||||
Brian Trump <btrump@yelp.com>
|
||||
Brice Jaglin <bjaglin@teads.tv>
|
||||
Briehan Lombaard <briehan.lombaard@gmail.com>
|
||||
Bruno Bigras <bigras.bruno@gmail.com>
|
||||
@@ -203,7 +197,6 @@ Campbell Allen <campbell.allen@gmail.com>
|
||||
Candid Dauth <cdauth@cdauth.eu>
|
||||
Carl Henrik Lunde <chlunde@ping.uio.no>
|
||||
Carl X. Su <bcbcarl@gmail.com>
|
||||
Carlos Alexandro Becker <caarlos0@gmail.com>
|
||||
Carlos Sanchez <carlos@apache.org>
|
||||
Carol Fager-Higgins <carol.fager-higgins@docker.com>
|
||||
Cary <caryhartline@users.noreply.github.com>
|
||||
@@ -215,7 +208,6 @@ Chance Zibolski <chance.zibolski@gmail.com>
|
||||
Chander G <chandergovind@gmail.com>
|
||||
Charles Chan <charleswhchan@users.noreply.github.com>
|
||||
Charles Hooper <charles.hooper@dotcloud.com>
|
||||
Charles Law <claw@conduce.com>
|
||||
Charles Lindsay <chaz@chazomatic.us>
|
||||
Charles Merriam <charles.merriam@gmail.com>
|
||||
Charles Sarrazin <charles@sarraz.in>
|
||||
@@ -245,7 +237,6 @@ Chris Weyl <cweyl@alumni.drew.edu>
|
||||
chrismckinnel <chris.mckinnel@tangentlabs.co.uk>
|
||||
Christian Berendt <berendt@b1-systems.de>
|
||||
Christian Böhme <developement@boehme3d.de>
|
||||
Christian Persson <saser@live.se>
|
||||
Christian Rotzoll <ch.rotzoll@gmail.com>
|
||||
Christian Simon <simon@swine.de>
|
||||
Christian Stefanescu <st.chris@gmail.com>
|
||||
@@ -278,7 +269,6 @@ Daan van Berkel <daan.v.berkel.1980@gmail.com>
|
||||
Daehyeok Mun <daehyeok@gmail.com>
|
||||
Dafydd Crosby <dtcrsby@gmail.com>
|
||||
dalanlan <dalanlan925@gmail.com>
|
||||
Damien Nadé <github@livna.org>
|
||||
Damien Nozay <damien.nozay@gmail.com>
|
||||
Damjan Georgievski <gdamjan@gmail.com>
|
||||
Dan Anolik <dan@anolik.net>
|
||||
@@ -316,7 +306,6 @@ Darren Shepherd <darren.s.shepherd@gmail.com>
|
||||
Darren Stahl <darst@microsoft.com>
|
||||
Dave Barboza <dbarboza@datto.com>
|
||||
Dave Henderson <Dave.Henderson@ca.ibm.com>
|
||||
Dave MacDonald <mindlapse@gmail.com>
|
||||
Dave Tucker <dt@docker.com>
|
||||
David Anderson <dave@natulte.net>
|
||||
David Calavera <david.calavera@gmail.com>
|
||||
@@ -341,7 +330,6 @@ Davide Ceretti <davide.ceretti@hogarthww.com>
|
||||
Dawn Chen <dawnchen@google.com>
|
||||
dcylabs <dcylabs@gmail.com>
|
||||
decadent <decadent@users.noreply.github.com>
|
||||
deed02392 <georgehafiz@gmail.com>
|
||||
Deng Guangxing <dengguangxing@huawei.com>
|
||||
Deni Bertovic <deni@kset.org>
|
||||
Denis Gladkikh <denis@gladkikh.email>
|
||||
@@ -359,13 +347,11 @@ Dharmit Shah <shahdharmit@gmail.com>
|
||||
Dieter Reuter <dieter.reuter@me.com>
|
||||
Dima Stopel <dima@twistlock.com>
|
||||
Dimitri John Ledkov <dimitri.j.ledkov@intel.com>
|
||||
Dimitry Andric <d.andric@activevideo.com>
|
||||
Dinesh Subhraveti <dineshs@altiscale.com>
|
||||
Diogo Monica <diogo@docker.com>
|
||||
DiuDiugirl <sophia.wang@pku.edu.cn>
|
||||
Djibril Koné <kone.djibril@gmail.com>
|
||||
dkumor <daniel@dkumor.com>
|
||||
Dmitri Logvinenko <dmitri.logvinenko@gmail.com>
|
||||
Dmitry Demeshchuk <demeshchuk@gmail.com>
|
||||
Dmitry Gusev <dmitry.gusev@gmail.com>
|
||||
Dmitry V. Krivenok <krivenok.dmitry@gmail.com>
|
||||
@@ -410,7 +396,6 @@ Eric Rafaloff <erafaloff@gmail.com>
|
||||
Eric Rosenberg <ehaydenr@users.noreply.github.com>
|
||||
Eric Sage <eric.david.sage@gmail.com>
|
||||
Eric Windisch <eric@windisch.us>
|
||||
Eric Yang <windfarer@gmail.com>
|
||||
Eric-Olivier Lamey <eo@lamey.me>
|
||||
Erik Bray <erik.m.bray@gmail.com>
|
||||
Erik Dubbelboer <erik@dubbelboer.com>
|
||||
@@ -438,7 +423,6 @@ Fabiano Rosas <farosas@br.ibm.com>
|
||||
Fabio Falci <fabiofalci@gmail.com>
|
||||
Fabio Rehm <fgrehm@gmail.com>
|
||||
Fabrizio Regini <freegenie@gmail.com>
|
||||
Fabrizio Soppelsa <fsoppelsa@mirantis.com>
|
||||
Faiz Khan <faizkhan00@gmail.com>
|
||||
falmp <chico.lopes@gmail.com>
|
||||
Fangyuan Gao <21551127@zju.edu.cn>
|
||||
@@ -457,7 +441,6 @@ Filipe Oliveira <contato@fmoliveira.com.br>
|
||||
fl0yd <fl0yd@me.com>
|
||||
Flavio Castelli <fcastelli@suse.com>
|
||||
FLGMwt <ryan.stelly@live.com>
|
||||
Florian <FWirtz@users.noreply.github.com>
|
||||
Florian Klein <florian.klein@free.fr>
|
||||
Florian Maier <marsmensch@users.noreply.github.com>
|
||||
Florian Weingarten <flo@hackvalue.de>
|
||||
@@ -474,7 +457,6 @@ Frederick F. Kautz IV <fkautz@redhat.com>
|
||||
Frederik Loeffert <frederik@zitrusmedia.de>
|
||||
Frederik Nordahl Jul Sabroe <frederikns@gmail.com>
|
||||
Freek Kalter <freek@kalteronline.org>
|
||||
fy2462 <fy2462@gmail.com>
|
||||
Félix Baylac-Jacqué <baylac.felix@gmail.com>
|
||||
Félix Cantournet <felix.cantournet@cloudwatt.com>
|
||||
Gabe Rosenhouse <gabe@missionst.com>
|
||||
@@ -524,7 +506,6 @@ gwx296173 <gaojing3@huawei.com>
|
||||
Günter Zöchbauer <guenter@gzoechbauer.com>
|
||||
Hans Kristian Flaatten <hans@starefossen.com>
|
||||
Hans Rødtang <hansrodtang@gmail.com>
|
||||
Hao Shu Wei <haosw@cn.ibm.com>
|
||||
Hao Zhang <21521210@zju.edu.cn>
|
||||
Harald Albers <github@albersweb.de>
|
||||
Harley Laue <losinggeneration@gmail.com>
|
||||
@@ -549,7 +530,6 @@ huqun <huqun@zju.edu.cn>
|
||||
Huu Nguyen <huu@prismskylabs.com>
|
||||
hyeongkyu.lee <hyeongkyu.lee@navercorp.com>
|
||||
hyp3rdino <markus.kortlang@lhsystems.com>
|
||||
Hyzhou <1187766782@qq.com>
|
||||
Ian Babrou <ibobrik@gmail.com>
|
||||
Ian Bishop <ianbishop@pace7.com>
|
||||
Ian Bull <irbull@gmail.com>
|
||||
@@ -572,7 +552,6 @@ Isabel Jimenez <contact.isabeljimenez@gmail.com>
|
||||
Isao Jonas <isao.jonas@gmail.com>
|
||||
Ivan Babrou <ibobrik@gmail.com>
|
||||
Ivan Fraixedes <ifcdev@gmail.com>
|
||||
Ivan Grcic <igrcic@gmail.com>
|
||||
J Bruni <joaohbruni@yahoo.com.br>
|
||||
J. Nunn <jbnunn@gmail.com>
|
||||
Jack Danger Canty <jackdanger@squareup.com>
|
||||
@@ -603,7 +582,6 @@ Jan-Jaap Driessen <janjaapdriessen@gmail.com>
|
||||
Jana Radhakrishnan <mrjana@docker.com>
|
||||
Januar Wayong <januar@gmail.com>
|
||||
Jared Biel <jared.biel@bolderthinking.com>
|
||||
Jared Hocutt <jaredh@netapp.com>
|
||||
Jaroslaw Zabiello <hipertracker@gmail.com>
|
||||
jaseg <jaseg@jaseg.net>
|
||||
Jasmine Hegman <jasmine@jhegman.com>
|
||||
@@ -648,7 +626,6 @@ Jesse Dubay <jesse@thefortytwo.net>
|
||||
Jessica Frazelle <jess@mesosphere.com>
|
||||
Jezeniel Zapanta <jpzapanta22@gmail.com>
|
||||
jgeiger <jgeiger@gmail.com>
|
||||
Jhon Honce <jhonce@redhat.com>
|
||||
Jian Zhang <zhangjian.fnst@cn.fujitsu.com>
|
||||
jianbosun <wonderflow.sun@gmail.com>
|
||||
Jilles Oldenbeuving <ojilles@gmail.com>
|
||||
@@ -720,7 +697,6 @@ Julien Bisconti <veggiemonk@users.noreply.github.com>
|
||||
Julien Bordellier <julienbordellier@gmail.com>
|
||||
Julien Dubois <julien.dubois@gmail.com>
|
||||
Julien Pervillé <julien.perville@perfect-memory.com>
|
||||
Julio Montes <imc.coder@gmail.com>
|
||||
Jun-Ru Chang <jrjang@gmail.com>
|
||||
Jussi Nummelin <jussi.nummelin@gmail.com>
|
||||
Justas Brazauskas <brazauskasjustas@gmail.com>
|
||||
@@ -728,14 +704,12 @@ Justin Cormack <justin.cormack@docker.com>
|
||||
Justin Force <justin.force@gmail.com>
|
||||
Justin Plock <jplock@users.noreply.github.com>
|
||||
Justin Simonelis <justin.p.simonelis@gmail.com>
|
||||
Justin Terry <juterry@microsoft.com>
|
||||
Jyrki Puttonen <jyrkiput@gmail.com>
|
||||
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
|
||||
Jörg Thalheim <joerg@higgsboson.tk>
|
||||
Kai Blin <kai@samba.org>
|
||||
Kai Qiang Wu(Kennan) <wkqwu@cn.ibm.com>
|
||||
Kamil Domański <kamil@domanski.co>
|
||||
kamjar gerami <kami.gerami@gmail.com>
|
||||
Kanstantsin Shautsou <kanstantsin.sha@gmail.com>
|
||||
Karan Lyons <karan@karanlyons.com>
|
||||
Kareem Khazem <karkhaz@karkhaz.com>
|
||||
@@ -747,7 +721,6 @@ Kato Kazuyoshi <kato.kazuyoshi@gmail.com>
|
||||
Katrina Owen <katrina.owen@gmail.com>
|
||||
Kawsar Saiyeed <kawsar.saiyeed@projiris.com>
|
||||
kayrus <kay.diam@gmail.com>
|
||||
Ke Xu <leonhartx.k@gmail.com>
|
||||
Keli Hu <dev@keli.hu>
|
||||
Ken Cochrane <kencochrane@gmail.com>
|
||||
Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com>
|
||||
@@ -762,7 +735,6 @@ Kevin P. Kucharczyk <kevinkucharczyk@gmail.com>
|
||||
Kevin Shi <kshi@andrew.cmu.edu>
|
||||
Kevin Wallace <kevin@pentabarf.net>
|
||||
Kevin Yap <me@kevinyap.ca>
|
||||
kevinmeredith <kevin.m.meredith@gmail.com>
|
||||
Keyvan Fatehi <keyvanfatehi@gmail.com>
|
||||
kies <lleelm@gmail.com>
|
||||
Kim BKC Carlbacker <kim.carlbacker@gmail.com>
|
||||
@@ -792,7 +764,6 @@ Lalatendu Mohanty <lmohanty@redhat.com>
|
||||
lalyos <lalyos@yahoo.com>
|
||||
Lance Chen <cyen0312@gmail.com>
|
||||
Lance Kinley <lkinley@loyaltymethods.com>
|
||||
Lars Butler <Lars.Butler@gmail.com>
|
||||
Lars Kellogg-Stedman <lars@redhat.com>
|
||||
Lars R. Damerow <lars@pixar.com>
|
||||
Laszlo Meszaros <lacienator@gmail.com>
|
||||
@@ -814,8 +785,6 @@ Liang Mingqiang <mqliang.zju@gmail.com>
|
||||
Liang-Chi Hsieh <viirya@gmail.com>
|
||||
liaoqingwei <liaoqingwei@huawei.com>
|
||||
limsy <seongyeol37@gmail.com>
|
||||
Lin Lu <doraalin@163.com>
|
||||
LingFaKe <lingfake@huawei.com>
|
||||
Linus Heckemann <lheckemann@twig-world.com>
|
||||
Liran Tal <liran.tal@gmail.com>
|
||||
Liron Levin <liron@twistlock.com>
|
||||
@@ -835,7 +804,6 @@ Lucas Chan <lucas-github@lucaschan.com>
|
||||
Luis Martínez de Bartolomé Izquierdo <lmartinez@biicode.com>
|
||||
Lukas Waslowski <cr7pt0gr4ph7@gmail.com>
|
||||
lukaspustina <lukas.pustina@centerdevice.com>
|
||||
Lukasz Zajaczkowski <Lukasz.Zajaczkowski@ts.fujitsu.com>
|
||||
lukemarsden <luke@digital-crocus.com>
|
||||
Lynda O'Leary <lyndaoleary29@gmail.com>
|
||||
Lénaïc Huard <lhuard@amadeus.com>
|
||||
@@ -850,7 +818,6 @@ Malte Janduda <mail@janduda.net>
|
||||
manchoz <giampaolo@trampolineup.com>
|
||||
Manfred Touron <m@42.am>
|
||||
Manfred Zabarauskas <manfredas@zabarauskas.com>
|
||||
mansinahar <mansinahar@users.noreply.github.com>
|
||||
Manuel Meurer <manuel@krautcomputing.com>
|
||||
Manuel Woelker <github@manuel.woelker.org>
|
||||
mapk0y <mapk0y@gmail.com>
|
||||
@@ -881,7 +848,7 @@ Martijn van Oosterhout <kleptog@svana.org>
|
||||
Martin Honermeyer <maze@strahlungsfrei.de>
|
||||
Martin Kelly <martin@surround.io>
|
||||
Martin Mosegaard Amdisen <martin.amdisen@praqma.com>
|
||||
Martin Redmond <redmond.martin@gmail.com>
|
||||
Martin Redmond <martin@tinychat.com>
|
||||
Mary Anthony <mary.anthony@docker.com>
|
||||
Masahito Zembutsu <zembutsu@users.noreply.github.com>
|
||||
Mason Malone <mason.malone@gmail.com>
|
||||
@@ -890,7 +857,7 @@ Mathias Monnerville <mathias@monnerville.com>
|
||||
Mathieu Le Marec - Pasquet <kiorky@cryptelium.net>
|
||||
Matt Apperson <me@mattapperson.com>
|
||||
Matt Bachmann <bachmann.matt@gmail.com>
|
||||
Matt Bentley <matt.bentley@docker.com>
|
||||
Matt Bentley <mbentley@mbentley.net>
|
||||
Matt Haggard <haggardii@gmail.com>
|
||||
Matt McCormick <matt.mccormick@kitware.com>
|
||||
Matt Moore <mattmoor@google.com>
|
||||
@@ -925,10 +892,8 @@ Michael Brown <michael@netdirect.ca>
|
||||
Michael Chiang <mchiang@docker.com>
|
||||
Michael Crosby <michael@docker.com>
|
||||
Michael Currie <mcurrie@bruceforceresearch.com>
|
||||
Michael Friis <friism@gmail.com>
|
||||
Michael Gorsuch <gorsuch@github.com>
|
||||
Michael Grauer <michael.grauer@kitware.com>
|
||||
Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
||||
Michael Hudson-Doyle <michael.hudson@linaro.org>
|
||||
Michael Huettermann <michael@huettermann.net>
|
||||
Michael Käufl <docker@c.michael-kaeufl.de>
|
||||
@@ -948,7 +913,7 @@ Michał Czeraszkiewicz <czerasz@gmail.com>
|
||||
Michiel@unhosted <michiel@unhosted.org>
|
||||
Miguel Angel Fernández <elmendalerenda@gmail.com>
|
||||
Miguel Morales <mimoralea@gmail.com>
|
||||
Mihai Borobocea <MihaiBorob@gmail.com>
|
||||
Mihai Borobocea <MihaiBorobocea@gmail.com>
|
||||
Mihuleacc Sergiu <mihuleac.sergiu@gmail.com>
|
||||
Mike Brown <brownwm@us.ibm.com>
|
||||
Mike Chelen <michael.chelen@gmail.com>
|
||||
@@ -961,7 +926,6 @@ Mike Leone <mleone896@gmail.com>
|
||||
Mike MacCana <mike.maccana@gmail.com>
|
||||
Mike Naberezny <mike@naberezny.com>
|
||||
Mike Snitzer <snitzer@redhat.com>
|
||||
mikelinjie <294893458@qq.com>
|
||||
Mikhail Sobolev <mss@mawhrin.net>
|
||||
Miloslav Trmač <mitr@redhat.com>
|
||||
mingqing <limingqing@cyou-inc.com>
|
||||
@@ -981,18 +945,14 @@ mqliang <mqliang.zju@gmail.com>
|
||||
Mrunal Patel <mrunalp@gmail.com>
|
||||
msabansal <sabansal@microsoft.com>
|
||||
mschurenko <matt.schurenko@gmail.com>
|
||||
muge <stevezhang2014@gmail.com>
|
||||
Mustafa Akın <mustafa91@gmail.com>
|
||||
Muthukumar R <muthur@gmail.com>
|
||||
Máximo Cuadros <mcuadros@gmail.com>
|
||||
Médi-Rémi Hashim <medimatrix@users.noreply.github.com>
|
||||
Nahum Shalman <nshalman@omniti.com>
|
||||
Nakul Pathak <nakulpathak3@hotmail.com>
|
||||
Nalin Dahyabhai <nalin@redhat.com>
|
||||
Nan Monnand Deng <monnand@gmail.com>
|
||||
Naoki Orii <norii@cs.cmu.edu>
|
||||
Natalie Parker <nparker@omnifone.com>
|
||||
Natanael Copa <natanael.copa@docker.com>
|
||||
Nate Brennand <nate.brennand@clever.com>
|
||||
Nate Eagleson <nate@nateeag.com>
|
||||
Nate Jones <nate@endot.org>
|
||||
@@ -1020,7 +980,6 @@ Nicolás Hock Isaza <nhocki@gmail.com>
|
||||
Nigel Poulton <nigelpoulton@hotmail.com>
|
||||
NikolaMandic <mn080202@gmail.com>
|
||||
nikolas <nnyby@columbia.edu>
|
||||
Nirmal Mehta <nirmalkmehta@gmail.com>
|
||||
Nishant Totla <nishanttotla@gmail.com>
|
||||
NIWA Hideyuki <niwa.niwa@nifty.ne.jp>
|
||||
noducks <onemannoducks@gmail.com>
|
||||
@@ -1029,7 +988,6 @@ nponeccop <andy.melnikov@gmail.com>
|
||||
Nuutti Kotivuori <naked@iki.fi>
|
||||
nzwsch <hi@nzwsch.com>
|
||||
O.S. Tezer <ostezer@gmail.com>
|
||||
objectified <objectified@gmail.com>
|
||||
OddBloke <daniel@daniel-watkins.co.uk>
|
||||
odk- <github@odkurzacz.org>
|
||||
Oguz Bilgic <fisyonet@gmail.com>
|
||||
@@ -1148,7 +1106,6 @@ Rick Wieman <git@rickw.nl>
|
||||
Rik Nijessen <rik@keefo.nl>
|
||||
Riku Voipio <riku.voipio@linaro.org>
|
||||
Riley Guerin <rileytg.dev@gmail.com>
|
||||
Ritesh H Shukla <sritesh@vmware.com>
|
||||
Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
|
||||
Rob Vesse <rvesse@dotnetrdf.org>
|
||||
Robert Bachmann <rb@robertbachmann.at>
|
||||
@@ -1161,20 +1118,17 @@ Robin Naundorf <r.naundorf@fh-muenster.de>
|
||||
Robin Schneider <ypid@riseup.net>
|
||||
Robin Speekenbrink <robin@kingsquare.nl>
|
||||
robpc <rpcann@gmail.com>
|
||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||
Rodrigo Vaz <rodrigo.vaz@gmail.com>
|
||||
Roel Van Nyen <roel.vannyen@gmail.com>
|
||||
Roger Peppe <rogpeppe@gmail.com>
|
||||
Rohit Jnagal <jnagal@google.com>
|
||||
Rohit Kadam <rohit.d.kadam@gmail.com>
|
||||
Roland Huß <roland@jolokia.org>
|
||||
Roland Kammerer <roland.kammerer@linbit.com>
|
||||
Roland Moriz <rmoriz@users.noreply.github.com>
|
||||
Roma Sokolov <sokolov.r.v@gmail.com>
|
||||
Roman Strashkin <roman.strashkin@gmail.com>
|
||||
Ron Smits <ron.smits@gmail.com>
|
||||
root <docker-dummy@example.com>
|
||||
root <root@localhost>
|
||||
root <root@ubuntu-14.04-amd64-vbox>
|
||||
root <root@webm215.cluster016.ha.ovh.net>
|
||||
Rory Hunter <roryhunter2@gmail.com>
|
||||
@@ -1203,7 +1157,6 @@ s00318865 <sunyuan3@huawei.com>
|
||||
Sabin Basyal <sabin.basyal@gmail.com>
|
||||
Sachin Joshi <sachin_jayant_joshi@hotmail.com>
|
||||
Sagar Hani <sagarhani33@gmail.com>
|
||||
Sainath Grandhi <sainath.grandhi@intel.com>
|
||||
Sally O'Malley <somalley@redhat.com>
|
||||
Sam Abed <sam.abed@gmail.com>
|
||||
Sam Alba <sam.alba@gmail.com>
|
||||
@@ -1232,17 +1185,14 @@ Scott Johnston <scott@docker.com>
|
||||
Scott Stamp <scottstamp851@gmail.com>
|
||||
Scott Walls <sawalls@umich.edu>
|
||||
sdreyesg <sdreyesg@gmail.com>
|
||||
Sean Christopherson <sean.j.christopherson@intel.com>
|
||||
Sean Cronin <seancron@gmail.com>
|
||||
Sean OMeara <sean@chef.io>
|
||||
Sean P. Kane <skane@newrelic.com>
|
||||
Sebastiaan van Steenis <mail@superseb.nl>
|
||||
Sebastiaan van Stijn <github@gone.nl>
|
||||
Senthil Kumar Selvaraj <senthil.thecoder@gmail.com>
|
||||
Senthil Kumaran <senthil@uthcode.com>
|
||||
SeongJae Park <sj38.park@gmail.com>
|
||||
Seongyeol Lim <seongyeol37@gmail.com>
|
||||
Serge Hallyn <serge.hallyn@ubuntu.com>
|
||||
Sergey Alekseev <sergey.alekseev.minsk@gmail.com>
|
||||
Sergey Evstifeev <sergey.evstifeev@gmail.com>
|
||||
Sevki Hasirci <s@sevki.org>
|
||||
@@ -1253,7 +1203,6 @@ Shawn Landden <shawn@churchofgit.com>
|
||||
Shawn Siefkas <shawn.siefkas@meredith.com>
|
||||
Shekhar Gulati <shekhargulati84@gmail.com>
|
||||
Sheng Yang <sheng@yasker.org>
|
||||
Shengbo Song <thomassong@tencent.com>
|
||||
Shih-Yuan Lee <fourdollars@gmail.com>
|
||||
Shijiang Wei <mountkin@gmail.com>
|
||||
Shishir Mahajan <shishir.mahajan@redhat.com>
|
||||
@@ -1277,8 +1226,8 @@ Spencer Brown <spencer@spencerbrown.org>
|
||||
Spencer Smith <robertspencersmith@gmail.com>
|
||||
Sridatta Thatipamala <sthatipamala@gmail.com>
|
||||
Sridhar Ratnakumar <sridharr@activestate.com>
|
||||
Srini Brahmaroutu <sbrahma@us.ibm.com>
|
||||
Srini Brahmaroutu <srbrahma@us.ibm.com>
|
||||
srinsriv <srinsriv@users.noreply.github.com>
|
||||
Steeve Morin <steeve.morin@gmail.com>
|
||||
Stefan Berger <stefanb@linux.vnet.ibm.com>
|
||||
Stefan J. Wernli <swernli@microsoft.com>
|
||||
@@ -1297,13 +1246,12 @@ Steven Iveson <sjiveson@outlook.com>
|
||||
Steven Merrill <steven.merrill@gmail.com>
|
||||
Steven Richards <steven@axiomzen.co>
|
||||
Steven Taylor <steven.taylor@me.com>
|
||||
Subhajit Ghosh <isubuz.g@gmail.com>
|
||||
Sujith Haridasan <sujith.h@gmail.com>
|
||||
Suryakumar Sudar <surya.trunks@gmail.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au>
|
||||
Swapnil Daingade <swapnil.daingade@gmail.com>
|
||||
Sylvain Baubeau <sbaubeau@redhat.com>
|
||||
Sylvain Bellemare <sylvain@ascribe.io>
|
||||
Sylvain Bellemare <sylvain.bellemare@ezeep.com>
|
||||
Sébastien <sebastien@yoozio.com>
|
||||
Sébastien Luttringer <seblu@seblu.net>
|
||||
Sébastien Stormacq <sebsto@users.noreply.github.com>
|
||||
@@ -1327,23 +1275,18 @@ Thijs Terlouw <thijsterlouw@gmail.com>
|
||||
Thomas Bikeev <thomas.bikeev@mac.com>
|
||||
Thomas Frössman <thomasf@jossystem.se>
|
||||
Thomas Gazagnaire <thomas@gazagnaire.org>
|
||||
Thomas Grainger <tagrain@gmail.com>
|
||||
Thomas Hansen <thomas.hansen@gmail.com>
|
||||
Thomas Leonard <thomas.leonard@docker.com>
|
||||
Thomas LEVEIL <thomasleveil@gmail.com>
|
||||
Thomas Orozco <thomas@orozco.fr>
|
||||
Thomas Riccardi <riccardi@systran.fr>
|
||||
Thomas Schroeter <thomas@cliqz.com>
|
||||
Thomas Sjögren <konstruktoid@users.noreply.github.com>
|
||||
Thomas Swift <tgs242@gmail.com>
|
||||
Thomas Tanaka <thomas.tanaka@oracle.com>
|
||||
Thomas Texier <sharkone@en-mousse.org>
|
||||
Tianon Gravi <admwiggin@gmail.com>
|
||||
Tibor Vass <teabee89@gmail.com>
|
||||
Tiffany Low <tiffany@box.com>
|
||||
Tim Bosse <taim@bosboot.org>
|
||||
Tim Dettrick <t.dettrick@uq.edu.au>
|
||||
Tim Düsterhus <tim@bastelstu.be>
|
||||
Tim Hockin <thockin@google.com>
|
||||
Tim Ruffles <oi@truffles.me.uk>
|
||||
Tim Smith <timbot@google.com>
|
||||
@@ -1438,7 +1381,6 @@ waitingkuo <waitingkuo0527@gmail.com>
|
||||
Walter Leibbrandt <github@wrl.co.za>
|
||||
Walter Stanish <walter@pratyeka.org>
|
||||
WANG Chao <wcwxyz@gmail.com>
|
||||
Wang Xing <hzwangxing@corp.netease.com>
|
||||
Ward Vandewege <ward@jhvc.com>
|
||||
WarheadsSE <max@warheads.net>
|
||||
Wayne Chang <wayne@neverfear.org>
|
||||
@@ -1446,7 +1388,6 @@ Wei-Ting Kuo <waitingkuo0527@gmail.com>
|
||||
weiyan <weiyan3@huawei.com>
|
||||
Weiyang Zhu <cnresonant@gmail.com>
|
||||
Wen Cheng Ma <wenchma@cn.ibm.com>
|
||||
Wendel Fleming <wfleming@usc.edu>
|
||||
Wenxuan Zhao <viz@linux.com>
|
||||
Wenyu You <21551128@zju.edu.cn>
|
||||
Wes Morgan <cap10morgan@gmail.com>
|
||||
@@ -1485,7 +1426,6 @@ Ying Li <cyli@twistedmatrix.com>
|
||||
Yohei Ueda <yohei@jp.ibm.com>
|
||||
Yong Tang <yong.tang.github@outlook.com>
|
||||
Yongzhi Pan <panyongzhi@gmail.com>
|
||||
yorkie <yorkiefixer@gmail.com>
|
||||
Youcef YEKHLEF <yyekhlef@gmail.com>
|
||||
Yuan Sun <sunyuan3@huawei.com>
|
||||
yuchangchun <yuchangchun1@huawei.com>
|
||||
@@ -1514,9 +1454,7 @@ zmarouf <zeid.marouf@gmail.com>
|
||||
Zoltan Tombol <zoltan.tombol@gmail.com>
|
||||
zqh <zqhxuyuan@gmail.com>
|
||||
Zuhayr Elahi <elahi.zuhayr@gmail.com>
|
||||
Zunayed Ali <zunayed@gmail.com>
|
||||
Álex González <agonzalezro@gmail.com>
|
||||
Álvaro Lázaro <alvaro.lazaro.g@gmail.com>
|
||||
Átila Camurça Alves <camurca.home@gmail.com>
|
||||
尹吉峰 <jifeng.yin@gmail.com>
|
||||
搏通 <yufeng.pyf@alibaba-inc.com>
|
||||
|
||||
704
CHANGELOG.md
704
CHANGELOG.md
@@ -5,687 +5,13 @@ 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.6 (2017-01-10)
|
||||
|
||||
**IMPORTANT**: Docker 1.12 ships with an updated systemd unit file for rpm
|
||||
based installs (which includes RHEL, Fedora, CentOS, and Oracle Linux 7). When
|
||||
upgrading from an older version of docker, the upgrade process may not
|
||||
automatically install the updated version of the unit file, or fail to start
|
||||
the docker service if;
|
||||
|
||||
- the systemd unit file (`/usr/lib/systemd/system/docker.service`) contains local changes, or
|
||||
- a systemd drop-in file is present, and contains `-H fd://` in the `ExecStart` directive
|
||||
|
||||
Starting the docker service will produce an error:
|
||||
|
||||
Failed to start docker.service: Unit docker.socket failed to load: No such file or directory.
|
||||
|
||||
or
|
||||
|
||||
no sockets found via socket activation: make sure the service was started by systemd.
|
||||
|
||||
To resolve this:
|
||||
|
||||
- Backup the current version of the unit file, and replace the file with the
|
||||
[version that ships with docker 1.12](https://raw.githubusercontent.com/docker/docker/v1.12.0/contrib/init/systemd/docker.service.rpm)
|
||||
- Remove the `Requires=docker.socket` directive from the `/usr/lib/systemd/system/docker.service` file if present
|
||||
- Remove `-H fd://` from the `ExecStart` directive (both in the main unit file, and in any drop-in files present).
|
||||
|
||||
After making those changes, run `sudo systemctl daemon-reload`, and `sudo
|
||||
systemctl restart docker` to reload changes and (re)start the docker daemon.
|
||||
|
||||
**NOTE**: Docker 1.12.5 will correctly validate that either an IPv6 subnet is provided or
|
||||
that the IPAM driver can provide one when you specify the `--ipv6` option.
|
||||
|
||||
If you are currently using the `--ipv6` option _without_ specifying the
|
||||
`--fixed-cidr-v6` option, the Docker daemon will refuse to start with the
|
||||
following message:
|
||||
|
||||
```none
|
||||
Error starting daemon: Error initializing network controller: Error creating
|
||||
default "bridge" network: failed to parse pool request
|
||||
for address space "LocalDefault" pool " subpool ":
|
||||
could not find an available, non-overlapping IPv6 address
|
||||
pool among the defaults to assign to the network
|
||||
```
|
||||
|
||||
To resolve this error, either remove the `--ipv6` flag (to preserve the same
|
||||
behavior as in Docker 1.12.3 and earlier), or provide an IPv6 subnet as the
|
||||
value of the `--fixed-cidr-v6` flag.
|
||||
|
||||
In a similar way, if you specify the `--ipv6` flag when creating a network
|
||||
with the default IPAM driver, without providing an IPv6 `--subnet`, network
|
||||
creation will fail with the following message:
|
||||
|
||||
```none
|
||||
Error response from daemon: failed to parse pool request for address space
|
||||
"LocalDefault" pool "" subpool "": could not find an
|
||||
available, non-overlapping IPv6 address pool among
|
||||
the defaults to assign to the network
|
||||
```
|
||||
|
||||
To resolve this, either remove the `--ipv6` flag (to preserve the same behavior
|
||||
as in Docker 1.12.3 and earlier), or provide an IPv6 subnet as the value of the
|
||||
`--subnet` flag.
|
||||
|
||||
The network network creation will instead succeed if you use an external IPAM driver
|
||||
which supports automatic allocation of IPv6 subnets.
|
||||
|
||||
### Runtime
|
||||
|
||||
- Fix runC privilege escalation (CVE-2016-9962)
|
||||
|
||||
## 1.12.5 (2016-12-15)
|
||||
|
||||
**IMPORTANT**: Docker 1.12 ships with an updated systemd unit file for rpm
|
||||
based installs (which includes RHEL, Fedora, CentOS, and Oracle Linux 7). When
|
||||
upgrading from an older version of docker, the upgrade process may not
|
||||
automatically install the updated version of the unit file, or fail to start
|
||||
the docker service if;
|
||||
|
||||
- the systemd unit file (`/usr/lib/systemd/system/docker.service`) contains local changes, or
|
||||
- a systemd drop-in file is present, and contains `-H fd://` in the `ExecStart` directive
|
||||
|
||||
Starting the docker service will produce an error:
|
||||
|
||||
Failed to start docker.service: Unit docker.socket failed to load: No such file or directory.
|
||||
|
||||
or
|
||||
|
||||
no sockets found via socket activation: make sure the service was started by systemd.
|
||||
|
||||
To resolve this:
|
||||
|
||||
- Backup the current version of the unit file, and replace the file with the
|
||||
[version that ships with docker 1.12](https://raw.githubusercontent.com/docker/docker/v1.12.0/contrib/init/systemd/docker.service.rpm)
|
||||
- Remove the `Requires=docker.socket` directive from the `/usr/lib/systemd/system/docker.service` file if present
|
||||
- Remove `-H fd://` from the `ExecStart` directive (both in the main unit file, and in any drop-in files present).
|
||||
|
||||
After making those changes, run `sudo systemctl daemon-reload`, and `sudo
|
||||
systemctl restart docker` to reload changes and (re)start the docker daemon.
|
||||
|
||||
**NOTE**: Docker 1.12.5 will correctly validate that either an IPv6 subnet is provided or
|
||||
that the IPAM driver can provide one when you specify the `--ipv6` option.
|
||||
|
||||
If you are currently using the `--ipv6` option _without_ specifying the
|
||||
`--fixed-cidr-v6` option, the Docker daemon will refuse to start with the
|
||||
following message:
|
||||
|
||||
```none
|
||||
Error starting daemon: Error initializing network controller: Error creating
|
||||
default "bridge" network: failed to parse pool request
|
||||
for address space "LocalDefault" pool " subpool ":
|
||||
could not find an available, non-overlapping IPv6 address
|
||||
pool among the defaults to assign to the network
|
||||
```
|
||||
|
||||
To resolve this error, either remove the `--ipv6` flag (to preserve the same
|
||||
behavior as in Docker 1.12.3 and earlier), or provide an IPv6 subnet as the
|
||||
value of the `--fixed-cidr-v6` flag.
|
||||
|
||||
In a similar way, if you specify the `--ipv6` flag when creating a network
|
||||
with the default IPAM driver, without providing an IPv6 `--subnet`, network
|
||||
creation will fail with the following message:
|
||||
|
||||
```none
|
||||
Error response from daemon: failed to parse pool request for address space
|
||||
"LocalDefault" pool "" subpool "": could not find an
|
||||
available, non-overlapping IPv6 address pool among
|
||||
the defaults to assign to the network
|
||||
```
|
||||
|
||||
To resolve this, either remove the `--ipv6` flag (to preserve the same behavior
|
||||
as in Docker 1.12.3 and earlier), or provide an IPv6 subnet as the value of the
|
||||
`--subnet` flag.
|
||||
|
||||
The network network creation will instead succeed if you use an external IPAM driver
|
||||
which supports automatic allocation of IPv6 subnets.
|
||||
|
||||
### Runtime
|
||||
|
||||
- Fix race on sending stdin close event [#29424](https://github.com/docker/docker/pull/29424)
|
||||
|
||||
### Networking
|
||||
|
||||
- Fix panic in docker network ls when a network was created with `--ipv6` and no ipv6 `--subnet` in older docker versions [#29416](https://github.com/docker/docker/pull/29416)
|
||||
|
||||
### Contrib
|
||||
|
||||
- Fix compilation on Darwin [#29370](https://github.com/docker/docker/pull/29370)
|
||||
|
||||
## 1.12.4 (2016-12-12)
|
||||
|
||||
**IMPORTANT**: Docker 1.12 ships with an updated systemd unit file for rpm
|
||||
based installs (which includes RHEL, Fedora, CentOS, and Oracle Linux 7). When
|
||||
upgrading from an older version of docker, the upgrade process may not
|
||||
automatically install the updated version of the unit file, or fail to start
|
||||
the docker service if;
|
||||
|
||||
- the systemd unit file (`/usr/lib/systemd/system/docker.service`) contains local changes, or
|
||||
- a systemd drop-in file is present, and contains `-H fd://` in the `ExecStart` directive
|
||||
|
||||
Starting the docker service will produce an error:
|
||||
|
||||
Failed to start docker.service: Unit docker.socket failed to load: No such file or directory.
|
||||
|
||||
or
|
||||
|
||||
no sockets found via socket activation: make sure the service was started by systemd.
|
||||
|
||||
To resolve this:
|
||||
|
||||
- Backup the current version of the unit file, and replace the file with the
|
||||
[version that ships with docker 1.12](https://raw.githubusercontent.com/docker/docker/v1.12.0/contrib/init/systemd/docker.service.rpm)
|
||||
- Remove the `Requires=docker.socket` directive from the `/usr/lib/systemd/system/docker.service` file if present
|
||||
- Remove `-H fd://` from the `ExecStart` directive (both in the main unit file, and in any drop-in files present).
|
||||
|
||||
After making those changes, run `sudo systemctl daemon-reload`, and `sudo
|
||||
systemctl restart docker` to reload changes and (re)start the docker daemon.
|
||||
|
||||
|
||||
### Runtime
|
||||
|
||||
- Fix issue where volume metadata was not removed [#29083](https://github.com/docker/docker/pull/29083)
|
||||
- Asynchronously close streams to prevent holding container lock [#29050](https://github.com/docker/docker/pull/29050)
|
||||
- Fix selinux labels for newly created container volumes [#29050](https://github.com/docker/docker/pull/29050)
|
||||
- Remove hostname validation [#28990](https://github.com/docker/docker/pull/28990)
|
||||
- Fix deadlocks caused by IO races [#29095](https://github.com/docker/docker/pull/29095) [#29141](https://github.com/docker/docker/pull/29141)
|
||||
- Return an empty stats if the container is restarting [#29150](https://github.com/docker/docker/pull/29150)
|
||||
- Fix volume store locking [#29151](https://github.com/docker/docker/pull/29151)
|
||||
- Ensure consistent status code in API [#29150](https://github.com/docker/docker/pull/29150)
|
||||
- Fix incorrect opaque directory permission in overlay2 [#29093](https://github.com/docker/docker/pull/29093)
|
||||
- Detect plugin content and error out on `docker pull` [#29297](https://github.com/docker/docker/pull/29297)
|
||||
|
||||
### Swarm Mode
|
||||
|
||||
* Update Swarmkit [#29047](https://github.com/docker/docker/pull/29047)
|
||||
- orchestrator/global: Fix deadlock on updates [docker/swarmkit#1760](https://github.com/docker/swarmkit/pull/1760)
|
||||
- on leader switchover preserve the vxlan id for existing networks [docker/swarmkit#1773](https://github.com/docker/swarmkit/pull/1773)
|
||||
- Refuse swarm spec not named "default" [#29152](https://github.com/docker/docker/pull/29152)
|
||||
|
||||
### Networking
|
||||
|
||||
* Update libnetwork [#29004](https://github.com/docker/docker/pull/29004) [#29146](https://github.com/docker/docker/pull/29146)
|
||||
- Fix panic in embedded DNS [docker/libnetwork#1561](https://github.com/docker/libnetwork/pull/1561)
|
||||
- Fix unmarhalling panic when passing --link-local-ip on global scope network [docker/libnetwork#1564](https://github.com/docker/libnetwork/pull/1564)
|
||||
- Fix panic when network plugin returns nil StaticRoutes [docker/libnetwork#1563](https://github.com/docker/libnetwork/pull/1563)
|
||||
- Fix panic in osl.(*networkNamespace).DeleteNeighbor [docker/libnetwork#1555](https://github.com/docker/libnetwork/pull/1555)
|
||||
- Fix panic in swarm networking concurrent map read/write [docker/libnetwork#1570](https://github.com/docker/libnetwork/pull/1570)
|
||||
* Allow encrypted networks when running docker inside a container [docker/libnetwork#1502](https://github.com/docker/libnetwork/pull/1502)
|
||||
- Do not block autoallocation of IPv6 pool [docker/libnetwork#1538](https://github.com/docker/libnetwork/pull/1538)
|
||||
- Set timeout for netlink calls [docker/libnetwork#1557](https://github.com/docker/libnetwork/pull/1557)
|
||||
- Increase networking local store timeout to one minute [docker/libkv#140](https://github.com/docker/libkv/pull/140)
|
||||
- Fix a panic in libnetwork.(*sandbox).execFunc [docker/libnetwork#1556](https://github.com/docker/libnetwork/pull/1556)
|
||||
- Honor icc=false for internal networks [docker/libnetwork#1525](https://github.com/docker/libnetwork/pull/1525)
|
||||
|
||||
### Logging
|
||||
|
||||
* Update syslog log driver [#29150](https://github.com/docker/docker/pull/29150)
|
||||
|
||||
### Contrib
|
||||
|
||||
- Run "dnf upgrade" before installing in fedora [#29150](https://github.com/docker/docker/pull/29150)
|
||||
- Add build-date back to RPM packages [#29150](https://github.com/docker/docker/pull/29150)
|
||||
- deb package filename changed to include distro to distinguish between distro code names [#27829](https://github.com/docker/docker/pull/27829)
|
||||
|
||||
## 1.12.3 (2016-10-26)
|
||||
|
||||
**IMPORTANT**: Docker 1.12 ships with an updated systemd unit file for rpm
|
||||
based installs (which includes RHEL, Fedora, CentOS, and Oracle Linux 7). When
|
||||
upgrading from an older version of docker, the upgrade process may not
|
||||
automatically install the updated version of the unit file, or fail to start
|
||||
the docker service if;
|
||||
|
||||
- the systemd unit file (`/usr/lib/systemd/system/docker.service`) contains local changes, or
|
||||
- a systemd drop-in file is present, and contains `-H fd://` in the `ExecStart` directive
|
||||
|
||||
Starting the docker service will produce an error:
|
||||
|
||||
Failed to start docker.service: Unit docker.socket failed to load: No such file or directory.
|
||||
|
||||
or
|
||||
|
||||
no sockets found via socket activation: make sure the service was started by systemd.
|
||||
|
||||
To resolve this:
|
||||
|
||||
- Backup the current version of the unit file, and replace the file with the
|
||||
[version that ships with docker 1.12](https://raw.githubusercontent.com/docker/docker/v1.12.0/contrib/init/systemd/docker.service.rpm)
|
||||
- Remove the `Requires=docker.socket` directive from the `/usr/lib/systemd/system/docker.service` file if present
|
||||
- Remove `-H fd://` from the `ExecStart` directive (both in the main unit file, and in any drop-in files present).
|
||||
|
||||
After making those changes, run `sudo systemctl daemon-reload`, and `sudo
|
||||
systemctl restart docker` to reload changes and (re)start the docker daemon.
|
||||
|
||||
|
||||
### Runtime
|
||||
|
||||
- Fix ambient capability usage in containers (CVE-2016-8867) [#27610](https://github.com/docker/docker/pull/27610)
|
||||
- Prevent a deadlock in libcontainerd for Windows [#27136](https://github.com/docker/docker/pull/27136)
|
||||
- Fix error reporting in CopyFileWithTar [#27075](https://github.com/docker/docker/pull/27075)
|
||||
* Reset health status to starting when a container is restarted [#27387](https://github.com/docker/docker/pull/27387)
|
||||
* Properly handle shared mount propagation in storage directory [#27609](https://github.com/docker/docker/pull/27609)
|
||||
- Fix docker exec [#27610](https://github.com/docker/docker/pull/27610)
|
||||
- Fix backward compatibility with containerd’s events log [#27693](https://github.com/docker/docker/pull/27693)
|
||||
|
||||
### Swarm Mode
|
||||
|
||||
- Fix conversion of restart-policy [#27062](https://github.com/docker/docker/pull/27062)
|
||||
* Update Swarmkit [#27554](https://github.com/docker/docker/pull/27554)
|
||||
* Avoid restarting a task that has already been restarted [docker/swarmkit#1305](https://github.com/docker/swarmkit/pull/1305)
|
||||
* Allow duplicate published ports when they use different protocols [docker/swarmkit#1632](https://github.com/docker/swarmkit/pull/1632)
|
||||
* Allow multiple randomly assigned published ports on service [docker/swarmkit#1657](https://github.com/docker/swarmkit/pull/1657)
|
||||
- Fix panic when allocations happen at init time [docker/swarmkit#1651](https://github.com/docker/swarmkit/pull/1651)
|
||||
|
||||
### Networking
|
||||
|
||||
* Update libnetwork [#27559](https://github.com/docker/docker/pull/27559)
|
||||
- Fix race in serializing sandbox to string [docker/libnetwork#1495](https://github.com/docker/libnetwork/pull/1495)
|
||||
- Fix race during deletion [docker/libnetwork#1503](https://github.com/docker/libnetwork/pull/1503)
|
||||
* Reset endpoint port info on connectivity revoke in bridge driver [docker/libnetwork#1504](https://github.com/docker/libnetwork/pull/1504)
|
||||
- Fix a deadlock in networking code [docker/libnetwork#1507](https://github.com/docker/libnetwork/pull/1507)
|
||||
- Fix a race in load balancer state [docker/libnetwork#1512](https://github.com/docker/libnetwork/pull/1512)
|
||||
|
||||
### Logging
|
||||
|
||||
* Update fluent-logger-golang to v1.2.1 [#27474](https://github.com/docker/docker/pull/27474)
|
||||
|
||||
### Contrib
|
||||
|
||||
* Update buildtags for armhf ubuntu-trusty [#27327](https://github.com/docker/docker/pull/27327)
|
||||
* Add AppArmor to runc buildtags for armhf [#27421](https://github.com/docker/docker/pull/27421)
|
||||
|
||||
## 1.12.2 (2016-10-11)
|
||||
|
||||
**IMPORTANT**: Docker 1.12 ships with an updated systemd unit file for rpm
|
||||
based installs (which includes RHEL, Fedora, CentOS, and Oracle Linux 7). When
|
||||
upgrading from an older version of docker, the upgrade process may not
|
||||
automatically install the updated version of the unit file, or fail to start
|
||||
the docker service if;
|
||||
|
||||
- the systemd unit file (`/usr/lib/systemd/system/docker.service`) contains local changes, or
|
||||
- a systemd drop-in file is present, and contains `-H fd://` in the `ExecStart` directive
|
||||
|
||||
Starting the docker service will produce an error:
|
||||
|
||||
Failed to start docker.service: Unit docker.socket failed to load: No such file or directory.
|
||||
|
||||
or
|
||||
|
||||
no sockets found via socket activation: make sure the service was started by systemd.
|
||||
|
||||
To resolve this:
|
||||
|
||||
- Backup the current version of the unit file, and replace the file with the
|
||||
[version that ships with docker 1.12](https://raw.githubusercontent.com/docker/docker/v1.12.0/contrib/init/systemd/docker.service.rpm)
|
||||
- Remove the `Requires=docker.socket` directive from the `/usr/lib/systemd/system/docker.service` file if present
|
||||
- Remove `-H fd://` from the `ExecStart` directive (both in the main unit file, and in any drop-in files present).
|
||||
|
||||
After making those changes, run `sudo systemctl daemon-reload`, and `sudo
|
||||
systemctl restart docker` to reload changes and (re)start the docker daemon.
|
||||
|
||||
|
||||
### Runtime
|
||||
|
||||
- Fix a panic due to a race condition filtering `docker ps` [#26049](https://github.com/docker/docker/pull/26049)
|
||||
* Implement retry logic to prevent "Unable to remove filesystem" errors when using the aufs storage driver [#26536](https://github.com/docker/docker/pull/26536)
|
||||
* Prevent devicemapper from removing device symlinks if `dm.use_deferred_removal` is enabled [#24740](https://github.com/docker/docker/pull/24740)
|
||||
- Fix an issue where the CLI did not return correct exit codes if a command was run with invalid options [#26777](https://github.com/docker/docker/pull/26777)
|
||||
- Fix a panic due to a bug in stdout / stderr processing in health checks [#26507](https://github.com/docker/docker/pull/26507)
|
||||
- Fix exec's children handling [#26874](https://github.com/docker/docker/pull/26874)
|
||||
- Fix exec form of HEALTHCHECK CMD [#26208](https://github.com/docker/docker/pull/26208)
|
||||
|
||||
### Networking
|
||||
|
||||
- Fix a daemon start panic on armv5 [#24315](https://github.com/docker/docker/issues/24315)
|
||||
* Vendor libnetwork [#26879](https://github.com/docker/docker/pull/26879) [#26953](https://github.com/docker/docker/pull/26953)
|
||||
* Avoid returning early on agent join failures [docker/libnetwork#1473](https://github.com/docker/libnetwork/pull/1473)
|
||||
- Fix service published port cleanup issues [docker/libetwork#1432](https://github.com/docker/libnetwork/pull/1432) [docker/libnetwork#1433](https://github.com/docker/libnetwork/pull/1433)
|
||||
* Recover properly from transient gossip failures [docker/libnetwork#1446](https://github.com/docker/libnetwork/pull/1446)
|
||||
* Disambiguate node names known to gossip cluster to avoid node name collision [docker/libnetwork#1451](https://github.com/docker/libnetwork/pull/1451)
|
||||
* Honor user provided listen address for gossip [docker/libnetwork#1460](https://github.com/docker/libnetwork/pull/1460)
|
||||
* Allow reachability via published port across services on the same host [docker/libnetwork#1398](https://github.com/docker/libnetwork/pull/1398)
|
||||
* Change the ingress sandbox name from random id to just `ingress_sbox` [docker/libnetwork#1449](https://github.com/docker/libnetwork/pull/1449)
|
||||
- Disable service discovery in ingress network [docker/libnetwork#1489](https://github.com/docker/libnetwork/pull/1489)
|
||||
|
||||
### Swarm Mode
|
||||
|
||||
* Fix remote detection of a node's address when it joins the cluster [#26211](https://github.com/docker/docker/pull/26211)
|
||||
* Vendor SwarmKit [#26765](https://github.com/docker/docker/pull/26765)
|
||||
* Bounce session after failed status update [docker/swarmkit#1539](https://github.com/docker/swarmkit/pull/1539)
|
||||
- Fix possible raft deadlocks [docker/swarmkit#1537](https://github.com/docker/swarmkit/pull/1537)
|
||||
- Fix panic and endpoint leak when a service is updated with no endpoints [docker/swarmkit#1481](https://github.com/docker/swarmkit/pull/1481)
|
||||
* Produce an error if the same port is published twice on `service create` or `service update` [docker/swarmkit#1495](https://github.com/docker/swarmkit/pull/1495)
|
||||
- Fix an issue where changes to a service were not detected, resulting in the service not being updated [docker/swarmkit#1497](https://github.com/docker/swarmkit/pull/1497)
|
||||
- Do not allow service creation on ingress network [docker/swarmkit#1600](https://github.com/docker/swarmkit/pull/1600)
|
||||
|
||||
### Contrib
|
||||
|
||||
* Update the debian sysv-init script to use `dockerd` instead of `docker daemon` [#25869](https://github.com/docker/docker/pull/25869)
|
||||
* Improve stability when running the docker client on MacOS Sierra [#26875](https://github.com/docker/docker/pull/26875)
|
||||
- Fix installation on debian stretch [#27184](https://github.com/docker/docker/pull/27184)
|
||||
|
||||
### Windows
|
||||
|
||||
- Fix an issue where arrow-navigation did not work when running the docker client in ConEmu [#25578](https://github.com/docker/docker/pull/25578)
|
||||
|
||||
## 1.12.1 (2016-08-18)
|
||||
|
||||
**IMPORTANT**: Docker 1.12 ships with an updated systemd unit file for rpm
|
||||
based installs (which includes RHEL, Fedora, CentOS, and Oracle Linux 7). When
|
||||
upgrading from an older version of docker, the upgrade process may not
|
||||
automatically install the updated version of the unit file, or fail to start
|
||||
the docker service if;
|
||||
|
||||
- the systemd unit file (`/usr/lib/systemd/system/docker.service`) contains local changes, or
|
||||
- a systemd drop-in file is present, and contains `-H fd://` in the `ExecStart` directive
|
||||
|
||||
Starting the docker service will produce an error:
|
||||
|
||||
Failed to start docker.service: Unit docker.socket failed to load: No such file or directory.
|
||||
|
||||
or
|
||||
|
||||
no sockets found via socket activation: make sure the service was started by systemd.
|
||||
|
||||
To resolve this:
|
||||
|
||||
- Backup the current version of the unit file, and replace the file with the
|
||||
[version that ships with docker 1.12](https://raw.githubusercontent.com/docker/docker/v1.12.0/contrib/init/systemd/docker.service.rpm)
|
||||
- Remove the `Requires=docker.socket` directive from the `/usr/lib/systemd/system/docker.service` file if present
|
||||
- Remove `-H fd://` from the `ExecStart` directive (both in the main unit file, and in any drop-in files present).
|
||||
|
||||
After making those changes, run `sudo systemctl daemon-reload`, and `sudo
|
||||
systemctl restart docker` to reload changes and (re)start the docker daemon.
|
||||
|
||||
|
||||
### Client
|
||||
|
||||
* Add `Joined at` information in `node inspect --pretty` [#25512](https://github.com/docker/docker/pull/25512)
|
||||
- Fix a crash on `service inspect` [#25454](https://github.com/docker/docker/pull/25454)
|
||||
- Fix issue preventing `service update --env-add` to work as intended [#25427](https://github.com/docker/docker/pull/25427)
|
||||
- Fix issue preventing `service update --publish-add` to work as intended [#25428](https://github.com/docker/docker/pull/25428)
|
||||
- Remove `service update --network-add` and `service update --network-rm` flags
|
||||
because this feature is not yet implemented in 1.12, but was inadvertently added
|
||||
to the client in 1.12.0 [#25646](https://github.com/docker/docker/pull/25646)
|
||||
|
||||
### Contrib
|
||||
|
||||
+ Official ARM installation for Debian Jessie, Ubuntu Trusty, and Raspbian Jessie [#24815](https://github.com/docker/docker/pull/24815) [#25591](https://github.com/docker/docker/pull/25637)
|
||||
- Add selinux policy per distro/version, fixing issue preventing successful installation on Fedora 24, and Oracle Linux [#25334](https://github.com/docker/docker/pull/25334) [#25593](https://github.com/docker/docker/pull/25593)
|
||||
|
||||
### Networking
|
||||
|
||||
- Fix issue that prevented containers to be accessed by hostname with Docker overlay driver in Swarm Mode [#25603](https://github.com/docker/docker/pull/25603) [#25648](https://github.com/docker/docker/pull/25648)
|
||||
- Fix random network issues on service with published port [#25603](https://github.com/docker/docker/pull/25603)
|
||||
- Fix unreliable inter-service communication after scaling down and up [#25603](https://github.com/docker/docker/pull/25603)
|
||||
- Fix issue where removing all tasks on a node and adding them back breaks connectivity with other services [#25603](https://github.com/docker/docker/pull/25603)
|
||||
- Fix issue where a task that fails to start results in a race, causing a `network xxx not found` error that masks the actual error [#25550](https://github.com/docker/docker/pull/25550)
|
||||
- Relax validation of SRV records for external services that use SRV records not formatted according to RFC 2782 [#25739](https://github.com/docker/docker/pull/25739)
|
||||
|
||||
### Plugins (experimental)
|
||||
|
||||
* Make daemon events listen for plugin lifecycle events [#24760](https://github.com/docker/docker/pull/24760)
|
||||
* Check for plugin state before enabling plugin [#25033](https://github.com/docker/docker/pull/25033)
|
||||
- Remove plugin root from filesystem on `plugin rm` [#25187](https://github.com/docker/docker/pull/25187)
|
||||
- Prevent deadlock when more than one plugin is installed [#25384](https://github.com/docker/docker/pull/25384)
|
||||
|
||||
### Runtime
|
||||
|
||||
* Mask join tokens in daemon logs [#25346](https://github.com/docker/docker/pull/25346)
|
||||
- Fix `docker ps --filter` causing the results to no longer be sorted by creation time [#25387](https://github.com/docker/docker/pull/25387)
|
||||
- Fix various crashes [#25053](https://github.com/docker/docker/pull/25053)
|
||||
|
||||
### Security
|
||||
|
||||
* Add `/proc/timer_list` to the masked paths list to prevent information leak from the host [#25630](https://github.com/docker/docker/pull/25630)
|
||||
* Allow systemd to run with only `--cap-add SYS_ADMIN` rather than having to also add `--cap-add DAC_READ_SEARCH` or disabling seccomp filtering [#25567](https://github.com/docker/docker/pull/25567)
|
||||
|
||||
### Swarm
|
||||
|
||||
- Fix an issue where the swarm can get stuck electing a new leader after quorum is lost [#25055](https://github.com/docker/docker/issues/25055)
|
||||
- Fix unwanted rescheduling of containers after a leader failover [#25017](https://github.com/docker/docker/issues/25017)
|
||||
- Change swarm root CA key to P256 curve [swarmkit#1376](https://github.com/docker/swarmkit/pull/1376)
|
||||
- Allow forced removal of a node from a swarm [#25159](https://github.com/docker/docker/pull/25159)
|
||||
- Fix connection leak when a node leaves a swarm [swarmkit/#1277](https://github.com/docker/swarmkit/pull/1277)
|
||||
- Backdate swarm certificates by one hour to tolerate more clock skew [swarmkit/#1243](https://github.com/docker/swarmkit/pull/1243)
|
||||
- Avoid high CPU use with many unschedulable tasks [swarmkit/#1287](https://github.com/docker/swarmkit/pull/1287)
|
||||
- Fix issue with global tasks not starting up [swarmkit/#1295](https://github.com/docker/swarmkit/pull/1295)
|
||||
- Garbage collect raft logs [swarmkit/#1327](https://github.com/docker/swarmkit/pull/1327)
|
||||
|
||||
### Volume
|
||||
|
||||
- Persist local volume options after a daemon restart [#25316](https://github.com/docker/docker/pull/25316)
|
||||
- Fix an issue where the mount ID was not returned on volume unmount [#25333](https://github.com/docker/docker/pull/25333)
|
||||
- Fix an issue where a volume mount could inadvertently create a bind mount [#25309](https://github.com/docker/docker/pull/25309)
|
||||
- `docker service create --mount type=bind,...` now correctly validates if the source path exists, instead of creating it [#25494](https://github.com/docker/docker/pull/25494)
|
||||
|
||||
## 1.12.0 (2016-07-28)
|
||||
|
||||
|
||||
**IMPORTANT**: Docker 1.12.0 ships with an updated systemd unit file for rpm
|
||||
based installs (which includes RHEL, Fedora, CentOS, and Oracle Linux 7). When
|
||||
upgrading from an older version of docker, the upgrade process may not
|
||||
automatically install the updated version of the unit file, or fail to start
|
||||
the docker service if;
|
||||
|
||||
- the systemd unit file (`/usr/lib/systemd/system/docker.service`) contains local changes, or
|
||||
- a systemd drop-in file is present, and contains `-H fd://` in the `ExecStart` directive
|
||||
|
||||
Starting the docker service will produce an error:
|
||||
|
||||
Failed to start docker.service: Unit docker.socket failed to load: No such file or directory.
|
||||
|
||||
or
|
||||
|
||||
no sockets found via socket activation: make sure the service was started by systemd.
|
||||
|
||||
To resolve this:
|
||||
|
||||
- Backup the current version of the unit file, and replace the file with the
|
||||
[version that ships with docker 1.12](https://raw.githubusercontent.com/docker/docker/v1.12.0/contrib/init/systemd/docker.service.rpm)
|
||||
- Remove the `Requires=docker.socket` directive from the `/usr/lib/systemd/system/docker.service` file if present
|
||||
- Remove `-H fd://` from the `ExecStart` directive (both in the main unit file, and in any drop-in files present).
|
||||
|
||||
After making those changes, run `sudo systemctl daemon-reload`, and `sudo
|
||||
systemctl restart docker` to reload changes and (re)start the docker daemon.
|
||||
|
||||
**IMPORTANT**: With Docker 1.12, a Linux docker installation now has two
|
||||
additional binaries; `dockerd`, and `docker-proxy`. If you have scripts for
|
||||
installing docker, please make sure to update them accordingly.
|
||||
|
||||
### 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)
|
||||
* Always save registry digest metadata about images pushed and pulled [#23996](https://github.com/docker/docker/pull/23996)
|
||||
|
||||
### 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)
|
||||
+ Routing Mesh using ingress overlay network [#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)
|
||||
- Windows: fix tty-related displaying issues [#23878](https://github.com/docker/docker/pull/23878)
|
||||
|
||||
### Runtime
|
||||
|
||||
+ Split the userland proxy to a separate binary (`docker-proxy`) [#23312](https://github.com/docker/docker/pull/23312)
|
||||
+ 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)
|
||||
+ Add `--oom-score-adjust` daemon flag with a default value of `-500` making the daemon less likely to be killed before containers [#24516](https://github.com/docker/docker/pull/24516)
|
||||
* 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`, `join-token`, `leave`, `update` subcommands [#23361](https://github.com/docker/docker/pull/23361) [#24823](https://github.com/docker/docker/pull/24823)
|
||||
+ New `service` command to manage swarm-wide services with `create`, `inspect`, `update`, `rm`, `ps` subcommands [#23361](https://github.com/docker/docker/pull/23361) [#25140](https://github.com/docker/docker/pull/25140)
|
||||
+ New `node` command to manage nodes with `accept`, `promote`, `demote`, `inspect`, `update`, `ps`, `ls` and `rm` subcommands [#23361](https://github.com/docker/docker/pull/23361) [#25140](https://github.com/docker/docker/pull/25140)
|
||||
+ (experimental) New `stack` and `deploy` commands to manage and deploy multi-service applications [#23522](https://github.com/docker/docker/pull/23522) [#25140](https://github.com/docker/docker/pull/25140)
|
||||
|
||||
### 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
|
||||
|
||||
- Fix a stale endpoint issue on overlay networks during ungraceful restart ([#23015](https://github.com/docker/docker/pull/23015))
|
||||
- Fix an issue where the wrong port could be reported by `docker inspect/ps/port` ([#22997](https://github.com/docker/docker/pull/22997))
|
||||
|
||||
### Runtime
|
||||
|
||||
- Fix a potential panic when running `docker build` ([#23032](https://github.com/docker/docker/pull/23032))
|
||||
- Fix interpretation of `--user` parameter ([#22998](https://github.com/docker/docker/pull/22998))
|
||||
- Fix a bug preventing container statistics to be correctly reported ([#22955](https://github.com/docker/docker/pull/22955))
|
||||
- Fix an issue preventing container to be restarted after daemon restart ([#22947](https://github.com/docker/docker/pull/22947))
|
||||
- Fix issues when running 32 bit binaries on Ubuntu 16.04 ([#22922](https://github.com/docker/docker/pull/22922))
|
||||
- Fix a possible deadlock on image deletion and container attach ([#22918](https://github.com/docker/docker/pull/22918))
|
||||
- Fix an issue where containers fail to start after a daemon restart if they depend on a containerized cluster store ([#22561](https://github.com/docker/docker/pull/22561))
|
||||
- Fix an issue causing `docker ps` to hang on CentOS when using devicemapper ([#22168](https://github.com/docker/docker/pull/22168), [#23067](https://github.com/docker/docker/pull/23067))
|
||||
- Fix a bug preventing to `docker exec` into a container when using devicemapper ([#22168](https://github.com/docker/docker/pull/22168), [#23067](https://github.com/docker/docker/pull/23067))
|
||||
|
||||
|
||||
## 1.11.1 (2016-04-26)
|
||||
|
||||
### Distribution
|
||||
|
||||
- Fix schema2 manifest media type to be of type `application/vnd.docker.container.image.v1+json` ([#21949](https://github.com/docker/docker/pull/21949))
|
||||
|
||||
### Documentation
|
||||
|
||||
+ Add missing API documentation for changes introduced with 1.11.0 ([#22048](https://github.com/docker/docker/pull/22048))
|
||||
|
||||
### Builder
|
||||
|
||||
* Append label passed to `docker build` as arguments as an implicit `LABEL` command at the end of the processed `Dockerfile` ([#22184](https://github.com/docker/docker/pull/22184))
|
||||
|
||||
### Networking
|
||||
|
||||
- Fix a panic that would occur when forwarding DNS query ([#22261](https://github.com/docker/docker/pull/22261))
|
||||
- Fix an issue where OS threads could end up within an incorrect network namespace when using user defined networks ([#22261](https://github.com/docker/docker/pull/22261))
|
||||
|
||||
### Runtime
|
||||
|
||||
- Fix a bug preventing labels configuration to be reloaded via the config file ([#22299](https://github.com/docker/docker/pull/22299))
|
||||
- Fix a regression where container mounting `/var/run` would prevent other containers from being removed ([#22256](https://github.com/docker/docker/pull/22256))
|
||||
- Fix an issue where it would be impossible to update both `memory-swap` and `memory` value together ([#22255](https://github.com/docker/docker/pull/22255))
|
||||
- Fix a regression from 1.11.0 where the `/auth` endpoint would not initialize `serveraddress` if it is not provided ([#22254](https://github.com/docker/docker/pull/22254))
|
||||
- Add missing cleanup of container temporary files when cancelling a schedule restart ([#22237](https://github.com/docker/docker/pull/22237))
|
||||
- Remove scary error message when no restart policy is specified ([#21993](https://github.com/docker/docker/pull/21993))
|
||||
- Fix a panic that would occur when the plugins were activated via the json spec ([#22191](https://github.com/docker/docker/pull/22191))
|
||||
- Fix restart backoff logic to correctly reset delay if container ran for at least 10secs ([#22125](https://github.com/docker/docker/pull/22125))
|
||||
- Remove error message when a container restart get cancelled ([#22123](https://github.com/docker/docker/pull/22123))
|
||||
- Fix an issue where `docker` would not correctly clean up after `docker exec` ([#22121](https://github.com/docker/docker/pull/22121))
|
||||
- Fix a panic that could occur when serving concurrent `docker stats` commands ([#22120](https://github.com/docker/docker/pull/22120))`
|
||||
- Revert deprecation of non-existent host directories auto-creation ([#22065](https://github.com/docker/docker/pull/22065))
|
||||
- Hide misleading rpc error on daemon shutdown ([#22058](https://github.com/docker/docker/pull/22058))
|
||||
|
||||
## 1.11.0 (2016-04-13)
|
||||
|
||||
**IMPORTANT**: With Docker 1.11, a Linux docker installation is now made of 4 binaries (`docker`, [`docker-containerd`](https://github.com/docker/containerd), [`docker-containerd-shim`](https://github.com/docker/containerd) and [`docker-runc`](https://github.com/opencontainers/runc)). If you have scripts relying on docker being a single static binaries, please make sure to update them. Interaction with the daemon stay the same otherwise, the usage of the other binaries should be transparent. A Windows docker installation remains a single binary, `docker.exe`.
|
||||
|
||||
### Builder
|
||||
|
||||
- Fix a bug where Docker would not use the correct uid/gid when processing the `WORKDIR` command ([#21033](https://github.com/docker/docker/pull/21033))
|
||||
- Fix a bug where Docker would not used the correct uid/gid when processing the `WORKDIR` command ([#21033](https://github.com/docker/docker/pull/21033))
|
||||
- Fix a bug where copy operations with userns would not use the proper uid/gid ([#20782](https://github.com/docker/docker/pull/20782), [#21162](https://github.com/docker/docker/pull/21162))
|
||||
|
||||
### Client
|
||||
@@ -701,16 +27,16 @@ installing docker, please make sure to update them accordingly.
|
||||
+ Docker learned how to use a SOCKS proxy ([#20366](https://github.com/docker/docker/pull/20366), [#18373](https://github.com/docker/docker/pull/18373))
|
||||
+ Docker now supports external credential stores ([#20107](https://github.com/docker/docker/pull/20107))
|
||||
* `docker ps` now supports displaying the list of volumes mounted inside a container ([#20017](https://github.com/docker/docker/pull/20017))
|
||||
* `docker info` now also reports Docker's root directory location ([#19986](https://github.com/docker/docker/pull/19986))
|
||||
* `docker info` now also report Docker's root directory location ([#19986](https://github.com/docker/docker/pull/19986))
|
||||
- Docker now prohibits login in with an empty username (spaces are trimmed) ([#19806](https://github.com/docker/docker/pull/19806))
|
||||
* Docker events attributes are now sorted by key ([#19761](https://github.com/docker/docker/pull/19761))
|
||||
* `docker ps` no longer shows exported port for stopped containers ([#19483](https://github.com/docker/docker/pull/19483))
|
||||
* `docker ps` no longer show exported port for stopped containers ([#19483](https://github.com/docker/docker/pull/19483))
|
||||
- Docker now cleans after itself if a save/export command fails ([#17849](https://github.com/docker/docker/pull/17849))
|
||||
* Docker load learned how to display a progress bar ([#17329](https://github.com/docker/docker/pull/17329), [#120078](https://github.com/docker/docker/pull/20078))
|
||||
|
||||
### Distribution
|
||||
|
||||
- Fix a panic that occurred when pulling an image with 0 layers ([#21222](https://github.com/docker/docker/pull/21222))
|
||||
- Fix a panic that occurred when pulling an images with 0 layers ([#21222](https://github.com/docker/docker/pull/21222))
|
||||
- Fix a panic that could occur on error while pushing to a registry with a misconfigured token service ([#21212](https://github.com/docker/docker/pull/21212))
|
||||
+ All first-level delegation roles are now signed when doing a trusted push ([#21046](https://github.com/docker/docker/pull/21046))
|
||||
+ OAuth support for registries was added ([#20970](https://github.com/docker/docker/pull/20970))
|
||||
@@ -735,14 +61,14 @@ installing docker, please make sure to update them accordingly.
|
||||
|
||||
### 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/21385))
|
||||
+ 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))
|
||||
+ 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))
|
||||
- Fix a few response body leaks ([#21258](https://github.com/docker/docker/pull/21258))
|
||||
- Docker, when run as a service with systemd, will now properly manage its processes cgroups ([#20633](https://github.com/docker/docker/pull/20633))
|
||||
* `docker info` now reports the value of cgroup KernelMemory or emits a warning if it is not supported ([#20863](https://github.com/docker/docker/pull/20863))
|
||||
* `docker info` now also reports the cgroup driver in use ([#20388](https://github.com/docker/docker/pull/20388))
|
||||
* Docker info now reports the value of cgroup KernelMemory or emits a warning if it is not supported ([#20863](https://github.com/docker/docker/pull/20863))
|
||||
* Docker info now also reports the cgroup driver in use ([#20388](https://github.com/docker/docker/pull/20388))
|
||||
* Docker completion is now available on PowerShell ([#19894](https://github.com/docker/docker/pull/19894))
|
||||
* `dockerinit` is no more ([#19490](https://github.com/docker/docker/pull/19490),[#19851](https://github.com/docker/docker/pull/19851))
|
||||
+ Support for building Docker on arm64 was added ([#19013](https://github.com/docker/docker/pull/19013))
|
||||
@@ -753,7 +79,7 @@ installing docker, please make sure to update them accordingly.
|
||||
- Fix panic if a node is forcibly removed from the cluster ([#21671](https://github.com/docker/docker/pull/21671))
|
||||
- Fix "error creating vxlan interface" when starting a container in a Swarm cluster ([#21671](https://github.com/docker/docker/pull/21671))
|
||||
* `docker network inspect` will now report all endpoints whether they have an active container or not ([#21160](https://github.com/docker/docker/pull/21160))
|
||||
+ Experimental support for the MacVlan and IPVlan network drivers has been added ([#21122](https://github.com/docker/docker/pull/21122))
|
||||
+ Experimental support for the MacVlan and IPVlan network drivers have been added ([#21122](https://github.com/docker/docker/pull/21122))
|
||||
* Output of `docker network ls` is now sorted by network name ([#20383](https://github.com/docker/docker/pull/20383))
|
||||
- Fix a bug where Docker would allow a network to be created with the reserved `default` name ([#19431](https://github.com/docker/docker/pull/19431))
|
||||
* `docker network inspect` returns whether a network is internal or not ([#19357](https://github.com/docker/docker/pull/19357))
|
||||
@@ -787,7 +113,7 @@ installing docker, please make sure to update them accordingly.
|
||||
- Fix a race with event timers stopping early ([#21692](https://github.com/docker/docker/pull/21692))
|
||||
- Fix race conditions in the layer store, potentially corrupting the map and crashing the process ([#21677](https://github.com/docker/docker/pull/21677))
|
||||
- Un-deprecate auto-creation of host directories for mounts. This feature was marked deprecated in ([#21666](https://github.com/docker/docker/pull/21666))
|
||||
Docker 1.9, but was decided to be too much of a backward-incompatible change, so it was decided to keep the feature.
|
||||
Docker 1.9, but was decided to be too much of an backward-incompatible change, so it was decided to keep the feature.
|
||||
+ It is now possible for containers to share the NET and IPC namespaces when `userns` is enabled ([#21383](https://github.com/docker/docker/pull/21383))
|
||||
+ `docker inspect <image-id>` will now expose the rootfs layers ([#21370](https://github.com/docker/docker/pull/21370))
|
||||
+ Docker Windows gained a minimal `top` implementation ([#21354](https://github.com/docker/docker/pull/21354))
|
||||
@@ -830,9 +156,9 @@ installing docker, please make sure to update them accordingly.
|
||||
### Volumes
|
||||
|
||||
* Output of `docker volume ls` is now sorted by volume name ([#20389](https://github.com/docker/docker/pull/20389))
|
||||
* Local volumes can now accept options similar to the unix `mount` tool ([#20262](https://github.com/docker/docker/pull/20262))
|
||||
* Local volumes can now accepts options similar to the unix `mount` tool ([#20262](https://github.com/docker/docker/pull/20262))
|
||||
- Fix an issue where one letter directory name could not be used as source for volumes ([#21106](https://github.com/docker/docker/pull/21106))
|
||||
+ `docker run -v` now accepts a new flag `nocopy`. This tells the runtime not to copy the container path content into the volume (which is the default behavior) ([#21223](https://github.com/docker/docker/pull/21223))
|
||||
+ `docker run -v` now accepts a new flag `nocopy`. This tell the runtime not to copy the container path content into the volume (which is the default behavior) ([#21223](https://github.com/docker/docker/pull/21223))
|
||||
|
||||
## 1.10.3 (2016-03-10)
|
||||
|
||||
@@ -1510,7 +836,7 @@ by another client (#15489)
|
||||
#### Security
|
||||
- Fix tar breakout vulnerability
|
||||
* Extractions are now sandboxed chroot
|
||||
- Security options are no longer comitted to images
|
||||
- Security options are no longer committed to images
|
||||
|
||||
#### Runtime
|
||||
- Fix deadlock in `docker ps -f exited=1`
|
||||
@@ -1719,7 +1045,7 @@ by another client (#15489)
|
||||
|
||||
#### Runtime
|
||||
* Support hairpin NAT without going through Docker server.
|
||||
- devicemapper: succeed immediately when removing non-existent devices.
|
||||
- devicemapper: succeed immediately when removing non-existing devices.
|
||||
- devicemapper: improve handling of devicemapper devices (add per device lock, increase sleep time and unlock while sleeping).
|
||||
- devicemapper: increase timeout in waitClose to 10 seconds.
|
||||
- devicemapper: ensure we shut down thin pool cleanly.
|
||||
@@ -1840,7 +1166,7 @@ by another client (#15489)
|
||||
- Improve deprecation message.
|
||||
- Fix attach exit on darwin.
|
||||
- devicemapper: improve handling of devicemapper devices (add per device lock, increase sleep time, unlock while sleeping).
|
||||
- devicemapper: succeed immediately when removing non-existent devices.
|
||||
- devicemapper: succeed immediately when removing non-existing devices.
|
||||
- devicemapper: increase timeout in waitClose to 10 seconds.
|
||||
- Remove goroutine leak on error.
|
||||
- Update parseLxcInfo to comply with new lxc1.0 format.
|
||||
@@ -1936,7 +1262,7 @@ by another client (#15489)
|
||||
* Update issue filing instructions
|
||||
* Warn against the use of symlinks for Docker's storage folder
|
||||
* Replace the Firefox example with an IceWeasel example
|
||||
* Rewrite the PostgreSQL example using a Dockerfile and add more details to it
|
||||
* Rewrite the PostgresSQL example using a Dockerfile and add more details to it
|
||||
* Improve the OS X documentation
|
||||
|
||||
#### Remote API
|
||||
|
||||
@@ -4,7 +4,7 @@ Want to hack on Docker? Awesome! We have a contributor's guide that explains
|
||||
[setting up a Docker development environment and the contribution
|
||||
process](https://docs.docker.com/opensource/project/who-written-for/).
|
||||
|
||||
[](https://docs.docker.com/opensource/project/who-written-for/)
|
||||

|
||||
|
||||
This page contains information about reporting issues as well as some tips and
|
||||
guidelines useful to experienced open source contributors. Finally, make sure
|
||||
@@ -58,11 +58,51 @@ When sending lengthy log-files, consider posting them as a gist (https://gist.gi
|
||||
Don't forget to remove sensitive data from your logfiles before posting (you can
|
||||
replace those parts with "REDACTED").
|
||||
|
||||
## Quick contribution tips and guidelines
|
||||
**Issue Report Template**:
|
||||
|
||||
```
|
||||
Description of problem:
|
||||
|
||||
|
||||
`docker version`:
|
||||
|
||||
|
||||
`docker info`:
|
||||
|
||||
|
||||
`uname -a`:
|
||||
|
||||
|
||||
Environment details (AWS, VirtualBox, physical, etc.):
|
||||
|
||||
|
||||
How reproducible:
|
||||
|
||||
|
||||
Steps to Reproduce:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
|
||||
Actual Results:
|
||||
|
||||
|
||||
Expected Results:
|
||||
|
||||
|
||||
Additional info:
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
##Quick contribution tips and guidelines
|
||||
|
||||
This section gives the experienced contributor some tips and guidelines.
|
||||
|
||||
### Pull requests are always welcome
|
||||
###Pull requests are always welcome
|
||||
|
||||
Not sure if that typo is worth a pull request? Found a bug and know how to fix
|
||||
it? Do it! We will appreciate it. Any significant improvement should be
|
||||
@@ -92,14 +132,6 @@ However, there might be a way to implement that feature *on top of* Docker.
|
||||
<table class="tg">
|
||||
<col width="45%">
|
||||
<col width="65%">
|
||||
<tr>
|
||||
<td>Forums</td>
|
||||
<td>
|
||||
A public forum for users to discuss questions and explore current design patterns and
|
||||
best practices about Docker and related projects in the Docker Ecosystem. To participate,
|
||||
just log in with your Docker Hub account on <a href="https://forums.docker.com" target="_blank">https://forums.docker.com</a>.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Internet Relay Chat (IRC)</td>
|
||||
<td>
|
||||
@@ -110,15 +142,15 @@ However, there might be a way to implement that feature *on top of* Docker.
|
||||
IRC is a rich chat protocol but it can overwhelm new users. You can search
|
||||
<a href="https://botbot.me/freenode/docker/#" target="_blank">our chat archives</a>.
|
||||
</p>
|
||||
<p>
|
||||
Read our <a href="https://docs.docker.com/opensource/get-help/#irc-quickstart" target="_blank">IRC quickstart guide</a>
|
||||
for an easy way to get started.
|
||||
</p>
|
||||
Read our <a href="https://docs.docker.com/opensource/get-help/#irc-quickstart" target="_blank">IRC quickstart guide</a> for an easy way to get started.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Google Group</td>
|
||||
<td>Google Groups</td>
|
||||
<td>
|
||||
There are two groups.
|
||||
<a href="https://groups.google.com/forum/#!forum/docker-user" target="_blank">Docker-user</a>
|
||||
is for people using Docker containers.
|
||||
The <a href="https://groups.google.com/forum/#!forum/docker-dev" target="_blank">docker-dev</a>
|
||||
group is for contributors and other people contributing to the Docker project.
|
||||
You can join them without a google account by sending an email to
|
||||
@@ -276,6 +308,10 @@ Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
If you set your `user.name` and `user.email` git configs, you can sign your
|
||||
commit automatically with `git commit -s`.
|
||||
|
||||
Note that the old-style `Docker-DCO-1.1-Signed-off-by: ...` format is still
|
||||
accepted, so there is no need to update outstanding pull requests to the new
|
||||
format right away, but please do adjust your processes for future contributions.
|
||||
|
||||
### How can I become a maintainer?
|
||||
|
||||
The procedures for adding new maintainers are explained in the
|
||||
@@ -378,7 +414,7 @@ The rules:
|
||||
5. Document _all_ declarations and methods, even private ones. Declare
|
||||
expectations, caveats and anything else that may be important. If a type
|
||||
gets exported, having the comments already there will ensure it's ready.
|
||||
6. Variable name length should be proportional to its context and no longer.
|
||||
6. Variable name length should be proportional to it's context and no longer.
|
||||
`noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.
|
||||
In practice, short methods will have short variable names and globals will
|
||||
have longer names.
|
||||
@@ -386,7 +422,7 @@ The rules:
|
||||
and re-examine why you need a compound name. If you still think you need a
|
||||
compound name, lose the underscore.
|
||||
8. No utils or helpers packages. If a function is not general enough to
|
||||
warrant its own package, it has not been written generally enough to be a
|
||||
warrant it's own package, it has not been written generally enough to be a
|
||||
part of a util package. Just leave it unexported and well-documented.
|
||||
9. All tests should run with `go test` and outside tooling should not be
|
||||
required. No, we don't need another unit testing framework. Assertion
|
||||
@@ -395,6 +431,6 @@ The rules:
|
||||
guidelines. Since you've read all the rules, you now know that.
|
||||
|
||||
If you are having trouble getting into the mood of idiomatic Go, we recommend
|
||||
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
|
||||
[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
|
||||
reading through [Effective Go](http://golang.org/doc/effective_go.html). The
|
||||
[Go Blog](http://blog.golang.org/) is also a great resource. Drinking the
|
||||
kool-aid is a lot easier than going thirsty.
|
||||
|
||||
68
Dockerfile
68
Dockerfile
@@ -30,6 +30,10 @@ RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys E87
|
||||
|| apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys E871F18B51E0147C77796AC81196BA81F6B0FC61
|
||||
RUN echo deb http://ppa.launchpad.net/zfs-native/stable/ubuntu trusty main > /etc/apt/sources.list.d/zfs.list
|
||||
|
||||
# add llvm repo
|
||||
RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 6084F3CF814B57C1CF12EFD515CF4D18AF4F7421 \
|
||||
|| apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 6084F3CF814B57C1CF12EFD515CF4D18AF4F7421
|
||||
RUN echo deb http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.8 main > /etc/apt/sources.list.d/llvm.list
|
||||
|
||||
# allow replacing httpredir mirror
|
||||
ARG APT_MIRROR=httpredir.debian.org
|
||||
@@ -42,11 +46,10 @@ RUN apt-get update && apt-get install -y \
|
||||
aufs-tools \
|
||||
automake \
|
||||
bash-completion \
|
||||
binutils-mingw-w64 \
|
||||
bsdmainutils \
|
||||
btrfs-tools \
|
||||
build-essential \
|
||||
clang \
|
||||
clang-3.8 \
|
||||
createrepo \
|
||||
curl \
|
||||
dpkg-sig \
|
||||
@@ -73,7 +76,10 @@ RUN apt-get update && apt-get install -y \
|
||||
tar \
|
||||
zip \
|
||||
--no-install-recommends \
|
||||
&& pip install awscli==1.10.15
|
||||
&& pip install awscli==1.10.15 \
|
||||
&& ln -snf /usr/bin/clang-3.8 /usr/local/bin/clang \
|
||||
&& ln -snf /usr/bin/clang++-3.8 /usr/local/bin/clang++
|
||||
|
||||
# Get lvm2 source for compiling statically
|
||||
ENV LVM2_VERSION 2.02.103
|
||||
RUN mkdir -p /usr/local/lvm2 \
|
||||
@@ -102,7 +108,7 @@ RUN set -x \
|
||||
ENV PATH /osxcross/target/bin:$PATH
|
||||
|
||||
# install seccomp: the version shipped in trusty is too old
|
||||
ENV SECCOMP_VERSION 2.3.1
|
||||
ENV SECCOMP_VERSION 2.3.0
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
@@ -120,7 +126,11 @@ RUN set -x \
|
||||
# IMPORTANT: If the version of Go is updated, the Windows to Linux CI machines
|
||||
# will need updating, to avoid errors. Ping #docker-maintainers on IRC
|
||||
# with a heads-up.
|
||||
ENV GO_VERSION 1.6.4
|
||||
ENV GO_VERSION 1.5.4
|
||||
RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
|
||||
|
||||
# Compile Go for cross compilation
|
||||
ENV DOCKER_CROSSPLATFORMS \
|
||||
@@ -129,20 +139,6 @@ ENV DOCKER_CROSSPLATFORMS \
|
||||
freebsd/amd64 freebsd/386 freebsd/arm \
|
||||
windows/amd64 windows/386
|
||||
|
||||
RUN curl -fsSL "https://storage.googleapis.com/golang/go1.4.3.linux-amd64.tar.gz" \
|
||||
| tar -xzC /root && \
|
||||
mv /root/go /root/go1.4 && \
|
||||
cd /usr/local && \
|
||||
curl -fsSL "https://storage.googleapis.com/golang/go$GO_VERSION.src.tar.gz" \
|
||||
| tar -xzC /usr/local && \
|
||||
cd go && \
|
||||
printf 'diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s\nindex e09b906..fa8ff2f 100644\n--- a/src/runtime/sys_darwin_amd64.s\n+++ b/src/runtime/sys_darwin_amd64.s\n@@ -157,6 +157,7 @@ systime:\n\t// Fall back to system call (usually first call in this thread).\n\tMOVQ\tSP, DI\n\tMOVQ\t$0, SI\n+\tMOVQ\t$0, DX // required as of Sierra; Issue 16570\n\tMOVL\t$(0x2000000+116), AX\n\tSYSCALL\n\tCMPQ\tAX, $0\n' | patch -p1 && \
|
||||
cd src && \
|
||||
./make.bash
|
||||
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
|
||||
|
||||
# This has been commented out and kept as reference because we don't support compiling with older Go anymore.
|
||||
# ENV GOFMT_VERSION 1.3.3
|
||||
# RUN curl -sSL https://storage.googleapis.com/golang/go${GOFMT_VERSION}.$(go env GOOS)-$(go env GOARCH).tar.gz | tar -C /go/bin -xz --strip-components=2 go/bin/gofmt
|
||||
@@ -178,9 +174,10 @@ RUN set -x \
|
||||
go build -o /usr/local/bin/registry-v2-schema1 github.com/docker/distribution/cmd/registry \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary and notary-server
|
||||
ENV NOTARY_VERSION v0.3.0
|
||||
# Install notary server
|
||||
ENV NOTARY_VERSION docker-v1.11-3
|
||||
RUN set -x \
|
||||
&& export GO15VENDOREXPERIMENT=1 \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
@@ -191,7 +188,7 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT 7befe694bd21e3c54bb1d7825270ea4bd6864c13
|
||||
ENV DOCKER_PY_COMMIT e2878cbcc3a7eef99917adc1be252800b0e41ece
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
@@ -226,7 +223,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.5 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
|
||||
&& 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.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 \
|
||||
@@ -241,29 +238,36 @@ RUN set -x \
|
||||
&& go build -v -o /usr/local/bin/tomlv github.com/BurntSushi/toml/cmd/tomlv \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT 50a19c6ff828c58e5dab13830bd3dacde268afe5
|
||||
# Build/install the tool for embedding resources in Windows binaries
|
||||
ENV RSRC_COMMIT ba14da1f827188454a4591717fff29999010887f
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git clone https://github.com/akavel/rsrc.git "$GOPATH/src/github.com/akavel/rsrc" \
|
||||
&& (cd "$GOPATH/src/github.com/akavel/rsrc" && git checkout -q "$RSRC_COMMIT") \
|
||||
&& go build -v -o /usr/local/bin/rsrc github.com/akavel/rsrc \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://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" \
|
||||
&& cp runc /usr/local/bin/docker-runc \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT 2a5e70cbf65457815ee76b7e5dd2a01292d9eca8
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
@@ -36,7 +36,6 @@ RUN apt-get update && apt-get install -y \
|
||||
libapparmor-dev \
|
||||
libc6-dev \
|
||||
libcap-dev \
|
||||
libltdl-dev \
|
||||
libsqlite3-dev \
|
||||
libsystemd-dev \
|
||||
mercurial \
|
||||
@@ -79,7 +78,7 @@ RUN cd /usr/local/lvm2 \
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
|
||||
# install seccomp: the version shipped in trusty is too old
|
||||
ENV SECCOMP_VERSION 2.3.1
|
||||
ENV SECCOMP_VERSION 2.3.0
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
@@ -97,7 +96,7 @@ RUN set -x \
|
||||
# We don't have official binary tarballs for ARM64, eigher for Go or bootstrap,
|
||||
# so we use the official armv6 released binaries as a GOROOT_BOOTSTRAP, and
|
||||
# build Go from source code.
|
||||
ENV GO_VERSION 1.6.4
|
||||
ENV GO_VERSION 1.5.4
|
||||
RUN mkdir /usr/src/go && curl -fsSL https://storage.googleapis.com/golang/go${GO_VERSION}.src.tar.gz | tar -v -C /usr/src/go -xz --strip-components=1 \
|
||||
&& cd /usr/src/go/src \
|
||||
&& GOOS=linux GOARCH=arm64 GOROOT_BOOTSTRAP="$(go env GOROOT)" ./make.bash
|
||||
@@ -117,9 +116,10 @@ RUN set -x \
|
||||
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary and notary-server
|
||||
ENV NOTARY_VERSION v0.3.0
|
||||
# Install notary server
|
||||
ENV NOTARY_VERSION docker-v1.11-3
|
||||
RUN set -x \
|
||||
&& export GO15VENDOREXPERIMENT=1 \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
@@ -130,7 +130,7 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT 7befe694bd21e3c54bb1d7825270ea4bd6864c13
|
||||
ENV DOCKER_PY_COMMIT e2878cbcc3a7eef99917adc1be252800b0e41ece
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
@@ -165,7 +165,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.5 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
|
||||
&& 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.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 \
|
||||
@@ -181,28 +181,26 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT 50a19c6ff828c58e5dab13830bd3dacde268afe5
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git clone git://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" \
|
||||
&& cp runc /usr/local/bin/docker-runc \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT 2a5e70cbf65457815ee76b7e5dd2a01292d9eca8
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
@@ -46,8 +46,7 @@ RUN apt-get update && apt-get install -y \
|
||||
python-websocket \
|
||||
xfsprogs \
|
||||
tar \
|
||||
--no-install-recommends \
|
||||
&& pip install awscli==1.10.15
|
||||
--no-install-recommends
|
||||
|
||||
# Get lvm2 source for compiling statically
|
||||
ENV LVM2_VERSION 2.02.103
|
||||
@@ -66,8 +65,10 @@ RUN cd /usr/local/lvm2 \
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
|
||||
# Install Go
|
||||
ENV GO_VERSION 1.6.4
|
||||
RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-armv6l.tar.gz" \
|
||||
# TODO Update to 1.5.4 once available, or build from source, as these builds
|
||||
# are marked "end of life", see http://dave.cheney.net/unofficial-arm-tarballs
|
||||
ENV GO_VERSION 1.5.3
|
||||
RUN curl -fsSL "http://dave.cheney.net/paste/go${GO_VERSION}.linux-arm.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
|
||||
@@ -95,7 +96,7 @@ RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint
|
||||
&& go install -v github.com/golang/lint/golint
|
||||
|
||||
# install seccomp: the version shipped in trusty is too old
|
||||
ENV SECCOMP_VERSION 2.3.1
|
||||
ENV SECCOMP_VERSION 2.3.0
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
@@ -126,9 +127,10 @@ RUN set -x \
|
||||
go build -o /usr/local/bin/registry-v2-schema1 github.com/docker/distribution/cmd/registry \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary and notary-server
|
||||
ENV NOTARY_VERSION v0.3.0
|
||||
# Install notary server
|
||||
ENV NOTARY_VERSION docker-v1.11-3
|
||||
RUN set -x \
|
||||
&& export GO15VENDOREXPERIMENT=1 \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
@@ -139,7 +141,7 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT 7befe694bd21e3c54bb1d7825270ea4bd6864c13
|
||||
ENV DOCKER_PY_COMMIT e2878cbcc3a7eef99917adc1be252800b0e41ece
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
@@ -174,7 +176,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.5 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
|
||||
&& 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.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,29 +191,35 @@ RUN set -x \
|
||||
&& go build -v -o /usr/local/bin/tomlv github.com/BurntSushi/toml/cmd/tomlv \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT 50a19c6ff828c58e5dab13830bd3dacde268afe5
|
||||
# Build/install the tool for embedding resources in Windows binaries
|
||||
ENV RSRC_VERSION v2
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git clone --depth 1 -b "$RSRC_VERSION" https://github.com/akavel/rsrc.git "$GOPATH/src/github.com/akavel/rsrc" \
|
||||
&& go build -v -o /usr/local/bin/rsrc github.com/akavel/rsrc \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://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" \
|
||||
&& cp runc /usr/local/bin/docker-runc \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT 2a5e70cbf65457815ee76b7e5dd2a01292d9eca8
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# docker build -t docker -f Dockerfile.gccgo .
|
||||
#
|
||||
|
||||
FROM gcc:6.1
|
||||
FROM gcc:5.3
|
||||
|
||||
# Packaged dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
@@ -43,7 +43,7 @@ RUN cd /usr/local/lvm2 \
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
|
||||
# install seccomp: the version shipped in jessie is too old
|
||||
ENV SECCOMP_VERSION v2.3.1
|
||||
ENV SECCOMP_VERSION v2.3.0
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH=$(mktemp -d) \
|
||||
&& git clone https://github.com/seccomp/libseccomp.git "$SECCOMP_PATH" \
|
||||
@@ -60,7 +60,7 @@ RUN set -x \
|
||||
ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT 7befe694bd21e3c54bb1d7825270ea4bd6864c13
|
||||
ENV DOCKER_PY_COMMIT e2878cbcc3a7eef99917adc1be252800b0e41ece
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT
|
||||
@@ -74,28 +74,26 @@ WORKDIR /go/src/github.com/docker/docker
|
||||
ENV DOCKER_BUILDTAGS apparmor seccomp selinux
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT 50a19c6ff828c58e5dab13830bd3dacde268afe5
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git clone git://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" \
|
||||
&& cp runc /usr/local/bin/docker-runc \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT 2a5e70cbf65457815ee76b7e5dd2a01292d9eca8
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
|
||||
FROM ppc64le/debian:jessie
|
||||
FROM ppc64le/gcc:5.3
|
||||
|
||||
# Packaged dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
@@ -71,29 +71,24 @@ RUN cd /usr/local/lvm2 \
|
||||
&& make install_device-mapper
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
|
||||
# install seccomp: the version shipped in jessie is too old
|
||||
ENV SECCOMP_VERSION 2.3.1
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||
&& ( \
|
||||
cd "$SECCOMP_PATH" \
|
||||
&& ./configure --prefix=/usr/local \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& ldconfig \
|
||||
) \
|
||||
&& rm -rf "$SECCOMP_PATH"
|
||||
# TODO install Go, using gccgo as GOROOT_BOOTSTRAP (Go 1.5+ supports ppc64le properly)
|
||||
# possibly a ppc64le/golang image?
|
||||
|
||||
## BUILD GOLANG
|
||||
ENV GO_VERSION 1.5.4
|
||||
ENV GO_DOWNLOAD_URL https://golang.org/dl/go${GO_VERSION}.src.tar.gz
|
||||
ENV GO_DOWNLOAD_SHA256 002acabce7ddc140d0d55891f9d4fcfbdd806b9332fb8b110c91bc91afb0bc93
|
||||
ENV GOROOT_BOOTSTRAP /usr/local
|
||||
|
||||
# Install Go
|
||||
# NOTE: official ppc64le go binaries weren't available until go 1.6.4 and 1.7.4
|
||||
ENV GO_VERSION 1.6.4
|
||||
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
RUN curl -fsSL "$GO_DOWNLOAD_URL" -o golang.tar.gz \
|
||||
&& echo "$GO_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - \
|
||||
&& tar -C /usr/src -xzf golang.tar.gz \
|
||||
&& rm golang.tar.gz \
|
||||
&& cd /usr/src/go/src && ./make.bash 2>&1
|
||||
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOROOT_BOOTSTRAP /usr/src/
|
||||
|
||||
ENV PATH /usr/src/go/bin/:/go/bin:$PATH
|
||||
ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
|
||||
|
||||
# This has been commented out and kept as reference because we don't support compiling with older Go anymore.
|
||||
@@ -132,19 +127,20 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary and notary-server
|
||||
ENV NOTARY_VERSION v0.3.0
|
||||
ENV NOTARY_VERSION docker-v1.11-3
|
||||
RUN set -x \
|
||||
&& export GO15VENDOREXPERIMENT=1 \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT 7befe694bd21e3c54bb1d7825270ea4bd6864c13
|
||||
ENV DOCKER_PY_COMMIT e2878cbcc3a7eef99917adc1be252800b0e41ece
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
@@ -159,7 +155,7 @@ RUN useradd --create-home --gid docker unprivilegeduser
|
||||
|
||||
VOLUME /var/lib/docker
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
ENV DOCKER_BUILDTAGS apparmor pkcs11 seccomp selinux
|
||||
ENV DOCKER_BUILDTAGS apparmor pkcs11 selinux
|
||||
|
||||
# Let us use a .bashrc file
|
||||
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||
@@ -179,7 +175,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.5 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
|
||||
&& 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.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 \
|
||||
@@ -194,29 +190,35 @@ RUN set -x \
|
||||
&& go build -v -o /usr/local/bin/tomlv github.com/BurntSushi/toml/cmd/tomlv \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT 50a19c6ff828c58e5dab13830bd3dacde268afe5
|
||||
# Build/install the tool for embedding resources in Windows binaries
|
||||
ENV RSRC_VERSION v2
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/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" \
|
||||
&& cp runc /usr/local/bin/docker-runc \
|
||||
&& git clone --depth 1 -b "$RSRC_VERSION" https://github.com/akavel/rsrc.git "$GOPATH/src/github.com/akavel/rsrc" \
|
||||
&& go build -v -o /usr/local/bin/rsrc github.com/akavel/rsrc \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT 2a5e70cbf65457815ee76b7e5dd2a01292d9eca8
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git clone git://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 selinux" \
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
|
||||
FROM s390x/gcc:6.1
|
||||
FROM s390x/gcc:5.3
|
||||
|
||||
# Packaged dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
@@ -48,21 +48,6 @@ RUN apt-get update && apt-get install -y \
|
||||
tar \
|
||||
--no-install-recommends
|
||||
|
||||
# install seccomp: the version shipped in jessie is too old
|
||||
ENV SECCOMP_VERSION 2.3.1
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||
&& ( \
|
||||
cd "$SECCOMP_PATH" \
|
||||
&& ./configure --prefix=/usr/local \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& ldconfig \
|
||||
) \
|
||||
&& rm -rf "$SECCOMP_PATH"
|
||||
|
||||
# Get lvm2 source for compiling statically
|
||||
ENV LVM2_VERSION 2.02.103
|
||||
RUN mkdir -p /usr/local/lvm2 \
|
||||
@@ -112,41 +97,29 @@ RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint
|
||||
&& go install -v github.com/golang/lint/golint
|
||||
|
||||
|
||||
# Install two versions of the registry. The first is an older version that
|
||||
# only supports schema1 manifests. The second is a newer version that supports
|
||||
# both. This allows integration-cli tests to cover push/pull with both schema1
|
||||
# and schema2 manifests.
|
||||
ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||
ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
|
||||
# Install registry
|
||||
ENV REGISTRY_COMMIT ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \
|
||||
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/registry-v2-schema1 github.com/docker/distribution/cmd/registry \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary and notary-server
|
||||
#
|
||||
# Note: We have to explicitly set GO15VENDOREXPERIMENT=0 because gccgo does not
|
||||
# support vendoring: https://github.com/golang/go/issues/15628
|
||||
ENV NOTARY_VERSION v0.3.0
|
||||
# Install notary server
|
||||
ENV NOTARY_VERSION docker-v1.11-3
|
||||
RUN set -x \
|
||||
&& export GO15VENDOREXPERIMENT=0 \
|
||||
&& export GO15VENDOREXPERIMENT=1 \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION" && ln -s . vendor/src) \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT 7befe694bd21e3c54bb1d7825270ea4bd6864c13
|
||||
ENV DOCKER_PY_COMMIT e2878cbcc3a7eef99917adc1be252800b0e41ece
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
@@ -161,7 +134,7 @@ RUN useradd --create-home --gid docker unprivilegeduser
|
||||
|
||||
VOLUME /var/lib/docker
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
ENV DOCKER_BUILDTAGS apparmor selinux seccomp
|
||||
ENV DOCKER_BUILDTAGS apparmor pkcs11 selinux
|
||||
|
||||
# Let us use a .bashrc file
|
||||
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||
@@ -181,7 +154,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.5 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
|
||||
&& 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.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 \
|
||||
@@ -196,29 +169,35 @@ RUN set -x \
|
||||
&& go build -v -o /usr/local/bin/tomlv github.com/BurntSushi/toml/cmd/tomlv \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT 50a19c6ff828c58e5dab13830bd3dacde268afe5
|
||||
# Build/install the tool for embedding resources in Windows binaries
|
||||
ENV RSRC_VERSION v2
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git clone --depth 1 -b "$RSRC_VERSION" https://github.com/akavel/rsrc.git "$GOPATH/src/github.com/akavel/rsrc" \
|
||||
&& go build -v -o /usr/local/bin/rsrc github.com/akavel/rsrc \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://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" \
|
||||
&& cp runc /usr/local/bin/docker-runc \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT 2a5e70cbf65457815ee76b7e5dd2a01292d9eca8
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
@@ -12,11 +12,10 @@ FROM debian:jessie
|
||||
# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#runtime-dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
btrfs-tools \
|
||||
build-essential \
|
||||
curl \
|
||||
gcc \
|
||||
git \
|
||||
libapparmor-dev \
|
||||
golang \
|
||||
libdevmapper-dev \
|
||||
libsqlite3-dev \
|
||||
\
|
||||
@@ -30,55 +29,27 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
aufs-tools \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# install seccomp: the version shipped in trusty is too old
|
||||
ENV SECCOMP_VERSION 2.3.1
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||
&& ( \
|
||||
cd "$SECCOMP_PATH" \
|
||||
&& ./configure --prefix=/usr/local \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& ldconfig \
|
||||
) \
|
||||
&& rm -rf "$SECCOMP_PATH"
|
||||
|
||||
# Install Go
|
||||
# IMPORTANT: If the version of Go is updated, the Windows to Linux CI machines
|
||||
# will need updating, to avoid errors. Ping #docker-maintainers on IRC
|
||||
# with a heads-up.
|
||||
ENV GO_VERSION 1.6.4
|
||||
RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
|
||||
ENV CGO_LDFLAGS -L/lib
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT 50a19c6ff828c58e5dab13830bd3dacde268afe5
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git clone git://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" \
|
||||
&& cp runc /usr/local/bin/docker-runc \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT 2a5e70cbf65457815ee76b7e5dd2a01292d9eca8
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
|
||||
&& rm -rf "$GOPATH"
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
ENV AUTO_GOPATH 1
|
||||
WORKDIR /usr/src/docker
|
||||
|
||||
32
Dockerfile.windows
Normal file → Executable file
32
Dockerfile.windows
Normal file → Executable file
@@ -1,10 +1,10 @@
|
||||
# This file describes the standard way to build Docker, using a docker container on Windows
|
||||
# This file describes the standard way to build Docker, using a docker container on Windows
|
||||
# Server 2016
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# # Assemble the full dev environment. This is slow the first time. Run this from
|
||||
# # a directory containing the sources you are validating. For example from
|
||||
# # a directory containing the sources you are validating. For example from
|
||||
# # c:\go\src\github.com\docker\docker
|
||||
#
|
||||
# docker build -t docker -f Dockerfile.windows .
|
||||
@@ -19,23 +19,30 @@
|
||||
# Important notes:
|
||||
# ---------------
|
||||
#
|
||||
# The posix utilities from GIT aren't usable interactively as at January 2016. This
|
||||
# is because they require a console window which isn't present in a container in Windows.
|
||||
# See the example at the top of this file. Do NOT use -it in that docker run!!!
|
||||
# 'Start-Sleep' is a deliberate workaround for a current problem on containers in Windows
|
||||
# Server 2016. It ensures that the network is up and available for when the command is
|
||||
# network related. This bug is being tracked internally at Microsoft and exists in TP4.
|
||||
# Generally sleep 1 or 2 is probably enough, but making it 5 to make the build file
|
||||
# as bullet proof as possible. This isn't a big deal as this only runs the first time.
|
||||
#
|
||||
# Don't try to use a volume for passing the source through. The posix utilities will
|
||||
# The cygwin posix utilities from GIT aren't usable interactively as at January 2016. This
|
||||
# is because they require a console window which isn't present in a container in Windows.
|
||||
# See the example at the top of this file. Do NOT use -it in that docker run!!!
|
||||
#
|
||||
# Don't try to use a volume for passing the source through. The cygwin posix utilities will
|
||||
# balk at reparse points. Again, see the example at the top of this file on how use a volume
|
||||
# to get the built binary out of the container.
|
||||
#
|
||||
# The steps are minimised dramatically to improve performance
|
||||
# The steps are minimised dramatically to improve performance (TP4 is slow on commit)
|
||||
|
||||
FROM windowsservercore
|
||||
|
||||
# Environment variable notes:
|
||||
# - GO_VERSION must consistent with 'Dockerfile' used by Linux'.
|
||||
# - FROM_DOCKERFILE is used for detection of building within a container.
|
||||
ENV GO_VERSION=1.6.4 \
|
||||
ENV GO_VERSION=1.5.4 \
|
||||
GIT_LOCATION=https://github.com/git-for-windows/git/releases/download/v2.7.2.windows.1/Git-2.7.2-64-bit.exe \
|
||||
RSRC_COMMIT=ba14da1f827188454a4591717fff29999010887f \
|
||||
GOPATH=C:/go;C:/go/src/github.com/docker/docker/vendor \
|
||||
FROM_DOCKERFILE=1
|
||||
|
||||
@@ -47,11 +54,12 @@ RUN \
|
||||
setx GOROOT "c:\go" && \
|
||||
powershell -command \
|
||||
$ErrorActionPreference = 'Stop'; \
|
||||
Start-Sleep -Seconds 5; \
|
||||
Function Download-File([string] $source, [string] $target) { \
|
||||
$wc = New-Object net.webclient; $wc.Downloadfile($source, $target) \
|
||||
} \
|
||||
\
|
||||
Write-Host INFO: Downloading git...; \
|
||||
Write-Host INFO: Downloading git...; \
|
||||
Download-File %GIT_LOCATION% gitsetup.exe; \
|
||||
\
|
||||
Write-Host INFO: Downloading go...; \
|
||||
@@ -82,8 +90,12 @@ RUN \
|
||||
Remove-Item go.msi; \
|
||||
Remove-Item gitsetup.exe; \
|
||||
\
|
||||
Write-Host INFO: Cloning and installing RSRC; \
|
||||
c:\git\bin\git.exe clone https://github.com/akavel/rsrc.git c:\go\src\github.com\akavel\rsrc; \
|
||||
cd \go\src\github.com\akavel\rsrc; c:\git\bin\git.exe checkout -q %RSRC_COMMIT%; c:\go\bin\go.exe install -v; \
|
||||
\
|
||||
Write-Host INFO: Completed
|
||||
|
||||
|
||||
# Prepare for building
|
||||
COPY . /go/src/github.com/docker/docker
|
||||
|
||||
|
||||
45
MAINTAINERS
45
MAINTAINERS
@@ -35,15 +35,15 @@
|
||||
"estesp",
|
||||
"icecrime",
|
||||
"jhowardmsft",
|
||||
"justincormack",
|
||||
"jfrazelle",
|
||||
"lk4d4",
|
||||
"mavenugo",
|
||||
"mhbauer",
|
||||
"runcom",
|
||||
"tianon",
|
||||
"tibor",
|
||||
"tonistiigi",
|
||||
"unclejack",
|
||||
"vbatts",
|
||||
"vdemeester"
|
||||
]
|
||||
|
||||
@@ -90,37 +90,6 @@
|
||||
# still stumble into him in our issue tracker, or on IRC.
|
||||
"erikh",
|
||||
|
||||
# Jessica Frazelle, also known as the "Keyser Söze of containers",
|
||||
# runs *everything* in containers. She started contributing to
|
||||
# Docker with a (fun fun) change involving both iptables and regular
|
||||
# expressions (coz, YOLO!) on July 10, 2014
|
||||
# https://github.com/docker/docker/pull/6950/commits/f3a68ffa390fb851115c77783fa4031f1d3b2995.
|
||||
# Jess was Release Captain for Docker 1.4, 1.6 and 1.7, and contributed
|
||||
# many features and improvement, among which "seccomp profiles" (making
|
||||
# containers a lot more secure). Besides being a maintainer, she
|
||||
# set up the CI infrastructure for the project, giving everyone
|
||||
# something to shout at if a PR failed ("noooo Janky!").
|
||||
# Jess is currently working on the DCOS security team at Mesosphere,
|
||||
# and contributing to various open source projects.
|
||||
# Be sure you don't miss her talks at a conference near you (a must-see),
|
||||
# read her blog at https://blog.jessfraz.com (a must-read), and
|
||||
# check out her open source projects on GitHub https://github.com/jfrazelle (a must-try).
|
||||
"jfrazelle",
|
||||
|
||||
# Vincent "vbatts!" Batts made his first contribution to the project
|
||||
# in November 2013, to become a maintainer a few months later, on
|
||||
# May 10, 2014 (https://github.com/docker/docker/commit/d6e666a87a01a5634c250358a94c814bf26cb778).
|
||||
# As a maintainer, Vincent made important contributions to core elements
|
||||
# of Docker, such as "distribution" (tarsum) and graphdrivers (btrfs, devicemapper).
|
||||
# He also contributed the "tar-split" library, an important element
|
||||
# for the content-addressable store.
|
||||
# Vincent is currently a member of the Open Containers Initiative
|
||||
# Technical Oversight Board (TOB), besides his work at Red Hat and
|
||||
# Project Atomic. You can still find him regularly hanging out in
|
||||
# our repository and the #docker-dev and #docker-maintainers IRC channels
|
||||
# for a chat, as he's always a lot of fun.
|
||||
"vbatts",
|
||||
|
||||
# Victor is one of the earliest contributors to Docker, having worked on the
|
||||
# project when it was still "dotCloud" in April 2013. He's been responsible
|
||||
# for multiple releases (https://github.com/docker/docker/pulls?q=is%3Apr+bump+in%3Atitle+author%3Avieux),
|
||||
@@ -205,21 +174,11 @@
|
||||
Email = "jess@linux.com"
|
||||
GitHub = "jfrazelle"
|
||||
|
||||
[people.justincormack]
|
||||
Name = "Justin Cormack"
|
||||
Email = "justin.cormack@docker.com"
|
||||
GitHub = "justincormack"
|
||||
|
||||
[people.lk4d4]
|
||||
Name = "Alexander Morozov"
|
||||
Email = "lk4d4@docker.com"
|
||||
GitHub = "lk4d4"
|
||||
|
||||
[people.mavenugo]
|
||||
Name = "Madhu Venugopal"
|
||||
Email = "madhu@docker.com"
|
||||
GitHub = "mavenugo"
|
||||
|
||||
[people.mhbauer]
|
||||
Name = "Morgan Bauer"
|
||||
Email = "mbauer@us.ibm.com"
|
||||
|
||||
64
Makefile
64
Makefile
@@ -1,24 +1,21 @@
|
||||
.PHONY: all binary build build-gccgo cross default docs docs-build docs-shell shell gccgo test test-docker-py test-integration-cli test-unit validate help
|
||||
|
||||
# set the graph driver as the current graphdriver if not set
|
||||
DOCKER_GRAPHDRIVER := $(if $(DOCKER_GRAPHDRIVER),$(DOCKER_GRAPHDRIVER),$(shell docker info 2>&1 | grep "Storage Driver" | sed 's/.*: //'))
|
||||
.PHONY: all binary build cross default docs docs-build docs-shell shell test test-docker-py test-integration-cli test-unit validate
|
||||
|
||||
# get OS/Arch of docker engine
|
||||
DOCKER_OSARCH := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $${DOCKER_ENGINE_OSARCH:-$$DOCKER_CLIENT_OSARCH}')
|
||||
DOCKERFILE := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $${DOCKERFILE}')
|
||||
|
||||
# env vars passed through directly to Docker's build scripts
|
||||
# to allow things like `make KEEPBUNDLE=1 binary` easily
|
||||
# to allow things like `make DOCKER_CLIENTONLY=1 binary` easily
|
||||
# `docs/sources/contributing/devenvironment.md ` and `project/PACKAGERS.md` have some limited documentation of some of these
|
||||
DOCKER_ENVS := \
|
||||
-e BUILDFLAGS \
|
||||
-e KEEPBUNDLE \
|
||||
-e DOCKER_BUILD_GOGC \
|
||||
-e DOCKER_BUILD_PKGS \
|
||||
-e DOCKER_CLIENTONLY \
|
||||
-e DOCKER_DEBUG \
|
||||
-e DOCKER_EXPERIMENTAL \
|
||||
-e DOCKER_GITCOMMIT \
|
||||
-e DOCKER_GRAPHDRIVER=$(DOCKER_GRAPHDRIVER) \
|
||||
-e DOCKER_GRAPHDRIVER \
|
||||
-e DOCKER_INCREMENTAL_BINARY \
|
||||
-e DOCKER_REMAP_ROOT \
|
||||
-e DOCKER_STORAGE_OPTS \
|
||||
@@ -40,9 +37,8 @@ DOCKER_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/do
|
||||
DOCKER_MOUNT := $(if $(DOCKER_MOUNT),$(DOCKER_MOUNT),-v "/go/src/github.com/docker/docker/bundles")
|
||||
|
||||
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||
GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g")
|
||||
DOCKER_IMAGE := docker-dev$(if $(GIT_BRANCH_CLEAN),:$(GIT_BRANCH_CLEAN))
|
||||
DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH_CLEAN),:$(GIT_BRANCH_CLEAN))
|
||||
DOCKER_IMAGE := docker-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
||||
DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
||||
|
||||
DOCKER_FLAGS := docker run --rm -i --privileged $(DOCKER_ENVS) $(DOCKER_MOUNT)
|
||||
|
||||
@@ -58,69 +54,53 @@ DOCKER_RUN_DOCKER := $(DOCKER_FLAGS) "$(DOCKER_IMAGE)"
|
||||
|
||||
default: binary
|
||||
|
||||
all: build ## validate all checks, build linux binaries, run all tests\ncross build non-linux binaries and generate archives
|
||||
all: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh
|
||||
|
||||
binary: build ## build the linux binaries
|
||||
binary: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary
|
||||
|
||||
build: bundles
|
||||
docker build ${DOCKER_BUILD_ARGS} -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" .
|
||||
|
||||
build-gccgo: bundles
|
||||
docker build ${DOCKER_BUILD_ARGS} -t "$(DOCKER_IMAGE)-gccgo" -f Dockerfile.gccgo .
|
||||
|
||||
bundles:
|
||||
mkdir bundles
|
||||
|
||||
cross: build ## cross build the binaries for darwin, freebsd and\nwindows
|
||||
cross: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross
|
||||
|
||||
win: build ## cross build the binary for windows
|
||||
win: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh win
|
||||
|
||||
tgz: build ## build the archives (.zip on windows and .tgz\notherwise) containing the binaries
|
||||
tgz: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross tgz
|
||||
|
||||
deb: build ## build the deb packages
|
||||
deb: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary build-deb
|
||||
|
||||
docs: ## build the docs
|
||||
docs:
|
||||
$(MAKE) -C docs docs
|
||||
|
||||
gccgo: build-gccgo ## build the gcc-go linux binaries
|
||||
$(DOCKER_FLAGS) "$(DOCKER_IMAGE)-gccgo" hack/make.sh gccgo
|
||||
gccgo: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh gccgo
|
||||
|
||||
install: ## install the linux binaries
|
||||
KEEPBUNDLE=1 hack/make.sh install-binary
|
||||
|
||||
rpm: build ## build the rpm packages
|
||||
rpm: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary build-rpm
|
||||
|
||||
shell: build ## start a shell inside the build env
|
||||
shell: build
|
||||
$(DOCKER_RUN_DOCKER) bash
|
||||
|
||||
test: build ## run the unit, integration and docker-py tests
|
||||
test: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary cross test-unit test-integration-cli test-docker-py
|
||||
|
||||
test-docker-py: build ## run the docker-py tests
|
||||
test-docker-py: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-docker-py
|
||||
|
||||
test-integration-cli: build ## run the integration tests
|
||||
test-integration-cli: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration-cli
|
||||
|
||||
test-unit: build ## run the unit tests
|
||||
test-unit: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh test-unit
|
||||
|
||||
validate: build ## validate DCO, Seccomp profile generation, gofmt,\n./pkg/ isolation, golint, tests, tomls, go vet and vendor
|
||||
validate: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh validate-dco validate-default-seccomp validate-gofmt validate-pkg validate-lint validate-test validate-toml validate-vet validate-vendor
|
||||
|
||||
manpages: ## Generate man pages from go source and markdown
|
||||
docker build -t docker-manpage-dev -f "man/$(DOCKERFILE)" ./man
|
||||
docker run \
|
||||
-v $(PWD):/go/src/github.com/docker/docker/ \
|
||||
docker-manpage-dev
|
||||
|
||||
help: ## this help
|
||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||
|
||||
|
||||
55
README.md
55
README.md
@@ -13,17 +13,17 @@ databases, and backend services without depending on a particular stack
|
||||
or provider.
|
||||
|
||||
Docker began as an open-source implementation of the deployment engine which
|
||||
powered [dotCloud](http://web.archive.org/web/20130530031104/https://www.dotcloud.com/),
|
||||
a popular Platform-as-a-Service. It benefits directly from the experience
|
||||
accumulated over several years of large-scale operation and support of hundreds
|
||||
of thousands of applications and databases.
|
||||
powers [dotCloud](https://www.dotcloud.com), a popular Platform-as-a-Service.
|
||||
It benefits directly from the experience accumulated over several years
|
||||
of large-scale operation and support of hundreds of thousands of
|
||||
applications and databases.
|
||||
|
||||

|
||||

|
||||
|
||||
## Security Disclosure
|
||||
|
||||
Security is very important to us. If you have any issue regarding security,
|
||||
please disclose the information responsibly by sending an email to
|
||||
Security is very important to us. If you have any issue regarding security,
|
||||
please disclose the information responsibly by sending an email to
|
||||
security@docker.com and not by creating a github issue.
|
||||
|
||||
## Better than VMs
|
||||
@@ -148,6 +148,9 @@ on servers for running them. To get started, [check out the installation
|
||||
instructions in the
|
||||
documentation](https://docs.docker.com/engine/installation/).
|
||||
|
||||
We also offer an [interactive tutorial](https://www.docker.com/tryit/)
|
||||
for quickly learning the basics of using Docker.
|
||||
|
||||
Usage examples
|
||||
==============
|
||||
|
||||
@@ -188,8 +191,8 @@ feels wrong or incomplete. Better yet, submit a PR and improve them yourself.
|
||||
Getting the development builds
|
||||
==============================
|
||||
|
||||
Want to run Docker from a master build? You can download
|
||||
master builds at [master.dockerproject.org](https://master.dockerproject.org).
|
||||
Want to run Docker from a master build? You can download
|
||||
master builds at [master.dockerproject.org](https://master.dockerproject.org).
|
||||
They are updated with each commit merged into the master branch.
|
||||
|
||||
Don't know how to use that super cool new feature in the master build? Check
|
||||
@@ -222,23 +225,17 @@ We are always open to suggestions on process improvements, and are always lookin
|
||||
Read our <a href="https://docs.docker.com/project/get-help/#irc-quickstart" target="_blank">IRC quickstart guide</a> for an easy way to get started.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Docker Community Forums</td>
|
||||
<td>
|
||||
The <a href="https://forums.docker.com/c/open-source-projects/de" target="_blank">Docker Engine</a>
|
||||
group is for users of the Docker Engine project.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Google Groups</td>
|
||||
<td>
|
||||
The <a href="https://groups.google.com/forum/#!forum/docker-dev"
|
||||
target="_blank">docker-dev</a> group is for contributors and other people
|
||||
contributing to the Docker project. You can join this group without a
|
||||
Google account by sending an email to <a
|
||||
href="mailto:docker-dev+subscribe@googlegroups.com">docker-dev+subscribe@googlegroups.com</a>.
|
||||
You'll receive a join-request message; simply reply to the message to
|
||||
confirm your subscribtion.
|
||||
There are two groups.
|
||||
<a href="https://groups.google.com/forum/#!forum/docker-user" target="_blank">Docker-user</a>
|
||||
is for people using Docker containers.
|
||||
The <a href="https://groups.google.com/forum/#!forum/docker-dev" target="_blank">docker-dev</a>
|
||||
group is for contributors and other people contributing to the Docker
|
||||
project.
|
||||
You can join them without an google account by sending an email to e.g. "docker-user+subscribe@googlegroups.com".
|
||||
After receiving the join-request message, you can simply reply to that to confirm the subscribtion.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -285,18 +282,18 @@ There are a number of projects under development that are based on Docker's
|
||||
core technology. These projects expand the tooling built around the
|
||||
Docker platform to broaden its application and utility.
|
||||
|
||||
* [Docker Registry](https://github.com/docker/distribution): Registry
|
||||
* [Docker Registry](https://github.com/docker/distribution): Registry
|
||||
server for Docker (hosting/delivery of repositories and images)
|
||||
* [Docker Machine](https://github.com/docker/machine): Machine management
|
||||
* [Docker Machine](https://github.com/docker/machine): Machine management
|
||||
for a container-centric world
|
||||
* [Docker Swarm](https://github.com/docker/swarm): A Docker-native clustering
|
||||
* [Docker Swarm](https://github.com/docker/swarm): A Docker-native clustering
|
||||
system
|
||||
* [Docker Compose](https://github.com/docker/compose) (formerly Fig):
|
||||
* [Docker Compose](https://github.com/docker/compose) (formerly Fig):
|
||||
Define and run multi-container apps
|
||||
* [Kitematic](https://github.com/docker/kitematic): The easiest way to use
|
||||
* [Kitematic](https://github.com/docker/kitematic): The easiest way to use
|
||||
Docker on Mac and Windows
|
||||
|
||||
If you know of another project underway that should be listed here, please help
|
||||
If you know of another project underway that should be listed here, please help
|
||||
us keep this list up-to-date by submitting a PR.
|
||||
|
||||
Awesome-Docker
|
||||
|
||||
@@ -38,7 +38,7 @@ to the MAJOR.MINOR.PATCH format."
|
||||
|
||||
## Vendoring cadence
|
||||
In order to avoid huge vendoring changes, it is recommended to have a regular
|
||||
cadence for vendoring updates. e.g. monthly.
|
||||
cadence for vendoring updates. eg. monthly.
|
||||
|
||||
## Pre-merge vendoring tests
|
||||
All related repos will be vendored into docker/docker.
|
||||
|
||||
109
api/client/attach.go
Normal file
109
api/client/attach.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// CmdAttach attaches to a running container.
|
||||
//
|
||||
// Usage: docker attach [OPTIONS] CONTAINER
|
||||
func (cli *DockerCli) CmdAttach(args ...string) error {
|
||||
cmd := Cli.Subcmd("attach", []string{"CONTAINER"}, Cli.DockerCommands["attach"].Description, true)
|
||||
noStdin := cmd.Bool([]string{"-no-stdin"}, false, "Do not attach STDIN")
|
||||
proxy := cmd.Bool([]string{"-sig-proxy"}, true, "Proxy all received signals to the process")
|
||||
detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
||||
|
||||
cmd.Require(flag.Exact, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
c, err := cli.client.ContainerInspect(context.Background(), cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !c.State.Running {
|
||||
return fmt.Errorf("You cannot attach to a stopped container, start it first")
|
||||
}
|
||||
|
||||
if c.State.Paused {
|
||||
return fmt.Errorf("You cannot attach to a paused container, unpause it first")
|
||||
}
|
||||
|
||||
if err := cli.CheckTtyInput(!*noStdin, c.Config.Tty); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *detachKeys != "" {
|
||||
cli.configFile.DetachKeys = *detachKeys
|
||||
}
|
||||
|
||||
options := types.ContainerAttachOptions{
|
||||
ContainerID: cmd.Arg(0),
|
||||
Stream: true,
|
||||
Stdin: !*noStdin && c.Config.OpenStdin,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
DetachKeys: cli.configFile.DetachKeys,
|
||||
}
|
||||
|
||||
var in io.ReadCloser
|
||||
if options.Stdin {
|
||||
in = cli.in
|
||||
}
|
||||
|
||||
if *proxy && !c.Config.Tty {
|
||||
sigc := cli.forwardAllSignals(options.ContainerID)
|
||||
defer signal.StopCatch(sigc)
|
||||
}
|
||||
|
||||
resp, err := cli.client.ContainerAttach(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Close()
|
||||
if in != nil && c.Config.Tty {
|
||||
if err := cli.setRawTerminal(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.restoreTerminal(in)
|
||||
}
|
||||
|
||||
if c.Config.Tty && cli.isTerminalOut {
|
||||
height, width := cli.getTtySize()
|
||||
// To handle the case where a user repeatedly attaches/detaches without resizing their
|
||||
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
|
||||
// resize it, then go back to normal. Without this, every attach after the first will
|
||||
// require the user to manually resize or hit enter.
|
||||
cli.resizeTtyTo(cmd.Arg(0), height+1, width+1, false)
|
||||
|
||||
// After the above resizing occurs, the call to monitorTtySize below will handle resetting back
|
||||
// to the actual size.
|
||||
if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
|
||||
logrus.Debugf("Error monitoring TTY size: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, status, err := getExitCode(cli, options.ContainerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status != 0 {
|
||||
return Cli.StatusError{StatusCode: status}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package image
|
||||
package client
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
@@ -14,14 +14,14 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/dockerignore"
|
||||
"github.com/docker/docker/cli"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/urlutil"
|
||||
@@ -30,89 +30,58 @@ import (
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type buildOptions struct {
|
||||
context string
|
||||
dockerfileName string
|
||||
tags opts.ListOpts
|
||||
labels opts.ListOpts
|
||||
buildArgs opts.ListOpts
|
||||
ulimits *runconfigopts.UlimitOpt
|
||||
memory string
|
||||
memorySwap string
|
||||
shmSize string
|
||||
cpuShares int64
|
||||
cpuPeriod int64
|
||||
cpuQuota int64
|
||||
cpuSetCpus string
|
||||
cpuSetMems string
|
||||
cgroupParent string
|
||||
isolation string
|
||||
quiet bool
|
||||
noCache bool
|
||||
rm bool
|
||||
forceRm bool
|
||||
pull bool
|
||||
}
|
||||
type translatorFunc func(reference.NamedTagged) (reference.Canonical, error)
|
||||
|
||||
// CmdBuild builds a new image from the source code at a given path.
|
||||
//
|
||||
// If '-' is provided instead of a path or URL, Docker will build an image from either a Dockerfile or tar archive read from STDIN.
|
||||
//
|
||||
// Usage: docker build [OPTIONS] PATH | URL | -
|
||||
func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
cmd := Cli.Subcmd("build", []string{"PATH | URL | -"}, Cli.DockerCommands["build"].Description, true)
|
||||
flTags := opts.NewListOpts(validateTag)
|
||||
cmd.Var(&flTags, []string{"t", "-tag"}, "Name and optionally a tag in the 'name:tag' format")
|
||||
suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the build output and print image ID on success")
|
||||
noCache := cmd.Bool([]string{"-no-cache"}, false, "Do not use cache when building the image")
|
||||
rm := cmd.Bool([]string{"-rm"}, true, "Remove intermediate containers after a successful build")
|
||||
forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers")
|
||||
pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image")
|
||||
dockerfileName := cmd.String([]string{"f", "-file"}, "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')")
|
||||
flMemoryString := cmd.String([]string{"m", "-memory"}, "", "Memory limit")
|
||||
flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
||||
flShmSize := cmd.String([]string{"-shm-size"}, "", "Size of /dev/shm, default value is 64MB")
|
||||
flCPUShares := cmd.Int64([]string{"#c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
|
||||
flCPUPeriod := cmd.Int64([]string{"-cpu-period"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
|
||||
flCPUQuota := cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
||||
flCPUSetCpus := cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
|
||||
flCPUSetMems := cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)")
|
||||
flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
|
||||
flBuildArg := opts.NewListOpts(runconfigopts.ValidateEnv)
|
||||
cmd.Var(&flBuildArg, []string{"-build-arg"}, "Set build-time variables")
|
||||
isolation := cmd.String([]string{"-isolation"}, "", "Container isolation technology")
|
||||
|
||||
flLabels := opts.NewListOpts(nil)
|
||||
cmd.Var(&flLabels, []string{"-label"}, "Set metadata for an image")
|
||||
|
||||
// NewBuildCommand creates a new `docker build` command
|
||||
func NewBuildCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
ulimits := make(map[string]*units.Ulimit)
|
||||
options := buildOptions{
|
||||
tags: opts.NewListOpts(validateTag),
|
||||
buildArgs: opts.NewListOpts(runconfigopts.ValidateEnv),
|
||||
ulimits: runconfigopts.NewUlimitOpt(&ulimits),
|
||||
labels: opts.NewListOpts(runconfigopts.ValidateEnv),
|
||||
}
|
||||
flUlimits := runconfigopts.NewUlimitOpt(&ulimits)
|
||||
cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "build [OPTIONS] PATH | URL | -",
|
||||
Short: "Build an image from a Dockerfile",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.context = args[0]
|
||||
return runBuild(dockerCli, options)
|
||||
},
|
||||
}
|
||||
cmd.Require(flag.Exact, 1)
|
||||
|
||||
flags := cmd.Flags()
|
||||
// For trusted pull on "FROM <image>" instruction.
|
||||
addTrustedFlags(cmd, true)
|
||||
|
||||
flags.VarP(&options.tags, "tag", "t", "Name and optionally a tag in the 'name:tag' format")
|
||||
flags.Var(&options.buildArgs, "build-arg", "Set build-time variables")
|
||||
flags.Var(options.ulimits, "ulimit", "Ulimit options")
|
||||
flags.StringVarP(&options.dockerfileName, "file", "f", "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')")
|
||||
flags.StringVarP(&options.memory, "memory", "m", "", "Memory limit")
|
||||
flags.StringVar(&options.memorySwap, "memory-swap", "", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
||||
flags.StringVar(&options.shmSize, "shm-size", "", "Size of /dev/shm, default value is 64MB")
|
||||
flags.Int64VarP(&options.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
|
||||
flags.Int64Var(&options.cpuPeriod, "cpu-period", 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
|
||||
flags.Int64Var(&options.cpuQuota, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
||||
flags.StringVar(&options.cpuSetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
|
||||
flags.StringVar(&options.cpuSetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
|
||||
flags.StringVar(&options.cgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container")
|
||||
flags.StringVar(&options.isolation, "isolation", "", "Container isolation technology")
|
||||
flags.Var(&options.labels, "label", "Set metadata for an image")
|
||||
flags.BoolVar(&options.noCache, "no-cache", false, "Do not use cache when building the image")
|
||||
flags.BoolVar(&options.rm, "rm", true, "Remove intermediate containers after a successful build")
|
||||
flags.BoolVar(&options.forceRm, "force-rm", false, "Always remove intermediate containers")
|
||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the build output and print image ID on success")
|
||||
flags.BoolVar(&options.pull, "pull", false, "Always attempt to pull a newer version of the image")
|
||||
|
||||
client.AddTrustedFlags(flags, true)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runBuild(dockerCli *client.DockerCli, options buildOptions) error {
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var (
|
||||
buildCtx io.ReadCloser
|
||||
err error
|
||||
ctx io.ReadCloser
|
||||
err error
|
||||
)
|
||||
|
||||
specifiedContext := options.context
|
||||
specifiedContext := cmd.Arg(0)
|
||||
|
||||
var (
|
||||
contextDir string
|
||||
@@ -122,27 +91,27 @@ func runBuild(dockerCli *client.DockerCli, options buildOptions) error {
|
||||
buildBuff io.Writer
|
||||
)
|
||||
|
||||
progBuff = dockerCli.Out()
|
||||
buildBuff = dockerCli.Out()
|
||||
if options.quiet {
|
||||
progBuff = cli.out
|
||||
buildBuff = cli.out
|
||||
if *suppressOutput {
|
||||
progBuff = bytes.NewBuffer(nil)
|
||||
buildBuff = bytes.NewBuffer(nil)
|
||||
}
|
||||
|
||||
switch {
|
||||
case specifiedContext == "-":
|
||||
buildCtx, relDockerfile, err = builder.GetContextFromReader(dockerCli.In(), options.dockerfileName)
|
||||
ctx, relDockerfile, err = builder.GetContextFromReader(cli.in, *dockerfileName)
|
||||
case urlutil.IsGitURL(specifiedContext):
|
||||
tempDir, relDockerfile, err = builder.GetContextFromGitURL(specifiedContext, options.dockerfileName)
|
||||
tempDir, relDockerfile, err = builder.GetContextFromGitURL(specifiedContext, *dockerfileName)
|
||||
case urlutil.IsURL(specifiedContext):
|
||||
buildCtx, relDockerfile, err = builder.GetContextFromURL(progBuff, specifiedContext, options.dockerfileName)
|
||||
ctx, relDockerfile, err = builder.GetContextFromURL(progBuff, specifiedContext, *dockerfileName)
|
||||
default:
|
||||
contextDir, relDockerfile, err = builder.GetContextFromLocalDir(specifiedContext, options.dockerfileName)
|
||||
contextDir, relDockerfile, err = builder.GetContextFromLocalDir(specifiedContext, *dockerfileName)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if options.quiet && urlutil.IsURL(specifiedContext) {
|
||||
fmt.Fprintln(dockerCli.Err(), progBuff)
|
||||
if *suppressOutput && urlutil.IsURL(specifiedContext) {
|
||||
fmt.Fprintln(cli.err, progBuff)
|
||||
}
|
||||
return fmt.Errorf("unable to prepare context: %s", err)
|
||||
}
|
||||
@@ -152,7 +121,7 @@ func runBuild(dockerCli *client.DockerCli, options buildOptions) error {
|
||||
contextDir = tempDir
|
||||
}
|
||||
|
||||
if buildCtx == nil {
|
||||
if ctx == nil {
|
||||
// And canonicalize dockerfile name to a platform-independent one
|
||||
relDockerfile, err = archive.CanonicalTarNameForPath(relDockerfile)
|
||||
if err != nil {
|
||||
@@ -190,7 +159,7 @@ func runBuild(dockerCli *client.DockerCli, options buildOptions) error {
|
||||
includes = append(includes, ".dockerignore", relDockerfile)
|
||||
}
|
||||
|
||||
buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
|
||||
ctx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
|
||||
Compression: archive.Uncompressed,
|
||||
ExcludePatterns: excludes,
|
||||
IncludeFiles: includes,
|
||||
@@ -200,23 +169,21 @@ func runBuild(dockerCli *client.DockerCli, options buildOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
var resolvedTags []*resolvedTag
|
||||
if client.IsTrusted() {
|
||||
if isTrusted() {
|
||||
// Wrap the tar archive to replace the Dockerfile entry with the rewritten
|
||||
// Dockerfile which uses trusted pulls.
|
||||
buildCtx = replaceDockerfileTarWrapper(ctx, buildCtx, relDockerfile, dockerCli.TrustedReference, &resolvedTags)
|
||||
ctx = replaceDockerfileTarWrapper(ctx, relDockerfile, cli.trustedReference, &resolvedTags)
|
||||
}
|
||||
|
||||
// Setup an upload progress bar
|
||||
progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true)
|
||||
|
||||
var body io.Reader = progress.NewProgressReader(buildCtx, progressOutput, 0, "", "Sending build context to Docker daemon")
|
||||
var body io.Reader = progress.NewProgressReader(ctx, progressOutput, 0, "", "Sending build context to Docker daemon")
|
||||
|
||||
var memory int64
|
||||
if options.memory != "" {
|
||||
parsedMemory, err := units.RAMInBytes(options.memory)
|
||||
if *flMemoryString != "" {
|
||||
parsedMemory, err := units.RAMInBytes(*flMemoryString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -224,11 +191,11 @@ func runBuild(dockerCli *client.DockerCli, options buildOptions) error {
|
||||
}
|
||||
|
||||
var memorySwap int64
|
||||
if options.memorySwap != "" {
|
||||
if options.memorySwap == "-1" {
|
||||
if *flMemorySwap != "" {
|
||||
if *flMemorySwap == "-1" {
|
||||
memorySwap = -1
|
||||
} else {
|
||||
parsedMemorySwap, err := units.RAMInBytes(options.memorySwap)
|
||||
parsedMemorySwap, err := units.RAMInBytes(*flMemorySwap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -237,74 +204,75 @@ func runBuild(dockerCli *client.DockerCli, options buildOptions) error {
|
||||
}
|
||||
|
||||
var shmSize int64
|
||||
if options.shmSize != "" {
|
||||
shmSize, err = units.RAMInBytes(options.shmSize)
|
||||
if *flShmSize != "" {
|
||||
shmSize, err = units.RAMInBytes(*flShmSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
buildOptions := types.ImageBuildOptions{
|
||||
options := types.ImageBuildOptions{
|
||||
Context: body,
|
||||
Memory: memory,
|
||||
MemorySwap: memorySwap,
|
||||
Tags: options.tags.GetAll(),
|
||||
SuppressOutput: options.quiet,
|
||||
NoCache: options.noCache,
|
||||
Remove: options.rm,
|
||||
ForceRemove: options.forceRm,
|
||||
PullParent: options.pull,
|
||||
Isolation: container.Isolation(options.isolation),
|
||||
CPUSetCPUs: options.cpuSetCpus,
|
||||
CPUSetMems: options.cpuSetMems,
|
||||
CPUShares: options.cpuShares,
|
||||
CPUQuota: options.cpuQuota,
|
||||
CPUPeriod: options.cpuPeriod,
|
||||
CgroupParent: options.cgroupParent,
|
||||
Tags: flTags.GetAll(),
|
||||
SuppressOutput: *suppressOutput,
|
||||
NoCache: *noCache,
|
||||
Remove: *rm,
|
||||
ForceRemove: *forceRm,
|
||||
PullParent: *pull,
|
||||
Isolation: container.Isolation(*isolation),
|
||||
CPUSetCPUs: *flCPUSetCpus,
|
||||
CPUSetMems: *flCPUSetMems,
|
||||
CPUShares: *flCPUShares,
|
||||
CPUQuota: *flCPUQuota,
|
||||
CPUPeriod: *flCPUPeriod,
|
||||
CgroupParent: *flCgroupParent,
|
||||
Dockerfile: relDockerfile,
|
||||
ShmSize: shmSize,
|
||||
Ulimits: options.ulimits.GetList(),
|
||||
BuildArgs: runconfigopts.ConvertKVStringsToMap(options.buildArgs.GetAll()),
|
||||
AuthConfigs: dockerCli.RetrieveAuthConfigs(),
|
||||
Labels: runconfigopts.ConvertKVStringsToMap(options.labels.GetAll()),
|
||||
Ulimits: flUlimits.GetList(),
|
||||
BuildArgs: runconfigopts.ConvertKVStringsToMap(flBuildArg.GetAll()),
|
||||
AuthConfigs: cli.retrieveAuthConfigs(),
|
||||
Labels: runconfigopts.ConvertKVStringsToMap(flLabels.GetAll()),
|
||||
}
|
||||
|
||||
response, err := dockerCli.Client().ImageBuild(ctx, body, buildOptions)
|
||||
response, err := cli.client.ImageBuild(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, dockerCli.OutFd(), dockerCli.IsTerminalOut(), nil)
|
||||
err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, cli.outFd, cli.isTerminalOut, nil)
|
||||
if err != nil {
|
||||
if jerr, ok := err.(*jsonmessage.JSONError); ok {
|
||||
// If no error code is set, default to 1
|
||||
if jerr.Code == 0 {
|
||||
jerr.Code = 1
|
||||
}
|
||||
if options.quiet {
|
||||
fmt.Fprintf(dockerCli.Err(), "%s%s", progBuff, buildBuff)
|
||||
if *suppressOutput {
|
||||
fmt.Fprintf(cli.err, "%s%s", progBuff, buildBuff)
|
||||
}
|
||||
return cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
|
||||
return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
|
||||
}
|
||||
}
|
||||
|
||||
// Windows: show error message about modified file permissions if the
|
||||
// daemon isn't running Windows.
|
||||
if response.OSType != "windows" && runtime.GOOS == "windows" {
|
||||
fmt.Fprintln(dockerCli.Err(), `SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`)
|
||||
fmt.Fprintln(cli.err, `SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`)
|
||||
}
|
||||
|
||||
// Everything worked so if -q was provided the output from the daemon
|
||||
// should be just the image ID and we'll print that to stdout.
|
||||
if options.quiet {
|
||||
fmt.Fprintf(dockerCli.Out(), "%s", buildBuff)
|
||||
if *suppressOutput {
|
||||
fmt.Fprintf(cli.out, "%s", buildBuff)
|
||||
}
|
||||
|
||||
if client.IsTrusted() {
|
||||
if isTrusted() {
|
||||
// Since the build was successful, now we must tag any of the resolved
|
||||
// images from the above Dockerfile rewrite.
|
||||
for _, resolved := range resolvedTags {
|
||||
if err := dockerCli.TagTrusted(ctx, resolved.digestRef, resolved.tagRef); err != nil {
|
||||
if err := cli.tagTrusted(resolved.digestRef, resolved.tagRef); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -313,8 +281,6 @@ func runBuild(dockerCli *client.DockerCli, options buildOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type translatorFunc func(context.Context, reference.NamedTagged) (reference.Canonical, error)
|
||||
|
||||
// validateTag checks if the given image name can be resolved.
|
||||
func validateTag(rawRepo string) (string, error) {
|
||||
_, err := reference.ParseNamed(rawRepo)
|
||||
@@ -338,7 +304,7 @@ type resolvedTag struct {
|
||||
// "FROM <image>" instructions to a digest reference. `translator` is a
|
||||
// function that takes a repository name and tag reference and returns a
|
||||
// trusted digest reference.
|
||||
func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) {
|
||||
func rewriteDockerfileFrom(dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) {
|
||||
scanner := bufio.NewScanner(dockerfile)
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
@@ -354,8 +320,8 @@ func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator
|
||||
return nil, nil, err
|
||||
}
|
||||
ref = reference.WithDefaultTag(ref)
|
||||
if ref, ok := ref.(reference.NamedTagged); ok && client.IsTrusted() {
|
||||
trustedRef, err := translator(ctx, ref)
|
||||
if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() {
|
||||
trustedRef, err := translator(ref)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -381,7 +347,7 @@ func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator
|
||||
// replaces the entry with the given Dockerfile name with the contents of the
|
||||
// new Dockerfile. Returns a new tar archive stream with the replaced
|
||||
// Dockerfile.
|
||||
func replaceDockerfileTarWrapper(ctx context.Context, inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser {
|
||||
func replaceDockerfileTarWrapper(inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
go func() {
|
||||
tarReader := tar.NewReader(inputTarStream)
|
||||
@@ -408,7 +374,7 @@ func replaceDockerfileTarWrapper(ctx context.Context, inputTarStream io.ReadClos
|
||||
// generated from a directory on the local filesystem, the
|
||||
// Dockerfile will only appear once in the archive.
|
||||
var newDockerfile []byte
|
||||
newDockerfile, *resolvedTags, err = rewriteDockerfileFrom(ctx, content, translator)
|
||||
newDockerfile, *resolvedTags, err = rewriteDockerfileFrom(content, translator)
|
||||
if err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
return
|
||||
@@ -1,71 +0,0 @@
|
||||
// +build experimental
|
||||
|
||||
package bundlefile
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Bundlefile stores the contents of a bundlefile
|
||||
type Bundlefile struct {
|
||||
Version string
|
||||
Services map[string]Service
|
||||
}
|
||||
|
||||
// Service is a service from a bundlefile
|
||||
type Service struct {
|
||||
Image string
|
||||
Command []string `json:",omitempty"`
|
||||
Args []string `json:",omitempty"`
|
||||
Env []string `json:",omitempty"`
|
||||
Labels map[string]string `json:",omitempty"`
|
||||
Ports []Port `json:",omitempty"`
|
||||
WorkingDir *string `json:",omitempty"`
|
||||
User *string `json:",omitempty"`
|
||||
Networks []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Port is a port as defined in a bundlefile
|
||||
type Port struct {
|
||||
Protocol string
|
||||
Port uint32
|
||||
}
|
||||
|
||||
// LoadFile loads a bundlefile from a path to the file
|
||||
func LoadFile(reader io.Reader) (*Bundlefile, error) {
|
||||
bundlefile := &Bundlefile{}
|
||||
|
||||
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, nil
|
||||
}
|
||||
|
||||
// Print writes the contents of the bundlefile to the output writer
|
||||
// as human readable json
|
||||
func Print(out io.Writer, bundle *Bundlefile) error {
|
||||
bytes, err := json.MarshalIndent(*bundle, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = out.Write(bytes)
|
||||
return err
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// +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"
|
||||
]`)
|
||||
}
|
||||
@@ -9,9 +9,8 @@ import (
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/docker/api"
|
||||
cliflags "github.com/docker/docker/cli/flags"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/cliconfig/configfile"
|
||||
"github.com/docker/docker/cliconfig/credentials"
|
||||
"github.com/docker/docker/dockerversion"
|
||||
"github.com/docker/docker/opts"
|
||||
@@ -28,7 +27,7 @@ type DockerCli struct {
|
||||
init func() error
|
||||
|
||||
// configFile has the client configuration file
|
||||
configFile *configfile.ConfigFile
|
||||
configFile *cliconfig.ConfigFile
|
||||
// in holds the input stream and closer (io.ReadCloser) for the client.
|
||||
in io.ReadCloser
|
||||
// out holds the output stream (io.Writer) for the client.
|
||||
@@ -47,10 +46,8 @@ type DockerCli struct {
|
||||
isTerminalOut bool
|
||||
// client is the http client that performs all API operations
|
||||
client client.APIClient
|
||||
// state holds the terminal input state
|
||||
inState *term.State
|
||||
// outState holds the terminal output state
|
||||
outState *term.State
|
||||
// state holds the terminal state
|
||||
state *term.State
|
||||
}
|
||||
|
||||
// Initialize calls the init function that will setup the configuration for the client
|
||||
@@ -62,41 +59,6 @@ func (cli *DockerCli) Initialize() error {
|
||||
return cli.init()
|
||||
}
|
||||
|
||||
// Client returns the APIClient
|
||||
func (cli *DockerCli) Client() client.APIClient {
|
||||
return cli.client
|
||||
}
|
||||
|
||||
// Out returns the writer used for stdout
|
||||
func (cli *DockerCli) Out() io.Writer {
|
||||
return cli.out
|
||||
}
|
||||
|
||||
// Err returns the writer used for stderr
|
||||
func (cli *DockerCli) Err() io.Writer {
|
||||
return cli.err
|
||||
}
|
||||
|
||||
// In returns the reader used for stdin
|
||||
func (cli *DockerCli) In() io.ReadCloser {
|
||||
return cli.in
|
||||
}
|
||||
|
||||
// ConfigFile returns the ConfigFile
|
||||
func (cli *DockerCli) ConfigFile() *configfile.ConfigFile {
|
||||
return cli.configFile
|
||||
}
|
||||
|
||||
// IsTerminalOut returns true if the clients stdin is a TTY
|
||||
func (cli *DockerCli) IsTerminalOut() bool {
|
||||
return cli.isTerminalOut
|
||||
}
|
||||
|
||||
// OutFd returns the fd for the stdout stream
|
||||
func (cli *DockerCli) OutFd() uintptr {
|
||||
return cli.outFd
|
||||
}
|
||||
|
||||
// CheckTtyInput checks if we are trying to attach to a container tty
|
||||
// from a non-tty client input stream, and if so, returns an error.
|
||||
func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error {
|
||||
@@ -104,11 +66,7 @@ func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error {
|
||||
// be a tty itself: redirecting or piping the client standard input is
|
||||
// incompatible with `docker run -t`, `docker exec -t` or `docker attach`.
|
||||
if ttyMode && attachStdin && !cli.isTerminalIn {
|
||||
eText := "the input device is not a TTY"
|
||||
if runtime.GOOS == "windows" {
|
||||
return errors.New(eText + ". If you are using mintty, try prefixing the command with 'winpty'")
|
||||
}
|
||||
return errors.New(eText)
|
||||
return errors.New("cannot enable tty mode on non tty input")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -126,31 +84,19 @@ func (cli *DockerCli) ImagesFormat() string {
|
||||
}
|
||||
|
||||
func (cli *DockerCli) setRawTerminal() error {
|
||||
if os.Getenv("NORAW") == "" {
|
||||
if cli.isTerminalIn {
|
||||
state, err := term.SetRawTerminal(cli.inFd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli.inState = state
|
||||
}
|
||||
if cli.isTerminalOut {
|
||||
state, err := term.SetRawTerminalOutput(cli.outFd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli.outState = state
|
||||
if cli.isTerminalIn && os.Getenv("NORAW") == "" {
|
||||
state, err := term.SetRawTerminal(cli.inFd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli.state = state
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) restoreTerminal(in io.Closer) error {
|
||||
if cli.inState != nil {
|
||||
term.RestoreTerminal(cli.inFd, cli.inState)
|
||||
}
|
||||
if cli.outState != nil {
|
||||
term.RestoreTerminal(cli.outFd, cli.outState)
|
||||
if cli.state != nil {
|
||||
term.RestoreTerminal(cli.inFd, cli.state)
|
||||
}
|
||||
// WARNING: DO NOT REMOVE THE OS CHECK !!!
|
||||
// For some reason this Close call blocks on darwin..
|
||||
@@ -166,7 +112,7 @@ func (cli *DockerCli) restoreTerminal(in io.Closer) error {
|
||||
// The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config
|
||||
// is set the client scheme will be set to https.
|
||||
// The client will be given a 32-second timeout (see https://github.com/docker/docker/pull/8035).
|
||||
func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cliflags.ClientFlags) *DockerCli {
|
||||
func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientFlags) *DockerCli {
|
||||
cli := &DockerCli{
|
||||
in: in,
|
||||
out: out,
|
||||
@@ -176,13 +122,40 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cliflags.Cl
|
||||
|
||||
cli.init = func() error {
|
||||
clientFlags.PostParse()
|
||||
cli.configFile = LoadDefaultConfigFile(err)
|
||||
configFile, e := cliconfig.Load(cliconfig.ConfigDir())
|
||||
if e != nil {
|
||||
fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e)
|
||||
}
|
||||
if !configFile.ContainsAuth() {
|
||||
credentials.DetectDefaultStore(configFile)
|
||||
}
|
||||
cli.configFile = configFile
|
||||
|
||||
client, err := NewAPIClientFromFlags(clientFlags, cli.configFile)
|
||||
host, err := getServerHost(clientFlags.Common.Hosts, clientFlags.Common.TLSOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
customHeaders := cli.configFile.HTTPHeaders
|
||||
if customHeaders == nil {
|
||||
customHeaders = map[string]string{}
|
||||
}
|
||||
customHeaders["User-Agent"] = clientUserAgent()
|
||||
|
||||
verStr := api.DefaultVersion.String()
|
||||
if tmpStr := os.Getenv("DOCKER_API_VERSION"); tmpStr != "" {
|
||||
verStr = tmpStr
|
||||
}
|
||||
|
||||
httpClient, err := newHTTPClient(host, clientFlags.Common.TLSOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := client.NewClient(host, verStr, httpClient, customHeaders)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli.client = client
|
||||
|
||||
if cli.in != nil {
|
||||
@@ -198,45 +171,6 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cliflags.Cl
|
||||
return cli
|
||||
}
|
||||
|
||||
// LoadDefaultConfigFile attempts to load the default config file and returns
|
||||
// an initialized ConfigFile struct if none is found.
|
||||
func LoadDefaultConfigFile(err io.Writer) *configfile.ConfigFile {
|
||||
configFile, e := cliconfig.Load(cliconfig.ConfigDir())
|
||||
if e != nil {
|
||||
fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e)
|
||||
}
|
||||
if !configFile.ContainsAuth() {
|
||||
credentials.DetectDefaultStore(configFile)
|
||||
}
|
||||
return configFile
|
||||
}
|
||||
|
||||
// NewAPIClientFromFlags creates a new APIClient from command line flags
|
||||
func NewAPIClientFromFlags(clientFlags *cliflags.ClientFlags, configFile *configfile.ConfigFile) (client.APIClient, error) {
|
||||
host, err := getServerHost(clientFlags.Common.Hosts, clientFlags.Common.TLSOptions)
|
||||
if err != nil {
|
||||
return &client.Client{}, err
|
||||
}
|
||||
|
||||
customHeaders := configFile.HTTPHeaders
|
||||
if customHeaders == nil {
|
||||
customHeaders = map[string]string{}
|
||||
}
|
||||
customHeaders["User-Agent"] = clientUserAgent()
|
||||
|
||||
verStr := api.DefaultVersion
|
||||
if tmpStr := os.Getenv("DOCKER_API_VERSION"); tmpStr != "" {
|
||||
verStr = tmpStr
|
||||
}
|
||||
|
||||
httpClient, err := newHTTPClient(host, clientFlags.Common.TLSOptions)
|
||||
if err != nil {
|
||||
return &client.Client{}, err
|
||||
}
|
||||
|
||||
return client.NewClient(host, verStr, httpClient, customHeaders)
|
||||
}
|
||||
|
||||
func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string, err error) {
|
||||
switch len(hosts) {
|
||||
case 0:
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package client
|
||||
|
||||
// Command returns a cli command handler if one exists
|
||||
func (cli *DockerCli) Command(name string) func(...string) error {
|
||||
return map[string]func(...string) error{
|
||||
"exec": cli.CmdExec,
|
||||
"info": cli.CmdInfo,
|
||||
"inspect": cli.CmdInspect,
|
||||
"update": cli.CmdUpdate,
|
||||
}[name]
|
||||
}
|
||||
85
api/client/commit.go
Normal file
85
api/client/commit.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
)
|
||||
|
||||
// CmdCommit creates a new image from a container's changes.
|
||||
//
|
||||
// Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
|
||||
func (cli *DockerCli) CmdCommit(args ...string) error {
|
||||
cmd := Cli.Subcmd("commit", []string{"CONTAINER [REPOSITORY[:TAG]]"}, Cli.DockerCommands["commit"].Description, true)
|
||||
flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit")
|
||||
flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
|
||||
flAuthor := cmd.String([]string{"a", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")
|
||||
flChanges := opts.NewListOpts(nil)
|
||||
cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
|
||||
// FIXME: --run is deprecated, it will be replaced with inline Dockerfile commands.
|
||||
flConfig := cmd.String([]string{"#-run"}, "", "This option is deprecated and will be removed in a future version in favor of inline Dockerfile-compatible commands")
|
||||
cmd.Require(flag.Max, 2)
|
||||
cmd.Require(flag.Min, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var (
|
||||
name = cmd.Arg(0)
|
||||
repositoryAndTag = cmd.Arg(1)
|
||||
repositoryName string
|
||||
tag string
|
||||
)
|
||||
|
||||
//Check if the given image name can be resolved
|
||||
if repositoryAndTag != "" {
|
||||
ref, err := reference.ParseNamed(repositoryAndTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repositoryName = ref.Name()
|
||||
|
||||
switch x := ref.(type) {
|
||||
case reference.Canonical:
|
||||
return errors.New("cannot commit to digest reference")
|
||||
case reference.NamedTagged:
|
||||
tag = x.Tag()
|
||||
}
|
||||
}
|
||||
|
||||
var config *container.Config
|
||||
if *flConfig != "" {
|
||||
config = &container.Config{}
|
||||
if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
options := types.ContainerCommitOptions{
|
||||
ContainerID: name,
|
||||
RepositoryName: repositoryName,
|
||||
Tag: tag,
|
||||
Comment: *flComment,
|
||||
Author: *flAuthor,
|
||||
Changes: flChanges.GetAll(),
|
||||
Pause: *flPause,
|
||||
Config: config,
|
||||
}
|
||||
|
||||
response, err := cli.client.ContainerCommit(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(cli.out, response.ID)
|
||||
return nil
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http/httputil"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type attachOptions struct {
|
||||
noStdin bool
|
||||
proxy bool
|
||||
detachKeys string
|
||||
|
||||
container string
|
||||
}
|
||||
|
||||
// NewAttachCommand creats a new cobra.Command for `docker attach`
|
||||
func NewAttachCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts attachOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "attach [OPTIONS] CONTAINER",
|
||||
Short: "Attach to a running container",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
return runAttach(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&opts.noStdin, "no-stdin", false, "Do not attach STDIN")
|
||||
flags.BoolVar(&opts.proxy, "sig-proxy", true, "Proxy all received signals to the process")
|
||||
flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runAttach(dockerCli *client.DockerCli, opts *attachOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !c.State.Running {
|
||||
return fmt.Errorf("You cannot attach to a stopped container, start it first")
|
||||
}
|
||||
|
||||
if c.State.Paused {
|
||||
return fmt.Errorf("You cannot attach to a paused container, unpause it first")
|
||||
}
|
||||
|
||||
if err := dockerCli.CheckTtyInput(!opts.noStdin, c.Config.Tty); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.detachKeys != "" {
|
||||
dockerCli.ConfigFile().DetachKeys = opts.detachKeys
|
||||
}
|
||||
|
||||
options := types.ContainerAttachOptions{
|
||||
Stream: true,
|
||||
Stdin: !opts.noStdin && c.Config.OpenStdin,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
DetachKeys: dockerCli.ConfigFile().DetachKeys,
|
||||
}
|
||||
|
||||
var in io.ReadCloser
|
||||
if options.Stdin {
|
||||
in = dockerCli.In()
|
||||
}
|
||||
|
||||
if opts.proxy && !c.Config.Tty {
|
||||
sigc := dockerCli.ForwardAllSignals(ctx, opts.container)
|
||||
defer signal.StopCatch(sigc)
|
||||
}
|
||||
|
||||
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, opts.container, options)
|
||||
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
||||
// ContainerAttach returns an ErrPersistEOF (connection closed)
|
||||
// means server met an error and put it in Hijacked connection
|
||||
// keep the error and read detailed error message from hijacked connection later
|
||||
return errAttach
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
if c.Config.Tty && dockerCli.IsTerminalOut() {
|
||||
height, width := dockerCli.GetTtySize()
|
||||
// To handle the case where a user repeatedly attaches/detaches without resizing their
|
||||
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
|
||||
// resize it, then go back to normal. Without this, every attach after the first will
|
||||
// require the user to manually resize or hit enter.
|
||||
dockerCli.ResizeTtyTo(ctx, opts.container, height+1, width+1, false)
|
||||
|
||||
// After the above resizing occurs, the call to MonitorTtySize below will handle resetting back
|
||||
// to the actual size.
|
||||
if err := dockerCli.MonitorTtySize(ctx, opts.container, false); err != nil {
|
||||
logrus.Debugf("Error monitoring TTY size: %s", err)
|
||||
}
|
||||
}
|
||||
if err := dockerCli.HoldHijackedConnection(ctx, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if errAttach != nil {
|
||||
return errAttach
|
||||
}
|
||||
|
||||
_, status, err := getExitCode(dockerCli, ctx, opts.container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status != 0 {
|
||||
return cli.StatusError{StatusCode: status}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
dockeropts "github.com/docker/docker/opts"
|
||||
"github.com/docker/engine-api/types"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type commitOptions struct {
|
||||
container string
|
||||
reference string
|
||||
|
||||
pause bool
|
||||
comment string
|
||||
author string
|
||||
changes dockeropts.ListOpts
|
||||
config string
|
||||
}
|
||||
|
||||
// NewCommitCommand creats a new cobra.Command for `docker commit`
|
||||
func NewCommitCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts commitOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]",
|
||||
Short: "Create a new image from a container's changes",
|
||||
Args: cli.RequiresRangeArgs(1, 2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
if len(args) > 1 {
|
||||
opts.reference = args[1]
|
||||
}
|
||||
return runCommit(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
|
||||
flags.BoolVarP(&opts.pause, "pause", "p", true, "Pause container during commit")
|
||||
flags.StringVarP(&opts.comment, "message", "m", "", "Commit message")
|
||||
flags.StringVarP(&opts.author, "author", "a", "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")
|
||||
|
||||
opts.changes = dockeropts.NewListOpts(nil)
|
||||
flags.VarP(&opts.changes, "change", "c", "Apply Dockerfile instruction to the created image")
|
||||
|
||||
// FIXME: --run is deprecated, it will be replaced with inline Dockerfile commands.
|
||||
flags.StringVar(&opts.config, "run", "", "This option is deprecated and will be removed in a future version in favor of inline Dockerfile-compatible commands")
|
||||
flags.MarkDeprecated("run", "it will be replaced with inline Dockerfile commands.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCommit(dockerCli *client.DockerCli, opts *commitOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
name := opts.container
|
||||
reference := opts.reference
|
||||
|
||||
var config *containertypes.Config
|
||||
if opts.config != "" {
|
||||
config = &containertypes.Config{}
|
||||
if err := json.Unmarshal([]byte(opts.config), config); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
options := types.ContainerCommitOptions{
|
||||
Reference: reference,
|
||||
Comment: opts.comment,
|
||||
Author: opts.author,
|
||||
Changes: opts.changes.GetAll(),
|
||||
Pause: opts.pause,
|
||||
Config: config,
|
||||
}
|
||||
|
||||
response, err := dockerCli.Client().ContainerCommit(ctx, name, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(dockerCli.Out(), response.ID)
|
||||
return nil
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
// FIXME migrate to docker/distribution/reference
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/docker/registry"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
apiclient "github.com/docker/engine-api/client"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type createOptions struct {
|
||||
name string
|
||||
}
|
||||
|
||||
// NewCreateCommand creats a new cobra.Command for `docker create`
|
||||
func NewCreateCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts createOptions
|
||||
var copts *runconfigopts.ContainerOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "create [OPTIONS] IMAGE [COMMAND] [ARG...]",
|
||||
Short: "Create a new container",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
copts.Image = args[0]
|
||||
if len(args) > 1 {
|
||||
copts.Args = args[1:]
|
||||
}
|
||||
return runCreate(dockerCli, cmd.Flags(), &opts, copts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
|
||||
flags.StringVar(&opts.name, "name", "", "Assign a name to the container")
|
||||
|
||||
// Add an explicit help that doesn't have a `-h` to prevent the conflict
|
||||
// with hostname
|
||||
flags.Bool("help", false, "Print usage")
|
||||
|
||||
client.AddTrustedFlags(flags, true)
|
||||
copts = runconfigopts.AddFlags(flags)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *createOptions, copts *runconfigopts.ContainerOptions) error {
|
||||
config, hostConfig, networkingConfig, err := runconfigopts.Parse(flags, copts)
|
||||
if err != nil {
|
||||
reportError(dockerCli.Err(), "create", err.Error(), true)
|
||||
return cli.StatusError{StatusCode: 125}
|
||||
}
|
||||
response, err := createContainer(context.Background(), dockerCli, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, opts.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", response.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func pullImage(ctx context.Context, dockerCli *client.DockerCli, image string, out io.Writer) error {
|
||||
ref, err := reference.ParseNamed(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Resolve the Repository name from fqn to RepositoryInfo
|
||||
repoInfo, err := registry.ParseRepositoryInfo(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authConfig := dockerCli.ResolveAuthConfig(ctx, repoInfo.Index)
|
||||
encodedAuth, err := client.EncodeAuthToBase64(authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options := types.ImageCreateOptions{
|
||||
RegistryAuth: encodedAuth,
|
||||
}
|
||||
|
||||
responseBody, err := dockerCli.Client().ImageCreate(ctx, image, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
return jsonmessage.DisplayJSONMessagesStream(
|
||||
responseBody,
|
||||
out,
|
||||
dockerCli.OutFd(),
|
||||
dockerCli.IsTerminalOut(),
|
||||
nil)
|
||||
}
|
||||
|
||||
type cidFile struct {
|
||||
path string
|
||||
file *os.File
|
||||
written bool
|
||||
}
|
||||
|
||||
func (cid *cidFile) Close() error {
|
||||
cid.file.Close()
|
||||
|
||||
if !cid.written {
|
||||
if err := os.Remove(cid.path); err != nil {
|
||||
return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cid *cidFile) Write(id string) error {
|
||||
if _, err := cid.file.Write([]byte(id)); err != nil {
|
||||
return fmt.Errorf("Failed to write the container ID to the file: %s", err)
|
||||
}
|
||||
cid.written = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func newCIDFile(path string) (*cidFile, error) {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
|
||||
}
|
||||
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
|
||||
}
|
||||
|
||||
return &cidFile{path: path, file: f}, nil
|
||||
}
|
||||
|
||||
func createContainer(ctx context.Context, dockerCli *client.DockerCli, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
||||
stderr := dockerCli.Err()
|
||||
|
||||
var containerIDFile *cidFile
|
||||
if cidfile != "" {
|
||||
var err error
|
||||
if containerIDFile, err = newCIDFile(cidfile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer containerIDFile.Close()
|
||||
}
|
||||
|
||||
var trustedRef reference.Canonical
|
||||
_, ref, err := reference.ParseIDOrReference(config.Image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ref != nil {
|
||||
ref = reference.WithDefaultTag(ref)
|
||||
|
||||
if ref, ok := ref.(reference.NamedTagged); ok && client.IsTrusted() {
|
||||
var err error
|
||||
trustedRef, err = dockerCli.TrustedReference(ctx, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Image = trustedRef.String()
|
||||
}
|
||||
}
|
||||
|
||||
//create the container
|
||||
response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
|
||||
|
||||
//if image not found try to pull it
|
||||
if err != nil {
|
||||
if apiclient.IsErrImageNotFound(err) && ref != nil {
|
||||
fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", ref.String())
|
||||
|
||||
// we don't want to write to stdout anything apart from container.ID
|
||||
if err = pullImage(ctx, dockerCli, config.Image, stderr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil {
|
||||
if err := dockerCli.TagTrusted(ctx, trustedRef, ref); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Retry
|
||||
var retryErr error
|
||||
response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
|
||||
if retryErr != nil {
|
||||
return nil, retryErr
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, warning := range response.Warnings {
|
||||
fmt.Fprintf(stderr, "WARNING: %s\n", warning)
|
||||
}
|
||||
if containerIDFile != nil {
|
||||
if err = containerIDFile.Write(response.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type diffOptions struct {
|
||||
container string
|
||||
}
|
||||
|
||||
// NewDiffCommand creates a new cobra.Command for `docker diff`
|
||||
func NewDiffCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts diffOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "diff CONTAINER",
|
||||
Short: "Inspect changes on a container's filesystem",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
return runDiff(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDiff(dockerCli *client.DockerCli, opts *diffOptions) error {
|
||||
if opts.container == "" {
|
||||
return fmt.Errorf("Container name cannot be empty")
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
changes, err := dockerCli.Client().ContainerDiff(ctx, opts.container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, change := range changes {
|
||||
var kind string
|
||||
switch change.Kind {
|
||||
case archive.ChangeModify:
|
||||
kind = "C"
|
||||
case archive.ChangeAdd:
|
||||
kind = "A"
|
||||
case archive.ChangeDelete:
|
||||
kind = "D"
|
||||
}
|
||||
fmt.Fprintf(dockerCli.Out(), "%s %s\n", kind, change.Path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type exportOptions struct {
|
||||
container string
|
||||
output string
|
||||
}
|
||||
|
||||
// NewExportCommand creates a new `docker export` command
|
||||
func NewExportCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts exportOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "export [OPTIONS] CONTAINER",
|
||||
Short: "Export a container's filesystem as a tar archive",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
return runExport(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.StringVarP(&opts.output, "output", "o", "", "Write to a file, instead of STDOUT")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runExport(dockerCli *client.DockerCli, opts exportOptions) error {
|
||||
if opts.output == "" && dockerCli.IsTerminalOut() {
|
||||
return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
|
||||
}
|
||||
|
||||
clnt := dockerCli.Client()
|
||||
|
||||
responseBody, err := clnt.ContainerExport(context.Background(), opts.container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
if opts.output == "" {
|
||||
_, err := io.Copy(dockerCli.Out(), responseBody)
|
||||
return err
|
||||
}
|
||||
|
||||
return client.CopyToFile(opts.output, responseBody)
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type killOptions struct {
|
||||
signal string
|
||||
|
||||
containers []string
|
||||
}
|
||||
|
||||
// NewKillCommand creats a new cobra.Command for `docker kill`
|
||||
func NewKillCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts killOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "kill [OPTIONS] CONTAINER [CONTAINER...]",
|
||||
Short: "Kill one or more running containers",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runKill(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&opts.signal, "signal", "s", "KILL", "Signal to send to the container")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runKill(dockerCli *client.DockerCli, opts *killOptions) error {
|
||||
var errs []string
|
||||
ctx := context.Background()
|
||||
for _, name := range opts.containers {
|
||||
if err := dockerCli.Client().ContainerKill(ctx, name, opts.signal); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
} else {
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", name)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var validDrivers = map[string]bool{
|
||||
"json-file": true,
|
||||
"journald": true,
|
||||
}
|
||||
|
||||
type logsOptions struct {
|
||||
follow bool
|
||||
since string
|
||||
timestamps bool
|
||||
details bool
|
||||
tail string
|
||||
|
||||
container string
|
||||
}
|
||||
|
||||
// NewLogsCommand creats a new cobra.Command for `docker logs`
|
||||
func NewLogsCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts logsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "logs [OPTIONS] CONTAINER",
|
||||
Short: "Fetch the logs of a container",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
return runLogs(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output")
|
||||
flags.StringVar(&opts.since, "since", "", "Show logs since timestamp")
|
||||
flags.BoolVarP(&opts.timestamps, "timestamps", "t", false, "Show timestamps")
|
||||
flags.BoolVar(&opts.details, "details", false, "Show extra details provided to logs")
|
||||
flags.StringVar(&opts.tail, "tail", "all", "Number of lines to show from the end of the logs")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runLogs(dockerCli *client.DockerCli, opts *logsOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !validDrivers[c.HostConfig.LogConfig.Type] {
|
||||
return fmt.Errorf("\"logs\" command is supported only for \"json-file\" and \"journald\" logging drivers (got: %s)", c.HostConfig.LogConfig.Type)
|
||||
}
|
||||
|
||||
options := types.ContainerLogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Since: opts.since,
|
||||
Timestamps: opts.timestamps,
|
||||
Follow: opts.follow,
|
||||
Tail: opts.tail,
|
||||
Details: opts.details,
|
||||
}
|
||||
responseBody, err := dockerCli.Client().ContainerLogs(ctx, opts.container, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
if c.Config.Tty {
|
||||
_, err = io.Copy(dockerCli.Out(), responseBody)
|
||||
} else {
|
||||
_, err = stdcopy.StdCopy(dockerCli.Out(), dockerCli.Err(), responseBody)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type pauseOptions struct {
|
||||
containers []string
|
||||
}
|
||||
|
||||
// NewPauseCommand creats a new cobra.Command for `docker pause`
|
||||
func NewPauseCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts pauseOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "pause CONTAINER [CONTAINER...]",
|
||||
Short: "Pause all processes within one or more containers",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runPause(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPause(dockerCli *client.DockerCli, opts *pauseOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
for _, container := range opts.containers {
|
||||
if err := dockerCli.Client().ContainerPause(ctx, container); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
} else {
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", container)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type portOptions struct {
|
||||
container string
|
||||
|
||||
port string
|
||||
}
|
||||
|
||||
// NewPortCommand creats a new cobra.Command for `docker port`
|
||||
func NewPortCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts portOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "port CONTAINER [PRIVATE_PORT[/PROTO]]",
|
||||
Short: "List port mappings or a specific mapping for the container",
|
||||
Args: cli.RequiresRangeArgs(1, 2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
if len(args) > 1 {
|
||||
opts.port = args[1]
|
||||
}
|
||||
return runPort(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPort(dockerCli *client.DockerCli, opts *portOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.port != "" {
|
||||
port := opts.port
|
||||
proto := "tcp"
|
||||
parts := strings.SplitN(port, "/", 2)
|
||||
|
||||
if len(parts) == 2 && len(parts[1]) != 0 {
|
||||
port = parts[0]
|
||||
proto = parts[1]
|
||||
}
|
||||
natPort := port + "/" + proto
|
||||
newP, err := nat.NewPort(proto, port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if frontends, exists := c.NetworkSettings.Ports[newP]; exists && frontends != nil {
|
||||
for _, frontend := range frontends {
|
||||
fmt.Fprintf(dockerCli.Out(), "%s:%s\n", frontend.HostIP, frontend.HostPort)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Error: No public port '%s' published for %s", natPort, opts.container)
|
||||
}
|
||||
|
||||
for from, frontends := range c.NetworkSettings.Ports {
|
||||
for _, frontend := range frontends {
|
||||
fmt.Fprintf(dockerCli.Out(), "%s -> %s:%s\n", from, frontend.HostIP, frontend.HostPort)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/api/client/formatter"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
|
||||
"github.com/docker/docker/utils/templates"
|
||||
"github.com/spf13/cobra"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
type psOptions struct {
|
||||
quiet bool
|
||||
size bool
|
||||
all bool
|
||||
noTrunc bool
|
||||
nLatest bool
|
||||
last int
|
||||
format string
|
||||
filter []string
|
||||
}
|
||||
|
||||
type preProcessor struct {
|
||||
opts *types.ContainerListOptions
|
||||
}
|
||||
|
||||
// Size sets the size option when called by a template execution.
|
||||
func (p *preProcessor) Size() bool {
|
||||
p.opts.Size = true
|
||||
return true
|
||||
}
|
||||
|
||||
// NewPsCommand creates a new cobra.Command for `docker ps`
|
||||
func NewPsCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts psOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "ps [OPTIONS]",
|
||||
Short: "List containers",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runPs(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display numeric IDs")
|
||||
flags.BoolVarP(&opts.size, "size", "s", false, "Display total file sizes")
|
||||
flags.BoolVarP(&opts.all, "all", "a", false, "Show all containers (default shows just running)")
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
flags.BoolVarP(&opts.nLatest, "latest", "l", false, "Show the latest created container (includes all states)")
|
||||
flags.IntVarP(&opts.last, "last", "n", -1, "Show n last created containers (includes all states)")
|
||||
flags.StringVarP(&opts.format, "format", "", "", "Pretty-print containers using a Go template")
|
||||
flags.StringSliceVarP(&opts.filter, "filter", "f", []string{}, "Filter output based on conditions provided")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPs(dockerCli *client.DockerCli, opts *psOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
if opts.nLatest && opts.last == -1 {
|
||||
opts.last = 1
|
||||
}
|
||||
|
||||
containerFilterArgs := filters.NewArgs()
|
||||
for _, f := range opts.filter {
|
||||
var err error
|
||||
containerFilterArgs, err = filters.ParseFlag(f, containerFilterArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
options := types.ContainerListOptions{
|
||||
All: opts.all,
|
||||
Limit: opts.last,
|
||||
Size: opts.size,
|
||||
Filter: containerFilterArgs,
|
||||
}
|
||||
|
||||
pre := &preProcessor{opts: &options}
|
||||
tmpl, err := templates.Parse(opts.format)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = tmpl.Execute(ioutil.Discard, pre)
|
||||
|
||||
containers, err := dockerCli.Client().ContainerList(ctx, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f := opts.format
|
||||
if len(f) == 0 {
|
||||
if len(dockerCli.PsFormat()) > 0 && !opts.quiet {
|
||||
f = dockerCli.PsFormat()
|
||||
} else {
|
||||
f = "table"
|
||||
}
|
||||
}
|
||||
|
||||
psCtx := formatter.ContainerContext{
|
||||
Context: formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: f,
|
||||
Quiet: opts.quiet,
|
||||
Trunc: !opts.noTrunc,
|
||||
},
|
||||
Size: opts.size,
|
||||
Containers: containers,
|
||||
}
|
||||
|
||||
psCtx.Write()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type renameOptions struct {
|
||||
oldName string
|
||||
newName string
|
||||
}
|
||||
|
||||
// NewRenameCommand creats a new cobra.Command for `docker rename`
|
||||
func NewRenameCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts renameOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rename CONTAINER NEW_NAME",
|
||||
Short: "Rename a container",
|
||||
Args: cli.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.oldName = args[0]
|
||||
opts.newName = args[1]
|
||||
return runRename(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRename(dockerCli *client.DockerCli, opts *renameOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
oldName := strings.TrimSpace(opts.oldName)
|
||||
newName := strings.TrimSpace(opts.newName)
|
||||
|
||||
if oldName == "" || newName == "" {
|
||||
return fmt.Errorf("Error: Neither old nor new names may be empty")
|
||||
}
|
||||
|
||||
if err := dockerCli.Client().ContainerRename(ctx, oldName, newName); err != nil {
|
||||
fmt.Fprintf(dockerCli.Err(), "%s\n", err)
|
||||
return fmt.Errorf("Error: failed to rename container named %s", oldName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type restartOptions struct {
|
||||
nSeconds int
|
||||
|
||||
containers []string
|
||||
}
|
||||
|
||||
// NewRestartCommand creates a new cobra.Command for `docker restart`
|
||||
func NewRestartCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts restartOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "restart [OPTIONS] CONTAINER [CONTAINER...]",
|
||||
Short: "Restart a container",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runRestart(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.IntVarP(&opts.nSeconds, "time", "t", 10, "Seconds to wait for stop before killing the container")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRestart(dockerCli *client.DockerCli, opts *restartOptions) error {
|
||||
ctx := context.Background()
|
||||
var errs []string
|
||||
for _, name := range opts.containers {
|
||||
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)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type rmOptions struct {
|
||||
rmVolumes bool
|
||||
rmLink bool
|
||||
force bool
|
||||
|
||||
containers []string
|
||||
}
|
||||
|
||||
// NewRmCommand creates a new cobra.Command for `docker rm`
|
||||
func NewRmCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts rmOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rm [OPTIONS] CONTAINER [CONTAINER...]",
|
||||
Short: "Remove one or more containers",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runRm(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.rmVolumes, "volumes", "v", false, "Remove the volumes associated with the container")
|
||||
flags.BoolVarP(&opts.rmLink, "link", "l", false, "Remove the specified link")
|
||||
flags.BoolVarP(&opts.force, "force", "f", false, "Force the removal of a running container (uses SIGKILL)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRm(dockerCli *client.DockerCli, opts *rmOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
for _, name := range opts.containers {
|
||||
if name == "" {
|
||||
return fmt.Errorf("Container name cannot be empty")
|
||||
}
|
||||
name = strings.Trim(name, "/")
|
||||
|
||||
if err := removeContainer(dockerCli, ctx, name, opts.rmVolumes, opts.rmLink, opts.force); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
} else {
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", name)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeContainer(dockerCli *client.DockerCli, ctx context.Context, container string, removeVolumes, removeLinks, force bool) error {
|
||||
options := types.ContainerRemoveOptions{
|
||||
RemoveVolumes: removeVolumes,
|
||||
RemoveLinks: removeLinks,
|
||||
Force: force,
|
||||
}
|
||||
if err := dockerCli.Client().ContainerRemove(ctx, container, options); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
opttypes "github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/promise"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/libnetwork/resolvconf/dns"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const (
|
||||
errCmdNotFound = "not found or does not exist"
|
||||
errCmdCouldNotBeInvoked = "could not be invoked"
|
||||
)
|
||||
|
||||
type runOptions struct {
|
||||
autoRemove bool
|
||||
detach bool
|
||||
sigProxy bool
|
||||
name string
|
||||
detachKeys string
|
||||
}
|
||||
|
||||
// NewRunCommand create a new `docker run` command
|
||||
func NewRunCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts runOptions
|
||||
var copts *runconfigopts.ContainerOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "run [OPTIONS] IMAGE [COMMAND] [ARG...]",
|
||||
Short: "Run a command in a new container",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
copts.Image = args[0]
|
||||
if len(args) > 1 {
|
||||
copts.Args = args[1:]
|
||||
}
|
||||
return runRun(dockerCli, cmd.Flags(), &opts, copts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
|
||||
// These are flags not stored in Config/HostConfig
|
||||
flags.BoolVar(&opts.autoRemove, "rm", false, "Automatically remove the container when it exits")
|
||||
flags.BoolVarP(&opts.detach, "detach", "d", false, "Run container in background and print container ID")
|
||||
flags.BoolVar(&opts.sigProxy, "sig-proxy", true, "Proxy received signals to the process")
|
||||
flags.StringVar(&opts.name, "name", "", "Assign a name to the container")
|
||||
flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
|
||||
|
||||
// Add an explicit help that doesn't have a `-h` to prevent the conflict
|
||||
// with hostname
|
||||
flags.Bool("help", false, "Print usage")
|
||||
|
||||
client.AddTrustedFlags(flags, true)
|
||||
copts = runconfigopts.AddFlags(flags)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, copts *runconfigopts.ContainerOptions) error {
|
||||
stdout, stderr, stdin := dockerCli.Out(), dockerCli.Err(), dockerCli.In()
|
||||
client := dockerCli.Client()
|
||||
// TODO: pass this as an argument
|
||||
cmdPath := "run"
|
||||
|
||||
var (
|
||||
flAttach *opttypes.ListOpts
|
||||
ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d")
|
||||
ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
|
||||
ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
||||
)
|
||||
|
||||
config, hostConfig, networkingConfig, err := runconfigopts.Parse(flags, copts)
|
||||
|
||||
// just in case the Parse does not exit
|
||||
if err != nil {
|
||||
reportError(stderr, cmdPath, err.Error(), true)
|
||||
return cli.StatusError{StatusCode: 125}
|
||||
}
|
||||
|
||||
if hostConfig.OomKillDisable != nil && *hostConfig.OomKillDisable && hostConfig.Memory == 0 {
|
||||
fmt.Fprintf(stderr, "WARNING: Disabling the OOM killer on containers without setting a '-m/--memory' limit may be dangerous.\n")
|
||||
}
|
||||
|
||||
if len(hostConfig.DNS) > 0 {
|
||||
// check the DNS settings passed via --dns against
|
||||
// localhost regexp to warn if they are trying to
|
||||
// set a DNS to a localhost address
|
||||
for _, dnsIP := range hostConfig.DNS {
|
||||
if dns.IsLocalhost(dnsIP) {
|
||||
fmt.Fprintf(stderr, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.ArgsEscaped = false
|
||||
|
||||
if !opts.detach {
|
||||
if err := dockerCli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if fl := flags.Lookup("attach"); fl != nil {
|
||||
flAttach = fl.Value.(*opttypes.ListOpts)
|
||||
if flAttach.Len() != 0 {
|
||||
return ErrConflictAttachDetach
|
||||
}
|
||||
}
|
||||
if opts.autoRemove {
|
||||
return ErrConflictDetachAutoRemove
|
||||
}
|
||||
|
||||
config.AttachStdin = false
|
||||
config.AttachStdout = false
|
||||
config.AttachStderr = false
|
||||
config.StdinOnce = false
|
||||
}
|
||||
|
||||
// Disable sigProxy when in TTY mode
|
||||
if config.Tty {
|
||||
opts.sigProxy = false
|
||||
}
|
||||
|
||||
// Telling the Windows daemon the initial size of the tty during start makes
|
||||
// a far better user experience rather than relying on subsequent resizes
|
||||
// to cause things to catch up.
|
||||
if runtime.GOOS == "windows" {
|
||||
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCli.GetTtySize()
|
||||
}
|
||||
|
||||
ctx, cancelFun := context.WithCancel(context.Background())
|
||||
|
||||
createResponse, err := createContainer(ctx, dockerCli, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, opts.name)
|
||||
if err != nil {
|
||||
reportError(stderr, cmdPath, err.Error(), true)
|
||||
return runStartContainerErr(err)
|
||||
}
|
||||
if opts.sigProxy {
|
||||
sigc := dockerCli.ForwardAllSignals(ctx, createResponse.ID)
|
||||
defer signal.StopCatch(sigc)
|
||||
}
|
||||
var (
|
||||
waitDisplayID chan struct{}
|
||||
errCh chan error
|
||||
)
|
||||
if !config.AttachStdout && !config.AttachStderr {
|
||||
// Make this asynchronous to allow the client to write to stdin before having to read the ID
|
||||
waitDisplayID = make(chan struct{})
|
||||
go func() {
|
||||
defer close(waitDisplayID)
|
||||
fmt.Fprintf(stdout, "%s\n", createResponse.ID)
|
||||
}()
|
||||
}
|
||||
if opts.autoRemove && (hostConfig.RestartPolicy.IsAlways() || hostConfig.RestartPolicy.IsOnFailure()) {
|
||||
return ErrConflictRestartPolicyAndAutoRemove
|
||||
}
|
||||
attach := config.AttachStdin || config.AttachStdout || config.AttachStderr
|
||||
if attach {
|
||||
var (
|
||||
out, cerr io.Writer
|
||||
in io.ReadCloser
|
||||
)
|
||||
if config.AttachStdin {
|
||||
in = stdin
|
||||
}
|
||||
if config.AttachStdout {
|
||||
out = stdout
|
||||
}
|
||||
if config.AttachStderr {
|
||||
if config.Tty {
|
||||
cerr = stdout
|
||||
} else {
|
||||
cerr = stderr
|
||||
}
|
||||
}
|
||||
|
||||
if opts.detachKeys != "" {
|
||||
dockerCli.ConfigFile().DetachKeys = opts.detachKeys
|
||||
}
|
||||
|
||||
options := types.ContainerAttachOptions{
|
||||
Stream: true,
|
||||
Stdin: config.AttachStdin,
|
||||
Stdout: config.AttachStdout,
|
||||
Stderr: config.AttachStderr,
|
||||
DetachKeys: dockerCli.ConfigFile().DetachKeys,
|
||||
}
|
||||
|
||||
resp, errAttach := client.ContainerAttach(ctx, createResponse.ID, options)
|
||||
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
||||
// ContainerAttach returns an ErrPersistEOF (connection closed)
|
||||
// means server met an error and put it in Hijacked connection
|
||||
// keep the error and read detailed error message from hijacked connection later
|
||||
return errAttach
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
errCh = promise.Go(func() error {
|
||||
errHijack := dockerCli.HoldHijackedConnection(ctx, config.Tty, in, out, cerr, resp)
|
||||
if errHijack == nil {
|
||||
return errAttach
|
||||
}
|
||||
return errHijack
|
||||
})
|
||||
}
|
||||
|
||||
if opts.autoRemove {
|
||||
defer func() {
|
||||
// Explicitly not sharing the context as it could be "Done" (by calling cancelFun)
|
||||
// and thus the container would not be removed.
|
||||
if err := removeContainer(dockerCli, context.Background(), createResponse.ID, true, false, true); err != nil {
|
||||
fmt.Fprintf(stderr, "%v\n", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
//start the container
|
||||
if err := client.ContainerStart(ctx, createResponse.ID, types.ContainerStartOptions{}); err != nil {
|
||||
// If we have holdHijackedConnection, we should notify
|
||||
// holdHijackedConnection we are going to exit and wait
|
||||
// to avoid the terminal are not restored.
|
||||
if attach {
|
||||
cancelFun()
|
||||
<-errCh
|
||||
}
|
||||
|
||||
reportError(stderr, cmdPath, err.Error(), false)
|
||||
return runStartContainerErr(err)
|
||||
}
|
||||
|
||||
if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && dockerCli.IsTerminalOut() {
|
||||
if err := dockerCli.MonitorTtySize(ctx, createResponse.ID, false); err != nil {
|
||||
fmt.Fprintf(stderr, "Error monitoring TTY size: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
if errCh != nil {
|
||||
if err := <-errCh; err != nil {
|
||||
logrus.Debugf("Error hijack: %s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Detached mode: wait for the id to be displayed and return.
|
||||
if !config.AttachStdout && !config.AttachStderr {
|
||||
// Detached mode
|
||||
<-waitDisplayID
|
||||
return nil
|
||||
}
|
||||
|
||||
var status int
|
||||
|
||||
// Attached mode
|
||||
if opts.autoRemove {
|
||||
// Autoremove: wait for the container to finish, retrieve
|
||||
// the exit code and remove the container
|
||||
if status, err = client.ContainerWait(ctx, createResponse.ID); err != nil {
|
||||
return runStartContainerErr(err)
|
||||
}
|
||||
if _, status, err = getExitCode(dockerCli, ctx, createResponse.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// No Autoremove: Simply retrieve the exit code
|
||||
if !config.Tty && hostConfig.RestartPolicy.IsNone() {
|
||||
// In non-TTY mode, we can't detach, so we must wait for container exit
|
||||
if status, err = client.ContainerWait(ctx, createResponse.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// In TTY mode, there is a race: if the process dies too slowly, the state could
|
||||
// be updated after the getExitCode call and result in the wrong exit code being reported
|
||||
if _, status, err = getExitCode(dockerCli, ctx, createResponse.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if status != 0 {
|
||||
return cli.StatusError{StatusCode: status}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// reportError is a utility method that prints a user-friendly message
|
||||
// containing the error that occurred during parsing and a suggestion to get help
|
||||
func reportError(stderr io.Writer, name string, str string, withHelp bool) {
|
||||
if withHelp {
|
||||
str += ".\nSee '" + os.Args[0] + " " + name + " --help'"
|
||||
}
|
||||
fmt.Fprintf(stderr, "%s: %s.\n", os.Args[0], str)
|
||||
}
|
||||
|
||||
// if container start fails with 'not found'/'no such' error, return 127
|
||||
// if container start fails with 'permission denied' error, return 126
|
||||
// return 125 for generic docker daemon failures
|
||||
func runStartContainerErr(err error) error {
|
||||
trimmedErr := strings.TrimPrefix(err.Error(), "Error response from daemon: ")
|
||||
statusError := cli.StatusError{StatusCode: 125}
|
||||
if strings.Contains(trimmedErr, "executable file not found") ||
|
||||
strings.Contains(trimmedErr, "no such file or directory") ||
|
||||
strings.Contains(trimmedErr, "system cannot find the file specified") {
|
||||
statusError = cli.StatusError{StatusCode: 127}
|
||||
} else if strings.Contains(trimmedErr, syscall.EACCES.Error()) {
|
||||
statusError = cli.StatusError{StatusCode: 126}
|
||||
}
|
||||
|
||||
return statusError
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/promise"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type startOptions struct {
|
||||
attach bool
|
||||
openStdin bool
|
||||
detachKeys string
|
||||
|
||||
containers []string
|
||||
}
|
||||
|
||||
// NewStartCommand creats a new cobra.Command for `docker start`
|
||||
func NewStartCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts startOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "start [OPTIONS] CONTAINER [CONTAINER...]",
|
||||
Short: "Start one or more stopped containers",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runStart(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.attach, "attach", "a", false, "Attach STDOUT/STDERR and forward signals")
|
||||
flags.BoolVarP(&opts.openStdin, "interactive", "i", false, "Attach container's STDIN")
|
||||
flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runStart(dockerCli *client.DockerCli, opts *startOptions) error {
|
||||
ctx, cancelFun := context.WithCancel(context.Background())
|
||||
|
||||
if opts.attach || opts.openStdin {
|
||||
// We're going to attach to a container.
|
||||
// 1. Ensure we only have one container.
|
||||
if len(opts.containers) > 1 {
|
||||
return fmt.Errorf("You cannot start and attach multiple containers at once.")
|
||||
}
|
||||
|
||||
// 2. Attach to the container.
|
||||
container := opts.containers[0]
|
||||
c, err := dockerCli.Client().ContainerInspect(ctx, container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We always use c.ID instead of container to maintain consistency during `docker start`
|
||||
if !c.Config.Tty {
|
||||
sigc := dockerCli.ForwardAllSignals(ctx, c.ID)
|
||||
defer signal.StopCatch(sigc)
|
||||
}
|
||||
|
||||
if opts.detachKeys != "" {
|
||||
dockerCli.ConfigFile().DetachKeys = opts.detachKeys
|
||||
}
|
||||
|
||||
options := types.ContainerAttachOptions{
|
||||
Stream: true,
|
||||
Stdin: opts.openStdin && c.Config.OpenStdin,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
DetachKeys: dockerCli.ConfigFile().DetachKeys,
|
||||
}
|
||||
|
||||
var in io.ReadCloser
|
||||
|
||||
if options.Stdin {
|
||||
in = dockerCli.In()
|
||||
}
|
||||
|
||||
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
|
||||
// keep the error and read detailed error message from hijacked connection
|
||||
return errAttach
|
||||
}
|
||||
defer resp.Close()
|
||||
cErr := promise.Go(func() error {
|
||||
errHijack := dockerCli.HoldHijackedConnection(ctx, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp)
|
||||
if errHijack == nil {
|
||||
return errAttach
|
||||
}
|
||||
return errHijack
|
||||
})
|
||||
|
||||
// 3. Start the container.
|
||||
if err := dockerCli.Client().ContainerStart(ctx, c.ID, types.ContainerStartOptions{}); err != nil {
|
||||
cancelFun()
|
||||
<-cErr
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. Wait for attachment to break.
|
||||
if c.Config.Tty && dockerCli.IsTerminalOut() {
|
||||
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, c.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status != 0 {
|
||||
return cli.StatusError{StatusCode: status}
|
||||
}
|
||||
} else {
|
||||
// We're not going to attach to anything.
|
||||
// Start as many containers as we want.
|
||||
return startContainersWithoutAttachments(dockerCli, ctx, opts.containers)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func startContainersWithoutAttachments(dockerCli *client.DockerCli, ctx context.Context, containers []string) error {
|
||||
var failedContainers []string
|
||||
for _, container := range containers {
|
||||
if err := dockerCli.Client().ContainerStart(ctx, container, types.ContainerStartOptions{}); err != nil {
|
||||
fmt.Fprintf(dockerCli.Err(), "%s\n", err)
|
||||
failedContainers = append(failedContainers, container)
|
||||
} else {
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", container)
|
||||
}
|
||||
}
|
||||
|
||||
if len(failedContainers) > 0 {
|
||||
return fmt.Errorf("Error: failed to start containers: %v", strings.Join(failedContainers, ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type stopOptions struct {
|
||||
time int
|
||||
|
||||
containers []string
|
||||
}
|
||||
|
||||
// NewStopCommand creats a new cobra.Command for `docker stop`
|
||||
func NewStopCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts stopOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "stop [OPTIONS] CONTAINER [CONTAINER...]",
|
||||
Short: "Stop one or more running containers",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runStop(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.IntVarP(&opts.time, "time", "t", 10, "Seconds to wait for stop before killing it")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runStop(dockerCli *client.DockerCli, opts *stopOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
for _, container := range opts.containers {
|
||||
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)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type topOptions struct {
|
||||
container string
|
||||
|
||||
args []string
|
||||
}
|
||||
|
||||
// NewTopCommand creates a new cobra.Command for `docker top`
|
||||
func NewTopCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts topOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "top CONTAINER [ps OPTIONS]",
|
||||
Short: "Display the running processes of a container",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
opts.args = args[1:]
|
||||
return runTop(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runTop(dockerCli *client.DockerCli, opts *topOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
procList, err := dockerCli.Client().ContainerTop(ctx, opts.container, opts.args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(dockerCli.Out(), 20, 1, 3, ' ', 0)
|
||||
fmt.Fprintln(w, strings.Join(procList.Titles, "\t"))
|
||||
|
||||
for _, proc := range procList.Processes {
|
||||
fmt.Fprintln(w, strings.Join(proc, "\t"))
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type unpauseOptions struct {
|
||||
containers []string
|
||||
}
|
||||
|
||||
// NewUnpauseCommand creats a new cobra.Command for `docker unpause`
|
||||
func NewUnpauseCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts unpauseOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "unpause CONTAINER [CONTAINER...]",
|
||||
Short: "Unpause all processes within one or more containers",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runUnpause(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUnpause(dockerCli *client.DockerCli, opts *unpauseOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
for _, container := range opts.containers {
|
||||
if err := dockerCli.Client().ContainerUnpause(ctx, container); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
} else {
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", container)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
clientapi "github.com/docker/engine-api/client"
|
||||
)
|
||||
|
||||
// getExitCode performs an inspect on the container. It returns
|
||||
// the running state and the exit code.
|
||||
func getExitCode(dockerCli *client.DockerCli, ctx context.Context, containerID string) (bool, int, error) {
|
||||
c, err := dockerCli.Client().ContainerInspect(ctx, containerID)
|
||||
if err != nil {
|
||||
// If we can't connect, then the daemon probably died.
|
||||
if err != clientapi.ErrConnectionFailed {
|
||||
return false, -1, err
|
||||
}
|
||||
return false, -1, nil
|
||||
}
|
||||
return c.State.Running, c.State.ExitCode, nil
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type waitOptions struct {
|
||||
containers []string
|
||||
}
|
||||
|
||||
// NewWaitCommand creates a new cobra.Command for `docker wait`
|
||||
func NewWaitCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts waitOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "wait CONTAINER [CONTAINER...]",
|
||||
Short: "Block until a container stops, then print its exit code",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runWait(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runWait(dockerCli *client.DockerCli, opts *waitOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
for _, container := range opts.containers {
|
||||
status, err := dockerCli.Client().ContainerWait(ctx, container)
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
} else {
|
||||
fmt.Fprintf(dockerCli.Out(), "%d\n", status)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package container
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -9,20 +9,13 @@ import (
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type copyOptions struct {
|
||||
source string
|
||||
destination string
|
||||
followLink bool
|
||||
}
|
||||
|
||||
type copyDirection int
|
||||
|
||||
const (
|
||||
@@ -35,45 +28,46 @@ type cpConfig struct {
|
||||
followLink bool
|
||||
}
|
||||
|
||||
// NewCopyCommand creates a new `docker cp` command
|
||||
func NewCopyCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts copyOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: `cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
|
||||
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH`,
|
||||
Short: "Copy files/folders between a container and the local filesystem",
|
||||
Long: strings.Join([]string{
|
||||
"Copy files/folders between a container and the local filesystem\n",
|
||||
// CmdCp copies files/folders to or from a path in a container.
|
||||
//
|
||||
// When copying from a container, if DEST_PATH is '-' the data is written as a
|
||||
// tar archive file to STDOUT.
|
||||
//
|
||||
// When copying to a container, if SRC_PATH is '-' the data is read as a tar
|
||||
// archive file from STDIN, and the destination CONTAINER:DEST_PATH, must specify
|
||||
// a directory.
|
||||
//
|
||||
// Usage:
|
||||
// docker cp CONTAINER:SRC_PATH DEST_PATH|-
|
||||
// docker cp SRC_PATH|- CONTAINER:DEST_PATH
|
||||
func (cli *DockerCli) CmdCp(args ...string) error {
|
||||
cmd := Cli.Subcmd(
|
||||
"cp",
|
||||
[]string{"CONTAINER:SRC_PATH DEST_PATH|-", "SRC_PATH|- CONTAINER:DEST_PATH"},
|
||||
strings.Join([]string{
|
||||
Cli.DockerCommands["cp"].Description,
|
||||
"\nUse '-' as the source to read a tar archive from stdin\n",
|
||||
"and extract it to a directory destination in a container.\n",
|
||||
"Use '-' as the destination to stream a tar archive of a\n",
|
||||
"container source to stdout.",
|
||||
}, ""),
|
||||
Args: cli.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if args[0] == "" {
|
||||
return fmt.Errorf("source can not be empty")
|
||||
}
|
||||
if args[1] == "" {
|
||||
return fmt.Errorf("destination can not be empty")
|
||||
}
|
||||
opts.source = args[0]
|
||||
opts.destination = args[1]
|
||||
return runCopy(dockerCli, opts)
|
||||
},
|
||||
true,
|
||||
)
|
||||
|
||||
followLink := cmd.Bool([]string{"L", "-follow-link"}, false, "Always follow symbol link in SRC_PATH")
|
||||
|
||||
cmd.Require(flag.Exact, 2)
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
if cmd.Arg(0) == "" {
|
||||
return fmt.Errorf("source can not be empty")
|
||||
}
|
||||
if cmd.Arg(1) == "" {
|
||||
return fmt.Errorf("destination can not be empty")
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.BoolVarP(&opts.followLink, "follow-link", "L", false, "Always follow symbol link in SRC_PATH")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCopy(dockerCli *client.DockerCli, opts copyOptions) error {
|
||||
srcContainer, srcPath := splitCpArg(opts.source)
|
||||
dstContainer, dstPath := splitCpArg(opts.destination)
|
||||
srcContainer, srcPath := splitCpArg(cmd.Arg(0))
|
||||
dstContainer, dstPath := splitCpArg(cmd.Arg(1))
|
||||
|
||||
var direction copyDirection
|
||||
if srcContainer != "" {
|
||||
@@ -84,16 +78,14 @@ func runCopy(dockerCli *client.DockerCli, opts copyOptions) error {
|
||||
}
|
||||
|
||||
cpParam := &cpConfig{
|
||||
followLink: opts.followLink,
|
||||
followLink: *followLink,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
switch direction {
|
||||
case fromContainer:
|
||||
return copyFromContainer(ctx, dockerCli, srcContainer, srcPath, dstPath, cpParam)
|
||||
return cli.copyFromContainer(srcContainer, srcPath, dstPath, cpParam)
|
||||
case toContainer:
|
||||
return copyToContainer(ctx, dockerCli, srcPath, dstContainer, dstPath, cpParam)
|
||||
return cli.copyToContainer(srcPath, dstContainer, dstPath, cpParam)
|
||||
case acrossContainers:
|
||||
// Copying between containers isn't supported.
|
||||
return fmt.Errorf("copying between containers is not supported")
|
||||
@@ -103,8 +95,39 @@ func runCopy(dockerCli *client.DockerCli, opts copyOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
func statContainerPath(ctx context.Context, dockerCli *client.DockerCli, containerName, path string) (types.ContainerPathStat, error) {
|
||||
return dockerCli.Client().ContainerStatPath(ctx, containerName, path)
|
||||
// We use `:` as a delimiter between CONTAINER and PATH, but `:` could also be
|
||||
// in a valid LOCALPATH, like `file:name.txt`. We can resolve this ambiguity by
|
||||
// requiring a LOCALPATH with a `:` to be made explicit with a relative or
|
||||
// absolute path:
|
||||
// `/path/to/file:name.txt` or `./file:name.txt`
|
||||
//
|
||||
// This is apparently how `scp` handles this as well:
|
||||
// http://www.cyberciti.biz/faq/rsync-scp-file-name-with-colon-punctuation-in-it/
|
||||
//
|
||||
// We can't simply check for a filepath separator because container names may
|
||||
// have a separator, e.g., "host0/cname1" if container is in a Docker cluster,
|
||||
// so we have to check for a `/` or `.` prefix. Also, in the case of a Windows
|
||||
// client, a `:` could be part of an absolute Windows path, in which case it
|
||||
// is immediately proceeded by a backslash.
|
||||
func splitCpArg(arg string) (container, path string) {
|
||||
if system.IsAbs(arg) {
|
||||
// Explicit local absolute path, e.g., `C:\foo` or `/foo`.
|
||||
return "", arg
|
||||
}
|
||||
|
||||
parts := strings.SplitN(arg, ":", 2)
|
||||
|
||||
if len(parts) == 1 || strings.HasPrefix(parts[0], ".") {
|
||||
// Either there's no `:` in the arg
|
||||
// OR it's an explicit local relative path like `./file:name.txt`.
|
||||
return "", arg
|
||||
}
|
||||
|
||||
return parts[0], parts[1]
|
||||
}
|
||||
|
||||
func (cli *DockerCli) statContainerPath(containerName, path string) (types.ContainerPathStat, error) {
|
||||
return cli.client.ContainerStatPath(context.Background(), containerName, path)
|
||||
}
|
||||
|
||||
func resolveLocalPath(localPath string) (absPath string, err error) {
|
||||
@@ -115,7 +138,7 @@ func resolveLocalPath(localPath string) (absPath string, err error) {
|
||||
return archive.PreserveTrailingDotOrSeparator(absPath, localPath), nil
|
||||
}
|
||||
|
||||
func copyFromContainer(ctx context.Context, dockerCli *client.DockerCli, srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
|
||||
func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
|
||||
if dstPath != "-" {
|
||||
// Get an absolute destination path.
|
||||
dstPath, err = resolveLocalPath(dstPath)
|
||||
@@ -127,7 +150,7 @@ func copyFromContainer(ctx context.Context, dockerCli *client.DockerCli, srcCont
|
||||
// if client requests to follow symbol link, then must decide target file to be copied
|
||||
var rebaseName string
|
||||
if cpParam.followLink {
|
||||
srcStat, err := statContainerPath(ctx, dockerCli, srcContainer, srcPath)
|
||||
srcStat, err := cli.statContainerPath(srcContainer, srcPath)
|
||||
|
||||
// If the destination is a symbolic link, we should follow it.
|
||||
if err == nil && srcStat.Mode&os.ModeSymlink != 0 {
|
||||
@@ -144,7 +167,7 @@ func copyFromContainer(ctx context.Context, dockerCli *client.DockerCli, srcCont
|
||||
|
||||
}
|
||||
|
||||
content, stat, err := dockerCli.Client().CopyFromContainer(ctx, srcContainer, srcPath)
|
||||
content, stat, err := cli.client.CopyFromContainer(context.Background(), srcContainer, srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -176,7 +199,7 @@ func copyFromContainer(ctx context.Context, dockerCli *client.DockerCli, srcCont
|
||||
return archive.CopyTo(preArchive, srcInfo, dstPath)
|
||||
}
|
||||
|
||||
func copyToContainer(ctx context.Context, dockerCli *client.DockerCli, srcPath, dstContainer, dstPath string, cpParam *cpConfig) (err error) {
|
||||
func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpParam *cpConfig) (err error) {
|
||||
if srcPath != "-" {
|
||||
// Get an absolute source path.
|
||||
srcPath, err = resolveLocalPath(srcPath)
|
||||
@@ -192,7 +215,7 @@ func copyToContainer(ctx context.Context, dockerCli *client.DockerCli, srcPath,
|
||||
|
||||
// Prepare destination copy info by stat-ing the container path.
|
||||
dstInfo := archive.CopyInfo{Path: dstPath}
|
||||
dstStat, err := statContainerPath(ctx, dockerCli, dstContainer, dstPath)
|
||||
dstStat, err := cli.statContainerPath(dstContainer, dstPath)
|
||||
|
||||
// If the destination is a symbolic link, we should evaluate it.
|
||||
if err == nil && dstStat.Mode&os.ModeSymlink != 0 {
|
||||
@@ -204,7 +227,7 @@ func copyToContainer(ctx context.Context, dockerCli *client.DockerCli, srcPath,
|
||||
}
|
||||
|
||||
dstInfo.Path = linkTarget
|
||||
dstStat, err = statContainerPath(ctx, dockerCli, dstContainer, linkTarget)
|
||||
dstStat, err = cli.statContainerPath(dstContainer, linkTarget)
|
||||
}
|
||||
|
||||
// Ignore any error and assume that the parent directory of the destination
|
||||
@@ -265,39 +288,11 @@ func copyToContainer(ctx context.Context, dockerCli *client.DockerCli, srcPath,
|
||||
}
|
||||
|
||||
options := types.CopyToContainerOptions{
|
||||
ContainerID: dstContainer,
|
||||
Path: resolvedDstPath,
|
||||
Content: content,
|
||||
AllowOverwriteDirWithFile: false,
|
||||
}
|
||||
|
||||
return dockerCli.Client().CopyToContainer(ctx, dstContainer, resolvedDstPath, content, options)
|
||||
}
|
||||
|
||||
// We use `:` as a delimiter between CONTAINER and PATH, but `:` could also be
|
||||
// in a valid LOCALPATH, like `file:name.txt`. We can resolve this ambiguity by
|
||||
// requiring a LOCALPATH with a `:` to be made explicit with a relative or
|
||||
// absolute path:
|
||||
// `/path/to/file:name.txt` or `./file:name.txt`
|
||||
//
|
||||
// This is apparently how `scp` handles this as well:
|
||||
// http://www.cyberciti.biz/faq/rsync-scp-file-name-with-colon-punctuation-in-it/
|
||||
//
|
||||
// We can't simply check for a filepath separator because container names may
|
||||
// have a separator, e.g., "host0/cname1" if container is in a Docker cluster,
|
||||
// so we have to check for a `/` or `.` prefix. Also, in the case of a Windows
|
||||
// client, a `:` could be part of an absolute Windows path, in which case it
|
||||
// is immediately proceeded by a backslash.
|
||||
func splitCpArg(arg string) (container, path string) {
|
||||
if system.IsAbs(arg) {
|
||||
// Explicit local absolute path, e.g., `C:\foo` or `/foo`.
|
||||
return "", arg
|
||||
}
|
||||
|
||||
parts := strings.SplitN(arg, ":", 2)
|
||||
|
||||
if len(parts) == 1 || strings.HasPrefix(parts[0], ".") {
|
||||
// Either there's no `:` in the arg
|
||||
// OR it's an explicit local relative path like `./file:name.txt`.
|
||||
return "", arg
|
||||
}
|
||||
|
||||
return parts[0], parts[1]
|
||||
return cli.client.CopyToContainer(context.Background(), options)
|
||||
}
|
||||
180
api/client/create.go
Normal file
180
api/client/create.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/docker/registry"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
"github.com/docker/engine-api/client"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
)
|
||||
|
||||
func (cli *DockerCli) pullImage(image string) error {
|
||||
return cli.pullImageCustomOut(image, cli.out)
|
||||
}
|
||||
|
||||
func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
|
||||
ref, err := reference.ParseNamed(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tag string
|
||||
switch x := reference.WithDefaultTag(ref).(type) {
|
||||
case reference.Canonical:
|
||||
tag = x.Digest().String()
|
||||
case reference.NamedTagged:
|
||||
tag = x.Tag()
|
||||
}
|
||||
|
||||
// Resolve the Repository name from fqn to RepositoryInfo
|
||||
repoInfo, err := registry.ParseRepositoryInfo(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authConfig := cli.resolveAuthConfig(repoInfo.Index)
|
||||
encodedAuth, err := encodeAuthToBase64(authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options := types.ImageCreateOptions{
|
||||
Parent: ref.Name(),
|
||||
Tag: tag,
|
||||
RegistryAuth: encodedAuth,
|
||||
}
|
||||
|
||||
responseBody, err := cli.client.ImageCreate(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
return jsonmessage.DisplayJSONMessagesStream(responseBody, out, cli.outFd, cli.isTerminalOut, nil)
|
||||
}
|
||||
|
||||
type cidFile struct {
|
||||
path string
|
||||
file *os.File
|
||||
written bool
|
||||
}
|
||||
|
||||
func newCIDFile(path string) (*cidFile, error) {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
|
||||
}
|
||||
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
|
||||
}
|
||||
|
||||
return &cidFile{path: path, file: f}, nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) createContainer(config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
||||
var containerIDFile *cidFile
|
||||
if cidfile != "" {
|
||||
var err error
|
||||
if containerIDFile, err = newCIDFile(cidfile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer containerIDFile.Close()
|
||||
}
|
||||
|
||||
var trustedRef reference.Canonical
|
||||
_, ref, err := reference.ParseIDOrReference(config.Image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ref != nil {
|
||||
ref = reference.WithDefaultTag(ref)
|
||||
|
||||
if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() {
|
||||
var err error
|
||||
trustedRef, err = cli.trustedReference(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Image = trustedRef.String()
|
||||
}
|
||||
}
|
||||
|
||||
//create the container
|
||||
response, err := cli.client.ContainerCreate(context.Background(), config, hostConfig, networkingConfig, name)
|
||||
|
||||
//if image not found try to pull it
|
||||
if err != nil {
|
||||
if client.IsErrImageNotFound(err) && ref != nil {
|
||||
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.String())
|
||||
|
||||
// we don't want to write to stdout anything apart from container.ID
|
||||
if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil {
|
||||
if err := cli.tagTrusted(trustedRef, ref); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Retry
|
||||
var retryErr error
|
||||
response, retryErr = cli.client.ContainerCreate(context.Background(), config, hostConfig, networkingConfig, name)
|
||||
if retryErr != nil {
|
||||
return nil, retryErr
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, warning := range response.Warnings {
|
||||
fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
|
||||
}
|
||||
if containerIDFile != nil {
|
||||
if err = containerIDFile.Write(response.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// CmdCreate creates a new container from a given image.
|
||||
//
|
||||
// Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
|
||||
func (cli *DockerCli) CmdCreate(args ...string) error {
|
||||
cmd := Cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, Cli.DockerCommands["create"].Description, true)
|
||||
addTrustedFlags(cmd, true)
|
||||
|
||||
// These are flags not stored in Config/HostConfig
|
||||
var (
|
||||
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
||||
)
|
||||
|
||||
config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
||||
|
||||
if err != nil {
|
||||
cmd.ReportError(err.Error(), true)
|
||||
os.Exit(1)
|
||||
}
|
||||
if config.Image == "" {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
response, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(cli.out, "%s\n", response.ID)
|
||||
return nil
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/cliconfig/configfile"
|
||||
"github.com/docker/docker/cliconfig/credentials"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// GetCredentials loads the user credentials from a credentials store.
|
||||
// The store is determined by the config file settings.
|
||||
func GetCredentials(c *configfile.ConfigFile, serverAddress string) (types.AuthConfig, error) {
|
||||
s := LoadCredentialsStore(c)
|
||||
return s.Get(serverAddress)
|
||||
}
|
||||
|
||||
// GetAllCredentials loads all credentials from a credentials store.
|
||||
// The store is determined by the config file settings.
|
||||
func GetAllCredentials(c *configfile.ConfigFile) (map[string]types.AuthConfig, error) {
|
||||
s := LoadCredentialsStore(c)
|
||||
return s.GetAll()
|
||||
}
|
||||
|
||||
// StoreCredentials saves the user credentials in a credentials store.
|
||||
// The store is determined by the config file settings.
|
||||
func StoreCredentials(c *configfile.ConfigFile, auth types.AuthConfig) error {
|
||||
s := LoadCredentialsStore(c)
|
||||
return s.Store(auth)
|
||||
}
|
||||
|
||||
// EraseCredentials removes the user credentials from a credentials store.
|
||||
// The store is determined by the config file settings.
|
||||
func EraseCredentials(c *configfile.ConfigFile, serverAddress string) error {
|
||||
s := LoadCredentialsStore(c)
|
||||
return s.Erase(serverAddress)
|
||||
}
|
||||
|
||||
// LoadCredentialsStore initializes a new credentials store based
|
||||
// in the settings provided in the configuration file.
|
||||
func LoadCredentialsStore(c *configfile.ConfigFile) credentials.Store {
|
||||
if c.CredentialsStore != "" {
|
||||
return credentials.NewNativeStore(c)
|
||||
}
|
||||
return credentials.NewFileStore(c)
|
||||
}
|
||||
49
api/client/diff.go
Normal file
49
api/client/diff.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
|
||||
// CmdDiff shows changes on a container's filesystem.
|
||||
//
|
||||
// Each changed file is printed on a separate line, prefixed with a single
|
||||
// character that indicates the status of the file: C (modified), A (added),
|
||||
// or D (deleted).
|
||||
//
|
||||
// Usage: docker diff CONTAINER
|
||||
func (cli *DockerCli) CmdDiff(args ...string) error {
|
||||
cmd := Cli.Subcmd("diff", []string{"CONTAINER"}, Cli.DockerCommands["diff"].Description, true)
|
||||
cmd.Require(flag.Exact, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
if cmd.Arg(0) == "" {
|
||||
return fmt.Errorf("Container name cannot be empty")
|
||||
}
|
||||
|
||||
changes, err := cli.client.ContainerDiff(context.Background(), cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, change := range changes {
|
||||
var kind string
|
||||
switch change.Kind {
|
||||
case archive.ChangeModify:
|
||||
kind = "C"
|
||||
case archive.ChangeAdd:
|
||||
kind = "A"
|
||||
case archive.ChangeDelete:
|
||||
kind = "D"
|
||||
}
|
||||
fmt.Fprintf(cli.out, "%s %s\n", kind, change.Path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
146
api/client/events.go
Normal file
146
api/client/events.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/jsonlog"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/engine-api/types"
|
||||
eventtypes "github.com/docker/engine-api/types/events"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
)
|
||||
|
||||
// CmdEvents prints a live stream of real time events from the server.
|
||||
//
|
||||
// Usage: docker events [OPTIONS]
|
||||
func (cli *DockerCli) CmdEvents(args ...string) error {
|
||||
cmd := Cli.Subcmd("events", nil, Cli.DockerCommands["events"].Description, true)
|
||||
since := cmd.String([]string{"-since"}, "", "Show all events created since timestamp")
|
||||
until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
|
||||
flFilter := opts.NewListOpts(nil)
|
||||
cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
|
||||
cmd.Require(flag.Exact, 0)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
eventFilterArgs := filters.NewArgs()
|
||||
|
||||
// Consolidate all filter flags, and sanity check them early.
|
||||
// They'll get process in the daemon/server.
|
||||
for _, f := range flFilter.GetAll() {
|
||||
var err error
|
||||
eventFilterArgs, err = filters.ParseFlag(f, eventFilterArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
options := types.EventsOptions{
|
||||
Since: *since,
|
||||
Until: *until,
|
||||
Filters: eventFilterArgs,
|
||||
}
|
||||
|
||||
responseBody, err := cli.client.Events(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
return streamEvents(responseBody, cli.out)
|
||||
}
|
||||
|
||||
// streamEvents decodes prints the incoming events in the provided output.
|
||||
func streamEvents(input io.Reader, output io.Writer) error {
|
||||
return decodeEvents(input, func(event eventtypes.Message, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printOutput(event, output)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type eventProcessor func(event eventtypes.Message, err error) error
|
||||
|
||||
func decodeEvents(input io.Reader, ep eventProcessor) error {
|
||||
dec := json.NewDecoder(input)
|
||||
for {
|
||||
var event eventtypes.Message
|
||||
err := dec.Decode(&event)
|
||||
if err != nil && err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if procErr := ep(event, err); procErr != nil {
|
||||
return procErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// printOutput prints all types of event information.
|
||||
// Each output includes the event type, actor id, name and action.
|
||||
// Actor attributes are printed at the end if the actor has any.
|
||||
func printOutput(event eventtypes.Message, output io.Writer) {
|
||||
if event.TimeNano != 0 {
|
||||
fmt.Fprintf(output, "%s ", time.Unix(0, event.TimeNano).Format(jsonlog.RFC3339NanoFixed))
|
||||
} else if event.Time != 0 {
|
||||
fmt.Fprintf(output, "%s ", time.Unix(event.Time, 0).Format(jsonlog.RFC3339NanoFixed))
|
||||
}
|
||||
|
||||
fmt.Fprintf(output, "%s %s %s", event.Type, event.Action, event.Actor.ID)
|
||||
|
||||
if len(event.Actor.Attributes) > 0 {
|
||||
var attrs []string
|
||||
var keys []string
|
||||
for k := range event.Actor.Attributes {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
v := event.Actor.Attributes[k]
|
||||
attrs = append(attrs, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
fmt.Fprintf(output, " (%s)", strings.Join(attrs, ", "))
|
||||
}
|
||||
fmt.Fprint(output, "\n")
|
||||
}
|
||||
|
||||
type eventHandler struct {
|
||||
handlers map[string]func(eventtypes.Message)
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (w *eventHandler) Handle(action string, h func(eventtypes.Message)) {
|
||||
w.mu.Lock()
|
||||
w.handlers[action] = h
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
// Watch ranges over the passed in event chan and processes the events based on the
|
||||
// handlers created for a given action.
|
||||
// To stop watching, close the event chan.
|
||||
func (w *eventHandler) Watch(c <-chan eventtypes.Message) {
|
||||
for e := range c {
|
||||
w.mu.Lock()
|
||||
h, exists := w.handlers[e.Action]
|
||||
w.mu.Unlock()
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
logrus.Debugf("event handler: received event: %v", e)
|
||||
go h(e)
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,12 @@ import (
|
||||
//
|
||||
// Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
|
||||
func (cli *DockerCli) CmdExec(args ...string) error {
|
||||
cmd := Cli.Subcmd("exec", []string{"[OPTIONS] CONTAINER COMMAND [ARG...]"}, Cli.DockerCommands["exec"].Description, true)
|
||||
cmd := Cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, Cli.DockerCommands["exec"].Description, true)
|
||||
detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
||||
|
||||
execConfig, err := ParseExec(cmd, args)
|
||||
container := cmd.Arg(0)
|
||||
// just in case the ParseExec does not exit
|
||||
if container == "" || err != nil {
|
||||
if execConfig.Container == "" || err != nil {
|
||||
return Cli.StatusError{StatusCode: 1}
|
||||
}
|
||||
|
||||
@@ -34,9 +33,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
||||
// Send client escape keys
|
||||
execConfig.DetachKeys = cli.configFile.DetachKeys
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
response, err := cli.client.ContainerExecCreate(ctx, container, *execConfig)
|
||||
response, err := cli.client.ContainerExecCreate(context.Background(), *execConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -58,7 +55,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
||||
Tty: execConfig.Tty,
|
||||
}
|
||||
|
||||
if err := cli.client.ContainerExecStart(ctx, execID, execStartCheck); err != nil {
|
||||
if err := cli.client.ContainerExecStart(context.Background(), execID, execStartCheck); err != nil {
|
||||
return err
|
||||
}
|
||||
// For now don't print this - wait for when we support exec wait()
|
||||
@@ -87,17 +84,23 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := cli.client.ContainerExecAttach(ctx, execID, *execConfig)
|
||||
resp, err := cli.client.ContainerExecAttach(context.Background(), execID, *execConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Close()
|
||||
if in != nil && execConfig.Tty {
|
||||
if err := cli.setRawTerminal(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.restoreTerminal(in)
|
||||
}
|
||||
errCh = promise.Go(func() error {
|
||||
return cli.HoldHijackedConnection(ctx, execConfig.Tty, in, out, stderr, resp)
|
||||
return cli.holdHijackedConnection(execConfig.Tty, in, out, stderr, resp)
|
||||
})
|
||||
|
||||
if execConfig.Tty && cli.isTerminalIn {
|
||||
if err := cli.MonitorTtySize(ctx, execID, true); err != nil {
|
||||
if err := cli.monitorTtySize(execID, true); err != nil {
|
||||
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
|
||||
}
|
||||
}
|
||||
@@ -108,7 +111,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
||||
}
|
||||
|
||||
var status int
|
||||
if _, status, err = cli.getExecExitCode(ctx, execID); err != nil {
|
||||
if _, status, err = getExecExitCode(cli, execID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -131,11 +134,13 @@ func ParseExec(cmd *flag.FlagSet, args []string) (*types.ExecConfig, error) {
|
||||
flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: <name|uid>[:<group|gid>])")
|
||||
flPrivileged = cmd.Bool([]string{"-privileged"}, false, "Give extended privileges to the command")
|
||||
execCmd []string
|
||||
container string
|
||||
)
|
||||
cmd.Require(flag.Min, 2)
|
||||
if err := cmd.ParseFlags(args, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
container = cmd.Arg(0)
|
||||
parsedArgs := cmd.Args()
|
||||
execCmd = parsedArgs[1:]
|
||||
|
||||
@@ -144,6 +149,7 @@ func ParseExec(cmd *flag.FlagSet, args []string) (*types.ExecConfig, error) {
|
||||
Privileged: *flPrivileged,
|
||||
Tty: *flTty,
|
||||
Cmd: execCmd,
|
||||
Container: container,
|
||||
Detach: *flDetach,
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ func TestParseExec(t *testing.T) {
|
||||
&arguments{
|
||||
[]string{"container", "command"},
|
||||
}: {
|
||||
Container: "container",
|
||||
Cmd: []string{"command"},
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
@@ -30,6 +31,7 @@ func TestParseExec(t *testing.T) {
|
||||
&arguments{
|
||||
[]string{"container", "command1", "command2"},
|
||||
}: {
|
||||
Container: "container",
|
||||
Cmd: []string{"command1", "command2"},
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
@@ -42,6 +44,7 @@ func TestParseExec(t *testing.T) {
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Tty: true,
|
||||
Container: "container",
|
||||
Cmd: []string{"command"},
|
||||
},
|
||||
&arguments{
|
||||
@@ -51,6 +54,7 @@ func TestParseExec(t *testing.T) {
|
||||
AttachStdout: false,
|
||||
AttachStderr: false,
|
||||
Detach: true,
|
||||
Container: "container",
|
||||
Cmd: []string{"command"},
|
||||
},
|
||||
&arguments{
|
||||
@@ -61,6 +65,7 @@ func TestParseExec(t *testing.T) {
|
||||
AttachStderr: false,
|
||||
Detach: true,
|
||||
Tty: true,
|
||||
Container: "container",
|
||||
Cmd: []string{"command"},
|
||||
},
|
||||
}
|
||||
@@ -98,6 +103,9 @@ func compareExecConfig(config1 *types.ExecConfig, config2 *types.ExecConfig) boo
|
||||
if config1.AttachStdout != config2.AttachStdout {
|
||||
return false
|
||||
}
|
||||
if config1.Container != config2.Container {
|
||||
return false
|
||||
}
|
||||
if config1.Detach != config2.Detach {
|
||||
return false
|
||||
}
|
||||
|
||||
42
api/client/export.go
Normal file
42
api/client/export.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
|
||||
// CmdExport exports a filesystem as a tar archive.
|
||||
//
|
||||
// The tar archive is streamed to STDOUT by default or written to a file.
|
||||
//
|
||||
// Usage: docker export [OPTIONS] CONTAINER
|
||||
func (cli *DockerCli) CmdExport(args ...string) error {
|
||||
cmd := Cli.Subcmd("export", []string{"CONTAINER"}, Cli.DockerCommands["export"].Description, true)
|
||||
outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT")
|
||||
cmd.Require(flag.Exact, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
if *outfile == "" && cli.isTerminalOut {
|
||||
return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
|
||||
}
|
||||
|
||||
responseBody, err := cli.client.ContainerExport(context.Background(), cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
if *outfile == "" {
|
||||
_, err := io.Copy(cli.out, responseBody)
|
||||
return err
|
||||
}
|
||||
|
||||
return copyToFile(*outfile, responseBody)
|
||||
|
||||
}
|
||||
@@ -234,10 +234,9 @@ func (c *baseSubContext) addHeader(header string) {
|
||||
}
|
||||
|
||||
func stripNamePrefix(ss []string) []string {
|
||||
sss := make([]string, len(ss))
|
||||
for i, s := range ss {
|
||||
sss[i] = s[1:]
|
||||
ss[i] = s[1:]
|
||||
}
|
||||
|
||||
return sss
|
||||
return ss
|
||||
}
|
||||
|
||||
@@ -114,27 +114,35 @@ type ImageContext struct {
|
||||
func (ctx ContainerContext) Write() {
|
||||
switch ctx.Format {
|
||||
case tableFormatKey:
|
||||
ctx.Format = defaultContainerTableFormat
|
||||
if ctx.Quiet {
|
||||
ctx.Format = defaultQuietFormat
|
||||
} else {
|
||||
ctx.Format = defaultContainerTableFormat
|
||||
if ctx.Size {
|
||||
ctx.Format += `\t{{.Size}}`
|
||||
}
|
||||
}
|
||||
case rawFormatKey:
|
||||
if ctx.Quiet {
|
||||
ctx.Format = `container_id: {{.ID}}`
|
||||
} else {
|
||||
ctx.Format = `container_id: {{.ID}}\nimage: {{.Image}}\ncommand: {{.Command}}\ncreated_at: {{.CreatedAt}}\nstatus: {{.Status}}\nnames: {{.Names}}\nlabels: {{.Labels}}\nports: {{.Ports}}\n`
|
||||
ctx.Format = `container_id: {{.ID}}
|
||||
image: {{.Image}}
|
||||
command: {{.Command}}
|
||||
created_at: {{.CreatedAt}}
|
||||
status: {{.Status}}
|
||||
names: {{.Names}}
|
||||
labels: {{.Labels}}
|
||||
ports: {{.Ports}}
|
||||
`
|
||||
if ctx.Size {
|
||||
ctx.Format += `size: {{.Size}}\n`
|
||||
ctx.Format += `size: {{.Size}}
|
||||
`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.buffer = bytes.NewBufferString("")
|
||||
ctx.preformat()
|
||||
if ctx.table && ctx.Size {
|
||||
ctx.finalFormat += "\t{{.Size}}"
|
||||
}
|
||||
|
||||
tmpl, err := ctx.parseFormat()
|
||||
if err != nil {
|
||||
@@ -155,10 +163,6 @@ 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:
|
||||
@@ -204,98 +208,42 @@ virtual_size: {{.Size}}
|
||||
}
|
||||
|
||||
for _, image := range ctx.Images {
|
||||
images := []*imageContext{}
|
||||
if isDangling(image) {
|
||||
images = append(images, &imageContext{
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
repo = ref.Name()
|
||||
|
||||
switch x := ref.(type) {
|
||||
case reference.Canonical:
|
||||
digest = x.Digest().String()
|
||||
case reference.NamedTagged:
|
||||
tag = x.Tag()
|
||||
}
|
||||
}
|
||||
imageCtx := &imageContext{
|
||||
trunc: ctx.Trunc,
|
||||
i: image,
|
||||
repo: "<none>",
|
||||
tag: "<none>",
|
||||
digest: "<none>",
|
||||
})
|
||||
} else {
|
||||
repoTags := map[string][]string{}
|
||||
repoDigests := map[string][]string{}
|
||||
|
||||
for _, refString := range append(image.RepoTags) {
|
||||
ref, err := reference.ParseNamed(refString)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if nt, ok := ref.(reference.NamedTagged); ok {
|
||||
repoTags[ref.Name()] = append(repoTags[ref.Name()], nt.Tag())
|
||||
}
|
||||
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
|
||||
|
||||
@@ -63,7 +63,7 @@ containerID2 ubuntu "" 24 hours ago
|
||||
},
|
||||
Size: true,
|
||||
},
|
||||
"IMAGE\nubuntu\nubuntu\n",
|
||||
"IMAGE SIZE\nubuntu 0 B\nubuntu 0 B\n",
|
||||
},
|
||||
{
|
||||
ContainerContext{
|
||||
@@ -230,25 +230,6 @@ func TestContainerContextWriteWithNoContainers(t *testing.T) {
|
||||
},
|
||||
Size: true,
|
||||
},
|
||||
"IMAGE\n",
|
||||
},
|
||||
{
|
||||
ContainerContext{
|
||||
Context: Context{
|
||||
Format: "table {{.Image}}\t{{.Size}}",
|
||||
Output: out,
|
||||
},
|
||||
},
|
||||
"IMAGE SIZE\n",
|
||||
},
|
||||
{
|
||||
ContainerContext{
|
||||
Context: Context{
|
||||
Format: "table {{.Image}}\t{{.Size}}",
|
||||
Output: out,
|
||||
},
|
||||
Size: true,
|
||||
},
|
||||
"IMAGE SIZE\n",
|
||||
},
|
||||
}
|
||||
@@ -301,6 +282,7 @@ 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
|
||||
`,
|
||||
@@ -311,7 +293,7 @@ image tag2 imageID2 24 hours ago
|
||||
Format: "table {{.Repository}}",
|
||||
},
|
||||
},
|
||||
"REPOSITORY\nimage\nimage\n<none>\n",
|
||||
"REPOSITORY\nimage\nimage\nimage\n<none>\n",
|
||||
},
|
||||
{
|
||||
ImageContext{
|
||||
@@ -321,6 +303,7 @@ image tag2 imageID2 24 hours ago
|
||||
Digest: true,
|
||||
},
|
||||
`REPOSITORY DIGEST
|
||||
image <none>
|
||||
image sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
|
||||
image <none>
|
||||
<none> <none>
|
||||
@@ -333,7 +316,7 @@ image <none>
|
||||
Quiet: true,
|
||||
},
|
||||
},
|
||||
"REPOSITORY\nimage\nimage\n<none>\n",
|
||||
"REPOSITORY\nimage\nimage\nimage\n<none>\n",
|
||||
},
|
||||
{
|
||||
ImageContext{
|
||||
@@ -342,7 +325,7 @@ image <none>
|
||||
Quiet: true,
|
||||
},
|
||||
},
|
||||
"imageID1\nimageID2\nimageID3\n",
|
||||
"imageID1\nimageID1\nimageID2\nimageID3\n",
|
||||
},
|
||||
{
|
||||
ImageContext{
|
||||
@@ -353,7 +336,8 @@ image <none>
|
||||
Digest: true,
|
||||
},
|
||||
`REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
|
||||
image tag1 sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf imageID1 24 hours ago 0 B
|
||||
image tag1 <none> imageID1 24 hours ago 0 B
|
||||
image <none> 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
|
||||
`,
|
||||
@@ -366,7 +350,7 @@ image tag2 <none>
|
||||
},
|
||||
Digest: true,
|
||||
},
|
||||
"imageID1\nimageID2\nimageID3\n",
|
||||
"imageID1\nimageID1\nimageID2\nimageID3\n",
|
||||
},
|
||||
// Raw Format
|
||||
{
|
||||
@@ -381,6 +365,12 @@ 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
|
||||
@@ -393,7 +383,7 @@ image_id: imageID3
|
||||
created_at: %s
|
||||
virtual_size: 0 B
|
||||
|
||||
`, expectedTime, expectedTime, expectedTime),
|
||||
`, expectedTime, expectedTime, expectedTime, expectedTime),
|
||||
},
|
||||
{
|
||||
ImageContext{
|
||||
@@ -404,6 +394,13 @@ 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
|
||||
@@ -423,7 +420,7 @@ image_id: imageID3
|
||||
created_at: %s
|
||||
virtual_size: 0 B
|
||||
|
||||
`, expectedTime, expectedTime, expectedTime),
|
||||
`, expectedTime, expectedTime, expectedTime, expectedTime),
|
||||
},
|
||||
{
|
||||
ImageContext{
|
||||
@@ -433,6 +430,7 @@ virtual_size: 0 B
|
||||
},
|
||||
},
|
||||
`image_id: imageID1
|
||||
image_id: imageID1
|
||||
image_id: imageID2
|
||||
image_id: imageID3
|
||||
`,
|
||||
@@ -444,7 +442,7 @@ image_id: imageID3
|
||||
Format: "{{.Repository}}",
|
||||
},
|
||||
},
|
||||
"image\nimage\n<none>\n",
|
||||
"image\nimage\nimage\n<none>\n",
|
||||
},
|
||||
{
|
||||
ImageContext{
|
||||
@@ -453,7 +451,7 @@ image_id: imageID3
|
||||
},
|
||||
Digest: true,
|
||||
},
|
||||
"image\nimage\n<none>\n",
|
||||
"image\nimage\nimage\n<none>\n",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -2,51 +2,24 @@ package client
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// HoldHijackedConnection handles copying input to and output from streams to the
|
||||
// connection
|
||||
func (cli *DockerCli) HoldHijackedConnection(ctx context.Context, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
||||
var (
|
||||
err error
|
||||
restoreOnce sync.Once
|
||||
)
|
||||
if inputStream != nil && tty {
|
||||
if err := cli.setRawTerminal(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
restoreOnce.Do(func() {
|
||||
cli.restoreTerminal(inputStream)
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
func (cli *DockerCli) holdHijackedConnection(tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
||||
var err error
|
||||
receiveStdout := make(chan error, 1)
|
||||
if outputStream != nil || errorStream != nil {
|
||||
go func() {
|
||||
// When TTY is ON, use regular copy
|
||||
if tty && outputStream != nil {
|
||||
_, err = io.Copy(outputStream, resp.Reader)
|
||||
// we should restore the terminal as soon as possible once connection end
|
||||
// so any following print messages will be in normal type.
|
||||
if inputStream != nil {
|
||||
restoreOnce.Do(func() {
|
||||
cli.restoreTerminal(inputStream)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
_, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader)
|
||||
}
|
||||
|
||||
logrus.Debug("[hijack] End of stdout")
|
||||
logrus.Debugf("[hijack] End of stdout")
|
||||
receiveStdout <- err
|
||||
}()
|
||||
}
|
||||
@@ -55,14 +28,7 @@ func (cli *DockerCli) HoldHijackedConnection(ctx context.Context, tty bool, inpu
|
||||
go func() {
|
||||
if inputStream != nil {
|
||||
io.Copy(resp.Conn, inputStream)
|
||||
// we should restore the terminal as soon as possible once connection end
|
||||
// so any following print messages will be in normal type.
|
||||
if tty {
|
||||
restoreOnce.Do(func() {
|
||||
cli.restoreTerminal(inputStream)
|
||||
})
|
||||
}
|
||||
logrus.Debug("[hijack] End of stdin")
|
||||
logrus.Debugf("[hijack] End of stdin")
|
||||
}
|
||||
|
||||
if err := resp.CloseWrite(); err != nil {
|
||||
@@ -79,16 +45,11 @@ func (cli *DockerCli) HoldHijackedConnection(ctx context.Context, tty bool, inpu
|
||||
}
|
||||
case <-stdinDone:
|
||||
if outputStream != nil || errorStream != nil {
|
||||
select {
|
||||
case err := <-receiveStdout:
|
||||
if err != nil {
|
||||
logrus.Debugf("Error receiveStdout: %s", err)
|
||||
return err
|
||||
}
|
||||
case <-ctx.Done():
|
||||
if err := <-receiveStdout; err != nil {
|
||||
logrus.Debugf("Error receiveStdout: %s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
76
api/client/history.go
Normal file
76
api/client/history.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
// CmdHistory shows the history of an image.
|
||||
//
|
||||
// Usage: docker history [OPTIONS] IMAGE
|
||||
func (cli *DockerCli) CmdHistory(args ...string) error {
|
||||
cmd := Cli.Subcmd("history", []string{"IMAGE"}, Cli.DockerCommands["history"].Description, true)
|
||||
human := cmd.Bool([]string{"H", "-human"}, true, "Print sizes and dates in human readable format")
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
|
||||
noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Don't truncate output")
|
||||
cmd.Require(flag.Exact, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
history, err := cli.client.ImageHistory(context.Background(), cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||
|
||||
if *quiet {
|
||||
for _, entry := range history {
|
||||
if *noTrunc {
|
||||
fmt.Fprintf(w, "%s\n", entry.ID)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\n", stringid.TruncateID(entry.ID))
|
||||
}
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
var imageID string
|
||||
var createdBy string
|
||||
var created string
|
||||
var size string
|
||||
|
||||
fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE\tCOMMENT")
|
||||
for _, entry := range history {
|
||||
imageID = entry.ID
|
||||
createdBy = strings.Replace(entry.CreatedBy, "\t", " ", -1)
|
||||
if *noTrunc == false {
|
||||
createdBy = stringutils.Truncate(createdBy, 45)
|
||||
imageID = stringid.TruncateID(entry.ID)
|
||||
}
|
||||
|
||||
if *human {
|
||||
created = units.HumanDuration(time.Now().UTC().Sub(time.Unix(entry.Created, 0))) + " ago"
|
||||
size = units.HumanSize(float64(entry.Size))
|
||||
} else {
|
||||
created = time.Unix(entry.Created, 0).Format(time.RFC3339)
|
||||
size = strconv.FormatInt(entry.Size, 10)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", imageID, created, createdBy, size, entry.Comment)
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package idresolver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/engine-api/client"
|
||||
"github.com/docker/engine-api/types/swarm"
|
||||
)
|
||||
|
||||
// IDResolver provides ID to Name resolution.
|
||||
type IDResolver struct {
|
||||
client client.APIClient
|
||||
noResolve bool
|
||||
cache map[string]string
|
||||
}
|
||||
|
||||
// New creates a new IDResolver.
|
||||
func New(client client.APIClient, noResolve bool) *IDResolver {
|
||||
return &IDResolver{
|
||||
client: client,
|
||||
noResolve: noResolve,
|
||||
cache: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *IDResolver) get(ctx context.Context, t interface{}, id string) (string, error) {
|
||||
switch t.(type) {
|
||||
case swarm.Node:
|
||||
node, _, err := r.client.NodeInspectWithRaw(ctx, id)
|
||||
if err != nil {
|
||||
return id, nil
|
||||
}
|
||||
if node.Spec.Annotations.Name != "" {
|
||||
return node.Spec.Annotations.Name, nil
|
||||
}
|
||||
if node.Description.Hostname != "" {
|
||||
return node.Description.Hostname, nil
|
||||
}
|
||||
return id, nil
|
||||
case swarm.Service:
|
||||
service, _, err := r.client.ServiceInspectWithRaw(ctx, id)
|
||||
if err != nil {
|
||||
return id, nil
|
||||
}
|
||||
return service.Spec.Annotations.Name, nil
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported type")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Resolve will attempt to resolve an ID to a Name by querying the manager.
|
||||
// Results are stored into a cache.
|
||||
// If the `-n` flag is used in the command-line, resolution is disabled.
|
||||
func (r *IDResolver) Resolve(ctx context.Context, t interface{}, id string) (string, error) {
|
||||
if r.noResolve {
|
||||
return id, nil
|
||||
}
|
||||
if name, ok := r.cache[id]; ok {
|
||||
return name, nil
|
||||
}
|
||||
name, err := r.get(ctx, t, id)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
r.cache[id] = name
|
||||
return name, nil
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type historyOptions struct {
|
||||
image string
|
||||
|
||||
human bool
|
||||
quiet bool
|
||||
noTrunc bool
|
||||
}
|
||||
|
||||
// NewHistoryCommand create a new `docker history` command
|
||||
func NewHistoryCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts historyOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "history [OPTIONS] IMAGE",
|
||||
Short: "Show the history of an image",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.image = args[0]
|
||||
return runHistory(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.BoolVarP(&opts.human, "human", "H", true, "Print sizes and dates in human readable format")
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only show numeric IDs")
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runHistory(dockerCli *client.DockerCli, opts historyOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
history, err := dockerCli.Client().ImageHistory(ctx, opts.image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(dockerCli.Out(), 20, 1, 3, ' ', 0)
|
||||
|
||||
if opts.quiet {
|
||||
for _, entry := range history {
|
||||
if opts.noTrunc {
|
||||
fmt.Fprintf(w, "%s\n", entry.ID)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\n", stringid.TruncateID(entry.ID))
|
||||
}
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
var imageID string
|
||||
var createdBy string
|
||||
var created string
|
||||
var size string
|
||||
|
||||
fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE\tCOMMENT")
|
||||
for _, entry := range history {
|
||||
imageID = entry.ID
|
||||
createdBy = strings.Replace(entry.CreatedBy, "\t", " ", -1)
|
||||
if opts.noTrunc == false {
|
||||
createdBy = stringutils.Truncate(createdBy, 45)
|
||||
imageID = stringid.TruncateID(entry.ID)
|
||||
}
|
||||
|
||||
if opts.human {
|
||||
created = units.HumanDuration(time.Now().UTC().Sub(time.Unix(entry.Created, 0))) + " ago"
|
||||
size = units.HumanSize(float64(entry.Size))
|
||||
} else {
|
||||
created = time.Unix(entry.Created, 0).Format(time.RFC3339)
|
||||
size = strconv.FormatInt(entry.Size, 10)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", imageID, created, createdBy, size, entry.Comment)
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/api/client/formatter"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type imagesOptions struct {
|
||||
matchName string
|
||||
|
||||
quiet bool
|
||||
all bool
|
||||
noTrunc bool
|
||||
showDigests bool
|
||||
format string
|
||||
filter []string
|
||||
}
|
||||
|
||||
// NewImagesCommand create a new `docker images` command
|
||||
func NewImagesCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts imagesOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "images [OPTIONS] [REPOSITORY[:TAG]]",
|
||||
Short: "List images",
|
||||
Args: cli.RequiresMaxArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
opts.matchName = args[0]
|
||||
}
|
||||
return runImages(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only show numeric IDs")
|
||||
flags.BoolVarP(&opts.all, "all", "a", false, "Show all images (default hides intermediate images)")
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
flags.BoolVar(&opts.showDigests, "digests", false, "Show digests")
|
||||
flags.StringVar(&opts.format, "format", "", "Pretty-print images using a Go template")
|
||||
flags.StringSliceVarP(&opts.filter, "filter", "f", []string{}, "Filter output based on conditions provided")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runImages(dockerCli *client.DockerCli, opts imagesOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
// Consolidate all filter flags, and sanity check them early.
|
||||
// They'll get process in the daemon/server.
|
||||
imageFilterArgs := filters.NewArgs()
|
||||
for _, f := range opts.filter {
|
||||
var err error
|
||||
imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
matchName := opts.matchName
|
||||
|
||||
options := types.ImageListOptions{
|
||||
MatchName: matchName,
|
||||
All: opts.all,
|
||||
Filters: imageFilterArgs,
|
||||
}
|
||||
|
||||
images, err := dockerCli.Client().ImageList(ctx, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f := opts.format
|
||||
if len(f) == 0 {
|
||||
if len(dockerCli.ImagesFormat()) > 0 && !opts.quiet {
|
||||
f = dockerCli.ImagesFormat()
|
||||
} else {
|
||||
f = "table"
|
||||
}
|
||||
}
|
||||
|
||||
imagesCtx := formatter.ImageContext{
|
||||
Context: formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: f,
|
||||
Quiet: opts.quiet,
|
||||
Trunc: !opts.noTrunc,
|
||||
},
|
||||
Digest: opts.showDigests,
|
||||
Images: images,
|
||||
}
|
||||
|
||||
imagesCtx.Write()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/urlutil"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type importOptions struct {
|
||||
source string
|
||||
reference string
|
||||
changes []string
|
||||
message string
|
||||
}
|
||||
|
||||
// NewImportCommand creates a new `docker import` command
|
||||
func NewImportCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts importOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]",
|
||||
Short: "Import the contents from a tarball to create a filesystem image",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.source = args[0]
|
||||
if len(args) > 1 {
|
||||
opts.reference = args[1]
|
||||
}
|
||||
return runImport(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.StringSliceVarP(&opts.changes, "change", "c", []string{}, "Apply Dockerfile instruction to the created image")
|
||||
flags.StringVarP(&opts.message, "message", "m", "", "Set commit message for imported image")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runImport(dockerCli *client.DockerCli, opts importOptions) error {
|
||||
var (
|
||||
in io.Reader
|
||||
srcName = opts.source
|
||||
)
|
||||
|
||||
if opts.source == "-" {
|
||||
in = dockerCli.In()
|
||||
} else if !urlutil.IsURL(opts.source) {
|
||||
srcName = "-"
|
||||
file, err := os.Open(opts.source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
in = file
|
||||
}
|
||||
|
||||
source := types.ImageImportSource{
|
||||
Source: in,
|
||||
SourceName: srcName,
|
||||
}
|
||||
|
||||
options := types.ImageImportOptions{
|
||||
Message: opts.message,
|
||||
Changes: opts.changes,
|
||||
}
|
||||
|
||||
clnt := dockerCli.Client()
|
||||
|
||||
responseBody, err := clnt.ImageImport(context.Background(), source, opts.reference, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
return jsonmessage.DisplayJSONMessagesStream(responseBody, dockerCli.Out(), dockerCli.OutFd(), dockerCli.IsTerminalOut(), nil)
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type loadOptions struct {
|
||||
input string
|
||||
quiet bool
|
||||
}
|
||||
|
||||
// NewLoadCommand creates a new `docker load` command
|
||||
func NewLoadCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts loadOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "load [OPTIONS]",
|
||||
Short: "Load an image from a tar archive or STDIN",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runLoad(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.StringVarP(&opts.input, "input", "i", "", "Read from tar archive file, instead of STDIN")
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress the load output")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runLoad(dockerCli *client.DockerCli, opts loadOptions) error {
|
||||
|
||||
var input io.Reader = dockerCli.In()
|
||||
if opts.input != "" {
|
||||
file, err := os.Open(opts.input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
input = file
|
||||
}
|
||||
if !dockerCli.IsTerminalOut() {
|
||||
opts.quiet = true
|
||||
}
|
||||
response, err := dockerCli.Client().ImageLoad(context.Background(), input, opts.quiet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.Body != nil && response.JSON {
|
||||
return jsonmessage.DisplayJSONMessagesStream(response.Body, dockerCli.Out(), dockerCli.OutFd(), dockerCli.IsTerminalOut(), nil)
|
||||
}
|
||||
|
||||
_, err = io.Copy(dockerCli.Out(), response.Body)
|
||||
return err
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type pullOptions struct {
|
||||
remote string
|
||||
all bool
|
||||
}
|
||||
|
||||
// NewPullCommand creates a new `docker pull` command
|
||||
func NewPullCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts pullOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "pull [OPTIONS] NAME[:TAG|@DIGEST]",
|
||||
Short: "Pull an image or a repository from a registry",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.remote = args[0]
|
||||
return runPull(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.BoolVarP(&opts.all, "all-tags", "a", false, "Download all tagged images in the repository")
|
||||
client.AddTrustedFlags(flags, true)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPull(dockerCli *client.DockerCli, opts pullOptions) error {
|
||||
distributionRef, err := reference.ParseNamed(opts.remote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.all && !reference.IsNameOnly(distributionRef) {
|
||||
return errors.New("tag can't be used with --all-tags/-a")
|
||||
}
|
||||
|
||||
if !opts.all && reference.IsNameOnly(distributionRef) {
|
||||
distributionRef = reference.WithDefaultTag(distributionRef)
|
||||
fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", reference.DefaultTag)
|
||||
}
|
||||
|
||||
var tag string
|
||||
switch x := distributionRef.(type) {
|
||||
case reference.Canonical:
|
||||
tag = x.Digest().String()
|
||||
case reference.NamedTagged:
|
||||
tag = x.Tag()
|
||||
}
|
||||
|
||||
registryRef := registry.ParseReference(tag)
|
||||
|
||||
// Resolve the Repository name from fqn to RepositoryInfo
|
||||
repoInfo, err := registry.ParseRepositoryInfo(distributionRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
authConfig := dockerCli.ResolveAuthConfig(ctx, repoInfo.Index)
|
||||
requestPrivilege := dockerCli.RegistryAuthenticationPrivilegedFunc(repoInfo.Index, "pull")
|
||||
|
||||
if client.IsTrusted() && !registryRef.HasDigest() {
|
||||
// Check if tag is digest
|
||||
err = dockerCli.TrustedPull(ctx, repoInfo, registryRef, authConfig, requestPrivilege)
|
||||
} else {
|
||||
err = dockerCli.ImagePullPrivileged(ctx, authConfig, distributionRef.String(), requestPrivilege, opts.all)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewPushCommand creates a new `docker push` command
|
||||
func NewPushCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
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 {
|
||||
return runPush(dockerCli, args[0])
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
client.AddTrustedFlags(flags, true)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPush(dockerCli *client.DockerCli, remote string) error {
|
||||
ref, err := reference.ParseNamed(remote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Resolve the Repository name from fqn to RepositoryInfo
|
||||
repoInfo, err := registry.ParseRepositoryInfo(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Resolve the Auth config relevant for this server
|
||||
authConfig := dockerCli.ResolveAuthConfig(ctx, repoInfo.Index)
|
||||
requestPrivilege := dockerCli.RegistryAuthenticationPrivilegedFunc(repoInfo.Index, "push")
|
||||
|
||||
if client.IsTrusted() {
|
||||
return dockerCli.TrustedPush(ctx, repoInfo, ref, authConfig, requestPrivilege)
|
||||
}
|
||||
|
||||
responseBody, err := dockerCli.ImagePushPrivileged(ctx, authConfig, ref.String(), requestPrivilege)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer responseBody.Close()
|
||||
|
||||
return jsonmessage.DisplayJSONMessagesStream(responseBody, dockerCli.Out(), dockerCli.OutFd(), dockerCli.IsTerminalOut(), nil)
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type removeOptions struct {
|
||||
force bool
|
||||
noPrune bool
|
||||
}
|
||||
|
||||
// NewRemoveCommand create a new `docker remove` command
|
||||
func NewRemoveCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts removeOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rmi [OPTIONS] IMAGE [IMAGE...]",
|
||||
Short: "Remove one or more images",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runRemove(dockerCli, opts, args)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.BoolVarP(&opts.force, "force", "f", false, "Force removal of the image")
|
||||
flags.BoolVar(&opts.noPrune, "no-prune", false, "Do not delete untagged parents")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRemove(dockerCli *client.DockerCli, opts removeOptions, images []string) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
options := types.ImageRemoveOptions{
|
||||
Force: opts.force,
|
||||
PruneChildren: !opts.noPrune,
|
||||
}
|
||||
|
||||
var errs []string
|
||||
for _, image := range images {
|
||||
dels, err := client.ImageRemove(ctx, image, options)
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
} else {
|
||||
for _, del := range dels {
|
||||
if del.Deleted != "" {
|
||||
fmt.Fprintf(dockerCli.Out(), "Deleted: %s\n", del.Deleted)
|
||||
} else {
|
||||
fmt.Fprintf(dockerCli.Out(), "Untagged: %s\n", del.Untagged)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type saveOptions struct {
|
||||
images []string
|
||||
output string
|
||||
}
|
||||
|
||||
// NewSaveCommand creates a new `docker save` command
|
||||
func NewSaveCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts saveOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "save [OPTIONS] IMAGE [IMAGE...]",
|
||||
Short: "Save one or more images to a tar archive (streamed to STDOUT by default)",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.images = args
|
||||
return runSave(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.StringVarP(&opts.output, "output", "o", "", "Write to a file, instead of STDOUT")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runSave(dockerCli *client.DockerCli, opts saveOptions) error {
|
||||
if opts.output == "" && dockerCli.IsTerminalOut() {
|
||||
return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
|
||||
}
|
||||
|
||||
responseBody, err := dockerCli.Client().ImageSave(context.Background(), opts.images)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
if opts.output == "" {
|
||||
_, err := io.Copy(dockerCli.Out(), responseBody)
|
||||
return err
|
||||
}
|
||||
|
||||
return client.CopyToFile(opts.output, responseBody)
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
registrytypes "github.com/docker/engine-api/types/registry"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type searchOptions struct {
|
||||
term string
|
||||
noTrunc bool
|
||||
limit int
|
||||
filter []string
|
||||
|
||||
// Deprecated
|
||||
stars uint
|
||||
automated bool
|
||||
}
|
||||
|
||||
// NewSearchCommand create a new `docker search` command
|
||||
func NewSearchCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts searchOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "search [OPTIONS] TERM",
|
||||
Short: "Search the Docker Hub for images",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.term = args[0]
|
||||
return runSearch(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
flags.StringSliceVarP(&opts.filter, "filter", "f", []string{}, "Filter output based on conditions provided")
|
||||
flags.IntVar(&opts.limit, "limit", registry.DefaultSearchLimit, "Max number of search results")
|
||||
|
||||
flags.BoolVar(&opts.automated, "automated", false, "Only show automated builds")
|
||||
flags.UintVarP(&opts.stars, "stars", "s", 0, "Only displays with at least x stars")
|
||||
|
||||
flags.MarkDeprecated("automated", "use --filter=automated=true instead")
|
||||
flags.MarkDeprecated("stars", "use --filter=stars=3 instead")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runSearch(dockerCli *client.DockerCli, opts searchOptions) error {
|
||||
indexInfo, err := registry.ParseSearchIndexInfo(opts.term)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
authConfig := dockerCli.ResolveAuthConfig(ctx, indexInfo)
|
||||
requestPrivilege := dockerCli.RegistryAuthenticationPrivilegedFunc(indexInfo, "search")
|
||||
|
||||
encodedAuth, err := client.EncodeAuthToBase64(authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
searchFilters := filters.NewArgs()
|
||||
for _, f := range opts.filter {
|
||||
var err error
|
||||
searchFilters, err = filters.ParseFlag(f, searchFilters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
options := types.ImageSearchOptions{
|
||||
RegistryAuth: encodedAuth,
|
||||
PrivilegeFunc: requestPrivilege,
|
||||
Filters: searchFilters,
|
||||
Limit: opts.limit,
|
||||
}
|
||||
|
||||
clnt := dockerCli.Client()
|
||||
|
||||
unorderedResults, err := clnt.ImageSearch(ctx, opts.term, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
results := searchResultsByStars(unorderedResults)
|
||||
sort.Sort(results)
|
||||
|
||||
w := tabwriter.NewWriter(dockerCli.Out(), 10, 1, 3, ' ', 0)
|
||||
fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n")
|
||||
for _, res := range results {
|
||||
// --automated and -s, --stars are deprecated since Docker 1.12
|
||||
if (opts.automated && !res.IsAutomated) || (int(opts.stars) > res.StarCount) {
|
||||
continue
|
||||
}
|
||||
desc := strings.Replace(res.Description, "\n", " ", -1)
|
||||
desc = strings.Replace(desc, "\r", " ", -1)
|
||||
if !opts.noTrunc && len(desc) > 45 {
|
||||
desc = stringutils.Truncate(desc, 42) + "..."
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\t%d\t", res.Name, desc, res.StarCount)
|
||||
if res.IsOfficial {
|
||||
fmt.Fprint(w, "[OK]")
|
||||
|
||||
}
|
||||
fmt.Fprint(w, "\t")
|
||||
if res.IsAutomated {
|
||||
fmt.Fprint(w, "[OK]")
|
||||
}
|
||||
fmt.Fprint(w, "\n")
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
// SearchResultsByStars sorts search results in descending order by number of stars.
|
||||
type searchResultsByStars []registrytypes.SearchResult
|
||||
|
||||
func (r searchResultsByStars) Len() int { return len(r) }
|
||||
func (r searchResultsByStars) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||
func (r searchResultsByStars) Less(i, j int) bool { return r[j].StarCount < r[i].StarCount }
|
||||
@@ -1,41 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type tagOptions struct {
|
||||
image string
|
||||
name string
|
||||
}
|
||||
|
||||
// NewTagCommand create a new `docker tag` command
|
||||
func NewTagCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts tagOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "tag IMAGE[:TAG] IMAGE[:TAG]",
|
||||
Short: "Tag an image into a repository",
|
||||
Args: cli.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.image = args[0]
|
||||
opts.name = args[1]
|
||||
return runTag(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runTag(dockerCli *client.DockerCli, opts tagOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
return dockerCli.Client().ImageTag(ctx, opts.image, opts.name)
|
||||
}
|
||||
81
api/client/images.go
Normal file
81
api/client/images.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client/formatter"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
)
|
||||
|
||||
// CmdImages lists the images in a specified repository, or all top-level images if no repository is specified.
|
||||
//
|
||||
// Usage: docker images [OPTIONS] [REPOSITORY]
|
||||
func (cli *DockerCli) CmdImages(args ...string) error {
|
||||
cmd := Cli.Subcmd("images", []string{"[REPOSITORY[:TAG]]"}, Cli.DockerCommands["images"].Description, true)
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
|
||||
all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)")
|
||||
noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Don't truncate output")
|
||||
showDigests := cmd.Bool([]string{"-digests"}, false, "Show digests")
|
||||
format := cmd.String([]string{"-format"}, "", "Pretty-print images using a Go template")
|
||||
|
||||
flFilter := opts.NewListOpts(nil)
|
||||
cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
|
||||
cmd.Require(flag.Max, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
// Consolidate all filter flags, and sanity check them early.
|
||||
// They'll get process in the daemon/server.
|
||||
imageFilterArgs := filters.NewArgs()
|
||||
for _, f := range flFilter.GetAll() {
|
||||
var err error
|
||||
imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var matchName string
|
||||
if cmd.NArg() == 1 {
|
||||
matchName = cmd.Arg(0)
|
||||
}
|
||||
|
||||
options := types.ImageListOptions{
|
||||
MatchName: matchName,
|
||||
All: *all,
|
||||
Filters: imageFilterArgs,
|
||||
}
|
||||
|
||||
images, err := cli.client.ImageList(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f := *format
|
||||
if len(f) == 0 {
|
||||
if len(cli.ImagesFormat()) > 0 && !*quiet {
|
||||
f = cli.ImagesFormat()
|
||||
} else {
|
||||
f = "table"
|
||||
}
|
||||
}
|
||||
|
||||
imagesCtx := formatter.ImageContext{
|
||||
Context: formatter.Context{
|
||||
Output: cli.out,
|
||||
Format: f,
|
||||
Quiet: *quiet,
|
||||
Trunc: !*noTrunc,
|
||||
},
|
||||
Digest: *showDigests,
|
||||
Images: images,
|
||||
}
|
||||
|
||||
imagesCtx.Write()
|
||||
|
||||
return nil
|
||||
}
|
||||
82
api/client/import.go
Normal file
82
api/client/import.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/urlutil"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// CmdImport creates an empty filesystem image, imports the contents of the tarball into the image, and optionally tags the image.
|
||||
//
|
||||
// The URL argument is the address of a tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) file or a path to local file relative to docker client. If the URL is '-', then the tar file is read from STDIN.
|
||||
//
|
||||
// Usage: docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
|
||||
func (cli *DockerCli) CmdImport(args ...string) error {
|
||||
cmd := Cli.Subcmd("import", []string{"file|URL|- [REPOSITORY[:TAG]]"}, Cli.DockerCommands["import"].Description, true)
|
||||
flChanges := opts.NewListOpts(nil)
|
||||
cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
|
||||
message := cmd.String([]string{"m", "-message"}, "", "Set commit message for imported image")
|
||||
cmd.Require(flag.Min, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var (
|
||||
in io.Reader
|
||||
tag string
|
||||
src = cmd.Arg(0)
|
||||
srcName = src
|
||||
repository = cmd.Arg(1)
|
||||
changes = flChanges.GetAll()
|
||||
)
|
||||
|
||||
if cmd.NArg() == 3 {
|
||||
fmt.Fprintf(cli.err, "[DEPRECATED] The format 'file|URL|- [REPOSITORY [TAG]]' has been deprecated. Please use file|URL|- [REPOSITORY[:TAG]]\n")
|
||||
tag = cmd.Arg(2)
|
||||
}
|
||||
|
||||
if repository != "" {
|
||||
//Check if the given image name can be resolved
|
||||
if _, err := reference.ParseNamed(repository); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if src == "-" {
|
||||
in = cli.in
|
||||
} else if !urlutil.IsURL(src) {
|
||||
srcName = "-"
|
||||
file, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
in = file
|
||||
}
|
||||
|
||||
options := types.ImageImportOptions{
|
||||
Source: in,
|
||||
SourceName: srcName,
|
||||
RepositoryName: repository,
|
||||
Message: *message,
|
||||
Tag: tag,
|
||||
Changes: changes,
|
||||
}
|
||||
|
||||
responseBody, err := cli.client.ImageImport(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
return jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, nil)
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package client
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
@@ -11,7 +10,6 @@ import (
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/engine-api/types/swarm"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
@@ -24,8 +22,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
ctx := context.Background()
|
||||
info, err := cli.client.Info(ctx)
|
||||
info, err := cli.client.Info(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -43,7 +40,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. Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.")
|
||||
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.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,10 +50,11 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
fmt.Fprintf(cli.out, "%s: %s\n", pair[0], pair[1])
|
||||
}
|
||||
}
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Execution Driver: %s\n", info.ExecutionDriver)
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Logging Driver: %s\n", info.LoggingDriver)
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Cgroup Driver: %s\n", info.CgroupDriver)
|
||||
|
||||
fmt.Fprintf(cli.out, "Plugins:\n")
|
||||
fmt.Fprintf(cli.out, "Plugins: \n")
|
||||
fmt.Fprintf(cli.out, " Volume:")
|
||||
fmt.Fprintf(cli.out, " %s", strings.Join(info.Plugins.Volume, " "))
|
||||
fmt.Fprintf(cli.out, "\n")
|
||||
@@ -70,50 +68,6 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
fmt.Fprintf(cli.out, "\n")
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.out, "Swarm: %v\n", info.Swarm.LocalNodeState)
|
||||
if info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive {
|
||||
fmt.Fprintf(cli.out, " NodeID: %s\n", info.Swarm.NodeID)
|
||||
if info.Swarm.Error != "" {
|
||||
fmt.Fprintf(cli.out, " Error: %v\n", info.Swarm.Error)
|
||||
}
|
||||
fmt.Fprintf(cli.out, " Is Manager: %v\n", info.Swarm.ControlAvailable)
|
||||
if info.Swarm.ControlAvailable {
|
||||
fmt.Fprintf(cli.out, " ClusterID: %s\n", info.Swarm.Cluster.ID)
|
||||
fmt.Fprintf(cli.out, " Managers: %d\n", info.Swarm.Managers)
|
||||
fmt.Fprintf(cli.out, " Nodes: %d\n", info.Swarm.Nodes)
|
||||
fmt.Fprintf(cli.out, " Orchestration:\n")
|
||||
fmt.Fprintf(cli.out, " Task History Retention Limit: %d\n", info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit)
|
||||
fmt.Fprintf(cli.out, " Raft:\n")
|
||||
fmt.Fprintf(cli.out, " Snapshot Interval: %d\n", info.Swarm.Cluster.Spec.Raft.SnapshotInterval)
|
||||
fmt.Fprintf(cli.out, " Heartbeat Tick: %d\n", info.Swarm.Cluster.Spec.Raft.HeartbeatTick)
|
||||
fmt.Fprintf(cli.out, " Election Tick: %d\n", info.Swarm.Cluster.Spec.Raft.ElectionTick)
|
||||
fmt.Fprintf(cli.out, " Dispatcher:\n")
|
||||
fmt.Fprintf(cli.out, " Heartbeat Period: %s\n", units.HumanDuration(time.Duration(info.Swarm.Cluster.Spec.Dispatcher.HeartbeatPeriod)))
|
||||
fmt.Fprintf(cli.out, " CA Configuration:\n")
|
||||
fmt.Fprintf(cli.out, " Expiry Duration: %s\n", units.HumanDuration(info.Swarm.Cluster.Spec.CAConfig.NodeCertExpiry))
|
||||
if len(info.Swarm.Cluster.Spec.CAConfig.ExternalCAs) > 0 {
|
||||
fmt.Fprintf(cli.out, " External CAs:\n")
|
||||
for _, entry := range info.Swarm.Cluster.Spec.CAConfig.ExternalCAs {
|
||||
fmt.Fprintf(cli.out, " %s: %s\n", entry.Protocol, entry.URL)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(cli.out, " Node Address: %s\n", info.Swarm.NodeAddr)
|
||||
}
|
||||
|
||||
if len(info.Runtimes) > 0 {
|
||||
fmt.Fprintf(cli.out, "Runtimes:")
|
||||
for name := range info.Runtimes {
|
||||
fmt.Fprintf(cli.out, " %s", name)
|
||||
}
|
||||
fmt.Fprint(cli.out, "\n")
|
||||
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)
|
||||
@@ -123,8 +77,8 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Name: %s\n", info.Name)
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "ID: %s\n", info.ID)
|
||||
fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", info.DockerRootDir)
|
||||
fmt.Fprintf(cli.out, "Debug Mode (client): %v\n", utils.IsDebugEnabled())
|
||||
fmt.Fprintf(cli.out, "Debug Mode (server): %v\n", info.Debug)
|
||||
fmt.Fprintf(cli.out, "Debug mode (client): %v\n", utils.IsDebugEnabled())
|
||||
fmt.Fprintf(cli.out, "Debug mode (server): %v\n", info.Debug)
|
||||
|
||||
if info.Debug {
|
||||
fmt.Fprintf(cli.out, " File Descriptors: %d\n", info.NFd)
|
||||
@@ -191,25 +145,11 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
|
||||
ioutils.FprintfIfTrue(cli.out, "Experimental: %v\n", info.ExperimentalBuild)
|
||||
if info.ClusterStore != "" {
|
||||
fmt.Fprintf(cli.out, "Cluster Store: %s\n", info.ClusterStore)
|
||||
fmt.Fprintf(cli.out, "Cluster store: %s\n", info.ClusterStore)
|
||||
}
|
||||
|
||||
if info.ClusterAdvertise != "" {
|
||||
fmt.Fprintf(cli.out, "Cluster Advertise: %s\n", info.ClusterAdvertise)
|
||||
}
|
||||
|
||||
if info.RegistryConfig != nil && (len(info.RegistryConfig.InsecureRegistryCIDRs) > 0 || len(info.RegistryConfig.IndexConfigs) > 0) {
|
||||
fmt.Fprintln(cli.out, "Insecure Registries:")
|
||||
for _, registry := range info.RegistryConfig.IndexConfigs {
|
||||
if registry.Secure == false {
|
||||
fmt.Fprintf(cli.out, " %s\n", registry.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, registry := range info.RegistryConfig.InsecureRegistryCIDRs {
|
||||
mask, _ := registry.Mask.Size()
|
||||
fmt.Fprintf(cli.out, " %s/%d\n", registry.IP.String(), mask)
|
||||
}
|
||||
fmt.Fprintf(cli.out, "Cluster advertise: %s\n", info.ClusterAdvertise)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,88 +8,120 @@ import (
|
||||
"github.com/docker/docker/api/client/inspect"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/utils/templates"
|
||||
"github.com/docker/engine-api/client"
|
||||
)
|
||||
|
||||
// CmdInspect displays low-level information on one or more containers, images or tasks.
|
||||
// CmdInspect displays low-level information on one or more containers or images.
|
||||
//
|
||||
// Usage: docker inspect [OPTIONS] CONTAINER|IMAGE|TASK [CONTAINER|IMAGE|TASK...]
|
||||
// Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]
|
||||
func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||
cmd := Cli.Subcmd("inspect", []string{"[OPTIONS] CONTAINER|IMAGE|TASK [CONTAINER|IMAGE|TASK...]"}, Cli.DockerCommands["inspect"].Description, true)
|
||||
cmd := Cli.Subcmd("inspect", []string{"CONTAINER|IMAGE [CONTAINER|IMAGE...]"}, Cli.DockerCommands["inspect"].Description, true)
|
||||
tmplStr := cmd.String([]string{"f", "-format"}, "", "Format the output using the given go template")
|
||||
inspectType := cmd.String([]string{"-type"}, "", "Return JSON for specified type, (e.g image, container or task)")
|
||||
inspectType := cmd.String([]string{"-type"}, "", "Return JSON for specified type, (e.g image or container)")
|
||||
size := cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes if the type is container")
|
||||
cmd.Require(flag.Min, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
if *inspectType != "" && *inspectType != "container" && *inspectType != "image" && *inspectType != "task" {
|
||||
if *inspectType != "" && *inspectType != "container" && *inspectType != "image" {
|
||||
return fmt.Errorf("%q is not a valid value for --type", *inspectType)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
var elementSearcher inspect.GetRefFunc
|
||||
var elementSearcher inspectSearcher
|
||||
switch *inspectType {
|
||||
case "container":
|
||||
elementSearcher = cli.inspectContainers(ctx, *size)
|
||||
elementSearcher = cli.inspectContainers(*size)
|
||||
case "image":
|
||||
elementSearcher = cli.inspectImages(ctx, *size)
|
||||
case "task":
|
||||
if *size {
|
||||
fmt.Fprintln(cli.err, "WARNING: --size ignored for tasks")
|
||||
}
|
||||
elementSearcher = cli.inspectTasks(ctx)
|
||||
elementSearcher = cli.inspectImages(*size)
|
||||
default:
|
||||
elementSearcher = cli.inspectAll(ctx, *size)
|
||||
elementSearcher = cli.inspectAll(*size)
|
||||
}
|
||||
|
||||
return inspect.Inspect(cli.out, cmd.Args(), *tmplStr, elementSearcher)
|
||||
return cli.inspectElements(*tmplStr, cmd.Args(), elementSearcher)
|
||||
}
|
||||
|
||||
func (cli *DockerCli) inspectContainers(ctx context.Context, getSize bool) inspect.GetRefFunc {
|
||||
func (cli *DockerCli) inspectContainers(getSize bool) inspectSearcher {
|
||||
return func(ref string) (interface{}, []byte, error) {
|
||||
return cli.client.ContainerInspectWithRaw(ctx, ref, getSize)
|
||||
return cli.client.ContainerInspectWithRaw(context.Background(), ref, getSize)
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) inspectImages(ctx context.Context, getSize bool) inspect.GetRefFunc {
|
||||
func (cli *DockerCli) inspectImages(getSize bool) inspectSearcher {
|
||||
return func(ref string) (interface{}, []byte, error) {
|
||||
return cli.client.ImageInspectWithRaw(ctx, ref, getSize)
|
||||
return cli.client.ImageInspectWithRaw(context.Background(), ref, getSize)
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) inspectTasks(ctx context.Context) inspect.GetRefFunc {
|
||||
func (cli *DockerCli) inspectAll(getSize bool) inspectSearcher {
|
||||
return func(ref string) (interface{}, []byte, error) {
|
||||
return cli.client.TaskInspectWithRaw(ctx, ref)
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) inspectAll(ctx context.Context, getSize bool) inspect.GetRefFunc {
|
||||
return func(ref string) (interface{}, []byte, error) {
|
||||
c, rawContainer, err := cli.client.ContainerInspectWithRaw(ctx, ref, getSize)
|
||||
c, rawContainer, err := cli.client.ContainerInspectWithRaw(context.Background(), ref, getSize)
|
||||
if err != nil {
|
||||
// Search for image with that id if a container doesn't exist.
|
||||
if client.IsErrContainerNotFound(err) {
|
||||
i, rawImage, err := cli.client.ImageInspectWithRaw(ctx, ref, getSize)
|
||||
i, rawImage, err := cli.client.ImageInspectWithRaw(context.Background(), ref, getSize)
|
||||
if err != nil {
|
||||
if client.IsErrImageNotFound(err) {
|
||||
// Search for task with that id if an image doesn't exists.
|
||||
t, rawTask, err := cli.client.TaskInspectWithRaw(ctx, ref)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error: No such image, container or task: %s", ref)
|
||||
}
|
||||
if getSize {
|
||||
fmt.Fprintln(cli.err, "WARNING: --size ignored for tasks")
|
||||
}
|
||||
return t, rawTask, nil
|
||||
return nil, nil, fmt.Errorf("Error: No such image or container: %s", ref)
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return i, rawImage, nil
|
||||
return i, rawImage, err
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return c, rawContainer, nil
|
||||
return c, rawContainer, err
|
||||
}
|
||||
}
|
||||
|
||||
type inspectSearcher func(ref string) (interface{}, []byte, error)
|
||||
|
||||
func (cli *DockerCli) inspectElements(tmplStr string, references []string, searchByReference inspectSearcher) error {
|
||||
elementInspector, err := cli.newInspectorWithTemplate(tmplStr)
|
||||
if err != nil {
|
||||
return Cli.StatusError{StatusCode: 64, Status: err.Error()}
|
||||
}
|
||||
|
||||
var inspectErr error
|
||||
for _, ref := range references {
|
||||
element, raw, err := searchByReference(ref)
|
||||
if err != nil {
|
||||
inspectErr = err
|
||||
break
|
||||
}
|
||||
|
||||
if err := elementInspector.Inspect(element, raw); err != nil {
|
||||
inspectErr = err
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := elementInspector.Flush(); err != nil {
|
||||
cli.inspectErrorStatus(err)
|
||||
}
|
||||
|
||||
if status := cli.inspectErrorStatus(inspectErr); status != 0 {
|
||||
return Cli.StatusError{StatusCode: status}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) inspectErrorStatus(err error) (status int) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
status = 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cli *DockerCli) newInspectorWithTemplate(tmplStr string) (inspect.Inspector, error) {
|
||||
elementInspector := inspect.NewIndentedInspector(cli.out)
|
||||
if tmplStr != "" {
|
||||
tmpl, err := templates.Parse(tmplStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Template parsing error: %s", err)
|
||||
}
|
||||
elementInspector = inspect.NewTemplateInspector(cli.out, tmpl)
|
||||
}
|
||||
return elementInspector, nil
|
||||
}
|
||||
|
||||
@@ -6,10 +6,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"text/template"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/utils/templates"
|
||||
)
|
||||
|
||||
// Inspector defines an interface to implement to process elements
|
||||
@@ -34,56 +30,6 @@ func NewTemplateInspector(outputStream io.Writer, tmpl *template.Template) Inspe
|
||||
}
|
||||
}
|
||||
|
||||
// NewTemplateInspectorFromString creates a new TemplateInspector from a string
|
||||
// which is compiled into a template.
|
||||
func NewTemplateInspectorFromString(out io.Writer, tmplStr string) (Inspector, error) {
|
||||
if tmplStr == "" {
|
||||
return NewIndentedInspector(out), nil
|
||||
}
|
||||
|
||||
tmpl, err := templates.Parse(tmplStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Template parsing error: %s", err)
|
||||
}
|
||||
return NewTemplateInspector(out, tmpl), nil
|
||||
}
|
||||
|
||||
// GetRefFunc is a function which used by Inspect to fetch an object from a
|
||||
// reference
|
||||
type GetRefFunc func(ref string) (interface{}, []byte, error)
|
||||
|
||||
// Inspect fetches objects by reference using GetRefFunc and writes the json
|
||||
// representation to the output writer.
|
||||
func Inspect(out io.Writer, references []string, tmplStr string, getRef GetRefFunc) error {
|
||||
inspector, err := NewTemplateInspectorFromString(out, tmplStr)
|
||||
if err != nil {
|
||||
return cli.StatusError{StatusCode: 64, Status: err.Error()}
|
||||
}
|
||||
|
||||
var inspectErr error
|
||||
for _, ref := range references {
|
||||
element, raw, err := getRef(ref)
|
||||
if err != nil {
|
||||
inspectErr = err
|
||||
break
|
||||
}
|
||||
|
||||
if err := inspector.Inspect(element, raw); err != nil {
|
||||
inspectErr = err
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := inspector.Flush(); err != nil {
|
||||
logrus.Errorf("%s\n", err)
|
||||
}
|
||||
|
||||
if inspectErr != nil {
|
||||
return cli.StatusError{StatusCode: 1, Status: inspectErr.Error()}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Inspect executes the inspect template.
|
||||
// It decodes the raw element into a map if the initial execution fails.
|
||||
// This allows docker cli to parse inspect structs injected with Swarm fields.
|
||||
@@ -93,35 +39,13 @@ func (i *TemplateInspector) Inspect(typedElement interface{}, rawElement []byte)
|
||||
if rawElement == nil {
|
||||
return fmt.Errorf("Template parsing error: %v", err)
|
||||
}
|
||||
return i.tryRawInspectFallback(rawElement)
|
||||
return i.tryRawInspectFallback(rawElement, err)
|
||||
}
|
||||
i.buffer.Write(buffer.Bytes())
|
||||
i.buffer.WriteByte('\n')
|
||||
return nil
|
||||
}
|
||||
|
||||
// tryRawInspectFallback executes the inspect template with a raw interface.
|
||||
// This allows docker cli to parse inspect structs injected with Swarm fields.
|
||||
func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte) error {
|
||||
var raw interface{}
|
||||
buffer := new(bytes.Buffer)
|
||||
rdr := bytes.NewReader(rawElement)
|
||||
dec := json.NewDecoder(rdr)
|
||||
|
||||
if rawErr := dec.Decode(&raw); rawErr != nil {
|
||||
return fmt.Errorf("unable to read inspect data: %v", rawErr)
|
||||
}
|
||||
|
||||
tmplMissingKey := i.tmpl.Option("missingkey=error")
|
||||
if rawErr := tmplMissingKey.Execute(buffer, raw); rawErr != nil {
|
||||
return fmt.Errorf("Template parsing error: %v", rawErr)
|
||||
}
|
||||
|
||||
i.buffer.Write(buffer.Bytes())
|
||||
i.buffer.WriteByte('\n')
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush write the result of inspecting all elements into the output stream.
|
||||
func (i *TemplateInspector) Flush() error {
|
||||
if i.buffer.Len() == 0 {
|
||||
|
||||
40
api/client/inspect/inspector_go14.go
Normal file
40
api/client/inspect/inspector_go14.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// +build !go1.5
|
||||
|
||||
package inspect
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// tryeRawInspectFallback executes the inspect template with a raw interface.
|
||||
// This allows docker cli to parse inspect structs injected with Swarm fields.
|
||||
// Unfortunately, go 1.4 doesn't fail executing invalid templates when the input is an interface.
|
||||
// It doesn't allow to modify this behavior either, sending <no value> messages to the output.
|
||||
// We assume that the template is invalid when there is a <no value>, if the template was valid
|
||||
// we'd get <nil> or "" values. In that case we fail with the original error raised executing the
|
||||
// template with the typed input.
|
||||
func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte, originalErr error) error {
|
||||
var raw interface{}
|
||||
buffer := new(bytes.Buffer)
|
||||
rdr := bytes.NewReader(rawElement)
|
||||
dec := json.NewDecoder(rdr)
|
||||
|
||||
if rawErr := dec.Decode(&raw); rawErr != nil {
|
||||
return fmt.Errorf("unable to read inspect data: %v", rawErr)
|
||||
}
|
||||
|
||||
if rawErr := i.tmpl.Execute(buffer, raw); rawErr != nil {
|
||||
return fmt.Errorf("Template parsing error: %v", rawErr)
|
||||
}
|
||||
|
||||
if strings.Contains(buffer.String(), "<no value>") {
|
||||
return fmt.Errorf("Template parsing error: %v", originalErr)
|
||||
}
|
||||
|
||||
i.buffer.Write(buffer.Bytes())
|
||||
i.buffer.WriteByte('\n')
|
||||
return nil
|
||||
}
|
||||
29
api/client/inspect/inspector_go15.go
Normal file
29
api/client/inspect/inspector_go15.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// +build go1.5
|
||||
|
||||
package inspect
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte, _ error) error {
|
||||
var raw interface{}
|
||||
buffer := new(bytes.Buffer)
|
||||
rdr := bytes.NewReader(rawElement)
|
||||
dec := json.NewDecoder(rdr)
|
||||
|
||||
if rawErr := dec.Decode(&raw); rawErr != nil {
|
||||
return fmt.Errorf("unable to read inspect data: %v", rawErr)
|
||||
}
|
||||
|
||||
tmplMissingKey := i.tmpl.Option("missingkey=error")
|
||||
if rawErr := tmplMissingKey.Execute(buffer, raw); rawErr != nil {
|
||||
return fmt.Errorf("Template parsing error: %v", rawErr)
|
||||
}
|
||||
|
||||
i.buffer.Write(buffer.Bytes())
|
||||
i.buffer.WriteByte('\n')
|
||||
return nil
|
||||
}
|
||||
35
api/client/kill.go
Normal file
35
api/client/kill.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
|
||||
// CmdKill kills one or more running container using SIGKILL or a specified signal.
|
||||
//
|
||||
// Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...]
|
||||
func (cli *DockerCli) CmdKill(args ...string) error {
|
||||
cmd := Cli.Subcmd("kill", []string{"CONTAINER [CONTAINER...]"}, Cli.DockerCommands["kill"].Description, true)
|
||||
signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
|
||||
cmd.Require(flag.Min, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var errs []string
|
||||
for _, name := range cmd.Args() {
|
||||
if err := cli.client.ContainerKill(context.Background(), name, *signal); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
50
api/client/load.go
Normal file
50
api/client/load.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
|
||||
// CmdLoad loads an image from a tar archive.
|
||||
//
|
||||
// The tar archive is read from STDIN by default, or from a tar archive file.
|
||||
//
|
||||
// Usage: docker load [OPTIONS]
|
||||
func (cli *DockerCli) CmdLoad(args ...string) error {
|
||||
cmd := Cli.Subcmd("load", nil, Cli.DockerCommands["load"].Description, true)
|
||||
infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the load output")
|
||||
cmd.Require(flag.Exact, 0)
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var input io.Reader = cli.in
|
||||
if *infile != "" {
|
||||
file, err := os.Open(*infile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
input = file
|
||||
}
|
||||
if !cli.isTerminalOut {
|
||||
*quiet = true
|
||||
}
|
||||
response, err := cli.client.ImageLoad(context.Background(), input, *quiet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.Body != nil && response.JSON {
|
||||
return jsonmessage.DisplayJSONMessagesStream(response.Body, cli.out, cli.outFd, cli.isTerminalOut, nil)
|
||||
}
|
||||
|
||||
_, err = io.Copy(cli.out, response.Body)
|
||||
return err
|
||||
}
|
||||
177
api/client/login.go
Normal file
177
api/client/login.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/cliconfig/credentials"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// CmdLogin logs in a user to a Docker registry service.
|
||||
//
|
||||
// If no server is specified, the user will be logged into or registered to the registry's index server.
|
||||
//
|
||||
// Usage: docker login SERVER
|
||||
func (cli *DockerCli) CmdLogin(args ...string) error {
|
||||
cmd := Cli.Subcmd("login", []string{"[SERVER]"}, Cli.DockerCommands["login"].Description+".\nIf no server is specified, the default is defined by the daemon.", true)
|
||||
cmd.Require(flag.Max, 1)
|
||||
|
||||
flUser := cmd.String([]string{"u", "-username"}, "", "Username")
|
||||
flPassword := cmd.String([]string{"p", "-password"}, "", "Password")
|
||||
|
||||
// Deprecated in 1.11: Should be removed in docker 1.13
|
||||
cmd.String([]string{"#e", "#-email"}, "", "Email")
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
// On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
|
||||
if runtime.GOOS == "windows" {
|
||||
cli.in = os.Stdin
|
||||
}
|
||||
|
||||
var serverAddress string
|
||||
var isDefaultRegistry bool
|
||||
if len(cmd.Args()) > 0 {
|
||||
serverAddress = cmd.Arg(0)
|
||||
} else {
|
||||
serverAddress = cli.electAuthServer()
|
||||
isDefaultRegistry = true
|
||||
}
|
||||
|
||||
authConfig, err := cli.configureAuth(*flUser, *flPassword, serverAddress, isDefaultRegistry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response, err := cli.client.RegistryLogin(context.Background(), authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response.IdentityToken != "" {
|
||||
authConfig.Password = ""
|
||||
authConfig.IdentityToken = response.IdentityToken
|
||||
}
|
||||
if err := storeCredentials(cli.configFile, authConfig); err != nil {
|
||||
return fmt.Errorf("Error saving credentials: %v", err)
|
||||
}
|
||||
|
||||
if response.Status != "" {
|
||||
fmt.Fprintln(cli.out, response.Status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) promptWithDefault(prompt string, configDefault string) {
|
||||
if configDefault == "" {
|
||||
fmt.Fprintf(cli.out, "%s: ", prompt)
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) configureAuth(flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) {
|
||||
authconfig, err := getCredentials(cli.configFile, serverAddress)
|
||||
if err != nil {
|
||||
return authconfig, err
|
||||
}
|
||||
|
||||
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.
|
||||
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)
|
||||
flUser = readInput(cli.in, cli.out)
|
||||
flUser = strings.TrimSpace(flUser)
|
||||
if flUser == "" {
|
||||
flUser = authconfig.Username
|
||||
}
|
||||
}
|
||||
|
||||
if flUser == "" {
|
||||
return authconfig, fmt.Errorf("Error: Non-null Username Required")
|
||||
}
|
||||
|
||||
if flPassword == "" {
|
||||
oldState, err := term.SaveState(cli.inFd)
|
||||
if err != nil {
|
||||
return authconfig, err
|
||||
}
|
||||
fmt.Fprintf(cli.out, "Password: ")
|
||||
term.DisableEcho(cli.inFd, oldState)
|
||||
|
||||
flPassword = readInput(cli.in, cli.out)
|
||||
fmt.Fprint(cli.out, "\n")
|
||||
|
||||
term.RestoreTerminal(cli.inFd, oldState)
|
||||
if flPassword == "" {
|
||||
return authconfig, fmt.Errorf("Error: Password Required")
|
||||
}
|
||||
}
|
||||
|
||||
authconfig.Username = flUser
|
||||
authconfig.Password = flPassword
|
||||
authconfig.ServerAddress = serverAddress
|
||||
authconfig.IdentityToken = ""
|
||||
|
||||
return authconfig, nil
|
||||
}
|
||||
|
||||
func readInput(in io.Reader, out io.Writer) string {
|
||||
reader := bufio.NewReader(in)
|
||||
line, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
fmt.Fprintln(out, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
return string(line)
|
||||
}
|
||||
|
||||
// getCredentials loads the user credentials from a credentials store.
|
||||
// The store is determined by the config file settings.
|
||||
func getCredentials(c *cliconfig.ConfigFile, serverAddress string) (types.AuthConfig, error) {
|
||||
s := loadCredentialsStore(c)
|
||||
return s.Get(serverAddress)
|
||||
}
|
||||
|
||||
func getAllCredentials(c *cliconfig.ConfigFile) (map[string]types.AuthConfig, error) {
|
||||
s := loadCredentialsStore(c)
|
||||
return s.GetAll()
|
||||
}
|
||||
|
||||
// storeCredentials saves the user credentials in a credentials store.
|
||||
// The store is determined by the config file settings.
|
||||
func storeCredentials(c *cliconfig.ConfigFile, auth types.AuthConfig) error {
|
||||
s := loadCredentialsStore(c)
|
||||
return s.Store(auth)
|
||||
}
|
||||
|
||||
// eraseCredentials removes the user credentials from a credentials store.
|
||||
// The store is determined by the config file settings.
|
||||
func eraseCredentials(c *cliconfig.ConfigFile, serverAddress string) error {
|
||||
s := loadCredentialsStore(c)
|
||||
return s.Erase(serverAddress)
|
||||
}
|
||||
|
||||
// loadCredentialsStore initializes a new credentials store based
|
||||
// in the settings provided in the configuration file.
|
||||
func loadCredentialsStore(c *cliconfig.ConfigFile) credentials.Store {
|
||||
if c.CredentialsStore != "" {
|
||||
return credentials.NewNativeStore(c)
|
||||
}
|
||||
return credentials.NewFileStore(c)
|
||||
}
|
||||
41
api/client/logout.go
Normal file
41
api/client/logout.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
|
||||
// CmdLogout logs a user out from a Docker registry.
|
||||
//
|
||||
// If no server is specified, the user will be logged out from the registry's index server.
|
||||
//
|
||||
// Usage: docker logout [SERVER]
|
||||
func (cli *DockerCli) CmdLogout(args ...string) error {
|
||||
cmd := Cli.Subcmd("logout", []string{"[SERVER]"}, Cli.DockerCommands["logout"].Description+".\nIf no server is specified, the default is defined by the daemon.", true)
|
||||
cmd.Require(flag.Max, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var serverAddress string
|
||||
if len(cmd.Args()) > 0 {
|
||||
serverAddress = cmd.Arg(0)
|
||||
} else {
|
||||
serverAddress = cli.electAuthServer()
|
||||
}
|
||||
|
||||
// check if we're logged in based on the records in the config file
|
||||
// which means it couldn't have user/pass cause they may be in the creds store
|
||||
if _, ok := cli.configFile.AuthConfigs[serverAddress]; !ok {
|
||||
fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress)
|
||||
if err := eraseCredentials(cli.configFile, serverAddress); err != nil {
|
||||
fmt.Fprintf(cli.out, "WARNING: could not erase credentials: %v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
65
api/client/logs.go
Normal file
65
api/client/logs.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
var validDrivers = map[string]bool{
|
||||
"json-file": true,
|
||||
"journald": true,
|
||||
}
|
||||
|
||||
// CmdLogs fetches the logs of a given container.
|
||||
//
|
||||
// docker logs [OPTIONS] CONTAINER
|
||||
func (cli *DockerCli) CmdLogs(args ...string) error {
|
||||
cmd := Cli.Subcmd("logs", []string{"CONTAINER"}, Cli.DockerCommands["logs"].Description, true)
|
||||
follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
|
||||
since := cmd.String([]string{"-since"}, "", "Show logs since timestamp")
|
||||
times := cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
|
||||
tail := cmd.String([]string{"-tail"}, "all", "Number of lines to show from the end of the logs")
|
||||
cmd.Require(flag.Exact, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
name := cmd.Arg(0)
|
||||
|
||||
c, err := cli.client.ContainerInspect(context.Background(), name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !validDrivers[c.HostConfig.LogConfig.Type] {
|
||||
return fmt.Errorf("\"logs\" command is supported only for \"json-file\" and \"journald\" logging drivers (got: %s)", c.HostConfig.LogConfig.Type)
|
||||
}
|
||||
|
||||
options := types.ContainerLogsOptions{
|
||||
ContainerID: name,
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Since: *since,
|
||||
Timestamps: *times,
|
||||
Follow: *follow,
|
||||
Tail: *tail,
|
||||
}
|
||||
responseBody, err := cli.client.ContainerLogs(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
if c.Config.Tty {
|
||||
_, err = io.Copy(cli.out, responseBody)
|
||||
} else {
|
||||
_, err = stdcopy.StdCopy(cli.out, cli.err, responseBody)
|
||||
}
|
||||
return err
|
||||
}
|
||||
392
api/client/network.go
Normal file
392
api/client/network.go
Normal file
@@ -0,0 +1,392 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
"github.com/docker/engine-api/types/network"
|
||||
)
|
||||
|
||||
// CmdNetwork is the parent subcommand for all network commands
|
||||
//
|
||||
// Usage: docker network <COMMAND> [OPTIONS]
|
||||
func (cli *DockerCli) CmdNetwork(args ...string) error {
|
||||
cmd := Cli.Subcmd("network", []string{"COMMAND [OPTIONS]"}, networkUsage(), false)
|
||||
cmd.Require(flag.Min, 1)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
cmd.Usage()
|
||||
return err
|
||||
}
|
||||
|
||||
// CmdNetworkCreate creates a new network with a given name
|
||||
//
|
||||
// Usage: docker network create [OPTIONS] <NETWORK-NAME>
|
||||
func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
|
||||
cmd := Cli.Subcmd("network create", []string{"NETWORK-NAME"}, "Creates a new network with a name specified by the user", false)
|
||||
flDriver := cmd.String([]string{"d", "-driver"}, "bridge", "Driver to manage the Network")
|
||||
flOpts := opts.NewMapOpts(nil, nil)
|
||||
|
||||
flIpamDriver := cmd.String([]string{"-ipam-driver"}, "default", "IP Address Management Driver")
|
||||
flIpamSubnet := opts.NewListOpts(nil)
|
||||
flIpamIPRange := opts.NewListOpts(nil)
|
||||
flIpamGateway := opts.NewListOpts(nil)
|
||||
flIpamAux := opts.NewMapOpts(nil, nil)
|
||||
flIpamOpt := opts.NewMapOpts(nil, nil)
|
||||
flLabels := opts.NewListOpts(nil)
|
||||
|
||||
cmd.Var(&flIpamSubnet, []string{"-subnet"}, "subnet in CIDR format that represents a network segment")
|
||||
cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range")
|
||||
cmd.Var(&flIpamGateway, []string{"-gateway"}, "ipv4 or ipv6 Gateway for the master subnet")
|
||||
cmd.Var(flIpamAux, []string{"-aux-address"}, "auxiliary ipv4 or ipv6 addresses used by Network driver")
|
||||
cmd.Var(flOpts, []string{"o", "-opt"}, "set driver specific options")
|
||||
cmd.Var(flIpamOpt, []string{"-ipam-opt"}, "set IPAM driver specific options")
|
||||
cmd.Var(&flLabels, []string{"-label"}, "set metadata on a network")
|
||||
|
||||
flInternal := cmd.Bool([]string{"-internal"}, false, "restricts external access to the network")
|
||||
flIPv6 := cmd.Bool([]string{"-ipv6"}, false, "enable IPv6 networking")
|
||||
|
||||
cmd.Require(flag.Exact, 1)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the default driver to "" if the user didn't set the value.
|
||||
// That way we can know whether it was user input or not.
|
||||
driver := *flDriver
|
||||
if !cmd.IsSet("-driver") && !cmd.IsSet("d") {
|
||||
driver = ""
|
||||
}
|
||||
|
||||
ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Construct network create request body
|
||||
nc := types.NetworkCreate{
|
||||
Name: cmd.Arg(0),
|
||||
Driver: driver,
|
||||
IPAM: network.IPAM{Driver: *flIpamDriver, Config: ipamCfg, Options: flIpamOpt.GetAll()},
|
||||
Options: flOpts.GetAll(),
|
||||
CheckDuplicate: true,
|
||||
Internal: *flInternal,
|
||||
EnableIPv6: *flIPv6,
|
||||
Labels: runconfigopts.ConvertKVStringsToMap(flLabels.GetAll()),
|
||||
}
|
||||
|
||||
resp, err := cli.client.NetworkCreate(context.Background(), nc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(cli.out, "%s\n", resp.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdNetworkRm deletes one or more networks
|
||||
//
|
||||
// Usage: docker network rm NETWORK-NAME|NETWORK-ID [NETWORK-NAME|NETWORK-ID...]
|
||||
func (cli *DockerCli) CmdNetworkRm(args ...string) error {
|
||||
cmd := Cli.Subcmd("network rm", []string{"NETWORK [NETWORK...]"}, "Deletes one or more networks", false)
|
||||
cmd.Require(flag.Min, 1)
|
||||
if err := cmd.ParseFlags(args, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
status := 0
|
||||
for _, net := range cmd.Args() {
|
||||
if err := cli.client.NetworkRemove(context.Background(), net); err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
if status != 0 {
|
||||
return Cli.StatusError{StatusCode: status}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdNetworkConnect connects a container to a network
|
||||
//
|
||||
// Usage: docker network connect [OPTIONS] <NETWORK> <CONTAINER>
|
||||
func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
|
||||
cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
|
||||
flIPAddress := cmd.String([]string{"-ip"}, "", "IP Address")
|
||||
flIPv6Address := cmd.String([]string{"-ip6"}, "", "IPv6 Address")
|
||||
flLinks := opts.NewListOpts(runconfigopts.ValidateLink)
|
||||
cmd.Var(&flLinks, []string{"-link"}, "Add link to another container")
|
||||
flAliases := opts.NewListOpts(nil)
|
||||
cmd.Var(&flAliases, []string{"-alias"}, "Add network-scoped alias for the container")
|
||||
cmd.Require(flag.Min, 2)
|
||||
if err := cmd.ParseFlags(args, true); err != nil {
|
||||
return err
|
||||
}
|
||||
epConfig := &network.EndpointSettings{
|
||||
IPAMConfig: &network.EndpointIPAMConfig{
|
||||
IPv4Address: *flIPAddress,
|
||||
IPv6Address: *flIPv6Address,
|
||||
},
|
||||
Links: flLinks.GetAll(),
|
||||
Aliases: flAliases.GetAll(),
|
||||
}
|
||||
return cli.client.NetworkConnect(context.Background(), cmd.Arg(0), cmd.Arg(1), epConfig)
|
||||
}
|
||||
|
||||
// CmdNetworkDisconnect disconnects a container from a network
|
||||
//
|
||||
// Usage: docker network disconnect <NETWORK> <CONTAINER>
|
||||
func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
|
||||
cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false)
|
||||
force := cmd.Bool([]string{"f", "-force"}, false, "Force the container to disconnect from a network")
|
||||
cmd.Require(flag.Exact, 2)
|
||||
if err := cmd.ParseFlags(args, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cli.client.NetworkDisconnect(context.Background(), cmd.Arg(0), cmd.Arg(1), *force)
|
||||
}
|
||||
|
||||
// CmdNetworkLs lists all the networks managed by docker daemon
|
||||
//
|
||||
// Usage: docker network ls [OPTIONS]
|
||||
func (cli *DockerCli) CmdNetworkLs(args ...string) error {
|
||||
cmd := Cli.Subcmd("network ls", nil, "Lists networks", true)
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
|
||||
noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")
|
||||
|
||||
flFilter := opts.NewListOpts(nil)
|
||||
cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
|
||||
|
||||
cmd.Require(flag.Exact, 0)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Consolidate all filter flags, and sanity check them early.
|
||||
// They'll get process after get response from server.
|
||||
netFilterArgs := filters.NewArgs()
|
||||
for _, f := range flFilter.GetAll() {
|
||||
if netFilterArgs, err = filters.ParseFlag(f, netFilterArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
options := types.NetworkListOptions{
|
||||
Filters: netFilterArgs,
|
||||
}
|
||||
|
||||
networkResources, err := cli.client.NetworkList(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||
|
||||
// unless quiet (-q) is specified, print field titles
|
||||
if !*quiet {
|
||||
fmt.Fprintln(wr, "NETWORK ID\tNAME\tDRIVER")
|
||||
}
|
||||
sort.Sort(byNetworkName(networkResources))
|
||||
for _, networkResource := range networkResources {
|
||||
ID := networkResource.ID
|
||||
netName := networkResource.Name
|
||||
if !*noTrunc {
|
||||
ID = stringid.TruncateID(ID)
|
||||
}
|
||||
if *quiet {
|
||||
fmt.Fprintln(wr, ID)
|
||||
continue
|
||||
}
|
||||
driver := networkResource.Driver
|
||||
fmt.Fprintf(wr, "%s\t%s\t%s\t",
|
||||
ID,
|
||||
netName,
|
||||
driver)
|
||||
fmt.Fprint(wr, "\n")
|
||||
}
|
||||
wr.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
type byNetworkName []types.NetworkResource
|
||||
|
||||
func (r byNetworkName) Len() int { return len(r) }
|
||||
func (r byNetworkName) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||
func (r byNetworkName) Less(i, j int) bool { return r[i].Name < r[j].Name }
|
||||
|
||||
// CmdNetworkInspect inspects the network object for more details
|
||||
//
|
||||
// Usage: docker network inspect [OPTIONS] <NETWORK> [NETWORK...]
|
||||
func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
|
||||
cmd := Cli.Subcmd("network inspect", []string{"NETWORK [NETWORK...]"}, "Displays detailed information on one or more networks", false)
|
||||
tmplStr := cmd.String([]string{"f", "-format"}, "", "Format the output using the given go template")
|
||||
cmd.Require(flag.Min, 1)
|
||||
|
||||
if err := cmd.ParseFlags(args, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inspectSearcher := func(name string) (interface{}, []byte, error) {
|
||||
i, err := cli.client.NetworkInspect(context.Background(), name)
|
||||
return i, nil, err
|
||||
}
|
||||
|
||||
return cli.inspectElements(*tmplStr, cmd.Args(), inspectSearcher)
|
||||
}
|
||||
|
||||
// Consolidates the ipam configuration as a group from different related configurations
|
||||
// user can configure network with multiple non-overlapping subnets and hence it is
|
||||
// possible to correlate the various related parameters and consolidate them.
|
||||
// consoidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into
|
||||
// structured ipam data.
|
||||
func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
|
||||
if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
|
||||
return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
|
||||
}
|
||||
iData := map[string]*network.IPAMConfig{}
|
||||
|
||||
// Populate non-overlapping subnets into consolidation map
|
||||
for _, s := range subnets {
|
||||
for k := range iData {
|
||||
ok1, err := subnetMatches(s, k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ok2, err := subnetMatches(k, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok1 || ok2 {
|
||||
return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
|
||||
}
|
||||
}
|
||||
iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
|
||||
}
|
||||
|
||||
// Validate and add valid ip ranges
|
||||
for _, r := range ranges {
|
||||
match := false
|
||||
for _, s := range subnets {
|
||||
ok, err := subnetMatches(s, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if iData[s].IPRange != "" {
|
||||
return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
|
||||
}
|
||||
d := iData[s]
|
||||
d.IPRange = r
|
||||
match = true
|
||||
}
|
||||
if !match {
|
||||
return nil, fmt.Errorf("no matching subnet for range %s", r)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate and add valid gateways
|
||||
for _, g := range gateways {
|
||||
match := false
|
||||
for _, s := range subnets {
|
||||
ok, err := subnetMatches(s, g)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if iData[s].Gateway != "" {
|
||||
return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
|
||||
}
|
||||
d := iData[s]
|
||||
d.Gateway = g
|
||||
match = true
|
||||
}
|
||||
if !match {
|
||||
return nil, fmt.Errorf("no matching subnet for gateway %s", g)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate and add aux-addresses
|
||||
for key, aa := range auxaddrs {
|
||||
match := false
|
||||
for _, s := range subnets {
|
||||
ok, err := subnetMatches(s, aa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
iData[s].AuxAddress[key] = aa
|
||||
match = true
|
||||
}
|
||||
if !match {
|
||||
return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
|
||||
}
|
||||
}
|
||||
|
||||
idl := []network.IPAMConfig{}
|
||||
for _, v := range iData {
|
||||
idl = append(idl, *v)
|
||||
}
|
||||
return idl, nil
|
||||
}
|
||||
|
||||
func subnetMatches(subnet, data string) (bool, error) {
|
||||
var (
|
||||
ip net.IP
|
||||
)
|
||||
|
||||
_, s, err := net.ParseCIDR(subnet)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
|
||||
}
|
||||
|
||||
if strings.Contains(data, "/") {
|
||||
ip, _, err = net.ParseCIDR(data)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
|
||||
}
|
||||
} else {
|
||||
ip = net.ParseIP(data)
|
||||
}
|
||||
|
||||
return s.Contains(ip), nil
|
||||
}
|
||||
|
||||
func networkUsage() string {
|
||||
networkCommands := map[string]string{
|
||||
"create": "Create a network",
|
||||
"connect": "Connect container to a network",
|
||||
"disconnect": "Disconnect container from a network",
|
||||
"inspect": "Display detailed network information",
|
||||
"ls": "List all networks",
|
||||
"rm": "Remove a network",
|
||||
}
|
||||
|
||||
help := "Commands:\n"
|
||||
|
||||
for cmd, description := range networkCommands {
|
||||
help += fmt.Sprintf(" %-25.25s%s\n", cmd, description)
|
||||
}
|
||||
|
||||
help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.")
|
||||
return help
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
)
|
||||
|
||||
// NewNetworkCommand returns a cobra command for `network` subcommands
|
||||
func NewNetworkCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "network",
|
||||
Short: "Manage Docker networks",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
||||
},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newConnectCommand(dockerCli),
|
||||
newCreateCommand(dockerCli),
|
||||
newDisconnectCommand(dockerCli),
|
||||
newInspectCommand(dockerCli),
|
||||
newListCommand(dockerCli),
|
||||
newRemoveCommand(dockerCli),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
"github.com/docker/engine-api/types/network"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type connectOptions struct {
|
||||
network string
|
||||
container string
|
||||
ipaddress string
|
||||
ipv6address string
|
||||
links opts.ListOpts
|
||||
aliases []string
|
||||
linklocalips []string
|
||||
}
|
||||
|
||||
func newConnectCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
opts := connectOptions{
|
||||
links: opts.NewListOpts(runconfigopts.ValidateLink),
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "connect [OPTIONS] NETWORK CONTAINER",
|
||||
Short: "Connect a container to a network",
|
||||
Args: cli.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.network = args[0]
|
||||
opts.container = args[1]
|
||||
return runConnect(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&opts.ipaddress, "ip", "", "IP Address")
|
||||
flags.StringVar(&opts.ipv6address, "ip6", "", "IPv6 Address")
|
||||
flags.Var(&opts.links, "link", "Add link to another container")
|
||||
flags.StringSliceVar(&opts.aliases, "alias", []string{}, "Add network-scoped alias for the container")
|
||||
flags.StringSliceVar(&opts.linklocalips, "link-local-ip", []string{}, "Add a link-local address for the container")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runConnect(dockerCli *client.DockerCli, opts connectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
epConfig := &network.EndpointSettings{
|
||||
IPAMConfig: &network.EndpointIPAMConfig{
|
||||
IPv4Address: opts.ipaddress,
|
||||
IPv6Address: opts.ipv6address,
|
||||
LinkLocalIPs: opts.linklocalips,
|
||||
},
|
||||
Links: opts.links.GetAll(),
|
||||
Aliases: opts.aliases,
|
||||
}
|
||||
|
||||
return client.NetworkConnect(context.Background(), opts.network, opts.container, epConfig)
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/network"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type createOptions struct {
|
||||
name string
|
||||
driver string
|
||||
driverOpts opts.MapOpts
|
||||
labels []string
|
||||
internal bool
|
||||
ipv6 bool
|
||||
|
||||
ipamDriver string
|
||||
ipamSubnet []string
|
||||
ipamIPRange []string
|
||||
ipamGateway []string
|
||||
ipamAux opts.MapOpts
|
||||
ipamOpt opts.MapOpts
|
||||
}
|
||||
|
||||
func newCreateCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
opts := createOptions{
|
||||
driverOpts: *opts.NewMapOpts(nil, nil),
|
||||
ipamAux: *opts.NewMapOpts(nil, nil),
|
||||
ipamOpt: *opts.NewMapOpts(nil, nil),
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "create [OPTIONS] NETWORK",
|
||||
Short: "Create a network",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.name = args[0]
|
||||
return runCreate(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&opts.driver, "driver", "d", "bridge", "Driver to manage the Network")
|
||||
flags.VarP(&opts.driverOpts, "opt", "o", "Set driver specific options")
|
||||
flags.StringSliceVar(&opts.labels, "label", []string{}, "Set metadata on a network")
|
||||
flags.BoolVar(&opts.internal, "internal", false, "Restrict external access to the network")
|
||||
flags.BoolVar(&opts.ipv6, "ipv6", false, "Enable IPv6 networking")
|
||||
|
||||
flags.StringVar(&opts.ipamDriver, "ipam-driver", "default", "IP Address Management Driver")
|
||||
flags.StringSliceVar(&opts.ipamSubnet, "subnet", []string{}, "Subnet in CIDR format that represents a network segment")
|
||||
flags.StringSliceVar(&opts.ipamIPRange, "ip-range", []string{}, "Allocate container ip from a sub-range")
|
||||
flags.StringSliceVar(&opts.ipamGateway, "gateway", []string{}, "IPv4 or IPv6 Gateway for the master subnet")
|
||||
|
||||
flags.Var(&opts.ipamAux, "aux-address", "Auxiliary IPv4 or IPv6 addresses used by Network driver")
|
||||
flags.Var(&opts.ipamOpt, "ipam-opt", "Set IPAM driver specific options")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(dockerCli *client.DockerCli, opts createOptions) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
ipamCfg, err := consolidateIpam(opts.ipamSubnet, opts.ipamIPRange, opts.ipamGateway, opts.ipamAux.GetAll())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Construct network create request body
|
||||
nc := types.NetworkCreate{
|
||||
Driver: opts.driver,
|
||||
Options: opts.driverOpts.GetAll(),
|
||||
IPAM: network.IPAM{
|
||||
Driver: opts.ipamDriver,
|
||||
Config: ipamCfg,
|
||||
Options: opts.ipamOpt.GetAll(),
|
||||
},
|
||||
CheckDuplicate: true,
|
||||
Internal: opts.internal,
|
||||
EnableIPv6: opts.ipv6,
|
||||
Labels: runconfigopts.ConvertKVStringsToMap(opts.labels),
|
||||
}
|
||||
|
||||
resp, err := client.NetworkCreate(context.Background(), opts.name, nc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", resp.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Consolidates the ipam configuration as a group from different related configurations
|
||||
// user can configure network with multiple non-overlapping subnets and hence it is
|
||||
// possible to correlate the various related parameters and consolidate them.
|
||||
// consoidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into
|
||||
// structured ipam data.
|
||||
func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
|
||||
if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
|
||||
return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
|
||||
}
|
||||
iData := map[string]*network.IPAMConfig{}
|
||||
|
||||
// Populate non-overlapping subnets into consolidation map
|
||||
for _, s := range subnets {
|
||||
for k := range iData {
|
||||
ok1, err := subnetMatches(s, k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ok2, err := subnetMatches(k, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok1 || ok2 {
|
||||
return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
|
||||
}
|
||||
}
|
||||
iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
|
||||
}
|
||||
|
||||
// Validate and add valid ip ranges
|
||||
for _, r := range ranges {
|
||||
match := false
|
||||
for _, s := range subnets {
|
||||
ok, err := subnetMatches(s, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if iData[s].IPRange != "" {
|
||||
return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
|
||||
}
|
||||
d := iData[s]
|
||||
d.IPRange = r
|
||||
match = true
|
||||
}
|
||||
if !match {
|
||||
return nil, fmt.Errorf("no matching subnet for range %s", r)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate and add valid gateways
|
||||
for _, g := range gateways {
|
||||
match := false
|
||||
for _, s := range subnets {
|
||||
ok, err := subnetMatches(s, g)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if iData[s].Gateway != "" {
|
||||
return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
|
||||
}
|
||||
d := iData[s]
|
||||
d.Gateway = g
|
||||
match = true
|
||||
}
|
||||
if !match {
|
||||
return nil, fmt.Errorf("no matching subnet for gateway %s", g)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate and add aux-addresses
|
||||
for key, aa := range auxaddrs {
|
||||
match := false
|
||||
for _, s := range subnets {
|
||||
ok, err := subnetMatches(s, aa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
iData[s].AuxAddress[key] = aa
|
||||
match = true
|
||||
}
|
||||
if !match {
|
||||
return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
|
||||
}
|
||||
}
|
||||
|
||||
idl := []network.IPAMConfig{}
|
||||
for _, v := range iData {
|
||||
idl = append(idl, *v)
|
||||
}
|
||||
return idl, nil
|
||||
}
|
||||
|
||||
func subnetMatches(subnet, data string) (bool, error) {
|
||||
var (
|
||||
ip net.IP
|
||||
)
|
||||
|
||||
_, s, err := net.ParseCIDR(subnet)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
|
||||
}
|
||||
|
||||
if strings.Contains(data, "/") {
|
||||
ip, _, err = net.ParseCIDR(data)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
|
||||
}
|
||||
} else {
|
||||
ip = net.ParseIP(data)
|
||||
}
|
||||
|
||||
return s.Contains(ip), nil
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type disconnectOptions struct {
|
||||
network string
|
||||
container string
|
||||
force bool
|
||||
}
|
||||
|
||||
func newDisconnectCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
opts := disconnectOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "disconnect [OPTIONS] NETWORK CONTAINER",
|
||||
Short: "Disconnect a container from a network",
|
||||
Args: cli.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.network = args[0]
|
||||
opts.container = args[1]
|
||||
return runDisconnect(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.force, "force", "f", false, "Force the container to disconnect from a network")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDisconnect(dockerCli *client.DockerCli, opts disconnectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
return client.NetworkDisconnect(context.Background(), opts.network, opts.container, opts.force)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/api/client/inspect"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type inspectOptions struct {
|
||||
format string
|
||||
names []string
|
||||
}
|
||||
|
||||
func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts inspectOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "inspect [OPTIONS] NETWORK [NETWORK...]",
|
||||
Short: "Display detailed information on one or more networks",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.names = args
|
||||
return runInspect(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&opts.format, "format", "f", "", "Format the output using the given go template")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
getNetFunc := func(name string) (interface{}, []byte, error) {
|
||||
return client.NetworkInspectWithRaw(ctx, name)
|
||||
}
|
||||
|
||||
return inspect.Inspect(dockerCli.Out(), opts.names, opts.format, getNetFunc)
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"text/tabwriter"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type byNetworkName []types.NetworkResource
|
||||
|
||||
func (r byNetworkName) Len() int { return len(r) }
|
||||
func (r byNetworkName) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||
func (r byNetworkName) Less(i, j int) bool { return r[i].Name < r[j].Name }
|
||||
|
||||
type listOptions struct {
|
||||
quiet bool
|
||||
noTrunc bool
|
||||
filter []string
|
||||
}
|
||||
|
||||
func newListCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts listOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "ls [OPTIONS]",
|
||||
Aliases: []string{"list"},
|
||||
Short: "List networks",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runList(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display network IDs")
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate the output")
|
||||
flags.StringSliceVarP(&opts.filter, "filter", "f", []string{}, "Provide filter values (i.e. 'dangling=true')")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runList(dockerCli *client.DockerCli, opts listOptions) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
netFilterArgs := filters.NewArgs()
|
||||
for _, f := range opts.filter {
|
||||
var err error
|
||||
netFilterArgs, err = filters.ParseFlag(f, netFilterArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
options := types.NetworkListOptions{
|
||||
Filters: netFilterArgs,
|
||||
}
|
||||
|
||||
networkResources, err := client.NetworkList(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(dockerCli.Out(), 20, 1, 3, ' ', 0)
|
||||
if !opts.quiet {
|
||||
fmt.Fprintf(w, "NETWORK ID\tNAME\tDRIVER\tSCOPE")
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
|
||||
sort.Sort(byNetworkName(networkResources))
|
||||
for _, networkResource := range networkResources {
|
||||
ID := networkResource.ID
|
||||
netName := networkResource.Name
|
||||
driver := networkResource.Driver
|
||||
scope := networkResource.Scope
|
||||
if !opts.noTrunc {
|
||||
ID = stringid.TruncateID(ID)
|
||||
}
|
||||
if opts.quiet {
|
||||
fmt.Fprintln(w, ID)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t",
|
||||
ID,
|
||||
netName,
|
||||
driver,
|
||||
scope)
|
||||
fmt.Fprint(w, "\n")
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newRemoveCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "rm NETWORK [NETWORK...]",
|
||||
Aliases: []string{"remove"},
|
||||
Short: "Remove one or more networks",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runRemove(dockerCli, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runRemove(dockerCli *client.DockerCli, networks []string) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
status := 0
|
||||
|
||||
for _, name := range networks {
|
||||
if err := client.NetworkRemove(ctx, name); err != nil {
|
||||
fmt.Fprintf(dockerCli.Err(), "%s\n", err)
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", name)
|
||||
}
|
||||
|
||||
if status != 0 {
|
||||
return cli.StatusError{StatusCode: status}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
apiclient "github.com/docker/engine-api/client"
|
||||
)
|
||||
|
||||
// NewNodeCommand returns a cobra command for `node` subcommands
|
||||
func NewNodeCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "node",
|
||||
Short: "Manage Docker Swarm nodes",
|
||||
Args: cli.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
|
||||
},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newDemoteCommand(dockerCli),
|
||||
newInspectCommand(dockerCli),
|
||||
newListCommand(dockerCli),
|
||||
newPromoteCommand(dockerCli),
|
||||
newRemoveCommand(dockerCli),
|
||||
newPSCommand(dockerCli),
|
||||
newUpdateCommand(dockerCli),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Reference returns the reference of a node. The special value "self" for a node
|
||||
// reference is mapped to the current node, hence the node ID is retrieved using
|
||||
// the `/info` endpoint.
|
||||
func Reference(client apiclient.APIClient, ctx context.Context, ref string) (string, error) {
|
||||
if ref == "self" {
|
||||
info, err := client.Info(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return info.Swarm.NodeID, nil
|
||||
}
|
||||
return ref, nil
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/engine-api/types/swarm"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newDemoteCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "demote NODE [NODE...]",
|
||||
Short: "Demote one or more nodes from manager in the swarm",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runDemote(dockerCli, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runDemote(dockerCli *client.DockerCli, nodes []string) error {
|
||||
demote := func(node *swarm.Node) error {
|
||||
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)
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/api/client/inspect"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/engine-api/types/swarm"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type inspectOptions struct {
|
||||
nodeIds []string
|
||||
format string
|
||||
pretty bool
|
||||
}
|
||||
|
||||
func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
var opts inspectOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "inspect [OPTIONS] self|NODE [NODE...]",
|
||||
Short: "Display detailed information on one or more nodes",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.nodeIds = args
|
||||
return runInspect(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&opts.format, "format", "f", "", "Format the output using the given go template")
|
||||
flags.BoolVar(&opts.pretty, "pretty", false, "Print the information in a human friendly format.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
getRef := func(ref string) (interface{}, []byte, error) {
|
||||
nodeRef, err := Reference(client, ctx, ref)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
node, _, err := client.NodeInspectWithRaw(ctx, nodeRef)
|
||||
return node, nil, err
|
||||
}
|
||||
|
||||
if !opts.pretty {
|
||||
return inspect.Inspect(dockerCli.Out(), opts.nodeIds, opts.format, getRef)
|
||||
}
|
||||
return printHumanFriendly(dockerCli.Out(), opts.nodeIds, getRef)
|
||||
}
|
||||
|
||||
func printHumanFriendly(out io.Writer, refs []string, getRef inspect.GetRefFunc) error {
|
||||
for idx, ref := range refs {
|
||||
obj, _, err := getRef(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printNode(out, obj.(swarm.Node))
|
||||
|
||||
// TODO: better way to do this?
|
||||
// print extra space between objects, but not after the last one
|
||||
if idx+1 != len(refs) {
|
||||
fmt.Fprintf(out, "\n\n")
|
||||
} else {
|
||||
fmt.Fprintf(out, "\n")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: use a template
|
||||
func printNode(out io.Writer, node swarm.Node) {
|
||||
fmt.Fprintf(out, "ID:\t\t\t%s\n", node.ID)
|
||||
ioutils.FprintfIfNotEmpty(out, "Name:\t\t\t%s\n", node.Spec.Name)
|
||||
if node.Spec.Labels != nil {
|
||||
fmt.Fprintln(out, "Labels:")
|
||||
for k, v := range node.Spec.Labels {
|
||||
fmt.Fprintf(out, " - %s = %s\n", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
ioutils.FprintfIfNotEmpty(out, "Hostname:\t\t%s\n", node.Description.Hostname)
|
||||
fmt.Fprintf(out, "Joined at:\t\t%s\n", client.PrettyPrint(node.CreatedAt))
|
||||
fmt.Fprintln(out, "Status:")
|
||||
fmt.Fprintf(out, " State:\t\t\t%s\n", client.PrettyPrint(node.Status.State))
|
||||
ioutils.FprintfIfNotEmpty(out, " Message:\t\t%s\n", client.PrettyPrint(node.Status.Message))
|
||||
fmt.Fprintf(out, " Availability:\t\t%s\n", client.PrettyPrint(node.Spec.Availability))
|
||||
|
||||
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))
|
||||
leader := "No"
|
||||
if node.ManagerStatus.Leader {
|
||||
leader = "Yes"
|
||||
}
|
||||
fmt.Fprintf(out, " Leader:\t\t%s\n", leader)
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, "Platform:")
|
||||
fmt.Fprintf(out, " Operating System:\t%s\n", node.Description.Platform.OS)
|
||||
fmt.Fprintf(out, " Architecture:\t\t%s\n", node.Description.Platform.Architecture)
|
||||
|
||||
fmt.Fprintln(out, "Resources:")
|
||||
fmt.Fprintf(out, " CPUs:\t\t\t%d\n", node.Description.Resources.NanoCPUs/1e9)
|
||||
fmt.Fprintf(out, " Memory:\t\t%s\n", units.BytesSize(float64(node.Description.Resources.MemoryBytes)))
|
||||
|
||||
var pluginTypes []string
|
||||
pluginNamesByType := map[string][]string{}
|
||||
for _, p := range node.Description.Engine.Plugins {
|
||||
// append to pluginTypes only if not done previously
|
||||
if _, ok := pluginNamesByType[p.Type]; !ok {
|
||||
pluginTypes = append(pluginTypes, p.Type)
|
||||
}
|
||||
pluginNamesByType[p.Type] = append(pluginNamesByType[p.Type], p.Name)
|
||||
}
|
||||
|
||||
if len(pluginTypes) > 0 {
|
||||
fmt.Fprintln(out, "Plugins:")
|
||||
sort.Strings(pluginTypes) // ensure stable output
|
||||
for _, pluginType := range pluginTypes {
|
||||
fmt.Fprintf(out, " %s:\t\t%s\n", pluginType, strings.Join(pluginNamesByType[pluginType], ", "))
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(out, "Engine Version:\t\t%s\n", node.Description.Engine.EngineVersion)
|
||||
|
||||
if len(node.Description.Engine.Labels) != 0 {
|
||||
fmt.Fprintln(out, "Engine Labels:")
|
||||
for k, v := range node.Description.Engine.Labels {
|
||||
fmt.Fprintf(out, " - %s = %s\n", k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"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/engine-api/types"
|
||||
"github.com/docker/engine-api/types/swarm"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
listItemFmt = "%s\t%s\t%s\t%s\t%s\n"
|
||||
)
|
||||
|
||||
type listOptions struct {
|
||||
quiet bool
|
||||
filter opts.FilterOpt
|
||||
}
|
||||
|
||||
func newListCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
opts := listOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "ls [OPTIONS]",
|
||||
Aliases: []string{"list"},
|
||||
Short: "List nodes in the swarm",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runList(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display IDs")
|
||||
flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runList(dockerCli *client.DockerCli, opts listOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
nodes, err := client.NodeList(
|
||||
ctx,
|
||||
types.NodeListOptions{Filter: opts.filter.Value()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err := client.Info(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out := dockerCli.Out()
|
||||
if opts.quiet {
|
||||
printQuiet(out, nodes)
|
||||
} else {
|
||||
printTable(out, nodes, info)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printTable(out io.Writer, nodes []swarm.Node, info types.Info) {
|
||||
writer := tabwriter.NewWriter(out, 0, 4, 2, ' ', 0)
|
||||
|
||||
// Ignore flushing errors
|
||||
defer writer.Flush()
|
||||
|
||||
fmt.Fprintf(writer, listItemFmt, "ID", "HOSTNAME", "STATUS", "AVAILABILITY", "MANAGER STATUS")
|
||||
for _, node := range nodes {
|
||||
name := node.Description.Hostname
|
||||
availability := string(node.Spec.Availability)
|
||||
|
||||
reachability := ""
|
||||
if node.ManagerStatus != nil {
|
||||
if node.ManagerStatus.Leader {
|
||||
reachability = "Leader"
|
||||
} else {
|
||||
reachability = string(node.ManagerStatus.Reachability)
|
||||
}
|
||||
}
|
||||
|
||||
ID := node.ID
|
||||
if node.ID == info.Swarm.NodeID {
|
||||
ID = ID + " *"
|
||||
}
|
||||
|
||||
fmt.Fprintf(
|
||||
writer,
|
||||
listItemFmt,
|
||||
ID,
|
||||
name,
|
||||
client.PrettyPrint(string(node.Status.State)),
|
||||
client.PrettyPrint(availability),
|
||||
client.PrettyPrint(reachability))
|
||||
}
|
||||
}
|
||||
|
||||
func printQuiet(out io.Writer, nodes []swarm.Node) {
|
||||
for _, node := range nodes {
|
||||
fmt.Fprintln(out, node.ID)
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/opts"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
"github.com/docker/engine-api/types/swarm"
|
||||
)
|
||||
|
||||
type nodeOptions struct {
|
||||
annotations
|
||||
role string
|
||||
availability string
|
||||
}
|
||||
|
||||
type annotations struct {
|
||||
name string
|
||||
labels opts.ListOpts
|
||||
}
|
||||
|
||||
func newNodeOptions() *nodeOptions {
|
||||
return &nodeOptions{
|
||||
annotations: annotations{
|
||||
labels: opts.NewListOpts(nil),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *nodeOptions) ToNodeSpec() (swarm.NodeSpec, error) {
|
||||
var spec swarm.NodeSpec
|
||||
|
||||
spec.Annotations.Name = opts.annotations.name
|
||||
spec.Annotations.Labels = runconfigopts.ConvertKVStringsToMap(opts.annotations.labels.GetAll())
|
||||
|
||||
switch swarm.NodeRole(strings.ToLower(opts.role)) {
|
||||
case swarm.NodeRoleWorker:
|
||||
spec.Role = swarm.NodeRoleWorker
|
||||
case swarm.NodeRoleManager:
|
||||
spec.Role = swarm.NodeRoleManager
|
||||
case "":
|
||||
default:
|
||||
return swarm.NodeSpec{}, fmt.Errorf("invalid role %q, only worker and manager are supported", opts.role)
|
||||
}
|
||||
|
||||
switch swarm.NodeAvailability(strings.ToLower(opts.availability)) {
|
||||
case swarm.NodeAvailabilityActive:
|
||||
spec.Availability = swarm.NodeAvailabilityActive
|
||||
case swarm.NodeAvailabilityPause:
|
||||
spec.Availability = swarm.NodeAvailabilityPause
|
||||
case swarm.NodeAvailabilityDrain:
|
||||
spec.Availability = swarm.NodeAvailabilityDrain
|
||||
case "":
|
||||
default:
|
||||
return swarm.NodeSpec{}, fmt.Errorf("invalid availability %q, only active, pause and drain are supported", opts.availability)
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/engine-api/types/swarm"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newPromoteCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "promote NODE [NODE...]",
|
||||
Short: "Promote one or more nodes to manager in the swarm",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runPromote(dockerCli, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runPromote(dockerCli *client.DockerCli, nodes []string) error {
|
||||
promote := func(node *swarm.Node) error {
|
||||
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)
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/api/client/idresolver"
|
||||
"github.com/docker/docker/api/client/task"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type psOptions struct {
|
||||
nodeID string
|
||||
noResolve bool
|
||||
filter opts.FilterOpt
|
||||
}
|
||||
|
||||
func newPSCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
opts := psOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "ps [OPTIONS] self|NODE",
|
||||
Short: "List tasks running on a node",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.nodeID = args[0]
|
||||
return runPS(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&opts.noResolve, "no-resolve", false, "Do not map IDs to Names")
|
||||
flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPS(dockerCli *client.DockerCli, opts psOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
nodeRef, err := Reference(client, ctx, opts.nodeID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
node, _, err := client.NodeInspectWithRaw(ctx, nodeRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filter := opts.filter.Value()
|
||||
filter.Add("node", node.ID)
|
||||
tasks, err := client.TaskList(
|
||||
ctx,
|
||||
types.TaskListOptions{Filter: filter})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return task.Print(dockerCli, ctx, tasks, idresolver.New(client, opts.noResolve))
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type removeOptions struct {
|
||||
force bool
|
||||
}
|
||||
|
||||
func newRemoveCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
opts := removeOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rm [OPTIONS] NODE [NODE...]",
|
||||
Aliases: []string{"remove"},
|
||||
Short: "Remove one or more nodes from the swarm",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runRemove(dockerCli, args, opts)
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&opts.force, "force", false, "Force remove an active node")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRemove(dockerCli *client.DockerCli, args []string, opts removeOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
for _, nodeID := range args {
|
||||
err := client.NodeRemove(ctx, nodeID, types.NodeRemoveOptions{Force: opts.force})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(dockerCli.Out(), "%s\n", nodeID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
"github.com/docker/engine-api/types/swarm"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||
nodeOpts := newNodeOptions()
|
||||
|
||||
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, cmd.Flags(), args[0])
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&nodeOpts.role, flagRole, "", "Role of the node (worker/manager)")
|
||||
flags.StringVar(&nodeOpts.availability, flagAvailability, "", "Availability of the node (active/pause/drain)")
|
||||
flags.Var(&nodeOpts.annotations.labels, flagLabelAdd, "Add or update a node label (key=value)")
|
||||
labelKeys := opts.NewListOpts(nil)
|
||||
flags.Var(&labelKeys, flagLabelRemove, "Remove a node label if exists")
|
||||
return cmd
|
||||
}
|
||||
|
||||
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) error, success func(nodeID string)) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
for _, nodeID := range nodes {
|
||||
node, _, err := client.NodeInspectWithRaw(ctx, nodeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = mergeNode(&node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = client.NodeUpdate(ctx, node.ID, node.Version, node.Spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
success(nodeID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) error {
|
||||
return func(node *swarm.Node) error {
|
||||
spec := &node.Spec
|
||||
|
||||
if flags.Changed(flagRole) {
|
||||
str, err := flags.GetString(flagRole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spec.Role = swarm.NodeRole(str)
|
||||
}
|
||||
if flags.Changed(flagAvailability) {
|
||||
str, err := flags.GetString(flagAvailability)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spec.Availability = swarm.NodeAvailability(str)
|
||||
}
|
||||
if spec.Annotations.Labels == nil {
|
||||
spec.Annotations.Labels = make(map[string]string)
|
||||
}
|
||||
if flags.Changed(flagLabelAdd) {
|
||||
labels := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetAll()
|
||||
for k, v := range runconfigopts.ConvertKVStringsToMap(labels) {
|
||||
spec.Annotations.Labels[k] = v
|
||||
}
|
||||
}
|
||||
if flags.Changed(flagLabelRemove) {
|
||||
keys := flags.Lookup(flagLabelRemove).Value.(*opts.ListOpts).GetAll()
|
||||
for _, k := range keys {
|
||||
// if a key doesn't exist, fail the command explicitly
|
||||
if _, exists := spec.Annotations.Labels[k]; !exists {
|
||||
return fmt.Errorf("key %s doesn't exist in node's labels", k)
|
||||
}
|
||||
delete(spec.Annotations.Labels, k)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
flagRole = "role"
|
||||
flagAvailability = "availability"
|
||||
flagLabelAdd = "label-add"
|
||||
flagLabelRemove = "label-rm"
|
||||
)
|
||||
34
api/client/pause.go
Normal file
34
api/client/pause.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
|
||||
// CmdPause pauses all processes within one or more containers.
|
||||
//
|
||||
// Usage: docker pause CONTAINER [CONTAINER...]
|
||||
func (cli *DockerCli) CmdPause(args ...string) error {
|
||||
cmd := Cli.Subcmd("pause", []string{"CONTAINER [CONTAINER...]"}, Cli.DockerCommands["pause"].Description, true)
|
||||
cmd.Require(flag.Min, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var errs []string
|
||||
for _, name := range cmd.Args() {
|
||||
if err := cli.client.ContainerPause(context.Background(), name); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user