mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-23 07:15:54 +00:00
Compare commits
73 Commits
docs
...
v1.5.0-rc4
| Author | SHA1 | Date | |
|---|---|---|---|
| a1cae772c4 | |||
| d4c731ecd6 | |||
| 2dba4e1386 | |||
| 06a7f471e0 | |||
| 4683d01691 | |||
| 6020a06399 | |||
| cc0bfccdf4 | |||
| 0c18ec62f3 | |||
| a9825c9bd8 | |||
| 908be50c44 | |||
| 2a82dba34d | |||
| 13fd2a908c | |||
| 464891aaf8 | |||
| 9974663ed7 | |||
| 76269e5c9d | |||
| 1121d7c4fd | |||
| 7e197575a2 | |||
| 3dc3059d94 | |||
| 7b6de74c9a | |||
| cad8adacb8 | |||
| 6226deeaf4 | |||
| 3ec19f56cf | |||
| 48c71787ed | |||
| 604731a930 | |||
| e8650e01f8 | |||
| 817d04d992 | |||
| cdff91a01c | |||
| 6f26bd0e16 | |||
| 3c090db4e9 | |||
| b7c3fdfd0d | |||
| aa682a845b | |||
| 218d0dcc9d | |||
| 510d8f8634 | |||
| b65600f6b6 | |||
| 79dcea718c | |||
| 072b09c45d | |||
| c2d9837745 | |||
| fa5dfbb18b | |||
| 6532a075f3 | |||
| 3b4a4bf809 | |||
| 4602909566 | |||
| 588f350b61 | |||
| 6e5ff509b2 | |||
| 61d341c2ca | |||
| b996d379a1 | |||
| b0935ea730 | |||
| 96fe13b49b | |||
| 12ccde442a | |||
| 4262cfe41f | |||
| ddc2e25546 | |||
| 6646cff646 | |||
| ac8fd856c0 | |||
| 48754d673c | |||
| 723684525a | |||
| 32aceadbe6 | |||
| c67d3e159c | |||
| a080e2add7 | |||
| 24d81b0ddb | |||
| 08f2fad40b | |||
| f91fbe39ce | |||
| 018ab080bb | |||
| fe94ecb2c1 | |||
| 7b2e67036f | |||
| e130faea1b | |||
| 38f09de334 | |||
| f9ba68ddfb | |||
| 16913455bd | |||
| 32f189cd08 | |||
| 526ca42282 | |||
| b98b42d843 | |||
| 7bf03dd132 | |||
| 034aa3b2c4 | |||
| 6da1e01e6c |
+1
-1
@@ -10,5 +10,5 @@ script:
|
|||||||
- rm integration-cli/docker_cli_daemon_test.go
|
- rm integration-cli/docker_cli_daemon_test.go
|
||||||
- rm integration-cli/docker_cli_exec_test.go
|
- rm integration-cli/docker_cli_exec_test.go
|
||||||
# Validate and test.
|
# Validate and test.
|
||||||
- hack/make.sh validate-dco validate-gofmt
|
- hack/make.sh validate-dco validate-gofmt validate-toml
|
||||||
- hack/make.sh binary cross test-unit test-integration-cli test-integration test-docker-py
|
- hack/make.sh binary cross test-unit test-integration-cli test-integration test-docker-py
|
||||||
|
|||||||
@@ -6,6 +6,24 @@
|
|||||||
#
|
#
|
||||||
# For explanation on this file format: man git-shortlog
|
# For explanation on this file format: man git-shortlog
|
||||||
|
|
||||||
|
Patrick Stapleton <github@gdi2290.com>
|
||||||
|
Shishir Mahajan <shishir.mahajan@redhat.com> <smahajan@redhat.com>
|
||||||
|
Erwin van der Koogh <info@erronis.nl>
|
||||||
|
Ahmed Kamal <email.ahmedkamal@googlemail.com>
|
||||||
|
Tejesh Mehta <tejesh.mehta@gmail.com> <tj@init.me>
|
||||||
|
Cristian Staretu <cristian.staretu@gmail.com>
|
||||||
|
Cristian Staretu <cristian.staretu@gmail.com> <unclejacksons@gmail.com>
|
||||||
|
Cristian Staretu <cristian.staretu@gmail.com> <unclejack@users.noreply.github.com>
|
||||||
|
Marcus Linke <marcus.linke@gmx.de>
|
||||||
|
Aleksandrs Fadins <aleks@s-ko.net>
|
||||||
|
Christopher Latham <sudosurootdev@gmail.com>
|
||||||
|
Hu Keping <hukeping@huawei.com>
|
||||||
|
Wayne Chang <wayne@neverfear.org>
|
||||||
|
Chen Chao <cc272309126@gmail.com>
|
||||||
|
Daehyeok Mun <daehyeok@gmail.com>
|
||||||
|
<daehyeok@gmail.com> <daehyeok@daehyeokui-MacBook-Air.local>
|
||||||
|
<jt@yadutaf.fr> <admin@jtlebi.fr>
|
||||||
|
<jeff@docker.com> <jefferya@programmerq.net>
|
||||||
<charles.hooper@dotcloud.com> <chooper@plumata.com>
|
<charles.hooper@dotcloud.com> <chooper@plumata.com>
|
||||||
<daniel.mizyrycki@dotcloud.com> <daniel@dotcloud.com>
|
<daniel.mizyrycki@dotcloud.com> <daniel@dotcloud.com>
|
||||||
<daniel.mizyrycki@dotcloud.com> <mzdaniel@glidelink.net>
|
<daniel.mizyrycki@dotcloud.com> <mzdaniel@glidelink.net>
|
||||||
@@ -58,7 +76,7 @@ Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
|
|||||||
<michael@docker.com> <michael@crosbymichael.com>
|
<michael@docker.com> <michael@crosbymichael.com>
|
||||||
<michael@docker.com> <crosby.michael@gmail.com>
|
<michael@docker.com> <crosby.michael@gmail.com>
|
||||||
<michael@docker.com> <crosbymichael@gmail.com>
|
<michael@docker.com> <crosbymichael@gmail.com>
|
||||||
<github@developersupport.net> <github@metaliveblog.com>
|
<github@developersupport.net> <github@metaliveblog.com>
|
||||||
<brandon@ifup.org> <brandon@ifup.co>
|
<brandon@ifup.org> <brandon@ifup.co>
|
||||||
<dano@spotify.com> <daniel.norberg@gmail.com>
|
<dano@spotify.com> <daniel.norberg@gmail.com>
|
||||||
<danny@codeaholics.org> <Danny.Yates@mailonline.co.uk>
|
<danny@codeaholics.org> <Danny.Yates@mailonline.co.uk>
|
||||||
@@ -74,7 +92,6 @@ 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> <SvenDowideit@users.noreply.github.com>
|
||||||
Sven Dowideit <SvenDowideit@home.org.au> <sven@t440s.home.gateway>
|
Sven Dowideit <SvenDowideit@home.org.au> <sven@t440s.home.gateway>
|
||||||
unclejack <unclejacksons@gmail.com> <unclejack@users.noreply.github.com>
|
|
||||||
<alexl@redhat.com> <alexander.larsson@gmail.com>
|
<alexl@redhat.com> <alexander.larsson@gmail.com>
|
||||||
Alexandr Morozov <lk4d4math@gmail.com>
|
Alexandr Morozov <lk4d4math@gmail.com>
|
||||||
<git.nivoc@neverbox.com> <kuehnle@online.de>
|
<git.nivoc@neverbox.com> <kuehnle@online.de>
|
||||||
|
|||||||
@@ -12,41 +12,46 @@ Adam Singer <financeCoding@gmail.com>
|
|||||||
Aditya <aditya@netroy.in>
|
Aditya <aditya@netroy.in>
|
||||||
Adrian Mouat <adrian.mouat@gmail.com>
|
Adrian Mouat <adrian.mouat@gmail.com>
|
||||||
Adrien Folie <folie.adrien@gmail.com>
|
Adrien Folie <folie.adrien@gmail.com>
|
||||||
|
Ahmed Kamal <email.ahmedkamal@googlemail.com>
|
||||||
Ahmet Alp Balkan <ahmetb@microsoft.com>
|
Ahmet Alp Balkan <ahmetb@microsoft.com>
|
||||||
|
Aidan Hobson Sayers <aidanhs@cantab.net>
|
||||||
AJ Bowen <aj@gandi.net>
|
AJ Bowen <aj@gandi.net>
|
||||||
|
Al Tobey <al@ooyala.com>
|
||||||
alambike <alambike@gmail.com>
|
alambike <alambike@gmail.com>
|
||||||
Alan Thompson <cloojure@gmail.com>
|
Alan Thompson <cloojure@gmail.com>
|
||||||
Albert Callarisa <shark234@gmail.com>
|
Albert Callarisa <shark234@gmail.com>
|
||||||
Albert Zhang <zhgwenming@gmail.com>
|
Albert Zhang <zhgwenming@gmail.com>
|
||||||
Aleksa Sarai <cyphar@cyphar.com>
|
Aleksa Sarai <cyphar@cyphar.com>
|
||||||
|
Aleksandrs Fadins <aleks@s-ko.net>
|
||||||
|
Alex Gaynor <alex.gaynor@gmail.com>
|
||||||
|
Alex Warhawk <ax.warhawk@gmail.com>
|
||||||
|
Alexander Boyd <alex@opengroove.org>
|
||||||
Alexander Larsson <alexl@redhat.com>
|
Alexander Larsson <alexl@redhat.com>
|
||||||
|
Alexander Morozov <lk4d4@docker.com>
|
||||||
Alexander Shopov <ash@kambanaria.org>
|
Alexander Shopov <ash@kambanaria.org>
|
||||||
Alexandr Morozov <lk4d4@docker.com>
|
Alexandr Morozov <lk4d4@docker.com>
|
||||||
Alexey Kotlyarov <alexey@infoxchange.net.au>
|
Alexey Kotlyarov <alexey@infoxchange.net.au>
|
||||||
Alexey Shamrin <shamrin@gmail.com>
|
Alexey Shamrin <shamrin@gmail.com>
|
||||||
Alex Gaynor <alex.gaynor@gmail.com>
|
|
||||||
Alexis THOMAS <fr.alexisthomas@gmail.com>
|
Alexis THOMAS <fr.alexisthomas@gmail.com>
|
||||||
Alex Warhawk <ax.warhawk@gmail.com>
|
|
||||||
almoehi <almoehi@users.noreply.github.com>
|
almoehi <almoehi@users.noreply.github.com>
|
||||||
Al Tobey <al@ooyala.com>
|
|
||||||
Álvaro Lázaro <alvaro.lazaro.g@gmail.com>
|
|
||||||
amangoel <amangoel@gmail.com>
|
amangoel <amangoel@gmail.com>
|
||||||
Amit Bakshi <ambakshi@gmail.com>
|
Amit Bakshi <ambakshi@gmail.com>
|
||||||
AnandkumarPatel <anandkumarpatel@gmail.com>
|
|
||||||
Anand Patil <anand.prabhakar.patil@gmail.com>
|
Anand Patil <anand.prabhakar.patil@gmail.com>
|
||||||
|
AnandkumarPatel <anandkumarpatel@gmail.com>
|
||||||
|
Andre Dublin <81dublin@gmail.com>
|
||||||
Andrea Luzzardi <aluzzardi@gmail.com>
|
Andrea Luzzardi <aluzzardi@gmail.com>
|
||||||
|
Andrea Turli <andrea.turli@gmail.com>
|
||||||
Andreas Köhler <andi5.py@gmx.net>
|
Andreas Köhler <andi5.py@gmx.net>
|
||||||
Andreas Savvides <andreas@editd.com>
|
Andreas Savvides <andreas@editd.com>
|
||||||
Andreas Tiefenthaler <at@an-ti.eu>
|
Andreas Tiefenthaler <at@an-ti.eu>
|
||||||
Andrea Turli <andrea.turli@gmail.com>
|
Andrew C. Bodine <acbodine@us.ibm.com>
|
||||||
Andre Dublin <81dublin@gmail.com>
|
|
||||||
Andrew Duckworth <grillopress@gmail.com>
|
Andrew Duckworth <grillopress@gmail.com>
|
||||||
Andrew France <andrew@avito.co.uk>
|
Andrew France <andrew@avito.co.uk>
|
||||||
Andrew Macgregor <andrew.macgregor@agworld.com.au>
|
Andrew Macgregor <andrew.macgregor@agworld.com.au>
|
||||||
Andrew Munsell <andrew@wizardapps.net>
|
Andrew Munsell <andrew@wizardapps.net>
|
||||||
Andrews Medina <andrewsmedina@gmail.com>
|
|
||||||
Andrew Weiss <andrew.weiss@outlook.com>
|
Andrew Weiss <andrew.weiss@outlook.com>
|
||||||
Andrew Williams <williams.andrew@gmail.com>
|
Andrew Williams <williams.andrew@gmail.com>
|
||||||
|
Andrews Medina <andrewsmedina@gmail.com>
|
||||||
Andrey Petrov <andrey.petrov@shazow.net>
|
Andrey Petrov <andrey.petrov@shazow.net>
|
||||||
Andrey Stolbovsky <andrey.stolbovsky@gmail.com>
|
Andrey Stolbovsky <andrey.stolbovsky@gmail.com>
|
||||||
Andy Chambers <anchambers@paypal.com>
|
Andy Chambers <anchambers@paypal.com>
|
||||||
@@ -56,6 +61,8 @@ Andy Kipp <andy@rstudio.com>
|
|||||||
Andy Rothfusz <github@developersupport.net>
|
Andy Rothfusz <github@developersupport.net>
|
||||||
Andy Smith <github@anarkystic.com>
|
Andy Smith <github@anarkystic.com>
|
||||||
Andy Wilson <wilson.andrew.j+github@gmail.com>
|
Andy Wilson <wilson.andrew.j+github@gmail.com>
|
||||||
|
Ankush Agarwal <ankushagarwal11@gmail.com>
|
||||||
|
Anthony Baire <Anthony.Baire@irisa.fr>
|
||||||
Anthony Bishopric <git@anthonybishopric.com>
|
Anthony Bishopric <git@anthonybishopric.com>
|
||||||
Anton Löfgren <anton.lofgren@gmail.com>
|
Anton Löfgren <anton.lofgren@gmail.com>
|
||||||
Anton Nikitin <anton.k.nikitin@gmail.com>
|
Anton Nikitin <anton.k.nikitin@gmail.com>
|
||||||
@@ -72,11 +79,11 @@ Barry Allard <barry.allard@gmail.com>
|
|||||||
Bartłomiej Piotrowski <b@bpiotrowski.pl>
|
Bartłomiej Piotrowski <b@bpiotrowski.pl>
|
||||||
bdevloed <boris.de.vloed@gmail.com>
|
bdevloed <boris.de.vloed@gmail.com>
|
||||||
Ben Firshman <ben@firshman.co.uk>
|
Ben Firshman <ben@firshman.co.uk>
|
||||||
Benjamin Atkin <ben@benatkin.com>
|
|
||||||
Benoit Chesneau <bchesneau@gmail.com>
|
|
||||||
Ben Sargent <ben@brokendigits.com>
|
Ben Sargent <ben@brokendigits.com>
|
||||||
Ben Toews <mastahyeti@gmail.com>
|
Ben Toews <mastahyeti@gmail.com>
|
||||||
Ben Wiklund <ben@daisyowl.com>
|
Ben Wiklund <ben@daisyowl.com>
|
||||||
|
Benjamin Atkin <ben@benatkin.com>
|
||||||
|
Benoit Chesneau <bchesneau@gmail.com>
|
||||||
Bernerd Schaefer <bj.schaefer@gmail.com>
|
Bernerd Schaefer <bj.schaefer@gmail.com>
|
||||||
Bert Goethals <bert@bertg.be>
|
Bert Goethals <bert@bertg.be>
|
||||||
Bhiraj Butala <abhiraj.butala@gmail.com>
|
Bhiraj Butala <abhiraj.butala@gmail.com>
|
||||||
@@ -113,18 +120,22 @@ Charles Hooper <charles.hooper@dotcloud.com>
|
|||||||
Charles Lindsay <chaz@chazomatic.us>
|
Charles Lindsay <chaz@chazomatic.us>
|
||||||
Charles Merriam <charles.merriam@gmail.com>
|
Charles Merriam <charles.merriam@gmail.com>
|
||||||
Charlie Lewis <charliel@lab41.org>
|
Charlie Lewis <charliel@lab41.org>
|
||||||
|
Chen Chao <cc272309126@gmail.com>
|
||||||
Chewey <prosto-chewey@users.noreply.github.com>
|
Chewey <prosto-chewey@users.noreply.github.com>
|
||||||
Chia-liang Kao <clkao@clkao.org>
|
Chia-liang Kao <clkao@clkao.org>
|
||||||
Chris Alfonso <calfonso@redhat.com>
|
Chris Alfonso <calfonso@redhat.com>
|
||||||
Chris Armstrong <chris@opdemand.com>
|
Chris Armstrong <chris@opdemand.com>
|
||||||
chrismckinnel <chris.mckinnel@tangentlabs.co.uk>
|
|
||||||
Chris Snow <chsnow123@gmail.com>
|
Chris Snow <chsnow123@gmail.com>
|
||||||
Chris St. Pierre <chris.a.st.pierre@gmail.com>
|
Chris St. Pierre <chris.a.st.pierre@gmail.com>
|
||||||
|
chrismckinnel <chris.mckinnel@tangentlabs.co.uk>
|
||||||
Christian Berendt <berendt@b1-systems.de>
|
Christian Berendt <berendt@b1-systems.de>
|
||||||
|
Christian Stefanescu <st.chris@gmail.com>
|
||||||
ChristoperBiscardi <biscarch@sketcht.com>
|
ChristoperBiscardi <biscarch@sketcht.com>
|
||||||
Christopher Currie <codemonkey+github@gmail.com>
|
|
||||||
Christopher Rigor <crigor@gmail.com>
|
|
||||||
Christophe Troestler <christophe.Troestler@umons.ac.be>
|
Christophe Troestler <christophe.Troestler@umons.ac.be>
|
||||||
|
Christopher Currie <codemonkey+github@gmail.com>
|
||||||
|
Christopher Latham <sudosurootdev@gmail.com>
|
||||||
|
Christopher Rigor <crigor@gmail.com>
|
||||||
|
Chun Chen <chenchun.feed@gmail.com>
|
||||||
Ciro S. Costa <ciro.costa@usp.br>
|
Ciro S. Costa <ciro.costa@usp.br>
|
||||||
Clayton Coleman <ccoleman@redhat.com>
|
Clayton Coleman <ccoleman@redhat.com>
|
||||||
Colin Dunklau <colin.dunklau@gmail.com>
|
Colin Dunklau <colin.dunklau@gmail.com>
|
||||||
@@ -132,15 +143,20 @@ Colin Rice <colin@daedrum.net>
|
|||||||
Colin Walters <walters@verbum.org>
|
Colin Walters <walters@verbum.org>
|
||||||
Cory Forsyth <cory.forsyth@gmail.com>
|
Cory Forsyth <cory.forsyth@gmail.com>
|
||||||
cressie176 <github@stephen-cresswell.net>
|
cressie176 <github@stephen-cresswell.net>
|
||||||
|
Cristian Staretu <cristian.staretu@gmail.com>
|
||||||
Cruceru Calin-Cristian <crucerucalincristian@gmail.com>
|
Cruceru Calin-Cristian <crucerucalincristian@gmail.com>
|
||||||
Daan van Berkel <daan.v.berkel.1980@gmail.com>
|
Daan van Berkel <daan.v.berkel.1980@gmail.com>
|
||||||
Daehyeok.Mun <daehyeok@gmail.com>
|
Daehyeok Mun <daehyeok@gmail.com>
|
||||||
Dafydd Crosby <dtcrsby@gmail.com>
|
Dafydd Crosby <dtcrsby@gmail.com>
|
||||||
Dan Buch <d.buch@modcloth.com>
|
Dan Buch <d.buch@modcloth.com>
|
||||||
Dan Cotora <dan@bluevision.ro>
|
Dan Cotora <dan@bluevision.ro>
|
||||||
Dan Griffin <dgriffin@peer1.com>
|
Dan Griffin <dgriffin@peer1.com>
|
||||||
Dan Hirsch <thequux@upstandinghackers.com>
|
Dan Hirsch <thequux@upstandinghackers.com>
|
||||||
Daniel, Dao Quang Minh <dqminh89@gmail.com>
|
Dan Keder <dan.keder@gmail.com>
|
||||||
|
Dan McPherson <dmcphers@redhat.com>
|
||||||
|
Dan Stine <sw@stinemail.com>
|
||||||
|
Dan Walsh <dwalsh@redhat.com>
|
||||||
|
Dan Williams <me@deedubs.com>
|
||||||
Daniel Exner <dex@dragonslave.de>
|
Daniel Exner <dex@dragonslave.de>
|
||||||
Daniel Farrell <dfarrell@redhat.com>
|
Daniel Farrell <dfarrell@redhat.com>
|
||||||
Daniel Garcia <daniel@danielgarcia.info>
|
Daniel Garcia <daniel@danielgarcia.info>
|
||||||
@@ -152,29 +168,27 @@ Daniel Nordberg <dnordberg@gmail.com>
|
|||||||
Daniel Robinson <gottagetmac@gmail.com>
|
Daniel Robinson <gottagetmac@gmail.com>
|
||||||
Daniel Von Fange <daniel@leancoder.com>
|
Daniel Von Fange <daniel@leancoder.com>
|
||||||
Daniel YC Lin <dlin.tw@gmail.com>
|
Daniel YC Lin <dlin.tw@gmail.com>
|
||||||
Dan Keder <dan.keder@gmail.com>
|
Daniel, Dao Quang Minh <dqminh89@gmail.com>
|
||||||
Dan McPherson <dmcphers@redhat.com>
|
|
||||||
Danny Berger <dpb587@gmail.com>
|
Danny Berger <dpb587@gmail.com>
|
||||||
Danny Yates <danny@codeaholics.org>
|
Danny Yates <danny@codeaholics.org>
|
||||||
Dan Stine <sw@stinemail.com>
|
|
||||||
Dan Walsh <dwalsh@redhat.com>
|
|
||||||
Dan Williams <me@deedubs.com>
|
|
||||||
Darren Coxall <darren@darrencoxall.com>
|
Darren Coxall <darren@darrencoxall.com>
|
||||||
Darren Shepherd <darren.s.shepherd@gmail.com>
|
Darren Shepherd <darren.s.shepherd@gmail.com>
|
||||||
David Anderson <dave@natulte.net>
|
David Anderson <dave@natulte.net>
|
||||||
David Calavera <david.calavera@gmail.com>
|
David Calavera <david.calavera@gmail.com>
|
||||||
David Corking <dmc-source@dcorking.com>
|
David Corking <dmc-source@dcorking.com>
|
||||||
Davide Ceretti <davide.ceretti@hogarthww.com>
|
|
||||||
David Gageot <david@gageot.net>
|
David Gageot <david@gageot.net>
|
||||||
David Gebler <davidgebler@gmail.com>
|
David Gebler <davidgebler@gmail.com>
|
||||||
|
David Mat <david@davidmat.com>
|
||||||
David Mcanulty <github@hellspark.com>
|
David Mcanulty <github@hellspark.com>
|
||||||
David Pelaez <pelaez89@gmail.com>
|
David Pelaez <pelaez89@gmail.com>
|
||||||
David Röthlisberger <david@rothlis.net>
|
David Röthlisberger <david@rothlis.net>
|
||||||
David Sissitka <me@dsissitka.com>
|
David Sissitka <me@dsissitka.com>
|
||||||
|
Davide Ceretti <davide.ceretti@hogarthww.com>
|
||||||
Dawn Chen <dawnchen@google.com>
|
Dawn Chen <dawnchen@google.com>
|
||||||
decadent <decadent@users.noreply.github.com>
|
decadent <decadent@users.noreply.github.com>
|
||||||
Deni Bertovic <deni@kset.org>
|
Deni Bertovic <deni@kset.org>
|
||||||
Derek <crq@kernel.org>
|
Derek <crq@kernel.org>
|
||||||
|
Derek <crquan@gmail.com>
|
||||||
Derek McGowan <derek@mcgstyle.net>
|
Derek McGowan <derek@mcgstyle.net>
|
||||||
Deric Crago <deric.crago@gmail.com>
|
Deric Crago <deric.crago@gmail.com>
|
||||||
Deshi Xiao <dxiao@redhat.com>
|
Deshi Xiao <dxiao@redhat.com>
|
||||||
@@ -182,14 +196,16 @@ Dinesh Subhraveti <dineshs@altiscale.com>
|
|||||||
Djibril Koné <kone.djibril@gmail.com>
|
Djibril Koné <kone.djibril@gmail.com>
|
||||||
dkumor <daniel@dkumor.com>
|
dkumor <daniel@dkumor.com>
|
||||||
Dmitry Demeshchuk <demeshchuk@gmail.com>
|
Dmitry Demeshchuk <demeshchuk@gmail.com>
|
||||||
|
Dmitry V. Krivenok <krivenok.dmitry@gmail.com>
|
||||||
Dolph Mathews <dolph.mathews@gmail.com>
|
Dolph Mathews <dolph.mathews@gmail.com>
|
||||||
Dominik Honnef <dominik@honnef.co>
|
Dominik Honnef <dominik@honnef.co>
|
||||||
|
Don Kjer <don.kjer@gmail.com>
|
||||||
Don Spaulding <donspauldingii@gmail.com>
|
Don Spaulding <donspauldingii@gmail.com>
|
||||||
Doug Davis <dug@us.ibm.com>
|
Doug Davis <dug@us.ibm.com>
|
||||||
doug tangren <d.tangren@gmail.com>
|
doug tangren <d.tangren@gmail.com>
|
||||||
|
Dr Nic Williams <drnicwilliams@gmail.com>
|
||||||
dragon788 <dragon788@users.noreply.github.com>
|
dragon788 <dragon788@users.noreply.github.com>
|
||||||
Dražen Lučanin <kermit666@gmail.com>
|
Dražen Lučanin <kermit666@gmail.com>
|
||||||
Dr Nic Williams <drnicwilliams@gmail.com>
|
|
||||||
Dustin Sallings <dustin@spy.net>
|
Dustin Sallings <dustin@spy.net>
|
||||||
Edmund Wagner <edmund-wagner@web.de>
|
Edmund Wagner <edmund-wagner@web.de>
|
||||||
Eiichi Tsukata <devel@etsukata.com>
|
Eiichi Tsukata <devel@etsukata.com>
|
||||||
@@ -197,19 +213,22 @@ Eike Herzbach <eike@herzbach.net>
|
|||||||
Eivind Uggedal <eivind@uggedal.com>
|
Eivind Uggedal <eivind@uggedal.com>
|
||||||
Elias Probst <mail@eliasprobst.eu>
|
Elias Probst <mail@eliasprobst.eu>
|
||||||
Emil Hernvall <emil@quench.at>
|
Emil Hernvall <emil@quench.at>
|
||||||
|
Emily Maier <emily@emilymaier.net>
|
||||||
Emily Rose <emily@contactvibe.com>
|
Emily Rose <emily@contactvibe.com>
|
||||||
Eric Hanchrow <ehanchrow@ine.com>
|
Eric Hanchrow <ehanchrow@ine.com>
|
||||||
Eric Lee <thenorthsecedes@gmail.com>
|
Eric Lee <thenorthsecedes@gmail.com>
|
||||||
Eric Myhre <hash@exultant.us>
|
Eric Myhre <hash@exultant.us>
|
||||||
Eric Paris <eparis@redhat.com>
|
Eric Paris <eparis@redhat.com>
|
||||||
Eric Windisch <ewindisch@docker.com>
|
Eric Windisch <ewindisch@docker.com>
|
||||||
|
Erik Dubbelboer <erik@dubbelboer.com>
|
||||||
Erik Hollensbe <github@hollensbe.org>
|
Erik Hollensbe <github@hollensbe.org>
|
||||||
Erik Inge Bolsø <knan@redpill-linpro.com>
|
Erik Inge Bolsø <knan@redpill-linpro.com>
|
||||||
Erik Kristensen <erik@erikkristensen.com>
|
Erik Kristensen <erik@erikkristensen.com>
|
||||||
Erno Hopearuoho <erno.hopearuoho@gmail.com>
|
Erno Hopearuoho <erno.hopearuoho@gmail.com>
|
||||||
|
Erwin van der Koogh <info@erronis.nl>
|
||||||
Eugene Yakubovich <eugene.yakubovich@coreos.com>
|
Eugene Yakubovich <eugene.yakubovich@coreos.com>
|
||||||
eugenkrizo <eugen.krizo@gmail.com>
|
eugenkrizo <eugen.krizo@gmail.com>
|
||||||
evanderkoogh <info@erronis.nl>
|
Evan Carmi <carmi@users.noreply.github.com>
|
||||||
Evan Hazlett <ejhazlett@gmail.com>
|
Evan Hazlett <ejhazlett@gmail.com>
|
||||||
Evan Krall <krall@yelp.com>
|
Evan Krall <krall@yelp.com>
|
||||||
Evan Phoenix <evan@fallingsnow.net>
|
Evan Phoenix <evan@fallingsnow.net>
|
||||||
@@ -230,9 +249,9 @@ Francisco Carriedo <fcarriedo@gmail.com>
|
|||||||
Francisco Souza <f@souza.cc>
|
Francisco Souza <f@souza.cc>
|
||||||
Frank Macreery <frank@macreery.com>
|
Frank Macreery <frank@macreery.com>
|
||||||
Frank Rosquin <frank.rosquin+github@gmail.com>
|
Frank Rosquin <frank.rosquin+github@gmail.com>
|
||||||
|
Fred Lifton <fred.lifton@docker.com>
|
||||||
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
|
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
|
||||||
Frederik Loeffert <frederik@zitrusmedia.de>
|
Frederik Loeffert <frederik@zitrusmedia.de>
|
||||||
Fred Lifton <fred.lifton@docker.com>
|
|
||||||
Freek Kalter <freek@kalteronline.org>
|
Freek Kalter <freek@kalteronline.org>
|
||||||
Gabe Rosenhouse <gabe@missionst.com>
|
Gabe Rosenhouse <gabe@missionst.com>
|
||||||
Gabor Nagy <mail@aigeruth.hu>
|
Gabor Nagy <mail@aigeruth.hu>
|
||||||
@@ -266,13 +285,15 @@ Hector Castro <hectcastro@gmail.com>
|
|||||||
Henning Sprang <henning.sprang@gmail.com>
|
Henning Sprang <henning.sprang@gmail.com>
|
||||||
Hobofan <goisser94@gmail.com>
|
Hobofan <goisser94@gmail.com>
|
||||||
Hollie Teal <hollie@docker.com>
|
Hollie Teal <hollie@docker.com>
|
||||||
|
Hu Keping <hukeping@huawei.com>
|
||||||
|
Hu Tao <hutao@cn.fujitsu.com>
|
||||||
Huayi Zhang <irachex@gmail.com>
|
Huayi Zhang <irachex@gmail.com>
|
||||||
Hugo Duncan <hugo@hugoduncan.org>
|
Hugo Duncan <hugo@hugoduncan.org>
|
||||||
Hunter Blanks <hunter@twilio.com>
|
Hunter Blanks <hunter@twilio.com>
|
||||||
Hu Tao <hutao@cn.fujitsu.com>
|
|
||||||
Huu Nguyen <huu@prismskylabs.com>
|
Huu Nguyen <huu@prismskylabs.com>
|
||||||
hyeongkyu.lee <hyeongkyu.lee@navercorp.com>
|
hyeongkyu.lee <hyeongkyu.lee@navercorp.com>
|
||||||
Ian Babrou <ibobrik@gmail.com>
|
Ian Babrou <ibobrik@gmail.com>
|
||||||
|
Ian Bishop <ianbishop@pace7.com>
|
||||||
Ian Bull <irbull@gmail.com>
|
Ian Bull <irbull@gmail.com>
|
||||||
Ian Main <imain@redhat.com>
|
Ian Main <imain@redhat.com>
|
||||||
Ian Truslove <ian.truslove@gmail.com>
|
Ian Truslove <ian.truslove@gmail.com>
|
||||||
@@ -284,8 +305,10 @@ Isabel Jimenez <contact.isabeljimenez@gmail.com>
|
|||||||
Isao Jonas <isao.jonas@gmail.com>
|
Isao Jonas <isao.jonas@gmail.com>
|
||||||
Ivan Fraixedes <ifcdev@gmail.com>
|
Ivan Fraixedes <ifcdev@gmail.com>
|
||||||
Jack Danger Canty <jackdanger@squareup.com>
|
Jack Danger Canty <jackdanger@squareup.com>
|
||||||
jakedt <jake@devtable.com>
|
Jacob Atzen <jacob@jacobatzen.dk>
|
||||||
|
Jacob Edelman <edelman.jd@gmail.com>
|
||||||
Jake Moshenko <jake@devtable.com>
|
Jake Moshenko <jake@devtable.com>
|
||||||
|
jakedt <jake@devtable.com>
|
||||||
James Allen <jamesallen0108@gmail.com>
|
James Allen <jamesallen0108@gmail.com>
|
||||||
James Carr <james.r.carr@gmail.com>
|
James Carr <james.r.carr@gmail.com>
|
||||||
James DeFelice <james.defelice@ishisystems.com>
|
James DeFelice <james.defelice@ishisystems.com>
|
||||||
@@ -306,49 +329,52 @@ Jason Plum <jplum@devonit.com>
|
|||||||
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
|
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
|
||||||
Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
|
Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
|
||||||
Jean-Paul Calderone <exarkun@twistedmatrix.com>
|
Jean-Paul Calderone <exarkun@twistedmatrix.com>
|
||||||
|
Jean-Tiare Le Bigot <jt@yadutaf.fr>
|
||||||
|
Jeff Anderson <jeff@docker.com>
|
||||||
Jeff Lindsay <progrium@gmail.com>
|
Jeff Lindsay <progrium@gmail.com>
|
||||||
Jeffrey Bolle <jeffreybolle@gmail.com>
|
|
||||||
Jeff Welch <whatthejeff@gmail.com>
|
Jeff Welch <whatthejeff@gmail.com>
|
||||||
|
Jeffrey Bolle <jeffreybolle@gmail.com>
|
||||||
Jeremy Grosser <jeremy@synack.me>
|
Jeremy Grosser <jeremy@synack.me>
|
||||||
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
|
|
||||||
Jesse Dubay <jesse@thefortytwo.net>
|
Jesse Dubay <jesse@thefortytwo.net>
|
||||||
Jessica Frazelle <jess@docker.com>
|
Jessica Frazelle <jess@docker.com>
|
||||||
Jezeniel Zapanta <jpzapanta22@gmail.com>
|
Jezeniel Zapanta <jpzapanta22@gmail.com>
|
||||||
Jilles Oldenbeuving <ojilles@gmail.com>
|
Jilles Oldenbeuving <ojilles@gmail.com>
|
||||||
Jim Alateras <jima@comware.com.au>
|
Jim Alateras <jima@comware.com.au>
|
||||||
Jimmy Cuadra <jimmy@jimmycuadra.com>
|
|
||||||
Jim Perrin <jperrin@centos.org>
|
Jim Perrin <jperrin@centos.org>
|
||||||
|
Jimmy Cuadra <jimmy@jimmycuadra.com>
|
||||||
Jiří Župka <jzupka@redhat.com>
|
Jiří Župka <jzupka@redhat.com>
|
||||||
Joe Beda <joe.github@bedafamily.com>
|
Joe Beda <joe.github@bedafamily.com>
|
||||||
Joe Ferguson <joe@infosiftr.com>
|
Joe Ferguson <joe@infosiftr.com>
|
||||||
Joel Handwell <joelhandwell@gmail.com>
|
|
||||||
Joe Shaw <joe@joeshaw.org>
|
Joe Shaw <joe@joeshaw.org>
|
||||||
Joe Van Dyk <joe@tanga.com>
|
Joe Van Dyk <joe@tanga.com>
|
||||||
|
Joel Friedly <joelfriedly@gmail.com>
|
||||||
|
Joel Handwell <joelhandwell@gmail.com>
|
||||||
Joffrey F <joffrey@docker.com>
|
Joffrey F <joffrey@docker.com>
|
||||||
Johan Euphrosine <proppy@google.com>
|
Johan Euphrosine <proppy@google.com>
|
||||||
Johannes 'fish' Ziemke <github@freigeist.org>
|
|
||||||
Johan Rydberg <johan.rydberg@gmail.com>
|
Johan Rydberg <johan.rydberg@gmail.com>
|
||||||
|
Johannes 'fish' Ziemke <github@freigeist.org>
|
||||||
John Costa <john.costa@gmail.com>
|
John Costa <john.costa@gmail.com>
|
||||||
John Feminella <jxf@jxf.me>
|
John Feminella <jxf@jxf.me>
|
||||||
John Gardiner Myers <jgmyers@proofpoint.com>
|
John Gardiner Myers <jgmyers@proofpoint.com>
|
||||||
John Gossman <johngos@microsoft.com>
|
John Gossman <johngos@microsoft.com>
|
||||||
John OBrien III <jobrieniii@yahoo.com>
|
John OBrien III <jobrieniii@yahoo.com>
|
||||||
John Warwick <jwarwick@gmail.com>
|
John Warwick <jwarwick@gmail.com>
|
||||||
|
Jon Wedaman <jweede@gmail.com>
|
||||||
Jonas Pfenniger <jonas@pfenniger.name>
|
Jonas Pfenniger <jonas@pfenniger.name>
|
||||||
|
Jonathan A. Sternberg <jonathansternberg@gmail.com>
|
||||||
Jonathan Boulle <jonathanboulle@gmail.com>
|
Jonathan Boulle <jonathanboulle@gmail.com>
|
||||||
Jonathan Camp <jonathan@irondojo.com>
|
Jonathan Camp <jonathan@irondojo.com>
|
||||||
Jonathan McCrohan <jmccrohan@gmail.com>
|
Jonathan McCrohan <jmccrohan@gmail.com>
|
||||||
Jonathan Mueller <j.mueller@apoveda.ch>
|
Jonathan Mueller <j.mueller@apoveda.ch>
|
||||||
Jonathan Pares <jonathanpa@users.noreply.github.com>
|
Jonathan Pares <jonathanpa@users.noreply.github.com>
|
||||||
Jonathan Rudenberg <jonathan@titanous.com>
|
Jonathan Rudenberg <jonathan@titanous.com>
|
||||||
Jon Wedaman <jweede@gmail.com>
|
|
||||||
Joost Cassee <joost@cassee.net>
|
Joost Cassee <joost@cassee.net>
|
||||||
Jordan Arentsen <blissdev@gmail.com>
|
Jordan Arentsen <blissdev@gmail.com>
|
||||||
Jordan Sissel <jls@semicomplete.com>
|
Jordan Sissel <jls@semicomplete.com>
|
||||||
Joseph Anthony Pasquale Holsten <joseph@josephholsten.com>
|
Joseph Anthony Pasquale Holsten <joseph@josephholsten.com>
|
||||||
Joseph Hager <ajhager@gmail.com>
|
Joseph Hager <ajhager@gmail.com>
|
||||||
Josh Hawn <josh.hawn@docker.com>
|
|
||||||
Josh <jokajak@gmail.com>
|
Josh <jokajak@gmail.com>
|
||||||
|
Josh Hawn <josh.hawn@docker.com>
|
||||||
Josh Poimboeuf <jpoimboe@redhat.com>
|
Josh Poimboeuf <jpoimboe@redhat.com>
|
||||||
Josiah Kiehl <jkiehl@riotgames.com>
|
Josiah Kiehl <jkiehl@riotgames.com>
|
||||||
JP <jpellerin@leapfrogonline.com>
|
JP <jpellerin@leapfrogonline.com>
|
||||||
@@ -360,6 +386,9 @@ Justin Force <justin.force@gmail.com>
|
|||||||
Justin Plock <jplock@users.noreply.github.com>
|
Justin Plock <jplock@users.noreply.github.com>
|
||||||
Justin Simonelis <justin.p.simonelis@gmail.com>
|
Justin Simonelis <justin.p.simonelis@gmail.com>
|
||||||
Jyrki Puttonen <jyrkiput@gmail.com>
|
Jyrki Puttonen <jyrkiput@gmail.com>
|
||||||
|
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
|
||||||
|
Jörg Thalheim <joerg@higgsboson.tk>
|
||||||
|
Kamil Domanski <kamil@domanski.co>
|
||||||
Karan Lyons <karan@karanlyons.com>
|
Karan Lyons <karan@karanlyons.com>
|
||||||
Karl Grzeszczak <karlgrz@gmail.com>
|
Karl Grzeszczak <karlgrz@gmail.com>
|
||||||
Kato Kazuyoshi <kato.kazuyoshi@gmail.com>
|
Kato Kazuyoshi <kato.kazuyoshi@gmail.com>
|
||||||
@@ -367,14 +396,13 @@ Kawsar Saiyeed <kawsar.saiyeed@projiris.com>
|
|||||||
Keli Hu <dev@keli.hu>
|
Keli Hu <dev@keli.hu>
|
||||||
Ken Cochrane <kencochrane@gmail.com>
|
Ken Cochrane <kencochrane@gmail.com>
|
||||||
Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com>
|
Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com>
|
||||||
|
Kevin "qwazerty" Houdebert <kevin.houdebert@gmail.com>
|
||||||
Kevin Clark <kevin.clark@gmail.com>
|
Kevin Clark <kevin.clark@gmail.com>
|
||||||
Kevin J. Lynagh <kevin@keminglabs.com>
|
Kevin J. Lynagh <kevin@keminglabs.com>
|
||||||
Kevin Menard <kevin@nirvdrum.com>
|
Kevin Menard <kevin@nirvdrum.com>
|
||||||
Kevin "qwazerty" Houdebert <kevin.houdebert@gmail.com>
|
|
||||||
Kevin Wallace <kevin@pentabarf.net>
|
Kevin Wallace <kevin@pentabarf.net>
|
||||||
Keyvan Fatehi <keyvanfatehi@gmail.com>
|
Keyvan Fatehi <keyvanfatehi@gmail.com>
|
||||||
kies <lleelm@gmail.com>
|
kies <lleelm@gmail.com>
|
||||||
kim0 <email.ahmedkamal@googlemail.com>
|
|
||||||
Kim BKC Carlbacker <kim.carlbacker@gmail.com>
|
Kim BKC Carlbacker <kim.carlbacker@gmail.com>
|
||||||
Kimbro Staken <kstaken@kstaken.com>
|
Kimbro Staken <kstaken@kstaken.com>
|
||||||
Kiran Gangadharan <kiran.daredevil@gmail.com>
|
Kiran Gangadharan <kiran.daredevil@gmail.com>
|
||||||
@@ -382,6 +410,7 @@ knappe <tyler.knappe@gmail.com>
|
|||||||
Kohei Tsuruta <coheyxyz@gmail.com>
|
Kohei Tsuruta <coheyxyz@gmail.com>
|
||||||
Konrad Kleine <konrad.wilhelm.kleine@gmail.com>
|
Konrad Kleine <konrad.wilhelm.kleine@gmail.com>
|
||||||
Konstantin Pelykh <kpelykh@zettaset.com>
|
Konstantin Pelykh <kpelykh@zettaset.com>
|
||||||
|
Krasimir Georgiev <support@vip-consult.co.uk>
|
||||||
krrg <krrgithub@gmail.com>
|
krrg <krrgithub@gmail.com>
|
||||||
Kyle Conroy <kyle.j.conroy@gmail.com>
|
Kyle Conroy <kyle.j.conroy@gmail.com>
|
||||||
kyu <leehk1227@gmail.com>
|
kyu <leehk1227@gmail.com>
|
||||||
@@ -397,13 +426,16 @@ Lei Jitang <leijitang@huawei.com>
|
|||||||
Len Weincier <len@cloudafrica.net>
|
Len Weincier <len@cloudafrica.net>
|
||||||
Leszek Kowalski <github@leszekkowalski.pl>
|
Leszek Kowalski <github@leszekkowalski.pl>
|
||||||
Levi Gross <levi@levigross.com>
|
Levi Gross <levi@levigross.com>
|
||||||
|
Lewis Marshall <lewis@lmars.net>
|
||||||
Lewis Peckover <lew+github@lew.io>
|
Lewis Peckover <lew+github@lew.io>
|
||||||
Liang-Chi Hsieh <viirya@gmail.com>
|
Liang-Chi Hsieh <viirya@gmail.com>
|
||||||
limsy <seongyeol37@gmail.com>
|
limsy <seongyeol37@gmail.com>
|
||||||
Lokesh Mandvekar <lsm5@fedoraproject.org>
|
Lokesh Mandvekar <lsm5@fedoraproject.org>
|
||||||
|
Lorenz Leutgeb <lorenz.leutgeb@gmail.com>
|
||||||
Louis Opter <kalessin@kalessin.fr>
|
Louis Opter <kalessin@kalessin.fr>
|
||||||
lukaspustina <lukas.pustina@centerdevice.com>
|
lukaspustina <lukas.pustina@centerdevice.com>
|
||||||
lukemarsden <luke@digital-crocus.com>
|
lukemarsden <luke@digital-crocus.com>
|
||||||
|
Lénaïc Huard <lhuard@amadeus.com>
|
||||||
Madhu Venugopal <madhu@socketplane.io>
|
Madhu Venugopal <madhu@socketplane.io>
|
||||||
Mahesh Tiyyagura <tmahesh@gmail.com>
|
Mahesh Tiyyagura <tmahesh@gmail.com>
|
||||||
Malte Janduda <mail@janduda.net>
|
Malte Janduda <mail@janduda.net>
|
||||||
@@ -412,12 +444,13 @@ Manuel Meurer <manuel@krautcomputing.com>
|
|||||||
Manuel Woelker <github@manuel.woelker.org>
|
Manuel Woelker <github@manuel.woelker.org>
|
||||||
Marc Abramowitz <marc@marc-abramowitz.com>
|
Marc Abramowitz <marc@marc-abramowitz.com>
|
||||||
Marc Kuo <kuomarc2@gmail.com>
|
Marc Kuo <kuomarc2@gmail.com>
|
||||||
Marco Hennings <marco.hennings@freiheit.com>
|
|
||||||
Marc Tamsky <mtamsky@gmail.com>
|
Marc Tamsky <mtamsky@gmail.com>
|
||||||
|
Marco Hennings <marco.hennings@freiheit.com>
|
||||||
Marcus Farkas <toothlessgear@finitebox.com>
|
Marcus Farkas <toothlessgear@finitebox.com>
|
||||||
marcuslinke <marcus.linke@gmx.de>
|
Marcus Linke <marcus.linke@gmx.de>
|
||||||
Marcus Ramberg <marcus@nordaaker.com>
|
Marcus Ramberg <marcus@nordaaker.com>
|
||||||
Marek Goldmann <marek.goldmann@gmail.com>
|
Marek Goldmann <marek.goldmann@gmail.com>
|
||||||
|
Marianna <mtesselh@gmail.com>
|
||||||
Marius Voila <marius.voila@gmail.com>
|
Marius Voila <marius.voila@gmail.com>
|
||||||
Mark Allen <mrallen1@yahoo.com>
|
Mark Allen <mrallen1@yahoo.com>
|
||||||
Mark McGranaghan <mmcgrana@gmail.com>
|
Mark McGranaghan <mmcgrana@gmail.com>
|
||||||
@@ -425,7 +458,9 @@ Marko Mikulicic <mmikulicic@gmail.com>
|
|||||||
Marko Tibold <marko@tibold.nl>
|
Marko Tibold <marko@tibold.nl>
|
||||||
Markus Fix <lispmeister@gmail.com>
|
Markus Fix <lispmeister@gmail.com>
|
||||||
Martijn van Oosterhout <kleptog@svana.org>
|
Martijn van Oosterhout <kleptog@svana.org>
|
||||||
|
Martin Honermeyer <maze@strahlungsfrei.de>
|
||||||
Martin Redmond <martin@tinychat.com>
|
Martin Redmond <martin@tinychat.com>
|
||||||
|
Mary Anthony <moxieandmore@gmail.com>
|
||||||
Mason Malone <mason.malone@gmail.com>
|
Mason Malone <mason.malone@gmail.com>
|
||||||
Mateusz Sulima <sulima.mateusz@gmail.com>
|
Mateusz Sulima <sulima.mateusz@gmail.com>
|
||||||
Mathias Monnerville <mathias@monnerville.com>
|
Mathias Monnerville <mathias@monnerville.com>
|
||||||
@@ -435,14 +470,14 @@ Matt Bachmann <bachmann.matt@gmail.com>
|
|||||||
Matt Haggard <haggardii@gmail.com>
|
Matt Haggard <haggardii@gmail.com>
|
||||||
Matthew Heon <mheon@redhat.com>
|
Matthew Heon <mheon@redhat.com>
|
||||||
Matthew Mueller <mattmuelle@gmail.com>
|
Matthew Mueller <mattmuelle@gmail.com>
|
||||||
|
Matthew Riley <mattdr@google.com>
|
||||||
Matthias Klumpp <matthias@tenstral.net>
|
Matthias Klumpp <matthias@tenstral.net>
|
||||||
Matthias Kühnle <git.nivoc@neverbox.com>
|
Matthias Kühnle <git.nivoc@neverbox.com>
|
||||||
mattymo <raytrac3r@gmail.com>
|
mattymo <raytrac3r@gmail.com>
|
||||||
mattyw <mattyw@me.com>
|
mattyw <mattyw@me.com>
|
||||||
Maxime Petazzoni <max@signalfuse.com>
|
|
||||||
Maxim Treskin <zerthurd@gmail.com>
|
|
||||||
Max Shytikov <mshytikov@gmail.com>
|
Max Shytikov <mshytikov@gmail.com>
|
||||||
Médi-Rémi Hashim <medimatrix@users.noreply.github.com>
|
Maxim Treskin <zerthurd@gmail.com>
|
||||||
|
Maxime Petazzoni <max@signalfuse.com>
|
||||||
meejah <meejah@meejah.ca>
|
meejah <meejah@meejah.ca>
|
||||||
Mengdi Gao <usrgdd@gmail.com>
|
Mengdi Gao <usrgdd@gmail.com>
|
||||||
Mert Yazıcıoğlu <merty@users.noreply.github.com>
|
Mert Yazıcıoğlu <merty@users.noreply.github.com>
|
||||||
@@ -451,12 +486,14 @@ Michael Crosby <michael@docker.com>
|
|||||||
Michael Gorsuch <gorsuch@github.com>
|
Michael Gorsuch <gorsuch@github.com>
|
||||||
Michael Hudson-Doyle <michael.hudson@linaro.org>
|
Michael Hudson-Doyle <michael.hudson@linaro.org>
|
||||||
Michael Neale <michael.neale@gmail.com>
|
Michael Neale <michael.neale@gmail.com>
|
||||||
Michaël Pailloncy <mpapo.dev@gmail.com>
|
|
||||||
Michael Prokop <github@michael-prokop.at>
|
Michael Prokop <github@michael-prokop.at>
|
||||||
Michael Scharf <github@scharf.gr>
|
Michael Scharf <github@scharf.gr>
|
||||||
Michael Stapelberg <michael+gh@stapelberg.de>
|
Michael Stapelberg <michael+gh@stapelberg.de>
|
||||||
|
Michael Steinert <mike.steinert@gmail.com>
|
||||||
Michael Thies <michaelthies78@gmail.com>
|
Michael Thies <michaelthies78@gmail.com>
|
||||||
Michal Jemala <michal.jemala@gmail.com>
|
Michal Jemala <michal.jemala@gmail.com>
|
||||||
|
Michal Minar <miminar@redhat.com>
|
||||||
|
Michaël Pailloncy <mpapo.dev@gmail.com>
|
||||||
Michiel@unhosted <michiel@unhosted.org>
|
Michiel@unhosted <michiel@unhosted.org>
|
||||||
Miguel Angel Fernández <elmendalerenda@gmail.com>
|
Miguel Angel Fernández <elmendalerenda@gmail.com>
|
||||||
Mike Chelen <michael.chelen@gmail.com>
|
Mike Chelen <michael.chelen@gmail.com>
|
||||||
@@ -471,12 +508,15 @@ Morten Siebuhr <sbhr@sbhr.dk>
|
|||||||
Mrunal Patel <mrunalp@gmail.com>
|
Mrunal Patel <mrunalp@gmail.com>
|
||||||
mschurenko <matt.schurenko@gmail.com>
|
mschurenko <matt.schurenko@gmail.com>
|
||||||
Mustafa Akın <mustafa91@gmail.com>
|
Mustafa Akın <mustafa91@gmail.com>
|
||||||
|
Médi-Rémi Hashim <medimatrix@users.noreply.github.com>
|
||||||
Nan Monnand Deng <monnand@gmail.com>
|
Nan Monnand Deng <monnand@gmail.com>
|
||||||
Naoki Orii <norii@cs.cmu.edu>
|
Naoki Orii <norii@cs.cmu.edu>
|
||||||
|
Nate Eagleson <nate@nateeag.com>
|
||||||
Nate Jones <nate@endot.org>
|
Nate Jones <nate@endot.org>
|
||||||
Nathan Hsieh <hsieh.nathan@gmail.com>
|
Nathan Hsieh <hsieh.nathan@gmail.com>
|
||||||
Nathan Kleyn <nathan@nathankleyn.com>
|
Nathan Kleyn <nathan@nathankleyn.com>
|
||||||
Nathan LeClaire <nathan.leclaire@docker.com>
|
Nathan LeClaire <nathan.leclaire@docker.com>
|
||||||
|
Neal McBurnett <neal@mcburnett.org>
|
||||||
Nelson Chen <crazysim@gmail.com>
|
Nelson Chen <crazysim@gmail.com>
|
||||||
Niall O'Higgins <niallo@unworkable.org>
|
Niall O'Higgins <niallo@unworkable.org>
|
||||||
Nicholas E. Rabenau <nerab@gmx.at>
|
Nicholas E. Rabenau <nerab@gmx.at>
|
||||||
@@ -491,18 +531,21 @@ NikolaMandic <mn080202@gmail.com>
|
|||||||
noducks <onemannoducks@gmail.com>
|
noducks <onemannoducks@gmail.com>
|
||||||
Nolan Darilek <nolan@thewordnerd.info>
|
Nolan Darilek <nolan@thewordnerd.info>
|
||||||
nzwsch <hi@nzwsch.com>
|
nzwsch <hi@nzwsch.com>
|
||||||
|
O.S. Tezer <ostezer@gmail.com>
|
||||||
OddBloke <daniel@daniel-watkins.co.uk>
|
OddBloke <daniel@daniel-watkins.co.uk>
|
||||||
odk- <github@odkurzacz.org>
|
odk- <github@odkurzacz.org>
|
||||||
Oguz Bilgic <fisyonet@gmail.com>
|
Oguz Bilgic <fisyonet@gmail.com>
|
||||||
Oh Jinkyun <tintypemolly@gmail.com>
|
Oh Jinkyun <tintypemolly@gmail.com>
|
||||||
Ole Reifschneider <mail@ole-reifschneider.de>
|
Ole Reifschneider <mail@ole-reifschneider.de>
|
||||||
Olivier Gambier <dmp42@users.noreply.github.com>
|
Olivier Gambier <dmp42@users.noreply.github.com>
|
||||||
O.S. Tezer <ostezer@gmail.com>
|
|
||||||
pandrew <letters@paulnotcom.se>
|
pandrew <letters@paulnotcom.se>
|
||||||
|
panticz <mail@konczalski.de>
|
||||||
Pascal Borreli <pascal@borreli.com>
|
Pascal Borreli <pascal@borreli.com>
|
||||||
Pascal Hartig <phartig@rdrei.net>
|
Pascal Hartig <phartig@rdrei.net>
|
||||||
Patrick Hemmer <patrick.hemmer@gmail.com>
|
Patrick Hemmer <patrick.hemmer@gmail.com>
|
||||||
|
Patrick Stapleton <github@gdi2290.com>
|
||||||
pattichen <craftsbear@gmail.com>
|
pattichen <craftsbear@gmail.com>
|
||||||
|
Paul <paul9869@gmail.com>
|
||||||
Paul Annesley <paul@annesley.cc>
|
Paul Annesley <paul@annesley.cc>
|
||||||
Paul Bowsher <pbowsher@globalpersonals.co.uk>
|
Paul Bowsher <pbowsher@globalpersonals.co.uk>
|
||||||
Paul Hammond <paul@paulhammond.org>
|
Paul Hammond <paul@paulhammond.org>
|
||||||
@@ -510,7 +553,6 @@ Paul Jimenez <pj@place.org>
|
|||||||
Paul Lietar <paul@lietar.net>
|
Paul Lietar <paul@lietar.net>
|
||||||
Paul Morie <pmorie@gmail.com>
|
Paul Morie <pmorie@gmail.com>
|
||||||
Paul Nasrat <pnasrat@gmail.com>
|
Paul Nasrat <pnasrat@gmail.com>
|
||||||
Paul <paul9869@gmail.com>
|
|
||||||
Paul Weaver <pauweave@cisco.com>
|
Paul Weaver <pauweave@cisco.com>
|
||||||
Pavlos Ratis <dastergon@gentoo.org>
|
Pavlos Ratis <dastergon@gentoo.org>
|
||||||
Peter Bourgon <peter@bourgon.org>
|
Peter Bourgon <peter@bourgon.org>
|
||||||
@@ -518,16 +560,18 @@ Peter Braden <peterbraden@peterbraden.co.uk>
|
|||||||
Peter Ericson <pdericson@gmail.com>
|
Peter Ericson <pdericson@gmail.com>
|
||||||
Peter Salvatore <peter@psftw.com>
|
Peter Salvatore <peter@psftw.com>
|
||||||
Peter Waller <p@pwaller.net>
|
Peter Waller <p@pwaller.net>
|
||||||
|
Phil <underscorephil@gmail.com>
|
||||||
Phil Estes <estesp@linux.vnet.ibm.com>
|
Phil Estes <estesp@linux.vnet.ibm.com>
|
||||||
|
Phil Spitler <pspitler@gmail.com>
|
||||||
Philipp Weissensteiner <mail@philippweissensteiner.com>
|
Philipp Weissensteiner <mail@philippweissensteiner.com>
|
||||||
Phillip Alexander <git@phillipalexander.io>
|
Phillip Alexander <git@phillipalexander.io>
|
||||||
Phil Spitler <pspitler@gmail.com>
|
|
||||||
Phil <underscorephil@gmail.com>
|
|
||||||
Piergiuliano Bossi <pgbossi@gmail.com>
|
Piergiuliano Bossi <pgbossi@gmail.com>
|
||||||
Pierre-Alain RIVIERE <pariviere@ippon.fr>
|
|
||||||
Pierre <py@poujade.org>
|
Pierre <py@poujade.org>
|
||||||
|
Pierre Wacrenier <pierre.wacrenier@gmail.com>
|
||||||
|
Pierre-Alain RIVIERE <pariviere@ippon.fr>
|
||||||
Piotr Bogdan <ppbogdan@gmail.com>
|
Piotr Bogdan <ppbogdan@gmail.com>
|
||||||
pixelistik <pixelistik@users.noreply.github.com>
|
pixelistik <pixelistik@users.noreply.github.com>
|
||||||
|
Porjo <porjo38@yahoo.com.au>
|
||||||
Prasanna Gautam <prasannagautam@gmail.com>
|
Prasanna Gautam <prasannagautam@gmail.com>
|
||||||
Przemek Hejman <przemyslaw.hejman@gmail.com>
|
Przemek Hejman <przemyslaw.hejman@gmail.com>
|
||||||
pysqz <randomq@126.com>
|
pysqz <randomq@126.com>
|
||||||
@@ -547,6 +591,7 @@ Renato Riccieri Santos Zannon <renato.riccieri@gmail.com>
|
|||||||
rgstephens <greg@udon.org>
|
rgstephens <greg@udon.org>
|
||||||
Rhys Hiltner <rhys@twitch.tv>
|
Rhys Hiltner <rhys@twitch.tv>
|
||||||
Richard Harvey <richard@squarecows.com>
|
Richard Harvey <richard@squarecows.com>
|
||||||
|
Richard Metzler <richard@paadee.com>
|
||||||
Richo Healey <richo@psych0tik.net>
|
Richo Healey <richo@psych0tik.net>
|
||||||
Rick Bradley <rick@users.noreply.github.com>
|
Rick Bradley <rick@users.noreply.github.com>
|
||||||
Rick van de Loo <rickvandeloo@gmail.com>
|
Rick van de Loo <rickvandeloo@gmail.com>
|
||||||
@@ -572,25 +617,26 @@ Ryan Fowler <rwfowler@gmail.com>
|
|||||||
Ryan O'Donnell <odonnellryanc@gmail.com>
|
Ryan O'Donnell <odonnellryanc@gmail.com>
|
||||||
Ryan Seto <ryanseto@yak.net>
|
Ryan Seto <ryanseto@yak.net>
|
||||||
Ryan Thomas <rthomas@atlassian.com>
|
Ryan Thomas <rthomas@atlassian.com>
|
||||||
|
Rémy Greinhofer <remy.greinhofer@livelovely.com>
|
||||||
Sam Alba <sam.alba@gmail.com>
|
Sam Alba <sam.alba@gmail.com>
|
||||||
Sam Bailey <cyprix@cyprix.com.au>
|
Sam Bailey <cyprix@cyprix.com.au>
|
||||||
Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
|
Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
|
||||||
Sam Reis <sreis@atlassian.com>
|
Sam Reis <sreis@atlassian.com>
|
||||||
Sam Rijs <srijs@airpost.net>
|
Sam Rijs <srijs@airpost.net>
|
||||||
|
Sami Wagiaalla <swagiaal@redhat.com>
|
||||||
Samuel Andaya <samuel@andaya.net>
|
Samuel Andaya <samuel@andaya.net>
|
||||||
Samuel PHAN <samuel-phan@users.noreply.github.com>
|
Samuel PHAN <samuel-phan@users.noreply.github.com>
|
||||||
|
Satnam Singh <satnam@raintown.org>
|
||||||
satoru <satorulogic@gmail.com>
|
satoru <satorulogic@gmail.com>
|
||||||
Satoshi Amemiya <satoshi_amemiya@voyagegroup.com>
|
Satoshi Amemiya <satoshi_amemiya@voyagegroup.com>
|
||||||
Scott Bessler <scottbessler@gmail.com>
|
Scott Bessler <scottbessler@gmail.com>
|
||||||
Scott Collier <emailscottcollier@gmail.com>
|
Scott Collier <emailscottcollier@gmail.com>
|
||||||
Scott Johnston <scott@docker.com>
|
Scott Johnston <scott@docker.com>
|
||||||
|
Scott Stamp <scottstamp851@gmail.com>
|
||||||
Scott Walls <sawalls@umich.edu>
|
Scott Walls <sawalls@umich.edu>
|
||||||
Sean Cronin <seancron@gmail.com>
|
Sean Cronin <seancron@gmail.com>
|
||||||
Sean P. Kane <skane@newrelic.com>
|
Sean P. Kane <skane@newrelic.com>
|
||||||
Sebastiaan van Stijn <github@gone.nl>
|
Sebastiaan van Stijn <github@gone.nl>
|
||||||
Sébastien Luttringer <seblu@seblu.net>
|
|
||||||
Sébastien <sebastien@yoozio.com>
|
|
||||||
Sébastien Stormacq <sebsto@users.noreply.github.com>
|
|
||||||
Senthil Kumar Selvaraj <senthil.thecoder@gmail.com>
|
Senthil Kumar Selvaraj <senthil.thecoder@gmail.com>
|
||||||
SeongJae Park <sj38.park@gmail.com>
|
SeongJae Park <sj38.park@gmail.com>
|
||||||
Shane Canon <scanon@lbl.gov>
|
Shane Canon <scanon@lbl.gov>
|
||||||
@@ -598,12 +644,12 @@ shaunol <shaunol@gmail.com>
|
|||||||
Shawn Landden <shawn@churchofgit.com>
|
Shawn Landden <shawn@churchofgit.com>
|
||||||
Shawn Siefkas <shawn.siefkas@meredith.com>
|
Shawn Siefkas <shawn.siefkas@meredith.com>
|
||||||
Shih-Yuan Lee <fourdollars@gmail.com>
|
Shih-Yuan Lee <fourdollars@gmail.com>
|
||||||
|
Shishir Mahajan <shishir.mahajan@redhat.com>
|
||||||
shuai-z <zs.broccoli@gmail.com>
|
shuai-z <zs.broccoli@gmail.com>
|
||||||
Silas Sewell <silas@sewell.org>
|
Silas Sewell <silas@sewell.org>
|
||||||
Simon Taranto <simon.taranto@gmail.com>
|
Simon Taranto <simon.taranto@gmail.com>
|
||||||
Sindhu S <sindhus@live.in>
|
Sindhu S <sindhus@live.in>
|
||||||
Sjoerd Langkemper <sjoerd-github@linuxonly.nl>
|
Sjoerd Langkemper <sjoerd-github@linuxonly.nl>
|
||||||
s-ko <aleks@s-ko.net>
|
|
||||||
Solomon Hykes <solomon@docker.com>
|
Solomon Hykes <solomon@docker.com>
|
||||||
Song Gao <song@gao.io>
|
Song Gao <song@gao.io>
|
||||||
Soulou <leo@unbekandt.eu>
|
Soulou <leo@unbekandt.eu>
|
||||||
@@ -611,18 +657,23 @@ soulshake <amy@gandi.net>
|
|||||||
Sridatta Thatipamala <sthatipamala@gmail.com>
|
Sridatta Thatipamala <sthatipamala@gmail.com>
|
||||||
Sridhar Ratnakumar <sridharr@activestate.com>
|
Sridhar Ratnakumar <sridharr@activestate.com>
|
||||||
Srini Brahmaroutu <sbrahma@us.ibm.com>
|
Srini Brahmaroutu <sbrahma@us.ibm.com>
|
||||||
|
Srini Brahmaroutu <srbrahma@us.ibm.com>
|
||||||
Steeve Morin <steeve.morin@gmail.com>
|
Steeve Morin <steeve.morin@gmail.com>
|
||||||
Stefan Praszalowicz <stefan@greplin.com>
|
Stefan Praszalowicz <stefan@greplin.com>
|
||||||
Stephen Crosby <stevecrozz@gmail.com>
|
Stephen Crosby <stevecrozz@gmail.com>
|
||||||
Steven Burgess <steven.a.burgess@hotmail.com>
|
Steven Burgess <steven.a.burgess@hotmail.com>
|
||||||
Steven Merrill <steven.merrill@gmail.com>
|
Steven Merrill <steven.merrill@gmail.com>
|
||||||
sudosurootdev <sudosurootdev@gmail.com>
|
|
||||||
Sven Dowideit <SvenDowideit@home.org.au>
|
Sven Dowideit <SvenDowideit@home.org.au>
|
||||||
Sylvain Bellemare <sylvain.bellemare@ezeep.com>
|
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>
|
||||||
tang0th <tang0th@gmx.com>
|
tang0th <tang0th@gmx.com>
|
||||||
Tatsuki Sugiura <sugi@nemui.org>
|
Tatsuki Sugiura <sugi@nemui.org>
|
||||||
|
Tatsushi Inagaki <e29253@jp.ibm.com>
|
||||||
Ted M. Young <tedyoung@gmail.com>
|
Ted M. Young <tedyoung@gmail.com>
|
||||||
Tehmasp Chaudhri <tehmasp@gmail.com>
|
Tehmasp Chaudhri <tehmasp@gmail.com>
|
||||||
|
Tejesh Mehta <tejesh.mehta@gmail.com>
|
||||||
Thatcher Peskens <thatcher@docker.com>
|
Thatcher Peskens <thatcher@docker.com>
|
||||||
Thermionix <bond711@gmail.com>
|
Thermionix <bond711@gmail.com>
|
||||||
Thijs Terlouw <thijsterlouw@gmail.com>
|
Thijs Terlouw <thijsterlouw@gmail.com>
|
||||||
@@ -636,11 +687,10 @@ Tianon Gravi <admwiggin@gmail.com>
|
|||||||
Tibor Vass <teabee89@gmail.com>
|
Tibor Vass <teabee89@gmail.com>
|
||||||
Tim Bosse <taim@bosboot.org>
|
Tim Bosse <taim@bosboot.org>
|
||||||
Tim Hockin <thockin@google.com>
|
Tim Hockin <thockin@google.com>
|
||||||
Timothy Hobbs <timothyhobbs@seznam.cz>
|
|
||||||
Tim Ruffles <oi@truffles.me.uk>
|
Tim Ruffles <oi@truffles.me.uk>
|
||||||
Tim Smith <timbot@google.com>
|
Tim Smith <timbot@google.com>
|
||||||
Tim Terhorst <mynamewastaken+git@gmail.com>
|
Tim Terhorst <mynamewastaken+git@gmail.com>
|
||||||
tjmehta <tj@init.me>
|
Timothy Hobbs <timothyhobbs@seznam.cz>
|
||||||
tjwebb123 <tjwebb123@users.noreply.github.com>
|
tjwebb123 <tjwebb123@users.noreply.github.com>
|
||||||
tobe <tobegit3hub@gmail.com>
|
tobe <tobegit3hub@gmail.com>
|
||||||
Tobias Bieniek <Tobias.Bieniek@gmx.de>
|
Tobias Bieniek <Tobias.Bieniek@gmx.de>
|
||||||
@@ -648,10 +698,12 @@ Tobias Gesellchen <tobias@gesellix.de>
|
|||||||
Tobias Schmidt <ts@soundcloud.com>
|
Tobias Schmidt <ts@soundcloud.com>
|
||||||
Tobias Schwab <tobias.schwab@dynport.de>
|
Tobias Schwab <tobias.schwab@dynport.de>
|
||||||
Todd Lunter <tlunter@gmail.com>
|
Todd Lunter <tlunter@gmail.com>
|
||||||
Tomasz Lipinski <tlipinski@users.noreply.github.com>
|
|
||||||
Tom Fotherby <tom+github@peopleperhour.com>
|
Tom Fotherby <tom+github@peopleperhour.com>
|
||||||
Tom Hulihan <hulihan.tom159@gmail.com>
|
Tom Hulihan <hulihan.tom159@gmail.com>
|
||||||
Tom Maaswinkel <tom.maaswinkel@12wiki.eu>
|
Tom Maaswinkel <tom.maaswinkel@12wiki.eu>
|
||||||
|
Tomas Tomecek <ttomecek@redhat.com>
|
||||||
|
Tomasz Lipinski <tlipinski@users.noreply.github.com>
|
||||||
|
Tomasz Nurkiewicz <nurkiewicz@gmail.com>
|
||||||
Tommaso Visconti <tommaso.visconti@gmail.com>
|
Tommaso Visconti <tommaso.visconti@gmail.com>
|
||||||
Tonis Tiigi <tonistiigi@gmail.com>
|
Tonis Tiigi <tonistiigi@gmail.com>
|
||||||
Tony Daws <tony@daws.ca>
|
Tony Daws <tony@daws.ca>
|
||||||
@@ -662,7 +714,8 @@ Trent Ogren <tedwardo2@gmail.com>
|
|||||||
Tyler Brock <tyler.brock@gmail.com>
|
Tyler Brock <tyler.brock@gmail.com>
|
||||||
Tzu-Jung Lee <roylee17@gmail.com>
|
Tzu-Jung Lee <roylee17@gmail.com>
|
||||||
Ulysse Carion <ulyssecarion@gmail.com>
|
Ulysse Carion <ulyssecarion@gmail.com>
|
||||||
unclejack <unclejacksons@gmail.com>
|
unknown <sebastiaan@ws-key-sebas3.dpi1.dpi>
|
||||||
|
Vaidas Jablonskis <jablonskis@gmail.com>
|
||||||
vgeta <gopikannan.venugopalsamy@gmail.com>
|
vgeta <gopikannan.venugopalsamy@gmail.com>
|
||||||
Victor Coisne <victor.coisne@dotcloud.com>
|
Victor Coisne <victor.coisne@dotcloud.com>
|
||||||
Victor Lyuboslavsky <victor@victoreda.com>
|
Victor Lyuboslavsky <victor@victoreda.com>
|
||||||
@@ -691,15 +744,15 @@ Walter Leibbrandt <github@wrl.co.za>
|
|||||||
Walter Stanish <walter@pratyeka.org>
|
Walter Stanish <walter@pratyeka.org>
|
||||||
Ward Vandewege <ward@jhvc.com>
|
Ward Vandewege <ward@jhvc.com>
|
||||||
WarheadsSE <max@warheads.net>
|
WarheadsSE <max@warheads.net>
|
||||||
|
Wayne Chang <wayne@neverfear.org>
|
||||||
Wes Morgan <cap10morgan@gmail.com>
|
Wes Morgan <cap10morgan@gmail.com>
|
||||||
Will Dietz <w@wdtz.org>
|
Will Dietz <w@wdtz.org>
|
||||||
|
Will Rouesnel <w.rouesnel@gmail.com>
|
||||||
|
Will Weaver <monkey@buildingbananas.com>
|
||||||
William Delanoue <william.delanoue@gmail.com>
|
William Delanoue <william.delanoue@gmail.com>
|
||||||
William Henry <whenry@redhat.com>
|
William Henry <whenry@redhat.com>
|
||||||
William Riancho <wr.wllm@gmail.com>
|
William Riancho <wr.wllm@gmail.com>
|
||||||
William Thurston <thurstw@amazon.com>
|
William Thurston <thurstw@amazon.com>
|
||||||
Will Rouesnel <w.rouesnel@gmail.com>
|
|
||||||
Will Weaver <monkey@buildingbananas.com>
|
|
||||||
wyc <wayne@neverfear.org>
|
|
||||||
Xiuming Chen <cc@cxm.cc>
|
Xiuming Chen <cc@cxm.cc>
|
||||||
xuzhaokui <cynicholas@gmail.com>
|
xuzhaokui <cynicholas@gmail.com>
|
||||||
Yang Bai <hamo.by@gmail.com>
|
Yang Bai <hamo.by@gmail.com>
|
||||||
@@ -715,4 +768,6 @@ Zilin Du <zilin.du@gmail.com>
|
|||||||
zimbatm <zimbatm@zimbatm.com>
|
zimbatm <zimbatm@zimbatm.com>
|
||||||
Zoltan Tombol <zoltan.tombol@gmail.com>
|
Zoltan Tombol <zoltan.tombol@gmail.com>
|
||||||
zqh <zqhxuyuan@gmail.com>
|
zqh <zqhxuyuan@gmail.com>
|
||||||
|
Álex González <agonzalezro@gmail.com>
|
||||||
|
Álvaro Lázaro <alvaro.lazaro.g@gmail.com>
|
||||||
尹吉峰 <jifeng.yin@gmail.com>
|
尹吉峰 <jifeng.yin@gmail.com>
|
||||||
|
|||||||
@@ -1,5 +1,44 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.5.0 (2015-02-05)
|
||||||
|
|
||||||
|
#### Builder
|
||||||
|
+ Dockerfile to use for a given `docker build` can be specified with the `-f` flag
|
||||||
|
* Dockerfile and .dockerignore files can be themselves excluded as part of the .dockerignore file, thus preventing modifications to these files invalidating ADD or COPY instructions cache
|
||||||
|
* ADD and COPY instructions accept relative paths
|
||||||
|
* Dockerfile `FROM scratch` instruction is now interpreted as a no-base specifier
|
||||||
|
* Improve performance when exposing a large number of ports
|
||||||
|
|
||||||
|
#### Hack
|
||||||
|
+ Allow client-side only integration tests for Windows
|
||||||
|
* Include docker-py integration tests against Docker daemon as part of our test suites
|
||||||
|
|
||||||
|
#### Packaging
|
||||||
|
+ Support for the new version of the registry HTTP API
|
||||||
|
* Speed up `docker push` for images with a majority of already existing layers
|
||||||
|
- Fixed contacting a private registry through a proxy
|
||||||
|
|
||||||
|
#### Remote API
|
||||||
|
+ A new endpoint will stream live container resource metrics and can be accessed with the `docker stats` command
|
||||||
|
+ Containers can be renamed using the new `rename` endpoint and the associated `docker rename` command
|
||||||
|
* Container `inspect` endpoint show the ID of `exec` commands running in this container
|
||||||
|
* Container `inspect` endpoint show the number of times Docker auto-restarted the container
|
||||||
|
* New types of event can be streamed by the `events` endpoint: ‘OOM’ (container died with out of memory), ‘exec_create’, and ‘exec_start'
|
||||||
|
- Fixed returned string fields which hold numeric characters incorrectly omitting surrounding double quotes
|
||||||
|
|
||||||
|
#### Runtime
|
||||||
|
+ Docker daemon has full IPv6 support
|
||||||
|
+ The `docker run` command can take the `--pid=host` flag to use the host PID namespace, which makes it possible for example to debug host processes using containerized debugging tools
|
||||||
|
+ The `docker run` command can take the `--read-only` flag to make the container’s root filesystem mounted as readonly, which can be used in combination with volumes to force a container’s processes to only write to locations that will be persisted
|
||||||
|
+ Container total memory usage can be limited for `docker run` using the `—memory-swap` flag
|
||||||
|
* Major stability improvements for devicemapper storage driver
|
||||||
|
* Better integration with host system: containers will reflect changes to the host's `/etc/resolv.conf` file when restarted
|
||||||
|
* Better integration with host system: per-container iptable rules are moved to the DOCKER chain
|
||||||
|
- Fixed container exiting on out of memory to return an invalid exit code
|
||||||
|
|
||||||
|
#### Other
|
||||||
|
* The HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environment variables are properly taken into account by the client when connecting to the Docker daemon
|
||||||
|
|
||||||
## 1.4.1 (2014-12-15)
|
## 1.4.1 (2014-12-15)
|
||||||
|
|
||||||
#### Runtime
|
#### Runtime
|
||||||
|
|||||||
+8
-2
@@ -84,8 +84,10 @@ RUN cd /usr/local/go/src && ./make.bash --no-clean 2>&1
|
|||||||
ENV DOCKER_CROSSPLATFORMS \
|
ENV DOCKER_CROSSPLATFORMS \
|
||||||
linux/386 linux/arm \
|
linux/386 linux/arm \
|
||||||
darwin/amd64 darwin/386 \
|
darwin/amd64 darwin/386 \
|
||||||
freebsd/amd64 freebsd/386 freebsd/arm \
|
freebsd/amd64 freebsd/386 freebsd/arm
|
||||||
windows/amd64 windows/386
|
|
||||||
|
# TODO when https://jenkins.dockerproject.com/job/Windows/ is green, add windows back to the list above
|
||||||
|
# windows/amd64 windows/386
|
||||||
|
|
||||||
# (set an explicit GOARM of 5 for maximum compatibility)
|
# (set an explicit GOARM of 5 for maximum compatibility)
|
||||||
ENV GOARM 5
|
ENV GOARM 5
|
||||||
@@ -153,6 +155,10 @@ RUN set -x \
|
|||||||
&& git clone -b v1.2 https://github.com/russross/blackfriday.git /go/src/github.com/russross/blackfriday \
|
&& git clone -b v1.2 https://github.com/russross/blackfriday.git /go/src/github.com/russross/blackfriday \
|
||||||
&& go install -v github.com/cpuguy83/go-md2man
|
&& go install -v github.com/cpuguy83/go-md2man
|
||||||
|
|
||||||
|
# install toml validator
|
||||||
|
RUN git clone -b v0.1.0 https://github.com/BurntSushi/toml.git /go/src/github.com/BurntSushi/toml \
|
||||||
|
&& go install -v github.com/BurntSushi/toml/cmd/tomlv
|
||||||
|
|
||||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||||
ENTRYPOINT ["hack/dind"]
|
ENTRYPOINT ["hack/dind"]
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ test-docker-py: build
|
|||||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary test-docker-py
|
$(DOCKER_RUN_DOCKER) hack/make.sh binary test-docker-py
|
||||||
|
|
||||||
validate: build
|
validate: build
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh validate-gofmt validate-dco
|
$(DOCKER_RUN_DOCKER) hack/make.sh validate-gofmt validate-dco validate-toml
|
||||||
|
|
||||||
shell: build
|
shell: build
|
||||||
$(DOCKER_RUN_DOCKER) bash
|
$(DOCKER_RUN_DOCKER) bash
|
||||||
|
|||||||
+3
-4
@@ -17,7 +17,6 @@ import (
|
|||||||
flag "github.com/docker/docker/pkg/mflag"
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
"github.com/docker/libtrust"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DockerCli struct {
|
type DockerCli struct {
|
||||||
@@ -27,7 +26,7 @@ type DockerCli struct {
|
|||||||
in io.ReadCloser
|
in io.ReadCloser
|
||||||
out io.Writer
|
out io.Writer
|
||||||
err io.Writer
|
err io.Writer
|
||||||
key libtrust.PrivateKey
|
keyFile string
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
scheme string
|
scheme string
|
||||||
// inFd holds file descriptor of the client's STDIN, if it's a valid file
|
// inFd holds file descriptor of the client's STDIN, if it's a valid file
|
||||||
@@ -122,7 +121,7 @@ func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDockerCli(in io.ReadCloser, out, err io.Writer, key libtrust.PrivateKey, proto, addr string, tlsConfig *tls.Config) *DockerCli {
|
func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, addr string, tlsConfig *tls.Config) *DockerCli {
|
||||||
var (
|
var (
|
||||||
inFd uintptr
|
inFd uintptr
|
||||||
outFd uintptr
|
outFd uintptr
|
||||||
@@ -177,7 +176,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, key libtrust.PrivateKey,
|
|||||||
in: in,
|
in: in,
|
||||||
out: out,
|
out: out,
|
||||||
err: err,
|
err: err,
|
||||||
key: key,
|
keyFile: keyFile,
|
||||||
inFd: inFd,
|
inFd: inFd,
|
||||||
outFd: outFd,
|
outFd: outFd,
|
||||||
isTerminalIn: isTerminalIn,
|
isTerminalIn: isTerminalIn,
|
||||||
|
|||||||
+60
-58
@@ -39,6 +39,7 @@ import (
|
|||||||
"github.com/docker/docker/pkg/parsers/filters"
|
"github.com/docker/docker/pkg/parsers/filters"
|
||||||
"github.com/docker/docker/pkg/promise"
|
"github.com/docker/docker/pkg/promise"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
|
"github.com/docker/docker/pkg/symlink"
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
"github.com/docker/docker/pkg/timeutils"
|
"github.com/docker/docker/pkg/timeutils"
|
||||||
"github.com/docker/docker/pkg/units"
|
"github.com/docker/docker/pkg/units"
|
||||||
@@ -46,7 +47,6 @@ import (
|
|||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
"github.com/docker/libtrust"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -148,36 +148,36 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var filename string // path to Dockerfile
|
filename := *dockerfileName // path to Dockerfile
|
||||||
var origDockerfile string // used for error msg
|
|
||||||
|
|
||||||
if *dockerfileName == "" {
|
if *dockerfileName == "" {
|
||||||
// No -f/--file was specified so use the default
|
// No -f/--file was specified so use the default
|
||||||
origDockerfile = api.DefaultDockerfileName
|
*dockerfileName = api.DefaultDockerfileName
|
||||||
*dockerfileName = origDockerfile
|
|
||||||
filename = path.Join(absRoot, *dockerfileName)
|
filename = path.Join(absRoot, *dockerfileName)
|
||||||
} else {
|
|
||||||
origDockerfile = *dockerfileName
|
|
||||||
if filename, err = filepath.Abs(*dockerfileName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that 'filename' is within the build context
|
|
||||||
if !strings.HasSuffix(absRoot, string(os.PathSeparator)) {
|
|
||||||
absRoot += string(os.PathSeparator)
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(filename, absRoot) {
|
|
||||||
return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", *dockerfileName, root)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now reset the dockerfileName to be relative to the build context
|
|
||||||
*dockerfileName = filename[len(absRoot):]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = os.Stat(filename); os.IsNotExist(err) {
|
origDockerfile := *dockerfileName // used for error msg
|
||||||
return fmt.Errorf("Can not locate Dockerfile: %s", origDockerfile)
|
|
||||||
|
if filename, err = filepath.Abs(filename); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
var includes []string = []string{"."}
|
|
||||||
|
// Verify that 'filename' is within the build context
|
||||||
|
filename, err = symlink.FollowSymlinkInScope(filename, absRoot)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", origDockerfile, root)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now reset the dockerfileName to be relative to the build context
|
||||||
|
*dockerfileName, err = filepath.Rel(absRoot, filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = os.Lstat(filename); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("Cannot locate Dockerfile: %s", origDockerfile)
|
||||||
|
}
|
||||||
|
var includes = []string{"."}
|
||||||
|
|
||||||
excludes, err := utils.ReadDockerIgnore(path.Join(root, ".dockerignore"))
|
excludes, err := utils.ReadDockerIgnore(path.Join(root, ".dockerignore"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1216,25 +1216,6 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
|||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("tag", tag)
|
v.Set("tag", tag)
|
||||||
|
|
||||||
body, _, err := readBody(cli.call("GET", "/images/"+remote+"/manifest?"+v.Encode(), nil, false))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
js, err := libtrust.NewJSONSignature(body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = js.Sign(cli.key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
signedBody, err := js.PrettySignature("signatures")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
push := func(authConfig registry.AuthConfig) error {
|
push := func(authConfig registry.AuthConfig) error {
|
||||||
buf, err := json.Marshal(authConfig)
|
buf, err := json.Marshal(authConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1244,7 +1225,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
|||||||
base64.URLEncoding.EncodeToString(buf),
|
base64.URLEncoding.EncodeToString(buf),
|
||||||
}
|
}
|
||||||
|
|
||||||
return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), bytes.NewReader(signedBody), cli.out, map[string][]string{
|
return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
|
||||||
"X-Registry-Auth": registryAuthHeader,
|
"X-Registry-Auth": registryAuthHeader,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -2634,7 +2615,12 @@ type containerStats struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *containerStats) Collect(stream io.ReadCloser) {
|
func (s *containerStats) Collect(cli *DockerCli) {
|
||||||
|
stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, false)
|
||||||
|
if err != nil {
|
||||||
|
s.err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
defer stream.Close()
|
defer stream.Close()
|
||||||
var (
|
var (
|
||||||
previousCpu uint64
|
previousCpu uint64
|
||||||
@@ -2714,28 +2700,44 @@ func (cli *DockerCli) CmdStats(args ...string) error {
|
|||||||
|
|
||||||
names := cmd.Args()
|
names := cmd.Args()
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
var cStats []*containerStats
|
var (
|
||||||
for _, n := range names {
|
cStats []*containerStats
|
||||||
s := &containerStats{Name: n}
|
w = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||||
cStats = append(cStats, s)
|
)
|
||||||
stream, _, err := cli.call("GET", "/containers/"+n+"/stats", nil, false)
|
printHeader := func() {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go s.Collect(stream)
|
|
||||||
}
|
|
||||||
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
|
||||||
for _ = range time.Tick(500 * time.Millisecond) {
|
|
||||||
fmt.Fprint(cli.out, "\033[2J")
|
fmt.Fprint(cli.out, "\033[2J")
|
||||||
fmt.Fprint(cli.out, "\033[H")
|
fmt.Fprint(cli.out, "\033[H")
|
||||||
fmt.Fprintln(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O")
|
fmt.Fprintln(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O")
|
||||||
|
}
|
||||||
|
for _, n := range names {
|
||||||
|
s := &containerStats{Name: n}
|
||||||
|
cStats = append(cStats, s)
|
||||||
|
go s.Collect(cli)
|
||||||
|
}
|
||||||
|
// do a quick pause so that any failed connections for containers that do not exist are able to be
|
||||||
|
// evicted before we display the initial or default values.
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
var errs []string
|
||||||
|
for _, c := range cStats {
|
||||||
|
c.mu.Lock()
|
||||||
|
if c.err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%s: %s", c.Name, c.err.Error()))
|
||||||
|
}
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return fmt.Errorf("%s", strings.Join(errs, ", "))
|
||||||
|
}
|
||||||
|
for _ = range time.Tick(500 * time.Millisecond) {
|
||||||
|
printHeader()
|
||||||
toRemove := []int{}
|
toRemove := []int{}
|
||||||
for i, s := range cStats {
|
for i, s := range cStats {
|
||||||
if err := s.Display(w); err != nil {
|
if err := s.Display(w); err != nil {
|
||||||
toRemove = append(toRemove, i)
|
toRemove = append(toRemove, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, i := range toRemove {
|
for j := len(toRemove) - 1; j >= 0; j-- {
|
||||||
|
i := toRemove[j]
|
||||||
cStats = append(cStats[:i], cStats[i+1:]...)
|
cStats = append(cStats[:i], cStats[i+1:]...)
|
||||||
}
|
}
|
||||||
if len(cStats) == 0 {
|
if len(cStats) == 0 {
|
||||||
|
|||||||
@@ -72,6 +72,15 @@ func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Con
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// When we set up a TCP connection for hijack, there could be long periods
|
||||||
|
// of inactivity (a long running command with no output) that in certain
|
||||||
|
// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
|
||||||
|
// state. Setting TCP KeepAlive on the socket connection will prohibit
|
||||||
|
// ECONNTIMEOUT unless the socket connection truly is broken
|
||||||
|
if tcpConn, ok := rawConn.(*net.TCPConn); ok {
|
||||||
|
tcpConn.SetKeepAlive(true)
|
||||||
|
tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
colonPos := strings.LastIndex(addr, ":")
|
colonPos := strings.LastIndex(addr, ":")
|
||||||
if colonPos == -1 {
|
if colonPos == -1 {
|
||||||
@@ -140,6 +149,15 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
|||||||
req.Host = cli.addr
|
req.Host = cli.addr
|
||||||
|
|
||||||
dial, err := cli.dial()
|
dial, err := cli.dial()
|
||||||
|
// When we set up a TCP connection for hijack, there could be long periods
|
||||||
|
// of inactivity (a long running command with no output) that in certain
|
||||||
|
// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
|
||||||
|
// state. Setting TCP KeepAlive on the socket connection will prohibit
|
||||||
|
// ECONNTIMEOUT unless the socket connection truly is broken
|
||||||
|
if tcpConn, ok := dial.(*net.TCPConn); ok {
|
||||||
|
tcpConn.SetKeepAlive(true)
|
||||||
|
tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
if strings.Contains(err.Error(), "connection refused") {
|
||||||
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||||
|
|||||||
+3
-3
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"mime"
|
"mime"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
@@ -55,7 +55,7 @@ func MatchesContentType(contentType, expectedType string) bool {
|
|||||||
// LoadOrCreateTrustKey attempts to load the libtrust key at the given path,
|
// LoadOrCreateTrustKey attempts to load the libtrust key at the given path,
|
||||||
// otherwise generates a new one
|
// otherwise generates a new one
|
||||||
func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
|
func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
|
||||||
err := os.MkdirAll(path.Dir(trustKeyPath), 0700)
|
err := os.MkdirAll(filepath.Dir(trustKeyPath), 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
|
|||||||
return nil, fmt.Errorf("Error saving key file: %s", err)
|
return nil, fmt.Errorf("Error saving key file: %s", err)
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, fmt.Errorf("Error loading key file: %s", err)
|
return nil, fmt.Errorf("Error loading key file %s: %s", trustKeyPath, err)
|
||||||
}
|
}
|
||||||
return trustKey, nil
|
return trustKey, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -621,18 +621,6 @@ func getImagesSearch(eng *engine.Engine, version version.Version, w http.Respons
|
|||||||
return job.Run()
|
return job.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getImageManifest(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
if err := parseForm(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
job := eng.Job("image_manifest", vars["name"])
|
|
||||||
job.Setenv("tag", r.Form.Get("tag"))
|
|
||||||
job.Stdout.Add(utils.NewWriteFlusher(w))
|
|
||||||
|
|
||||||
return job.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if vars == nil {
|
if vars == nil {
|
||||||
return fmt.Errorf("Missing parameter")
|
return fmt.Errorf("Missing parameter")
|
||||||
@@ -664,15 +652,9 @@ func postImagesPush(eng *engine.Engine, version version.Version, w http.Response
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest, err := ioutil.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
job := eng.Job("push", vars["name"])
|
job := eng.Job("push", vars["name"])
|
||||||
job.SetenvJson("metaHeaders", metaHeaders)
|
job.SetenvJson("metaHeaders", metaHeaders)
|
||||||
job.SetenvJson("authConfig", authConfig)
|
job.SetenvJson("authConfig", authConfig)
|
||||||
job.Setenv("manifest", string(manifest))
|
|
||||||
job.Setenv("tag", r.Form.Get("tag"))
|
job.Setenv("tag", r.Form.Get("tag"))
|
||||||
if version.GreaterThan("1.0") {
|
if version.GreaterThan("1.0") {
|
||||||
job.SetenvBool("json", true)
|
job.SetenvBool("json", true)
|
||||||
@@ -1325,7 +1307,6 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
|||||||
"/images/viz": getImagesViz,
|
"/images/viz": getImagesViz,
|
||||||
"/images/search": getImagesSearch,
|
"/images/search": getImagesSearch,
|
||||||
"/images/get": getImagesGet,
|
"/images/get": getImagesGet,
|
||||||
"/images/{name:.*}/manifest": getImageManifest,
|
|
||||||
"/images/{name:.*}/get": getImagesGet,
|
"/images/{name:.*}/get": getImagesGet,
|
||||||
"/images/{name:.*}/history": getImagesHistory,
|
"/images/{name:.*}/history": getImagesHistory,
|
||||||
"/images/{name:.*}/json": getImagesByName,
|
"/images/{name:.*}/json": getImagesByName,
|
||||||
|
|||||||
+21
-21
@@ -6,21 +6,21 @@ import "time"
|
|||||||
|
|
||||||
type ThrottlingData struct {
|
type ThrottlingData struct {
|
||||||
// Number of periods with throttling active
|
// Number of periods with throttling active
|
||||||
Periods uint64 `json:"periods,omitempty"`
|
Periods uint64 `json:"periods"`
|
||||||
// Number of periods when the container hit its throttling limit.
|
// Number of periods when the container hit its throttling limit.
|
||||||
ThrottledPeriods uint64 `json:"throttled_periods,omitempty"`
|
ThrottledPeriods uint64 `json:"throttled_periods"`
|
||||||
// Aggregate time the container was throttled for in nanoseconds.
|
// Aggregate time the container was throttled for in nanoseconds.
|
||||||
ThrottledTime uint64 `json:"throttled_time,omitempty"`
|
ThrottledTime uint64 `json:"throttled_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// All CPU stats are aggregated since container inception.
|
// All CPU stats are aggregated since container inception.
|
||||||
type CpuUsage struct {
|
type CpuUsage struct {
|
||||||
// Total CPU time consumed.
|
// Total CPU time consumed.
|
||||||
// Units: nanoseconds.
|
// Units: nanoseconds.
|
||||||
TotalUsage uint64 `json:"total_usage,omitempty"`
|
TotalUsage uint64 `json:"total_usage"`
|
||||||
// Total CPU time consumed per core.
|
// Total CPU time consumed per core.
|
||||||
// Units: nanoseconds.
|
// Units: nanoseconds.
|
||||||
PercpuUsage []uint64 `json:"percpu_usage,omitempty"`
|
PercpuUsage []uint64 `json:"percpu_usage"`
|
||||||
// Time spent by tasks of the cgroup in kernel mode.
|
// Time spent by tasks of the cgroup in kernel mode.
|
||||||
// Units: nanoseconds.
|
// Units: nanoseconds.
|
||||||
UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
||||||
@@ -30,41 +30,41 @@ type CpuUsage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CpuStats struct {
|
type CpuStats struct {
|
||||||
CpuUsage CpuUsage `json:"cpu_usage,omitempty"`
|
CpuUsage CpuUsage `json:"cpu_usage"`
|
||||||
SystemUsage uint64 `json:"system_cpu_usage"`
|
SystemUsage uint64 `json:"system_cpu_usage"`
|
||||||
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MemoryStats struct {
|
type MemoryStats struct {
|
||||||
// current res_counter usage for memory
|
// current res_counter usage for memory
|
||||||
Usage uint64 `json:"usage,omitempty"`
|
Usage uint64 `json:"usage"`
|
||||||
// maximum usage ever recorded.
|
// maximum usage ever recorded.
|
||||||
MaxUsage uint64 `json:"max_usage,omitempty"`
|
MaxUsage uint64 `json:"max_usage"`
|
||||||
// TODO(vishh): Export these as stronger types.
|
// TODO(vishh): Export these as stronger types.
|
||||||
// all the stats exported via memory.stat.
|
// all the stats exported via memory.stat.
|
||||||
Stats map[string]uint64 `json:"stats,omitempty"`
|
Stats map[string]uint64 `json:"stats"`
|
||||||
// number of times memory usage hits limits.
|
// number of times memory usage hits limits.
|
||||||
Failcnt uint64 `json:"failcnt"`
|
Failcnt uint64 `json:"failcnt"`
|
||||||
Limit uint64 `json:"limit"`
|
Limit uint64 `json:"limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlkioStatEntry struct {
|
type BlkioStatEntry struct {
|
||||||
Major uint64 `json:"major,omitempty"`
|
Major uint64 `json:"major"`
|
||||||
Minor uint64 `json:"minor,omitempty"`
|
Minor uint64 `json:"minor"`
|
||||||
Op string `json:"op,omitempty"`
|
Op string `json:"op"`
|
||||||
Value uint64 `json:"value,omitempty"`
|
Value uint64 `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlkioStats struct {
|
type BlkioStats struct {
|
||||||
// number of bytes tranferred to and from the block device
|
// number of bytes tranferred to and from the block device
|
||||||
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"`
|
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive"`
|
||||||
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive,omitempty"`
|
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive"`
|
||||||
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,omitempty"`
|
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive"`
|
||||||
IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive,omitempty"`
|
IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive"`
|
||||||
IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive,omitempty"`
|
IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive"`
|
||||||
IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive,omitempty"`
|
IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive"`
|
||||||
IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive,omitempty"`
|
IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive"`
|
||||||
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive,omitempty"`
|
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Network struct {
|
type Network struct {
|
||||||
|
|||||||
+12
-8
@@ -24,7 +24,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
@@ -32,6 +32,7 @@ import (
|
|||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
"github.com/docker/docker/engine"
|
"github.com/docker/docker/engine"
|
||||||
"github.com/docker/docker/pkg/fileutils"
|
"github.com/docker/docker/pkg/fileutils"
|
||||||
|
"github.com/docker/docker/pkg/symlink"
|
||||||
"github.com/docker/docker/pkg/tarsum"
|
"github.com/docker/docker/pkg/tarsum"
|
||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
@@ -169,12 +170,15 @@ func (b *Builder) Run(context io.Reader) (string, error) {
|
|||||||
|
|
||||||
// Reads a Dockerfile from the current context. It assumes that the
|
// Reads a Dockerfile from the current context. It assumes that the
|
||||||
// 'filename' is a relative path from the root of the context
|
// 'filename' is a relative path from the root of the context
|
||||||
func (b *Builder) readDockerfile(filename string) error {
|
func (b *Builder) readDockerfile(origFile string) error {
|
||||||
filename = path.Join(b.contextPath, filename)
|
filename, err := symlink.FollowSymlinkInScope(filepath.Join(b.contextPath, origFile), b.contextPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("The Dockerfile (%s) must be within the build context", origFile)
|
||||||
|
}
|
||||||
|
|
||||||
fi, err := os.Stat(filename)
|
fi, err := os.Lstat(filename)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return fmt.Errorf("Cannot build a directory without a Dockerfile")
|
return fmt.Errorf("Cannot locate specified Dockerfile: %s", origFile)
|
||||||
}
|
}
|
||||||
if fi.Size() == 0 {
|
if fi.Size() == 0 {
|
||||||
return ErrDockerfileEmpty
|
return ErrDockerfileEmpty
|
||||||
@@ -201,13 +205,13 @@ func (b *Builder) readDockerfile(filename string) error {
|
|||||||
// Note that this assumes the Dockerfile has been read into memory and
|
// Note that this assumes the Dockerfile has been read into memory and
|
||||||
// is now safe to be removed.
|
// is now safe to be removed.
|
||||||
|
|
||||||
excludes, _ := utils.ReadDockerIgnore(path.Join(b.contextPath, ".dockerignore"))
|
excludes, _ := utils.ReadDockerIgnore(filepath.Join(b.contextPath, ".dockerignore"))
|
||||||
if rm, _ := fileutils.Matches(".dockerignore", excludes); rm == true {
|
if rm, _ := fileutils.Matches(".dockerignore", excludes); rm == true {
|
||||||
os.Remove(path.Join(b.contextPath, ".dockerignore"))
|
os.Remove(filepath.Join(b.contextPath, ".dockerignore"))
|
||||||
b.context.(tarsum.BuilderContext).Remove(".dockerignore")
|
b.context.(tarsum.BuilderContext).Remove(".dockerignore")
|
||||||
}
|
}
|
||||||
if rm, _ := fileutils.Matches(b.dockerfileName, excludes); rm == true {
|
if rm, _ := fileutils.Matches(b.dockerfileName, excludes); rm == true {
|
||||||
os.Remove(path.Join(b.contextPath, b.dockerfileName))
|
os.Remove(filepath.Join(b.contextPath, b.dockerfileName))
|
||||||
b.context.(tarsum.BuilderContext).Remove(b.dockerfileName)
|
b.context.(tarsum.BuilderContext).Remove(b.dockerfileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
imagepkg "github.com/docker/docker/image"
|
imagepkg "github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
"github.com/docker/docker/pkg/chrootarchive"
|
"github.com/docker/docker/pkg/chrootarchive"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/docker/pkg/parsers"
|
"github.com/docker/docker/pkg/parsers"
|
||||||
"github.com/docker/docker/pkg/symlink"
|
"github.com/docker/docker/pkg/symlink"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
@@ -433,7 +434,7 @@ func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
|
|||||||
job.SetenvBool("json", b.StreamFormatter.Json())
|
job.SetenvBool("json", b.StreamFormatter.Json())
|
||||||
job.SetenvBool("parallel", true)
|
job.SetenvBool("parallel", true)
|
||||||
job.SetenvJson("authConfig", pullRegistryAuth)
|
job.SetenvJson("authConfig", pullRegistryAuth)
|
||||||
job.Stdout.Add(b.OutOld)
|
job.Stdout.Add(ioutils.NopWriteCloser(b.OutOld))
|
||||||
if err := job.Run(); err != nil {
|
if err := job.Run(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,22 @@ __docker_pos_first_nonflag() {
|
|||||||
echo $counter
|
echo $counter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Transforms a multiline list of strings into a single line string
|
||||||
|
# with the words separated by "|".
|
||||||
|
# This is used to prepare arguments to __docker_pos_first_nonflag().
|
||||||
|
__docker_to_alternatives() {
|
||||||
|
local parts=( $1 )
|
||||||
|
local IFS='|'
|
||||||
|
echo "${parts[*]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Transforms a multiline list of options into an extglob pattern
|
||||||
|
# suitable for use in case statements.
|
||||||
|
__docker_to_extglob() {
|
||||||
|
local extglob=$( __docker_to_alternatives "$1" )
|
||||||
|
echo "@($extglob)"
|
||||||
|
}
|
||||||
|
|
||||||
__docker_resolve_hostname() {
|
__docker_resolve_hostname() {
|
||||||
command -v host >/dev/null 2>&1 || return
|
command -v host >/dev/null 2>&1 || return
|
||||||
COMPREPLY=( $(host 2>/dev/null "${cur%:}" | awk '/has address/ {print $4}') )
|
COMPREPLY=( $(host 2>/dev/null "${cur%:}" | awk '/has address/ {print $4}') )
|
||||||
@@ -154,15 +170,47 @@ __docker_capabilities() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_docker_docker() {
|
_docker_docker() {
|
||||||
|
local boolean_options="
|
||||||
|
--api-enable-cors
|
||||||
|
--daemon -d
|
||||||
|
--debug -D
|
||||||
|
--help -h
|
||||||
|
--icc
|
||||||
|
--ip-forward
|
||||||
|
--ip-masq
|
||||||
|
--iptables
|
||||||
|
--ipv6
|
||||||
|
--selinux-enabled
|
||||||
|
--tls
|
||||||
|
--tlsverify
|
||||||
|
--version -v
|
||||||
|
"
|
||||||
|
|
||||||
case "$prev" in
|
case "$prev" in
|
||||||
-H)
|
--graph|-g)
|
||||||
|
_filedir -d
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--log-level|-l)
|
||||||
|
COMPREPLY=( $( compgen -W "debug info warn error fatal" -- "$cur" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--pidfile|-p|--tlscacert|--tlscert|--tlskey)
|
||||||
|
_filedir
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--storage-driver|-s)
|
||||||
|
COMPREPLY=( $( compgen -W "aufs devicemapper btrfs overlay" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
$main_options_with_args_glob )
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
-*)
|
-*)
|
||||||
COMPREPLY=( $( compgen -W "-H" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$boolean_options $main_options_with_args" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
COMPREPLY=( $( compgen -W "${commands[*]} help" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "${commands[*]} help" -- "$cur" ) )
|
||||||
@@ -561,6 +609,8 @@ _docker_run() {
|
|||||||
--sig-proxy
|
--sig-proxy
|
||||||
"
|
"
|
||||||
|
|
||||||
|
local options_with_args_glob=$(__docker_to_extglob "$options_with_args")
|
||||||
|
|
||||||
case "$prev" in
|
case "$prev" in
|
||||||
--add-host)
|
--add-host)
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
@@ -677,7 +727,7 @@ _docker_run() {
|
|||||||
__docker_containers_all
|
__docker_containers_all
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
--cpuset|--cpu-shares|-c|--dns|--dns-search|--entrypoint|--expose|--hostname|-h|--lxc-conf|--mac-address|--memory|-m|--name|-n|--publish|-p|--user|-u|--workdir|-w)
|
$options_with_args_glob )
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -687,7 +737,7 @@ _docker_run() {
|
|||||||
COMPREPLY=( $( compgen -W "$all_options" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$all_options" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
local counter=$( __docker_pos_first_nonflag $( echo $options_with_args | tr -d "\n" | tr " " "|" ) )
|
local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
|
||||||
|
|
||||||
if [ $cword -eq $counter ]; then
|
if [ $cword -eq $counter ]; then
|
||||||
__docker_image_repos_and_tags_and_ids
|
__docker_image_repos_and_tags_and_ids
|
||||||
@@ -739,6 +789,10 @@ _docker_start() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_docker_stats() {
|
||||||
|
__docker_containers_running
|
||||||
|
}
|
||||||
|
|
||||||
_docker_stop() {
|
_docker_stop() {
|
||||||
case "$prev" in
|
case "$prev" in
|
||||||
--time|-t)
|
--time|-t)
|
||||||
@@ -801,6 +855,9 @@ _docker_wait() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_docker() {
|
_docker() {
|
||||||
|
local previous_extglob_setting=$(shopt -p extglob)
|
||||||
|
shopt -s extglob
|
||||||
|
|
||||||
local commands=(
|
local commands=(
|
||||||
attach
|
attach
|
||||||
build
|
build
|
||||||
@@ -833,6 +890,7 @@ _docker() {
|
|||||||
save
|
save
|
||||||
search
|
search
|
||||||
start
|
start
|
||||||
|
stats
|
||||||
stop
|
stop
|
||||||
tag
|
tag
|
||||||
top
|
top
|
||||||
@@ -841,6 +899,33 @@ _docker() {
|
|||||||
wait
|
wait
|
||||||
)
|
)
|
||||||
|
|
||||||
|
local main_options_with_args="
|
||||||
|
--bip
|
||||||
|
--bridge -b
|
||||||
|
--dns
|
||||||
|
--dns-search
|
||||||
|
--exec-driver -e
|
||||||
|
--fixed-cidr
|
||||||
|
--fixed-cidr-v6
|
||||||
|
--graph -g
|
||||||
|
--group -G
|
||||||
|
--host -H
|
||||||
|
--insecure-registry
|
||||||
|
--ip
|
||||||
|
--label
|
||||||
|
--log-level -l
|
||||||
|
--mtu
|
||||||
|
--pidfile -p
|
||||||
|
--registry-mirror
|
||||||
|
--storage-driver -s
|
||||||
|
--storage-opt
|
||||||
|
--tlscacert
|
||||||
|
--tlscert
|
||||||
|
--tlskey
|
||||||
|
"
|
||||||
|
|
||||||
|
local main_options_with_args_glob=$(__docker_to_extglob "$main_options_with_args")
|
||||||
|
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
local cur prev words cword
|
local cur prev words cword
|
||||||
_get_comp_words_by_ref -n : cur prev words cword
|
_get_comp_words_by_ref -n : cur prev words cword
|
||||||
@@ -849,7 +934,7 @@ _docker() {
|
|||||||
local counter=1
|
local counter=1
|
||||||
while [ $counter -lt $cword ]; do
|
while [ $counter -lt $cword ]; do
|
||||||
case "${words[$counter]}" in
|
case "${words[$counter]}" in
|
||||||
-H)
|
$main_options_with_args_glob )
|
||||||
(( counter++ ))
|
(( counter++ ))
|
||||||
;;
|
;;
|
||||||
-*)
|
-*)
|
||||||
@@ -867,6 +952,7 @@ _docker() {
|
|||||||
local completions_func=_docker_${command}
|
local completions_func=_docker_${command}
|
||||||
declare -F $completions_func >/dev/null && $completions_func
|
declare -F $completions_func >/dev/null && $completions_func
|
||||||
|
|
||||||
|
eval "$previous_extglob_setting"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,23 +51,28 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -s d -l daemon -d 'Enable
|
|||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l dns -d 'Force Docker to use specific DNS servers'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l dns -d 'Force Docker to use specific DNS servers'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l dns-search -d 'Force Docker to use specific DNS search domains'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l dns-search -d 'Force Docker to use specific DNS search domains'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s e -l exec-driver -d 'Force the Docker runtime to use a specific exec driver'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -s e -l exec-driver -d 'Force the Docker runtime to use a specific exec driver'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l fixed-cidr -d 'IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l fixed-cidr -d 'IPv4 subnet for fixed IPs (e.g. 10.20.0.0/16)'
|
||||||
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l fixed-cidr-v6 -d 'IPv6 subnet for fixed IPs (e.g.: 2001:a02b/48)'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s G -l group -d 'Group to assign the unix socket specified by -H when running in daemon mode'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -s G -l group -d 'Group to assign the unix socket specified by -H when running in daemon mode'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s g -l graph -d 'Path to use as the root of the Docker runtime'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -s g -l graph -d 'Path to use as the root of the Docker runtime'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s H -l host -d 'The socket(s) to bind to in daemon mode or connect to in client mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -s H -l host -d 'The socket(s) to bind to in daemon mode or connect to in client mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l icc -d 'Enable inter-container communication'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -s h -l help -d 'Print usage'
|
||||||
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l icc -d 'Allow unrestricted inter-container and Docker daemon host communication'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l insecure-registry -d 'Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback) (e.g., localhost:5000 or 10.20.0.0/16)'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l insecure-registry -d 'Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback) (e.g., localhost:5000 or 10.20.0.0/16)'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l ip -d 'Default IP address to use when binding container ports'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l ip -d 'Default IP address to use when binding container ports'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l ip-forward -d 'Enable net.ipv4.ip_forward'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l ip-forward -d 'Enable net.ipv4.ip_forward and IPv6 forwarding if --fixed-cidr-v6 is defined. IPv6 forwarding may interfere with your existing IPv6 configuration when using Router Advertisement.'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l ip-masq -d "Enable IP masquerading for bridge's IP range"
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l ip-masq -d "Enable IP masquerading for bridge's IP range"
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l iptables -d "Enable Docker's addition of iptables rules"
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l iptables -d "Enable Docker's addition of iptables rules"
|
||||||
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l ipv6 -d 'Enable IPv6 networking'
|
||||||
|
complete -c docker -f -n '__fish_docker_no_subcommand' -s l -l log-level -d 'Set the logging level (debug, info, warn, error, fatal)'
|
||||||
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l label -d 'Set key=value labels to the daemon (displayed in `docker info`)'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l mtu -d 'Set the containers network MTU'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l mtu -d 'Set the containers network MTU'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s p -l pidfile -d 'Path to use for daemon PID file'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -s p -l pidfile -d 'Path to use for daemon PID file'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l registry-mirror -d 'Specify a preferred Docker registry mirror'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l registry-mirror -d 'Specify a preferred Docker registry mirror'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s s -l storage-driver -d 'Force the Docker runtime to use a specific storage driver'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -s s -l storage-driver -d 'Force the Docker runtime to use a specific storage driver'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l selinux-enabled -d 'Enable selinux support. SELinux does not presently support the BTRFS storage driver'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l selinux-enabled -d 'Enable selinux support. SELinux does not presently support the BTRFS storage driver'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l storage-opt -d 'Set storage driver options'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l storage-opt -d 'Set storage driver options'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l tls -d 'Use TLS; implied by tls-verify flags'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l tls -d 'Use TLS; implied by --tlsverify flag'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l tlscacert -d 'Trust only remotes providing a certificate signed by the CA given here'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l tlscacert -d 'Trust only remotes providing a certificate signed by the CA given here'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l tlscert -d 'Path to TLS certificate file'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l tlscert -d 'Path to TLS certificate file'
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l tlskey -d 'Path to TLS key file'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -l tlskey -d 'Path to TLS key file'
|
||||||
@@ -77,14 +82,18 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -s v -l version -d 'Print
|
|||||||
# subcommands
|
# subcommands
|
||||||
# attach
|
# attach
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a attach -d 'Attach to a running container'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a attach -d 'Attach to a running container'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -l no-stdin -d 'Do not attach STDIN'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -l no-stdin -d 'Do not attach STDIN'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -l sig-proxy -d 'Proxy all received signals to the process (even in non-TTY mode). SIGCHLD, SIGKILL, and SIGSTOP are not proxied.'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -l sig-proxy -d 'Proxy all received signals to the process (non-TTY mode only). SIGCHLD, SIGKILL, and SIGSTOP are not proxied.'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -a '(__fish_print_docker_containers running)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||||
|
|
||||||
# build
|
# build
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a build -d 'Build an image from a Dockerfile'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a build -d 'Build an image from a Dockerfile'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s f -l file -d "Name of the Dockerfile(Default is 'Dockerfile' at context root)"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l force-rm -d 'Always remove intermediate containers, even after unsuccessful builds'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l force-rm -d 'Always remove intermediate containers, even after unsuccessful builds'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l no-cache -d 'Do not use cache when building the image'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l no-cache -d 'Do not use cache when building the image'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l pull -d 'Always attempt to pull a newer version of the image'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s q -l quiet -d 'Suppress the verbose output generated by the containers'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s q -l quiet -d 'Suppress the verbose output generated by the containers'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l rm -d 'Remove intermediate containers after a successful build'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l rm -d 'Remove intermediate containers after a successful build'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s t -l tag -d 'Repository name (and optionally a tag) to be applied to the resulting image in case of success'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s t -l tag -d 'Repository name (and optionally a tag) to be applied to the resulting image in case of success'
|
||||||
@@ -92,12 +101,14 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s t -l tag -d '
|
|||||||
# commit
|
# commit
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a commit -d "Create a new image from a container's changes"
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a commit -d "Create a new image from a container's changes"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -s a -l author -d 'Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -s a -l author -d 'Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -s m -l message -d 'Commit message'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -s m -l message -d 'Commit message'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -s p -l pause -d 'Pause container during commit'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -s p -l pause -d 'Pause container during commit'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -a '(__fish_print_docker_containers all)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -a '(__fish_print_docker_containers all)' -d "Container"
|
||||||
|
|
||||||
# cp
|
# cp
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a cp -d "Copy files/folders from a container's filesystem to the host path"
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a cp -d "Copy files/folders from a container's filesystem to the host path"
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from cp' -l help -d 'Print usage'
|
||||||
|
|
||||||
# create
|
# create
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a create -d 'Create a new container'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a create -d 'Create a new container'
|
||||||
@@ -108,23 +119,29 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l cap-add -d '
|
|||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l cap-drop -d 'Drop Linux capabilities'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l cap-drop -d 'Drop Linux capabilities'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l cidfile -d 'Write the container ID to the file'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l cidfile -d 'Write the container ID to the file'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l cpuset -d 'CPUs in which to allow execution (0-3, 0,1)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l cpuset -d 'CPUs in which to allow execution (0-3, 0,1)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l device -d 'Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l device -d 'Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l dns -d 'Set custom DNS servers'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l dns -d 'Set custom DNS servers'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l dns-search -d 'Set custom DNS search domains'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l dns-search -d "Set custom DNS search domains (Use --dns-search=. if you don't wish to set the search domain)"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s e -l env -d 'Set environment variables'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s e -l env -d 'Set environment variables'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l entrypoint -d 'Overwrite the default ENTRYPOINT of the image'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l entrypoint -d 'Overwrite the default ENTRYPOINT of the image'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l env-file -d 'Read in a line delimited file of environment variables'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l env-file -d 'Read in a line delimited file of environment variables'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l expose -d 'Expose a port from the container without publishing it to your host'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l expose -d 'Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s h -l hostname -d 'Container host name'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s h -l hostname -d 'Container host name'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s i -l interactive -d 'Keep STDIN open even if not attached'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s i -l interactive -d 'Keep STDIN open even if not attached'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l link -d 'Add link to another container in the form of name:alias'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l ipc -d 'Default is to create a private IPC namespace (POSIX SysV IPC) for the container'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l link -d 'Add link to another container in the form of <name|id>:alias'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l lxc-conf -d '(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l lxc-conf -d '(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s m -l memory -d 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s m -l memory -d 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l mac-address -d 'Container MAC address (e.g. 92:d0:c6:0a:29:33)'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number><optional unit>, where unit = b, k, m or g)"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l name -d 'Assign a name to the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l name -d 'Assign a name to the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l net -d 'Set the Network mode for the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l net -d 'Set the Network mode for the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s p -l publish -d "Publish a container's port to the host"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s p -l publish -d "Publish a container's port to the host"
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l pid -d 'Default is to create a private PID namespace for the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l privileged -d 'Give extended privileges to this container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l privileged -d 'Give extended privileges to this container'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l read-only -d "Mount the container's root filesystem as read only"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l restart -d 'Restart policy to apply when a container exits (no, on-failure[:max-retry], always)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l restart -d 'Restart policy to apply when a container exits (no, on-failure[:max-retry], always)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l security-opt -d 'Security Options'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l security-opt -d 'Security Options'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s t -l tty -d 'Allocate a pseudo-TTY'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s t -l tty -d 'Allocate a pseudo-TTY'
|
||||||
@@ -136,26 +153,32 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from create' -a '(__fish_pri
|
|||||||
|
|
||||||
# diff
|
# diff
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a diff -d "Inspect changes on a container's filesystem"
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a diff -d "Inspect changes on a container's filesystem"
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from diff' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from diff' -a '(__fish_print_docker_containers all)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from diff' -a '(__fish_print_docker_containers all)' -d "Container"
|
||||||
|
|
||||||
# events
|
# events
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a events -d 'Get real time events from the server'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a events -d 'Get real time events from the server'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from events' -s f -l filter -d "Provide filter values (i.e., 'event=stop')"
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from events' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from events' -l since -d 'Show all events created since timestamp'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from events' -l since -d 'Show all events created since timestamp'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from events' -l until -d 'Stream events until this timestamp'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from events' -l until -d 'Stream events until this timestamp'
|
||||||
|
|
||||||
# exec
|
# exec
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a exec -d 'Run a command in an existing container'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a exec -d 'Run a command in a running container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from exec' -s d -l detach -d 'Detached mode: run command in the background'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from exec' -s d -l detach -d 'Detached mode: run command in the background'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from exec' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from exec' -s i -l interactive -d 'Keep STDIN open even if not attached'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from exec' -s i -l interactive -d 'Keep STDIN open even if not attached'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from exec' -s t -l tty -d 'Allocate a pseudo-TTY'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from exec' -s t -l tty -d 'Allocate a pseudo-TTY'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from exec' -a '(__fish_print_docker_containers running)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from exec' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||||
|
|
||||||
# export
|
# export
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a export -d 'Stream the contents of a container as a tar archive'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a export -d 'Stream the contents of a container as a tar archive'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from export' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from export' -a '(__fish_print_docker_containers all)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from export' -a '(__fish_print_docker_containers all)' -d "Container"
|
||||||
|
|
||||||
# history
|
# history
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a history -d 'Show the history of an image'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a history -d 'Show the history of an image'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from history' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from history' -l no-trunc -d "Don't truncate output"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from history' -l no-trunc -d "Don't truncate output"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from history' -s q -l quiet -d 'Only show numeric IDs'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from history' -s q -l quiet -d 'Only show numeric IDs'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from history' -a '(__fish_print_docker_images)' -d "Image"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from history' -a '(__fish_print_docker_images)' -d "Image"
|
||||||
@@ -164,34 +187,40 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from history' -a '(__fish_pr
|
|||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a images -d 'List images'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a images -d 'List images'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s a -l all -d 'Show all images (by default filter out the intermediate image layers)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s a -l all -d 'Show all images (by default filter out the intermediate image layers)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s f -l filter -d "Provide filter values (i.e., 'dangling=true')"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s f -l filter -d "Provide filter values (i.e., 'dangling=true')"
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -l no-trunc -d "Don't truncate output"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -l no-trunc -d "Don't truncate output"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s q -l quiet -d 'Only show numeric IDs'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s q -l quiet -d 'Only show numeric IDs'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -a '(__fish_print_docker_repositories)' -d "Repository"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -a '(__fish_print_docker_repositories)' -d "Repository"
|
||||||
|
|
||||||
# import
|
# import
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a import -d 'Create a new filesystem image from the contents of a tarball'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a import -d 'Create a new filesystem image from the contents of a tarball'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from import' -l help -d 'Print usage'
|
||||||
|
|
||||||
# info
|
# info
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a info -d 'Display system-wide information'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a info -d 'Display system-wide information'
|
||||||
|
|
||||||
# inspect
|
# inspect
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a inspect -d 'Return low-level information on a container'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a inspect -d 'Return low-level information on a container or image'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -s f -l format -d 'Format the output using the given go template.'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -s f -l format -d 'Format the output using the given go template.'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -a '(__fish_print_docker_images)' -d "Image"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -a '(__fish_print_docker_images)' -d "Image"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -a '(__fish_print_docker_containers all)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -a '(__fish_print_docker_containers all)' -d "Container"
|
||||||
|
|
||||||
# kill
|
# kill
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a kill -d 'Kill a running container'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a kill -d 'Kill a running container'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from kill' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from kill' -s s -l signal -d 'Signal to send to the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from kill' -s s -l signal -d 'Signal to send to the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from kill' -a '(__fish_print_docker_containers running)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from kill' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||||
|
|
||||||
# load
|
# load
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a load -d 'Load an image from a tar archive'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a load -d 'Load an image from a tar archive'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from load' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from load' -s i -l input -d 'Read from a tar archive file, instead of STDIN'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from load' -s i -l input -d 'Read from a tar archive file, instead of STDIN'
|
||||||
|
|
||||||
# login
|
# login
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a login -d 'Register or log in to a Docker registry server'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a login -d 'Register or log in to a Docker registry server'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s e -l email -d 'Email'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s e -l email -d 'Email'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s p -l password -d 'Password'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s p -l password -d 'Password'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s u -l username -d 'Username'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s u -l username -d 'Username'
|
||||||
|
|
||||||
@@ -201,12 +230,14 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -a logout -d 'Log out fro
|
|||||||
# logs
|
# logs
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a logs -d 'Fetch the logs of a container'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a logs -d 'Fetch the logs of a container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -s f -l follow -d 'Follow log output'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -s f -l follow -d 'Follow log output'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -s t -l timestamps -d 'Show timestamps'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -s t -l timestamps -d 'Show timestamps'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -l tail -d 'Output the specified number of lines at the end of logs (defaults to all logs)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -l tail -d 'Output the specified number of lines at the end of logs (defaults to all logs)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -a '(__fish_print_docker_containers running)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||||
|
|
||||||
# port
|
# port
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a port -d 'Lookup the public-facing port that is NAT-ed to PRIVATE_PORT'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a port -d 'Lookup the public-facing port that is NAT-ed to PRIVATE_PORT'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from port' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from port' -a '(__fish_print_docker_containers running)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from port' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||||
|
|
||||||
# pause
|
# pause
|
||||||
@@ -218,32 +249,40 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -a ps -d 'List containers
|
|||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s a -l all -d 'Show all containers. Only running containers are shown by default.'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s a -l all -d 'Show all containers. Only running containers are shown by default.'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -l before -d 'Show only container created before Id or Name, include non-running ones.'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -l before -d 'Show only container created before Id or Name, include non-running ones.'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s f -l filter -d 'Provide filter values. Valid filters:'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s f -l filter -d 'Provide filter values. Valid filters:'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s l -l latest -d 'Show only the latest created container, include non-running ones.'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s l -l latest -d 'Show only the latest created container, include non-running ones.'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s n -d 'Show n last created containers, include non-running ones.'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s n -d 'Show n last created containers, include non-running ones.'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -l no-trunc -d "Don't truncate output"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -l no-trunc -d "Don't truncate output"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s q -l quiet -d 'Only display numeric IDs'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s q -l quiet -d 'Only display numeric IDs'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s s -l size -d 'Display sizes'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s s -l size -d 'Display total file sizes'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -l since -d 'Show only containers created since Id or Name, include non-running ones.'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -l since -d 'Show only containers created since Id or Name, include non-running ones.'
|
||||||
|
|
||||||
# pull
|
# pull
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a pull -d 'Pull an image or a repository from a Docker registry server'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a pull -d 'Pull an image or a repository from a Docker registry server'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from pull' -s a -l all-tags -d 'Download all tagged images in the repository'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from pull' -s a -l all-tags -d 'Download all tagged images in the repository'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from pull' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from pull' -a '(__fish_print_docker_images)' -d "Image"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from pull' -a '(__fish_print_docker_images)' -d "Image"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from pull' -a '(__fish_print_docker_repositories)' -d "Repository"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from pull' -a '(__fish_print_docker_repositories)' -d "Repository"
|
||||||
|
|
||||||
# push
|
# push
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a push -d 'Push an image or a repository to a Docker registry server'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a push -d 'Push an image or a repository to a Docker registry server'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from push' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from push' -a '(__fish_print_docker_images)' -d "Image"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from push' -a '(__fish_print_docker_images)' -d "Image"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from push' -a '(__fish_print_docker_repositories)' -d "Repository"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from push' -a '(__fish_print_docker_repositories)' -d "Repository"
|
||||||
|
|
||||||
|
# rename
|
||||||
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a rename -d 'Rename an existing container'
|
||||||
|
|
||||||
# restart
|
# restart
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a restart -d 'Restart a running container'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a restart -d 'Restart a running container'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from restart' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from restart' -s t -l time -d 'Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default is 10 seconds.'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from restart' -s t -l time -d 'Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default is 10 seconds.'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from restart' -a '(__fish_print_docker_containers running)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from restart' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||||
|
|
||||||
# rm
|
# rm
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a rm -d 'Remove one or more containers'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a rm -d 'Remove one or more containers'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s f -l force -d 'Force the removal of a running container (uses SIGKILL)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s f -l force -d 'Force the removal of a running container (uses SIGKILL)'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s l -l link -d 'Remove the specified link and not the underlying container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s l -l link -d 'Remove the specified link and not the underlying container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s v -l volumes -d 'Remove the volumes associated with the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s v -l volumes -d 'Remove the volumes associated with the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -a '(__fish_print_docker_containers stopped)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -a '(__fish_print_docker_containers stopped)' -d "Container"
|
||||||
@@ -251,6 +290,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -a '(__fish_print_d
|
|||||||
# rmi
|
# rmi
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a rmi -d 'Remove one or more images'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a rmi -d 'Remove one or more images'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rmi' -s f -l force -d 'Force removal of the image'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from rmi' -s f -l force -d 'Force removal of the image'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from rmi' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rmi' -l no-prune -d 'Do not delete untagged parents'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from rmi' -l no-prune -d 'Do not delete untagged parents'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rmi' -a '(__fish_print_docker_images)' -d "Image"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from rmi' -a '(__fish_print_docker_images)' -d "Image"
|
||||||
|
|
||||||
@@ -264,27 +304,33 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l cap-drop -d 'Dr
|
|||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l cidfile -d 'Write the container ID to the file'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l cidfile -d 'Write the container ID to the file'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l cpuset -d 'CPUs in which to allow execution (0-3, 0,1)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l cpuset -d 'CPUs in which to allow execution (0-3, 0,1)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s d -l detach -d 'Detached mode: run the container in the background and print the new container ID'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s d -l detach -d 'Detached mode: run the container in the background and print the new container ID'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l device -d 'Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l device -d 'Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l dns -d 'Set custom DNS servers'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l dns -d 'Set custom DNS servers'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l dns-search -d 'Set custom DNS search domains'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l dns-search -d "Set custom DNS search domains (Use --dns-search=. if you don't wish to set the search domain)"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s e -l env -d 'Set environment variables'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s e -l env -d 'Set environment variables'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l entrypoint -d 'Overwrite the default ENTRYPOINT of the image'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l entrypoint -d 'Overwrite the default ENTRYPOINT of the image'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l env-file -d 'Read in a line delimited file of environment variables'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l env-file -d 'Read in a line delimited file of environment variables'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l expose -d 'Expose a port from the container without publishing it to your host'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l expose -d 'Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s h -l hostname -d 'Container host name'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s h -l hostname -d 'Container host name'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s i -l interactive -d 'Keep STDIN open even if not attached'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s i -l interactive -d 'Keep STDIN open even if not attached'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l link -d 'Add link to another container in the form of name:alias'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l ipc -d 'Default is to create a private IPC namespace (POSIX SysV IPC) for the container'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l link -d 'Add link to another container in the form of <name|id>:alias'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l lxc-conf -d '(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l lxc-conf -d '(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s m -l memory -d 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s m -l memory -d 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l mac-address -d 'Container MAC address (e.g. 92:d0:c6:0a:29:33)'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number><optional unit>, where unit = b, k, m or g)"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l net -d 'Set the Network mode for the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l net -d 'Set the Network mode for the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s p -l publish -d "Publish a container's port to the host"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s p -l publish -d "Publish a container's port to the host"
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l pid -d 'Default is to create a private PID namespace for the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l privileged -d 'Give extended privileges to this container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l privileged -d 'Give extended privileges to this container'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l read-only -d "Mount the container's root filesystem as read only"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l restart -d 'Restart policy to apply when a container exits (no, on-failure[:max-retry], always)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l restart -d 'Restart policy to apply when a container exits (no, on-failure[:max-retry], always)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l rm -d 'Automatically remove the container when it exits (incompatible with -d)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l rm -d 'Automatically remove the container when it exits (incompatible with -d)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l security-opt -d 'Security Options'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l security-opt -d 'Security Options'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l sig-proxy -d 'Proxy received signals to the process (even in non-TTY mode). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l sig-proxy -d 'Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-TTY'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-TTY'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s v -l volume -d 'Bind mount a volume (e.g., from the host: -v /host:/container, from Docker: -v /container)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s v -l volume -d 'Bind mount a volume (e.g., from the host: -v /host:/container, from Docker: -v /container)'
|
||||||
@@ -294,32 +340,43 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from run' -a '(__fish_print_
|
|||||||
|
|
||||||
# save
|
# save
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a save -d 'Save an image to a tar archive'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a save -d 'Save an image to a tar archive'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from save' -s o -l output -d 'Write to a file, instead of STDOUT'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from save' -l help -d 'Print usage'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from save' -s o -l output -d 'Write to an file, instead of STDOUT'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from save' -a '(__fish_print_docker_images)' -d "Image"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from save' -a '(__fish_print_docker_images)' -d "Image"
|
||||||
|
|
||||||
# search
|
# search
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a search -d 'Search for an image on the Docker Hub'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a search -d 'Search for an image on the Docker Hub'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from search' -l automated -d 'Only show automated builds'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from search' -l automated -d 'Only show automated builds'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from search' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from search' -l no-trunc -d "Don't truncate output"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from search' -l no-trunc -d "Don't truncate output"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from search' -s s -l stars -d 'Only displays with at least x stars'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from search' -s s -l stars -d 'Only displays with at least x stars'
|
||||||
|
|
||||||
# start
|
# start
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a start -d 'Start a stopped container'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a start -d 'Start a stopped container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from start' -s a -l attach -d "Attach container's STDOUT and STDERR and forward all signals to the process"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from start' -s a -l attach -d "Attach container's STDOUT and STDERR and forward all signals to the process"
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from start' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from start' -s i -l interactive -d "Attach container's STDIN"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from start' -s i -l interactive -d "Attach container's STDIN"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from start' -a '(__fish_print_docker_containers stopped)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from start' -a '(__fish_print_docker_containers stopped)' -d "Container"
|
||||||
|
|
||||||
|
# stats
|
||||||
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a stats -d "Display a live stream of one or more containers' resource usage statistics"
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -l help -d 'Print usage'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||||
|
|
||||||
# stop
|
# stop
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a stop -d 'Stop a running container'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a stop -d 'Stop a running container'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from stop' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from stop' -s t -l time -d 'Number of seconds to wait for the container to stop before killing it. Default is 10 seconds.'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from stop' -s t -l time -d 'Number of seconds to wait for the container to stop before killing it. Default is 10 seconds.'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from stop' -a '(__fish_print_docker_containers running)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from stop' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||||
|
|
||||||
# tag
|
# tag
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a tag -d 'Tag an image into a repository'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a tag -d 'Tag an image into a repository'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from tag' -s f -l force -d 'Force'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from tag' -s f -l force -d 'Force'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from tag' -l help -d 'Print usage'
|
||||||
|
|
||||||
# top
|
# top
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a top -d 'Lookup the running processes of a container'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a top -d 'Lookup the running processes of a container'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from top' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from top' -a '(__fish_print_docker_containers running)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from top' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||||
|
|
||||||
# unpause
|
# unpause
|
||||||
@@ -331,6 +388,7 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -a version -d 'Show the D
|
|||||||
|
|
||||||
# wait
|
# wait
|
||||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a wait -d 'Block until a container stops, then print its exit code'
|
complete -c docker -f -n '__fish_docker_no_subcommand' -a wait -d 'Block until a container stops, then print its exit code'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -l help -d 'Print usage'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -a '(__fish_print_docker_containers running)' -d "Container"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-6
@@ -820,7 +820,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error)
|
|||||||
if os.Geteuid() != 0 {
|
if os.Geteuid() != 0 {
|
||||||
return nil, fmt.Errorf("The Docker daemon needs to be run as root")
|
return nil, fmt.Errorf("The Docker daemon needs to be run as root")
|
||||||
}
|
}
|
||||||
if err := checkKernelAndArch(); err != nil {
|
if err := checkKernel(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1205,11 +1205,7 @@ func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*i
|
|||||||
return match, nil
|
return match, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkKernelAndArch() error {
|
func checkKernel() error {
|
||||||
// Check for unsupported architectures
|
|
||||||
if runtime.GOARCH != "amd64" {
|
|
||||||
return fmt.Errorf("The Docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
|
|
||||||
}
|
|
||||||
// Check for unsupported kernel versions
|
// Check for unsupported kernel versions
|
||||||
// FIXME: it would be cleaner to not test for specific versions, but rather
|
// FIXME: it would be cleaner to not test for specific versions, but rather
|
||||||
// test for specific functionalities.
|
// test for specific functionalities.
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ lxc.aa_profile = unconfined
|
|||||||
# In non-privileged mode, lxc will automatically mount /proc and /sys in readonly mode
|
# In non-privileged mode, lxc will automatically mount /proc and /sys in readonly mode
|
||||||
# for security. See: http://man7.org/linux/man-pages/man5/lxc.container.conf.5.html
|
# for security. See: http://man7.org/linux/man-pages/man5/lxc.container.conf.5.html
|
||||||
lxc.mount.auto = proc sys
|
lxc.mount.auto = proc sys
|
||||||
{{if .AppArmor}}
|
{{if .AppArmorProfile}}
|
||||||
lxc.aa_profile = .AppArmorProfile
|
lxc.aa_profile = {{.AppArmorProfile}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|||||||
@@ -248,7 +248,8 @@ func TestCustomLxcConfigMisc(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(root)
|
defer os.RemoveAll(root)
|
||||||
os.MkdirAll(path.Join(root, "containers", "1"), 0777)
|
os.MkdirAll(path.Join(root, "containers", "1"), 0777)
|
||||||
driver, err := NewDriver(root, "", false)
|
driver, err := NewDriver(root, "", true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -271,9 +272,10 @@ func TestCustomLxcConfigMisc(t *testing.T) {
|
|||||||
Bridge: "docker0",
|
Bridge: "docker0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProcessConfig: processConfig,
|
ProcessConfig: processConfig,
|
||||||
CapAdd: []string{"net_admin", "syslog"},
|
CapAdd: []string{"net_admin", "syslog"},
|
||||||
CapDrop: []string{"kill", "mknod"},
|
CapDrop: []string{"kill", "mknod"},
|
||||||
|
AppArmorProfile: "lxc-container-default-with-nesting",
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := driver.generateLXCConfig(command)
|
p, err := driver.generateLXCConfig(command)
|
||||||
@@ -287,7 +289,7 @@ func TestCustomLxcConfigMisc(t *testing.T) {
|
|||||||
grepFile(t, p, "lxc.network.ipv4 = 10.10.10.10/24")
|
grepFile(t, p, "lxc.network.ipv4 = 10.10.10.10/24")
|
||||||
grepFile(t, p, "lxc.network.ipv4.gateway = 10.10.10.1")
|
grepFile(t, p, "lxc.network.ipv4.gateway = 10.10.10.1")
|
||||||
grepFile(t, p, "lxc.network.flags = up")
|
grepFile(t, p, "lxc.network.flags = up")
|
||||||
|
grepFile(t, p, "lxc.aa_profile = lxc-container-default-with-nesting")
|
||||||
// hostname
|
// hostname
|
||||||
grepFile(t, p, "lxc.utsname = testhost")
|
grepFile(t, p, "lxc.utsname = testhost")
|
||||||
grepFile(t, p, "lxc.cgroup.cpuset.cpus = 0,1")
|
grepFile(t, p, "lxc.cgroup.cpuset.cpus = 0,1")
|
||||||
|
|||||||
@@ -150,6 +150,21 @@ func InitDriver(job *engine.Job) engine.Status {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a bridge might exist but not have any IPv6 addr associated with it yet
|
||||||
|
// (for example, an existing Docker installation that has only been used
|
||||||
|
// with IPv4 and docker0 already is set up) In that case, we can perform
|
||||||
|
// the bridge init for IPv6 here, else we will error out below if --ipv6=true
|
||||||
|
if len(addrsv6) == 0 && enableIPv6 {
|
||||||
|
if err := setupIPv6Bridge(bridgeIPv6); err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
// recheck addresses now that IPv6 is setup on the bridge
|
||||||
|
addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface)
|
||||||
|
if err != nil {
|
||||||
|
return job.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Check if route to fixedCIDRv6 is set
|
// TODO: Check if route to fixedCIDRv6 is set
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +188,6 @@ func InitDriver(job *engine.Job) engine.Status {
|
|||||||
|
|
||||||
networkv4 = addrv4.(*net.IPNet)
|
networkv4 = addrv4.(*net.IPNet)
|
||||||
|
|
||||||
log.Infof("enableIPv6 = %t", enableIPv6)
|
|
||||||
if enableIPv6 {
|
if enableIPv6 {
|
||||||
if len(addrsv6) == 0 {
|
if len(addrsv6) == 0 {
|
||||||
return job.Error(errors.New("IPv6 enabled but no IPv6 detected"))
|
return job.Error(errors.New("IPv6 enabled but no IPv6 detected"))
|
||||||
@@ -396,26 +410,14 @@ func configureBridge(bridgeIP string, bridgeIPv6 string, enableIPv6 bool) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil {
|
if err := netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil {
|
||||||
return fmt.Errorf("Unable to add private network: %s", err)
|
return fmt.Errorf("Unable to add private network: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if enableIPv6 {
|
if enableIPv6 {
|
||||||
// Enable IPv6 on the bridge
|
if err := setupIPv6Bridge(bridgeIPv6); err != nil {
|
||||||
procFile := "/proc/sys/net/ipv6/conf/" + iface.Name + "/disable_ipv6"
|
|
||||||
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil {
|
|
||||||
return fmt.Errorf("unable to enable IPv6 addresses on bridge: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ipAddr6, ipNet6, err := net.ParseCIDR(bridgeIPv6)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("BridgeIPv6 parsing failed")
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if netlink.NetworkLinkAddIp(iface, ipAddr6, ipNet6); err != nil {
|
|
||||||
return fmt.Errorf("Unable to add private IPv6 network: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := netlink.NetworkLinkUp(iface); err != nil {
|
if err := netlink.NetworkLinkUp(iface); err != nil {
|
||||||
@@ -424,6 +426,30 @@ func configureBridge(bridgeIP string, bridgeIPv6 string, enableIPv6 bool) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupIPv6Bridge(bridgeIPv6 string) error {
|
||||||
|
|
||||||
|
iface, err := net.InterfaceByName(bridgeIface)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Enable IPv6 on the bridge
|
||||||
|
procFile := "/proc/sys/net/ipv6/conf/" + iface.Name + "/disable_ipv6"
|
||||||
|
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil {
|
||||||
|
return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipAddr6, ipNet6, err := net.ParseCIDR(bridgeIPv6)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to parse bridge IPv6 address: %q, error: %v", bridgeIPv6, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := netlink.NetworkLinkAddIp(iface, ipAddr6, ipNet6); err != nil {
|
||||||
|
return fmt.Errorf("Unable to add private IPv6 network: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func createBridgeIface(name string) error {
|
func createBridgeIface(name string) error {
|
||||||
kv, err := kernel.GetKernelVersion()
|
kv, err := kernel.GetKernelVersion()
|
||||||
// only set the bridge's mac address if the kernel version is > 3.3
|
// only set the bridge's mac address if the kernel version is > 3.3
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func NetworkRange(network *net.IPNet) (net.IP, net.IP) {
|
|||||||
return netIP.Mask(network.Mask), net.IP(lastIP)
|
return netIP.Mask(network.Mask), net.IP(lastIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the IPv4 address of a network interface
|
// Return the first IPv4 address and slice of IPv6 addresses for the specified network interface
|
||||||
func GetIfaceAddr(name string) (net.Addr, []net.Addr, error) {
|
func GetIfaceAddr(name string) (net.Addr, []net.Addr, error) {
|
||||||
iface, err := net.InterfaceByName(name)
|
iface, err := net.InterfaceByName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+2
-7
@@ -79,11 +79,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
|
protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
|
||||||
|
|
||||||
trustKey, err := api.LoadOrCreateTrustKey(*flTrustKey)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cli *client.DockerCli
|
cli *client.DockerCli
|
||||||
tlsConfig tls.Config
|
tlsConfig tls.Config
|
||||||
@@ -125,9 +120,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *flTls || *flTlsVerify {
|
if *flTls || *flTlsVerify {
|
||||||
cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, trustKey, protoAddrParts[0], protoAddrParts[1], &tlsConfig)
|
cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, *flTrustKey, protoAddrParts[0], protoAddrParts[1], &tlsConfig)
|
||||||
} else {
|
} else {
|
||||||
cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, trustKey, protoAddrParts[0], protoAddrParts[1], nil)
|
cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, *flTrustKey, protoAddrParts[0], protoAddrParts[1], nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cli.Cmd(flag.Args()...); err != nil {
|
if err := cli.Cmd(flag.Args()...); err != nil {
|
||||||
|
|||||||
+1
-1
@@ -97,7 +97,7 @@ func init() {
|
|||||||
{"images", "List images"},
|
{"images", "List images"},
|
||||||
{"import", "Create a new filesystem image from the contents of a tarball"},
|
{"import", "Create a new filesystem image from the contents of a tarball"},
|
||||||
{"info", "Display system-wide information"},
|
{"info", "Display system-wide information"},
|
||||||
{"inspect", "Return low-level information on a container"},
|
{"inspect", "Return low-level information on a container or image"},
|
||||||
{"kill", "Kill a running container"},
|
{"kill", "Kill a running container"},
|
||||||
{"load", "Load an image from a tar archive"},
|
{"load", "Load an image from a tar archive"},
|
||||||
{"login", "Register or log in to a Docker registry server"},
|
{"login", "Register or log in to a Docker registry server"},
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ as context.
|
|||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
## Building an image using a Dockefile located inside the current directory
|
## Building an image using a Dockerfile located inside the current directory
|
||||||
|
|
||||||
Docker images can be built using the build command and a Dockerfile:
|
Docker images can be built using the build command and a Dockerfile:
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ no hard rules here but it is best to give the names consideration.
|
|||||||
|
|
||||||
The **-t**/**--tag** flag is used to rename an image. Here are some examples:
|
The **-t**/**--tag** flag is used to rename an image. Here are some examples:
|
||||||
|
|
||||||
Though it is not a good practice, image names can be arbtrary:
|
Though it is not a good practice, image names can be arbitrary:
|
||||||
|
|
||||||
docker build -t myimage .
|
docker build -t myimage .
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ unix://[/path/to/socket] to use.
|
|||||||
Display system-wide information
|
Display system-wide information
|
||||||
|
|
||||||
**docker-inspect(1)**
|
**docker-inspect(1)**
|
||||||
Return low-level information on a container
|
Return low-level information on a container or image
|
||||||
|
|
||||||
**docker-kill(1)**
|
**docker-kill(1)**
|
||||||
Kill a running container (which includes the wrapper process and everything
|
Kill a running container (which includes the wrapper process and everything
|
||||||
|
|||||||
@@ -69,6 +69,11 @@ pages:
|
|||||||
- ['docker-hub/builds.md', 'Docker Hub', 'Automated Builds']
|
- ['docker-hub/builds.md', 'Docker Hub', 'Automated Builds']
|
||||||
- ['docker-hub/official_repos.md', 'Docker Hub', 'Official Repo Guidelines']
|
- ['docker-hub/official_repos.md', 'Docker Hub', 'Official Repo Guidelines']
|
||||||
|
|
||||||
|
# Docker Hub Enterprise
|
||||||
|
- ['docker-hub-enterprise/index.md', '**HIDDEN**' ]
|
||||||
|
- ['docker-hub-enterprise/install-config.md', 'Docker Hub Enterprise', 'Installation and Configuration' ]
|
||||||
|
- ['docker-hub-enterprise/usage.md', 'Docker Hub Enterprise', 'User Guide' ]
|
||||||
|
|
||||||
# Examples:
|
# Examples:
|
||||||
- ['examples/index.md', '**HIDDEN**']
|
- ['examples/index.md', '**HIDDEN**']
|
||||||
- ['examples/nodejs_web_app.md', 'Examples', 'Dockerizing a Node.js web application']
|
- ['examples/nodejs_web_app.md', 'Examples', 'Dockerizing a Node.js web application']
|
||||||
@@ -86,6 +91,7 @@ pages:
|
|||||||
- ['articles/networking.md', 'Articles', 'Advanced networking']
|
- ['articles/networking.md', 'Articles', 'Advanced networking']
|
||||||
- ['articles/security.md', 'Articles', 'Security']
|
- ['articles/security.md', 'Articles', 'Security']
|
||||||
- ['articles/https.md', 'Articles', 'Running Docker with HTTPS']
|
- ['articles/https.md', 'Articles', 'Running Docker with HTTPS']
|
||||||
|
- ['articles/registry_mirror.md', 'Articles', 'Run a local registry mirror']
|
||||||
- ['articles/host_integration.md', 'Articles', 'Automatically starting containers']
|
- ['articles/host_integration.md', 'Articles', 'Automatically starting containers']
|
||||||
- ['articles/baseimages.md', 'Articles', 'Creating a base image']
|
- ['articles/baseimages.md', 'Articles', 'Creating a base image']
|
||||||
- ['articles/dockerfile_best-practices.md', 'Articles', 'Best practices for writing Dockerfiles']
|
- ['articles/dockerfile_best-practices.md', 'Articles', 'Best practices for writing Dockerfiles']
|
||||||
|
|||||||
+6
-2
@@ -72,6 +72,9 @@ setup_s3() {
|
|||||||
|
|
||||||
build_current_documentation() {
|
build_current_documentation() {
|
||||||
mkdocs build
|
mkdocs build
|
||||||
|
cd site/
|
||||||
|
gzip -9k -f search_content.json
|
||||||
|
cd ..
|
||||||
}
|
}
|
||||||
|
|
||||||
upload_current_documentation() {
|
upload_current_documentation() {
|
||||||
@@ -87,8 +90,6 @@ upload_current_documentation() {
|
|||||||
echo "Uploading $src"
|
echo "Uploading $src"
|
||||||
echo " to $dst"
|
echo " to $dst"
|
||||||
echo
|
echo
|
||||||
#s3cmd --recursive --follow-symlinks --preserve --acl-public sync "$src" "$dst"
|
|
||||||
#aws s3 cp --profile $BUCKET --cache-control "max-age=3600" --acl public-read "site/search_content.json" "$dst"
|
|
||||||
|
|
||||||
# a really complicated way to send only the files we want
|
# a really complicated way to send only the files we want
|
||||||
# if there are too many in any one set, aws s3 sync seems to fall over with 2 files to go
|
# if there are too many in any one set, aws s3 sync seems to fall over with 2 files to go
|
||||||
@@ -100,6 +101,9 @@ upload_current_documentation() {
|
|||||||
echo "$run"
|
echo "$run"
|
||||||
echo "======================="
|
echo "======================="
|
||||||
$run
|
$run
|
||||||
|
|
||||||
|
# Make sure the search_content.json.gz file has the right content-encoding
|
||||||
|
aws s3 cp --profile $BUCKET --cache-control $cache --content-encoding="gzip" --acl public-read "site/search_content.json.gz" "$dst"
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidate_cache() {
|
invalidate_cache() {
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
# Articles
|
|
||||||
|
|
||||||
- [Docker Basics](basics/)
|
|
||||||
- [Docker Security](security/)
|
|
||||||
- [Running the Docker daemon with HTTPS](https/)
|
|
||||||
- [Configure Networking](networking/)
|
|
||||||
- [Using Supervisor with Docker](using_supervisord/)
|
|
||||||
- [Process Management with CFEngine](cfengine_process_management/)
|
|
||||||
- [Using Puppet](puppet/)
|
|
||||||
- [Create a Base Image](baseimages/)
|
|
||||||
- [Runtime Metrics](runmetrics/)
|
|
||||||
- [Automatically Start Containers](host_integration/)
|
|
||||||
- [Link via an Ambassador Container](ambassador_pattern_linking/)
|
|
||||||
- [Increase a Boot2Docker Volume](b2d_volume_resize/)
|
|
||||||
- [Run a Local Registry Mirror](registry_mirror/)
|
|
||||||
@@ -26,7 +26,7 @@ it will only connect to servers with a certificate signed by that CA.
|
|||||||
|
|
||||||
## Create a CA, server and client keys with OpenSSL
|
## Create a CA, server and client keys with OpenSSL
|
||||||
|
|
||||||
> **Note:** replace all instances of `$HOST` in the following example with the
|
> **Note**: replace all instances of `$HOST` in the following example with the
|
||||||
> DNS name of your Docker daemon's host.
|
> DNS name of your Docker daemon's host.
|
||||||
|
|
||||||
First generate CA private and public keys:
|
First generate CA private and public keys:
|
||||||
@@ -40,26 +40,26 @@ First generate CA private and public keys:
|
|||||||
Verifying - Enter pass phrase for ca-key.pem:
|
Verifying - Enter pass phrase for ca-key.pem:
|
||||||
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
|
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
|
||||||
Enter pass phrase for ca-key.pem:
|
Enter pass phrase for ca-key.pem:
|
||||||
You are about to be asked to enter information that will be incorporated
|
You are about to be asked to enter information that will be incorporated
|
||||||
into your certificate request.
|
into your certificate request.
|
||||||
What you are about to enter is what is called a Distinguished Name or a DN.
|
What you are about to enter is what is called a Distinguished Name or a DN.
|
||||||
There are quite a few fields but you can leave some blank
|
There are quite a few fields but you can leave some blank
|
||||||
For some fields there will be a default value,
|
For some fields there will be a default value,
|
||||||
If you enter '.', the field will be left blank.
|
If you enter '.', the field will be left blank.
|
||||||
-----
|
-----
|
||||||
Country Name (2 letter code) [AU]:
|
Country Name (2 letter code) [AU]:
|
||||||
State or Province Name (full name) [Some-State]:Queensland
|
State or Province Name (full name) [Some-State]:Queensland
|
||||||
Locality Name (eg, city) []:Brisbane
|
Locality Name (eg, city) []:Brisbane
|
||||||
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc
|
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc
|
||||||
Organizational Unit Name (eg, section) []:Boot2Docker
|
Organizational Unit Name (eg, section) []:Boot2Docker
|
||||||
Common Name (e.g. server FQDN or YOUR name) []:$HOST
|
Common Name (e.g. server FQDN or YOUR name) []:$HOST
|
||||||
Email Address []:Sven@home.org.au
|
Email Address []:Sven@home.org.au
|
||||||
|
|
||||||
Now that we have a CA, you can create a server key and certificate
|
Now that we have a CA, you can create a server key and certificate
|
||||||
signing request (CSR). Make sure that "Common Name" (i.e., server FQDN or YOUR
|
signing request (CSR). Make sure that "Common Name" (i.e., server FQDN or YOUR
|
||||||
name) matches the hostname you will use to connect to Docker:
|
name) matches the hostname you will use to connect to Docker:
|
||||||
|
|
||||||
> **Note:** replace all instances of `$HOST` in the following example with the
|
> **Note**: replace all instances of `$HOST` in the following example with the
|
||||||
> DNS name of your Docker daemon's host.
|
> DNS name of your Docker daemon's host.
|
||||||
|
|
||||||
$ openssl genrsa -out server-key.pem 2048
|
$ openssl genrsa -out server-key.pem 2048
|
||||||
@@ -69,7 +69,7 @@ name) matches the hostname you will use to connect to Docker:
|
|||||||
e is 65537 (0x10001)
|
e is 65537 (0x10001)
|
||||||
$ openssl req -subj "/CN=$HOST" -new -key server-key.pem -out server.csr
|
$ openssl req -subj "/CN=$HOST" -new -key server-key.pem -out server.csr
|
||||||
|
|
||||||
Next, we're going to sign the key with our CA:
|
Next, we're going to sign the public key with our CA:
|
||||||
|
|
||||||
$ openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem \
|
$ openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem \
|
||||||
-CAcreateserial -out server-cert.pem
|
-CAcreateserial -out server-cert.pem
|
||||||
@@ -93,7 +93,7 @@ config file:
|
|||||||
|
|
||||||
$ echo extendedKeyUsage = clientAuth > extfile.cnf
|
$ echo extendedKeyUsage = clientAuth > extfile.cnf
|
||||||
|
|
||||||
Now sign the key:
|
Now sign the public key:
|
||||||
|
|
||||||
$ openssl x509 -req -days 365 -in client.csr -CA ca.pem -CAkey ca-key.pem \
|
$ openssl x509 -req -days 365 -in client.csr -CA ca.pem -CAkey ca-key.pem \
|
||||||
-CAcreateserial -out cert.pem -extfile extfile.cnf
|
-CAcreateserial -out cert.pem -extfile extfile.cnf
|
||||||
@@ -102,6 +102,24 @@ Now sign the key:
|
|||||||
Getting CA Private Key
|
Getting CA Private Key
|
||||||
Enter pass phrase for ca-key.pem:
|
Enter pass phrase for ca-key.pem:
|
||||||
|
|
||||||
|
After generating `cert.pem` and `server-cert.pem` you can safely remove the
|
||||||
|
two certificate signing requests:
|
||||||
|
|
||||||
|
$ rm -v client.csr server.csr
|
||||||
|
|
||||||
|
With a default `umask` of 022, your secret keys will be *world-readable* and
|
||||||
|
writable for you and your group.
|
||||||
|
|
||||||
|
In order to protect your keys from accidental damage, you will want to remove their
|
||||||
|
write permissions. To make them only readable by you, change file modes as follows:
|
||||||
|
|
||||||
|
$ chmod -v 0400 ca-key.pem key.pem server-key.pem
|
||||||
|
|
||||||
|
Certificates can be world-readable, but you might want to remove write access to
|
||||||
|
prevent accidental damage:
|
||||||
|
|
||||||
|
$ chmod -v 0444 ca.pem server-cert.pem cert.pem
|
||||||
|
|
||||||
Now you can make the Docker daemon only accept connections from clients
|
Now you can make the Docker daemon only accept connections from clients
|
||||||
providing a certificate trusted by our CA:
|
providing a certificate trusted by our CA:
|
||||||
|
|
||||||
@@ -111,7 +129,7 @@ providing a certificate trusted by our CA:
|
|||||||
To be able to connect to Docker and validate its certificate, you now
|
To be able to connect to Docker and validate its certificate, you now
|
||||||
need to provide your client keys, certificates and trusted CA:
|
need to provide your client keys, certificates and trusted CA:
|
||||||
|
|
||||||
> **Note:** replace all instances of `$HOST` in the following example with the
|
> **Note**: replace all instances of `$HOST` in the following example with the
|
||||||
> DNS name of your Docker daemon's host.
|
> DNS name of your Docker daemon's host.
|
||||||
|
|
||||||
$ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \
|
$ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \
|
||||||
@@ -130,16 +148,13 @@ need to provide your client keys, certificates and trusted CA:
|
|||||||
## Secure by default
|
## Secure by default
|
||||||
|
|
||||||
If you want to secure your Docker client connections by default, you can move
|
If you want to secure your Docker client connections by default, you can move
|
||||||
the files to the `.docker` directory in your home directory - and set the
|
the files to the `.docker` directory in your home directory -- and set the
|
||||||
`DOCKER_HOST` and `DOCKER_TLS_VERIFY` variables as well (instead of passing
|
`DOCKER_HOST` and `DOCKER_TLS_VERIFY` variables as well (instead of passing
|
||||||
`-H=tcp://:2376` and `--tlsverify` on every call).
|
`-H=tcp://$HOST:2376` and `--tlsverify` on every call).
|
||||||
|
|
||||||
$ mkdir -p ~/.docker
|
$ mkdir -pv ~/.docker
|
||||||
$ cp ca.pem ~/.docker/ca.pem
|
$ cp -v {ca,cert,key}.pem ~/.docker
|
||||||
$ cp cert.pem ~/.docker/cert.pem
|
$ export DOCKER_HOST=tcp://$HOST:2376 DOCKER_TLS_VERIFY=1
|
||||||
$ cp key.pem ~/.docker/key.pem
|
|
||||||
$ export DOCKER_HOST=tcp://:2376
|
|
||||||
$ export DOCKER_TLS_VERIFY=1
|
|
||||||
|
|
||||||
Docker will now connect securely by default:
|
Docker will now connect securely by default:
|
||||||
|
|
||||||
@@ -165,11 +180,11 @@ Docker in various other modes by mixing the flags.
|
|||||||
certificate and authenticate server based on given CA
|
certificate and authenticate server based on given CA
|
||||||
|
|
||||||
If found, the client will send its client certificate, so you just need
|
If found, the client will send its client certificate, so you just need
|
||||||
to drop your keys into `~/.docker/<ca, cert or key>.pem`. Alternatively,
|
to drop your keys into `~/.docker/{ca,cert,key}.pem`. Alternatively,
|
||||||
if you want to store your keys in another location, you can specify that
|
if you want to store your keys in another location, you can specify that
|
||||||
location using the environment variable `DOCKER_CERT_PATH`.
|
location using the environment variable `DOCKER_CERT_PATH`.
|
||||||
|
|
||||||
$ export DOCKER_CERT_PATH=${HOME}/.docker/zone1/
|
$ export DOCKER_CERT_PATH=~/.docker/zone1/
|
||||||
$ docker --tlsverify ps
|
$ docker --tlsverify ps
|
||||||
|
|
||||||
### Connecting to the Secure Docker port using `curl`
|
### Connecting to the Secure Docker port using `curl`
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ cert: build
|
|||||||
certs: cert
|
certs: cert
|
||||||
|
|
||||||
run:
|
run:
|
||||||
docker -d -D --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem -H=0.0.0.0:6666 --pidfile=$(pwd)/docker.pid --graph=$(pwd)/graph
|
sudo docker -d -D --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem -H=0.0.0.0:6666 --pidfile=$(pwd)/docker.pid --graph=$(pwd)/graph
|
||||||
|
|
||||||
client:
|
client:
|
||||||
docker --tls --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=$(HOST):6666 version
|
sudo docker --tls --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=$(HOST):6666 version
|
||||||
docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=$(HOST):6666 info
|
sudo docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=$(HOST):6666 info
|
||||||
|
sudo curl https://$(HOST):6666/images/json --cert cert.pem --key key.pem --cacert ca.pem
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm ca-key.pem ca.pem ca.srl cert.pem client.csr extfile.cnf key.pem server-cert.pem server-key.pem server.csr
|
rm ca-key.pem ca.pem ca.srl cert.pem client.csr extfile.cnf key.pem server-cert.pem server-key.pem server.csr
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
echo "#!/bin/sh"
|
echo "#!/bin/sh"
|
||||||
cat ../https.md | awk '{if (sub(/\\$/,"")) printf "%s", $0; else print $0}' | grep ' $ ' | sed 's/ $ //g' | sed 's/2375/7777/g' | sed 's/2376/7778/g'
|
cat ../https.md | awk '{if (sub(/\\$/,"")) printf "%s", $0; else print $0}' \
|
||||||
|
| grep ' $ ' \
|
||||||
|
| sed 's/ $ //g' \
|
||||||
|
| sed 's/2375/7777/g' \
|
||||||
|
| sed 's/2376/7778/g' \
|
||||||
|
| sed 's/^docker/# docker/g' \
|
||||||
|
| sed 's/^curl/# curl/g'
|
||||||
|
|||||||
@@ -687,6 +687,7 @@ stopping the service and removing the interface:
|
|||||||
$ sudo service docker stop
|
$ sudo service docker stop
|
||||||
$ sudo ip link set dev docker0 down
|
$ sudo ip link set dev docker0 down
|
||||||
$ sudo brctl delbr docker0
|
$ sudo brctl delbr docker0
|
||||||
|
$ sudo iptables -t nat -F POSTROUTING
|
||||||
|
|
||||||
Then, before starting the Docker service, create your own bridge and
|
Then, before starting the Docker service, create your own bridge and
|
||||||
give it whatever configuration you want. Here we will create a simple
|
give it whatever configuration you want. Here we will create a simple
|
||||||
@@ -713,6 +714,15 @@ illustrate the technique.
|
|||||||
$ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker
|
$ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker
|
||||||
$ sudo service docker start
|
$ sudo service docker start
|
||||||
|
|
||||||
|
# Confirming new outgoing NAT masquerade is set up
|
||||||
|
|
||||||
|
$ sudo iptables -t nat -L -n
|
||||||
|
...
|
||||||
|
Chain POSTROUTING (policy ACCEPT)
|
||||||
|
target prot opt source destination
|
||||||
|
MASQUERADE all -- 192.168.5.0/24 0.0.0.0/0
|
||||||
|
|
||||||
|
|
||||||
The result should be that the Docker server starts successfully and is
|
The result should be that the Docker server starts successfully and is
|
||||||
now prepared to bind containers to the new bridge. After pausing to
|
now prepared to bind containers to the new bridge. After pausing to
|
||||||
verify the bridge's configuration, try creating a container — you will
|
verify the bridge's configuration, try creating a container — you will
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ our container.
|
|||||||
|
|
||||||
Here we're installing the `openssh-server`,
|
Here we're installing the `openssh-server`,
|
||||||
`apache2` and `supervisor`
|
`apache2` and `supervisor`
|
||||||
(which provides the Supervisor daemon) packages. We're also creating two
|
(which provides the Supervisor daemon) packages. We're also creating four
|
||||||
new directories that are needed to run our SSH daemon and Supervisor.
|
new directories that are needed to run our SSH daemon and Supervisor.
|
||||||
|
|
||||||
## Adding Supervisor's configuration file
|
## Adding Supervisor's configuration file
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ used for all tests, builds and releases. The standard development
|
|||||||
environment defines all build dependencies: system libraries and
|
environment defines all build dependencies: system libraries and
|
||||||
binaries, go environment, go dependencies, etc.
|
binaries, go environment, go dependencies, etc.
|
||||||
|
|
||||||
|
**Things you need:**
|
||||||
|
|
||||||
|
* Docker
|
||||||
|
* git
|
||||||
|
* make
|
||||||
|
|
||||||
## Install Docker
|
## Install Docker
|
||||||
|
|
||||||
Docker's build environment itself is a Docker container, so the first
|
Docker's build environment itself is a Docker container, so the first
|
||||||
@@ -43,18 +49,18 @@ with the name of branch or revision number.
|
|||||||
This following command builds a development environment using the
|
This following command builds a development environment using the
|
||||||
`Dockerfile` in the current directory. Essentially, it installs all
|
`Dockerfile` in the current directory. Essentially, it installs all
|
||||||
the build and runtime dependencies necessary to build and test Docker.
|
the build and runtime dependencies necessary to build and test Docker.
|
||||||
Your first build will take some time to complete. On Linux systems:
|
Your first build will take some time to complete. On Linux systems and on Mac
|
||||||
|
OS X from within the `boot2docker` shell:
|
||||||
$ sudo make build
|
|
||||||
|
|
||||||
On Mac OS X, from within the `boot2docker` shell:
|
|
||||||
|
|
||||||
$ make build
|
$ make build
|
||||||
|
|
||||||
> **Note**:
|
> **Note**:
|
||||||
> On Mac OS X, the Docker make targets such as `build`, `binary`, and `test`
|
> On Mac OS X, the Docker make targets such as `build`, `binary`, and `test`
|
||||||
> should **not** be built by the 'root' user. Therefore, you shouldn't use `sudo` when
|
> should **not** be built by the 'root' user. Therefore, you shouldn't use `sudo` when
|
||||||
> running these commands on OS X.
|
> running these commands on OS X.
|
||||||
|
> On Linux, we suggest you add your current user to the `docker` group via
|
||||||
|
> [these
|
||||||
|
> instructions](http://docs.docker.com/installation/ubuntulinux/#giving-non-root-access).
|
||||||
|
|
||||||
If the build is successful, congratulations! You have produced a clean
|
If the build is successful, congratulations! You have produced a clean
|
||||||
build of docker, neatly encapsulated in a standard build environment.
|
build of docker, neatly encapsulated in a standard build environment.
|
||||||
@@ -64,13 +70,13 @@ build of docker, neatly encapsulated in a standard build environment.
|
|||||||
|
|
||||||
To create the Docker binary, run this command:
|
To create the Docker binary, run this command:
|
||||||
|
|
||||||
$ sudo make binary
|
$ make binary
|
||||||
|
|
||||||
This will create the Docker binary in `./bundles/<version>-dev/binary/`. If you
|
This will create the Docker binary in `./bundles/<version>-dev/binary/`. If you
|
||||||
do not see files in the `./bundles` directory in your host, your `BINDDIR`
|
do not see files in the `./bundles` directory in your host, your `BINDDIR`
|
||||||
setting is not set quite right. You want to run the following command:
|
setting is not set quite right. You want to run the following command:
|
||||||
|
|
||||||
$ sudo make BINDDIR=. binary
|
$ make BINDDIR=. binary
|
||||||
|
|
||||||
If you are on a non-Linux platform, e.g., OSX, you'll want to run `make cross`
|
If you are on a non-Linux platform, e.g., OSX, you'll want to run `make cross`
|
||||||
or `make BINDDIR=. cross`.
|
or `make BINDDIR=. cross`.
|
||||||
@@ -91,7 +97,7 @@ on ubuntu:
|
|||||||
|
|
||||||
To execute the test cases, run this command:
|
To execute the test cases, run this command:
|
||||||
|
|
||||||
$ sudo make test
|
$ make test
|
||||||
|
|
||||||
If the test are successful then the tail of the output should look
|
If the test are successful then the tail of the output should look
|
||||||
something like this
|
something like this
|
||||||
@@ -138,7 +144,7 @@ is recommended.
|
|||||||
|
|
||||||
You can run an interactive session in the newly built container:
|
You can run an interactive session in the newly built container:
|
||||||
|
|
||||||
$ sudo make shell
|
$ make shell
|
||||||
|
|
||||||
# type 'exit' or Ctrl-D to exit
|
# type 'exit' or Ctrl-D to exit
|
||||||
|
|
||||||
@@ -148,7 +154,7 @@ If you want to read the documentation from a local website, or are
|
|||||||
making changes to it, you can build the documentation and then serve it
|
making changes to it, you can build the documentation and then serve it
|
||||||
by:
|
by:
|
||||||
|
|
||||||
$ sudo make docs
|
$ make docs
|
||||||
|
|
||||||
# when its done, you can point your browser to http://yourdockerhost:8000
|
# when its done, you can point your browser to http://yourdockerhost:8000
|
||||||
# type Ctrl-C to exit
|
# type Ctrl-C to exit
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
page_title: Using Docker Hub Enterprise Installation
|
||||||
|
page_description: Docker Hub Enterprise Installation
|
||||||
|
page_keywords: docker hub enterprise
|
||||||
|
|
||||||
|
# Docker Hub Enterprise Installation
|
||||||
|
|
||||||
|
Documenation coming soon.
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
page_title: Using Docker Hub Enterprise
|
||||||
|
page_description: Docker Hub Enterprise
|
||||||
|
page_keywords: docker hub enterprise
|
||||||
|
|
||||||
|
# Docker Hub Enterprise
|
||||||
|
|
||||||
|
Documenation coming soon.
|
||||||
|
|
||||||
|
|
||||||
@@ -278,6 +278,10 @@ Webhooks are available under the Settings menu of each Repository.
|
|||||||
> **Note:** If you want to test your webhook out we recommend using
|
> **Note:** If you want to test your webhook out we recommend using
|
||||||
> a tool like [requestb.in](http://requestb.in/).
|
> a tool like [requestb.in](http://requestb.in/).
|
||||||
|
|
||||||
|
> **Note**: The Docker Hub servers are currently in the IP range
|
||||||
|
> `162.242.195.64 - 162.242.195.127`, so you can restrict your webhooks to
|
||||||
|
> accept webhook requests from that set of IP addresses.
|
||||||
|
|
||||||
### Webhook chains
|
### Webhook chains
|
||||||
|
|
||||||
Webhook chains allow you to chain calls to multiple services. For example,
|
Webhook chains allow you to chain calls to multiple services. For example,
|
||||||
|
|||||||
@@ -105,9 +105,6 @@ Settings page. A webhook is called only after a successful `push` is
|
|||||||
made. The webhook calls are HTTP POST requests with a JSON payload
|
made. The webhook calls are HTTP POST requests with a JSON payload
|
||||||
similar to the example shown below.
|
similar to the example shown below.
|
||||||
|
|
||||||
> **Note:** For testing, you can try an HTTP request tool like
|
|
||||||
> [requestb.in](http://requestb.in/).
|
|
||||||
|
|
||||||
*Example webhook JSON payload:*
|
*Example webhook JSON payload:*
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -141,6 +138,13 @@ new updates to your images and repositories. To get started adding webhooks,
|
|||||||
go to the desired repo in the Hub, and click "Webhooks" under the "Settings"
|
go to the desired repo in the Hub, and click "Webhooks" under the "Settings"
|
||||||
box.
|
box.
|
||||||
|
|
||||||
|
> **Note:** For testing, you can try an HTTP request tool like
|
||||||
|
> [requestb.in](http://requestb.in/).
|
||||||
|
|
||||||
|
> **Note**: The Docker Hub servers are currently in the IP range
|
||||||
|
> `162.242.195.64 - 162.242.195.127`, so you can restrict your webhooks to
|
||||||
|
> accept webhook requests from that set of IP addresses.
|
||||||
|
|
||||||
### Webhook chains
|
### Webhook chains
|
||||||
|
|
||||||
Webhook chains allow you to chain calls to multiple services. For example,
|
Webhook chains allow you to chain calls to multiple services. For example,
|
||||||
|
|||||||
@@ -385,6 +385,41 @@ Status Codes:
|
|||||||
- **404** – no such container
|
- **404** – no such container
|
||||||
- **500** – server error
|
- **500** – server error
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
|
|||||||
@@ -385,6 +385,41 @@ Status Codes:
|
|||||||
- **404** – no such container
|
- **404** – no such container
|
||||||
- **500** – server error
|
- **500** – server error
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -45,7 +45,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -55,7 +55,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -65,7 +65,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -119,7 +119,7 @@ Create a container
|
|||||||
"Cmd":[
|
"Cmd":[
|
||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":{
|
"Volumes":{
|
||||||
"/tmp": {}
|
"/tmp": {}
|
||||||
},
|
},
|
||||||
@@ -194,7 +194,7 @@ Return low-level information on the container `id`
|
|||||||
"Cmd": [
|
"Cmd": [
|
||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"WorkingDir":""
|
"WorkingDir":""
|
||||||
|
|
||||||
@@ -539,6 +539,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1)
|
5. Goto 1)
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
@@ -667,7 +702,7 @@ Create an image, either by pull it from the registry or by importing
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -739,7 +774,7 @@ Return low-level information on the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/ubuntu/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -766,7 +801,7 @@ Return low-level information on the image `name`
|
|||||||
"StdinOnce":false,
|
"StdinOnce":false,
|
||||||
"Env":null,
|
"Env":null,
|
||||||
"Cmd": ["/bin/bash"]
|
"Cmd": ["/bin/bash"]
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":null,
|
"Volumes":null,
|
||||||
"WorkingDir":""
|
"WorkingDir":""
|
||||||
},
|
},
|
||||||
@@ -787,7 +822,7 @@ Return the history of the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/ubuntu/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1194,10 +1229,10 @@ and Docker images will report:
|
|||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||||
|
|
||||||
Query Parameters:
|
Query Parameters:
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -45,7 +45,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -55,7 +55,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -65,7 +65,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -119,7 +119,7 @@ Create a container
|
|||||||
"Cmd":[
|
"Cmd":[
|
||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":{
|
"Volumes":{
|
||||||
"/tmp": {}
|
"/tmp": {}
|
||||||
},
|
},
|
||||||
@@ -195,7 +195,7 @@ Return low-level information on the container `id`
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -574,6 +574,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1)
|
5. Goto 1)
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
@@ -701,7 +736,7 @@ Create an image, either by pull it from the registry or by importing i
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -742,7 +777,7 @@ Return low-level information on the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/ubuntu/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -770,7 +805,7 @@ Return low-level information on the image `name`
|
|||||||
"Env":null,
|
"Env":null,
|
||||||
"Cmd": ["/bin/bash"],
|
"Cmd": ["/bin/bash"],
|
||||||
"Dns":null,
|
"Dns":null,
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":null,
|
"Volumes":null,
|
||||||
"VolumesFrom":"",
|
"VolumesFrom":"",
|
||||||
"WorkingDir":""
|
"WorkingDir":""
|
||||||
@@ -792,7 +827,7 @@ Return the history of the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/ubuntu/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1224,10 +1259,10 @@ and Docker images will report:
|
|||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||||
|
|
||||||
Query Parameters:
|
Query Parameters:
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -46,7 +46,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -56,7 +56,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -66,7 +66,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -127,7 +127,7 @@ Create a container
|
|||||||
"Cmd":[
|
"Cmd":[
|
||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":{
|
"Volumes":{
|
||||||
"/tmp": {}
|
"/tmp": {}
|
||||||
},
|
},
|
||||||
@@ -204,7 +204,7 @@ Return low-level information on the container `id`
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -622,6 +622,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1
|
5. Goto 1
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
@@ -760,7 +795,7 @@ Create an image, either by pull it from the registry or by importing i
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -803,7 +838,7 @@ Return low-level information on the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/ubuntu/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -829,7 +864,7 @@ Return low-level information on the image `name`
|
|||||||
"Env": null,
|
"Env": null,
|
||||||
"Cmd": ["/bin/bash"],
|
"Cmd": ["/bin/bash"],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": null,
|
"Volumes": null,
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -853,7 +888,7 @@ Return the history of the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/ubuntu/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1290,10 +1325,10 @@ and Docker images will report:
|
|||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||||
|
|
||||||
Query Parameters:
|
Query Parameters:
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -46,7 +46,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -56,7 +56,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -66,7 +66,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -121,7 +121,7 @@ Create a container
|
|||||||
"Cmd":[
|
"Cmd":[
|
||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":{
|
"Volumes":{
|
||||||
"/tmp": {}
|
"/tmp": {}
|
||||||
},
|
},
|
||||||
@@ -198,7 +198,7 @@ Return low-level information on the container `id`
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -615,6 +615,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1
|
5. Goto 1
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
@@ -749,7 +784,7 @@ Create an image, either by pulling it from the registry or by importing it
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -792,7 +827,7 @@ Return low-level information on the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/ubuntu/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -818,7 +853,7 @@ Return low-level information on the image `name`
|
|||||||
"Env": null,
|
"Env": null,
|
||||||
"Cmd": ["/bin/bash"],
|
"Cmd": ["/bin/bash"],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": null,
|
"Volumes": null,
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -842,7 +877,7 @@ Return the history of the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/ubuntu/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1279,10 +1314,10 @@ and Docker images will report:
|
|||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||||
|
|
||||||
Query Parameters:
|
Query Parameters:
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -46,7 +46,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -56,7 +56,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -66,7 +66,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -124,7 +124,7 @@ Create a container
|
|||||||
"Cmd":[
|
"Cmd":[
|
||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Image":"base",
|
"Image":"ubuntu",
|
||||||
"Volumes":{
|
"Volumes":{
|
||||||
"/tmp": {}
|
"/tmp": {}
|
||||||
},
|
},
|
||||||
@@ -207,7 +207,7 @@ Return low-level information on the container `id`
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -625,6 +625,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1
|
5. Goto 1
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
@@ -759,7 +794,7 @@ Create an image, either by pulling it from the registry or by importing it
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -802,7 +837,7 @@ Return low-level information on the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/ubuntu/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -828,7 +863,7 @@ Return low-level information on the image `name`
|
|||||||
"Env": null,
|
"Env": null,
|
||||||
"Cmd": ["/bin/bash"],
|
"Cmd": ["/bin/bash"],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": null,
|
"Volumes": null,
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -852,7 +887,7 @@ Return the history of the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/ubuntu/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1289,10 +1324,10 @@ and Docker images will report:
|
|||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||||
|
|
||||||
Query Parameters:
|
Query Parameters:
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -46,7 +46,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -56,7 +56,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -66,7 +66,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -128,7 +128,7 @@ Create a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Entrypoint": "",
|
"Entrypoint": "",
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {
|
"Volumes": {
|
||||||
"/tmp": {}
|
"/tmp": {}
|
||||||
},
|
},
|
||||||
@@ -148,6 +148,7 @@ Create a container
|
|||||||
"Privileged": false,
|
"Privileged": false,
|
||||||
"Dns": ["8.8.8.8"],
|
"Dns": ["8.8.8.8"],
|
||||||
"DnsSearch": [""],
|
"DnsSearch": [""],
|
||||||
|
"ExtraHosts": null,
|
||||||
"VolumesFrom": ["parent", "other:ro"],
|
"VolumesFrom": ["parent", "other:ro"],
|
||||||
"CapAdd": ["NET_ADMIN"],
|
"CapAdd": ["NET_ADMIN"],
|
||||||
"CapDrop": ["MKNOD"],
|
"CapDrop": ["MKNOD"],
|
||||||
@@ -220,6 +221,8 @@ Json Parameters:
|
|||||||
a boolean value.
|
a boolean value.
|
||||||
- **Dns** - A list of dns servers for the container to use.
|
- **Dns** - A list of dns servers for the container to use.
|
||||||
- **DnsSearch** - A list of DNS search domains
|
- **DnsSearch** - A list of DNS search domains
|
||||||
|
- **ExtraHosts** - A list of hostnames/IP mappings to be added to the
|
||||||
|
container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`.
|
||||||
- **VolumesFrom** - A list of volumes to inherit from another container.
|
- **VolumesFrom** - A list of volumes to inherit from another container.
|
||||||
Specified in the form `<container name>[:<ro|rw>]`
|
Specified in the form `<container name>[:<ro|rw>]`
|
||||||
- **CapAdd** - A list of kernel capabilties to add to the container.
|
- **CapAdd** - A list of kernel capabilties to add to the container.
|
||||||
@@ -288,7 +291,7 @@ Return low-level information on the container `id`
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -767,6 +770,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1
|
5. Goto 1
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
@@ -901,7 +939,7 @@ Create an image, either by pulling it from the registry or by importing it
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -945,7 +983,7 @@ Return low-level information on the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/ubuntu/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -971,7 +1009,7 @@ Return low-level information on the image `name`
|
|||||||
"Env": null,
|
"Env": null,
|
||||||
"Cmd": ["/bin/bash"],
|
"Cmd": ["/bin/bash"],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": null,
|
"Volumes": null,
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -995,7 +1033,7 @@ Return the history of the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/ubuntu/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1433,10 +1471,10 @@ and Docker images will report:
|
|||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||||
|
|
||||||
Query Parameters:
|
Query Parameters:
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -46,7 +46,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -56,7 +56,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -66,7 +66,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -128,7 +128,7 @@ Create a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Entrypoint": "",
|
"Entrypoint": "",
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {
|
"Volumes": {
|
||||||
"/tmp": {}
|
"/tmp": {}
|
||||||
},
|
},
|
||||||
@@ -148,6 +148,7 @@ Create a container
|
|||||||
"Privileged": false,
|
"Privileged": false,
|
||||||
"Dns": ["8.8.8.8"],
|
"Dns": ["8.8.8.8"],
|
||||||
"DnsSearch": [""],
|
"DnsSearch": [""],
|
||||||
|
"ExtraHosts": null,
|
||||||
"VolumesFrom": ["parent", "other:ro"],
|
"VolumesFrom": ["parent", "other:ro"],
|
||||||
"CapAdd": ["NET_ADMIN"],
|
"CapAdd": ["NET_ADMIN"],
|
||||||
"CapDrop": ["MKNOD"],
|
"CapDrop": ["MKNOD"],
|
||||||
@@ -220,6 +221,8 @@ Json Parameters:
|
|||||||
a boolean value.
|
a boolean value.
|
||||||
- **Dns** - A list of dns servers for the container to use.
|
- **Dns** - A list of dns servers for the container to use.
|
||||||
- **DnsSearch** - A list of DNS search domains
|
- **DnsSearch** - A list of DNS search domains
|
||||||
|
- **ExtraHosts** - A list of hostnames/IP mappings to be added to the
|
||||||
|
container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`.
|
||||||
- **VolumesFrom** - A list of volumes to inherit from another container.
|
- **VolumesFrom** - A list of volumes to inherit from another container.
|
||||||
Specified in the form `<container name>[:<ro|rw>]`
|
Specified in the form `<container name>[:<ro|rw>]`
|
||||||
- **CapAdd** - A list of kernel capabilties to add to the container.
|
- **CapAdd** - A list of kernel capabilties to add to the container.
|
||||||
@@ -288,7 +291,7 @@ Return low-level information on the container `id`
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -713,6 +716,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1
|
5. Goto 1
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
@@ -847,7 +885,7 @@ Create an image, either by pulling it from the registry or by importing it
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -891,7 +929,7 @@ Return low-level information on the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/ubuntu/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -917,7 +955,7 @@ Return low-level information on the image `name`
|
|||||||
"Env": null,
|
"Env": null,
|
||||||
"Cmd": ["/bin/bash"],
|
"Cmd": ["/bin/bash"],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": null,
|
"Volumes": null,
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -941,7 +979,7 @@ Return the history of the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/ubuntu/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1389,10 +1427,10 @@ and Docker images will report:
|
|||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||||
|
|
||||||
Query Parameters:
|
Query Parameters:
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ List containers
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Id": "8dfafdbc3a40",
|
"Id": "8dfafdbc3a40",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 1",
|
"Command": "echo 1",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -46,7 +46,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cd87474be90",
|
"Id": "9cd87474be90",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 222222",
|
"Command": "echo 222222",
|
||||||
"Created": 1367854155,
|
"Created": 1367854155,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -56,7 +56,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3176a2479c92",
|
"Id": "3176a2479c92",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 3333333333333333",
|
"Command": "echo 3333333333333333",
|
||||||
"Created": 1367854154,
|
"Created": 1367854154,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -66,7 +66,7 @@ List containers
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4cb07b47f9fb",
|
"Id": "4cb07b47f9fb",
|
||||||
"Image": "base:latest",
|
"Image": "ubuntu:latest",
|
||||||
"Command": "echo 444444444444444444444444444444444",
|
"Command": "echo 444444444444444444444444444444444",
|
||||||
"Created": 1367854152,
|
"Created": 1367854152,
|
||||||
"Status": "Exit 0",
|
"Status": "Exit 0",
|
||||||
@@ -128,7 +128,7 @@ Create a container
|
|||||||
"date"
|
"date"
|
||||||
],
|
],
|
||||||
"Entrypoint": "",
|
"Entrypoint": "",
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": {
|
"Volumes": {
|
||||||
"/tmp": {}
|
"/tmp": {}
|
||||||
},
|
},
|
||||||
@@ -149,6 +149,7 @@ Create a container
|
|||||||
"ReadonlyRootfs": false,
|
"ReadonlyRootfs": false,
|
||||||
"Dns": ["8.8.8.8"],
|
"Dns": ["8.8.8.8"],
|
||||||
"DnsSearch": [""],
|
"DnsSearch": [""],
|
||||||
|
"ExtraHosts": null,
|
||||||
"VolumesFrom": ["parent", "other:ro"],
|
"VolumesFrom": ["parent", "other:ro"],
|
||||||
"CapAdd": ["NET_ADMIN"],
|
"CapAdd": ["NET_ADMIN"],
|
||||||
"CapDrop": ["MKNOD"],
|
"CapDrop": ["MKNOD"],
|
||||||
@@ -223,6 +224,8 @@ Json Parameters:
|
|||||||
Specified as a boolean value.
|
Specified as a boolean value.
|
||||||
- **Dns** - A list of dns servers for the container to use.
|
- **Dns** - A list of dns servers for the container to use.
|
||||||
- **DnsSearch** - A list of DNS search domains
|
- **DnsSearch** - A list of DNS search domains
|
||||||
|
- **ExtraHosts** - A list of hostnames/IP mappings to be added to the
|
||||||
|
container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`.
|
||||||
- **VolumesFrom** - A list of volumes to inherit from another container.
|
- **VolumesFrom** - A list of volumes to inherit from another container.
|
||||||
Specified in the form `<container name>[:<ro|rw>]`
|
Specified in the form `<container name>[:<ro|rw>]`
|
||||||
- **CapAdd** - A list of kernel capabilties to add to the container.
|
- **CapAdd** - A list of kernel capabilties to add to the container.
|
||||||
@@ -870,6 +873,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1
|
5. Goto 1
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
@@ -1058,7 +1096,7 @@ Create an image, either by pulling it from the registry or by importing it
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1102,7 +1140,7 @@ Return low-level information on the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/json HTTP/1.1
|
GET /images/ubuntu/json HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1128,7 +1166,7 @@ Return low-level information on the image `name`
|
|||||||
"Env": null,
|
"Env": null,
|
||||||
"Cmd": ["/bin/bash"],
|
"Cmd": ["/bin/bash"],
|
||||||
"Dns": null,
|
"Dns": null,
|
||||||
"Image": "base",
|
"Image": "ubuntu",
|
||||||
"Volumes": null,
|
"Volumes": null,
|
||||||
"VolumesFrom": "",
|
"VolumesFrom": "",
|
||||||
"WorkingDir": ""
|
"WorkingDir": ""
|
||||||
@@ -1152,7 +1190,7 @@ Return the history of the image `name`
|
|||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
GET /images/base/history HTTP/1.1
|
GET /images/ubuntu/history HTTP/1.1
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
@@ -1551,10 +1589,10 @@ and Docker images will report:
|
|||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||||
|
|
||||||
Query Parameters:
|
Query Parameters:
|
||||||
|
|
||||||
|
|||||||
@@ -397,6 +397,41 @@ Status Codes:
|
|||||||
- **404** – no such container
|
- **404** – no such container
|
||||||
- **500** – server error
|
- **500** – server error
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
|
|||||||
@@ -445,6 +445,41 @@ Status Codes:
|
|||||||
- **404** – no such container
|
- **404** – no such container
|
||||||
- **500** – server error
|
- **500** – server error
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
|
|||||||
@@ -460,6 +460,41 @@ Status Codes:
|
|||||||
- **404** – no such container
|
- **404** – no such container
|
||||||
- **500** – server error
|
- **500** – server error
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
|
|||||||
@@ -458,6 +458,41 @@ Status Codes:
|
|||||||
- **404** – no such container
|
- **404** – no such container
|
||||||
- **500** – server error
|
- **500** – server error
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
|
|||||||
@@ -564,6 +564,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1)
|
5. Goto 1)
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
|
|||||||
@@ -509,6 +509,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1)
|
5. Goto 1)
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
|
|||||||
@@ -557,6 +557,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1)
|
5. Goto 1)
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
|
|||||||
@@ -561,6 +561,41 @@ Status Codes:
|
|||||||
4. Read the extracted size and output it on the correct output
|
4. Read the extracted size and output it on the correct output
|
||||||
5. Goto 1)
|
5. Goto 1)
|
||||||
|
|
||||||
|
### Attach to a container (websocket)
|
||||||
|
|
||||||
|
`GET /containers/(id)/attach/ws`
|
||||||
|
|
||||||
|
Attach to the container `id` via websocket
|
||||||
|
|
||||||
|
Implements websocket protocol handshake according to [RFC 6455](http://tools.ietf.org/html/rfc6455)
|
||||||
|
|
||||||
|
**Example request**
|
||||||
|
|
||||||
|
GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
|
||||||
|
|
||||||
|
**Example response**
|
||||||
|
|
||||||
|
{{ STREAM }}
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
|
||||||
|
- **logs** – 1/True/true or 0/False/false, return logs. Default false
|
||||||
|
- **stream** – 1/True/true or 0/False/false, return stream.
|
||||||
|
Default false
|
||||||
|
- **stdin** – 1/True/true or 0/False/false, if stream=true, attach
|
||||||
|
to stdin. Default false
|
||||||
|
- **stdout** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stdout log, if stream=true, attach to stdout. Default false
|
||||||
|
- **stderr** – 1/True/true or 0/False/false, if logs=true, return
|
||||||
|
stderr log, if stream=true, attach to stderr. Default false
|
||||||
|
|
||||||
|
Status Codes:
|
||||||
|
|
||||||
|
- **200** – no error
|
||||||
|
- **400** – bad parameter
|
||||||
|
- **404** – no such container
|
||||||
|
- **500** – server error
|
||||||
|
|
||||||
### Wait a container
|
### Wait a container
|
||||||
|
|
||||||
`POST /containers/(id)/wait`
|
`POST /containers/(id)/wait`
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ This will mount the host directory, `/src/webapp`, into the container at
|
|||||||
`/opt/webapp`.
|
`/opt/webapp`.
|
||||||
|
|
||||||
> **Note:**
|
> **Note:**
|
||||||
> If the path `/opt/webapp` already exists inside the container's image, it's
|
> If the path `/opt/webapp` already exists inside the container's image, its
|
||||||
> contents will be replaced by the contents of `/src/webapp` on the host to stay
|
> contents will be replaced by the contents of `/src/webapp` on the host to stay
|
||||||
> consistent with the expected behavior of `mount`
|
> consistent with the expected behavior of `mount`
|
||||||
|
|
||||||
|
|||||||
@@ -167,8 +167,9 @@ host. You might be asking about now: why wouldn't we just want to always
|
|||||||
use 1:1 port mappings in Docker containers rather than mapping to high
|
use 1:1 port mappings in Docker containers rather than mapping to high
|
||||||
ports? Well 1:1 mappings have the constraint of only being able to map
|
ports? Well 1:1 mappings have the constraint of only being able to map
|
||||||
one of each port on your local host. Let's say you want to test two
|
one of each port on your local host. Let's say you want to test two
|
||||||
Python applications: both bound to port 5000 inside your container.
|
Python applications: both bound to port 5000 inside their own containers.
|
||||||
Without Docker's port mapping you could only access one at a time.
|
Without Docker's port mapping you could only access one at a time on the
|
||||||
|
Docker host.
|
||||||
|
|
||||||
So let's now browse to port 49155 in a web browser to
|
So let's now browse to port 49155 in a web browser to
|
||||||
see the application.
|
see the application.
|
||||||
|
|||||||
Vendored
+1
@@ -60,6 +60,7 @@ pre {
|
|||||||
/* Main Navigation */
|
/* Main Navigation */
|
||||||
#nav_menu > #docsnav {
|
#nav_menu > #docsnav {
|
||||||
max-width: 940px;
|
max-width: 940px;
|
||||||
|
width: 940px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
#nav_menu > #docsnav > #nav_search {
|
#nav_menu > #docsnav > #nav_search {
|
||||||
|
|||||||
Vendored
-4
@@ -801,10 +801,6 @@ div + .form-inline {
|
|||||||
transition: box-shadow linear 0.2s, background linear 0.3s, width linear 0.3s;
|
transition: box-shadow linear 0.2s, background linear 0.3s, width linear 0.3s;
|
||||||
width: 140px;
|
width: 140px;
|
||||||
}
|
}
|
||||||
#topmostnav .navbar-index-search .search-query:focus,
|
|
||||||
#topmostnav .navbar-index-search .search-query.focused {
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
#topmostnav.public {
|
#topmostnav.public {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
|
|||||||
Vendored
+10
-8
@@ -1,13 +1,14 @@
|
|||||||
<div id="topmostnav" class="topmostnav_loggedout navbar navbar-static-top public">
|
<div id="topmostnav" class="topmostnav_loggedout navbar navbar-static-top">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a href="http://www.docker.com/" title="Homepage">
|
<a href="/" title="Docker Docs Home"><div class="brand logo"><img src="/img/nav/docker-logo-loggedin.png"> </div></a>
|
||||||
<div class="brand logo"><img src="/img/nav/docker-logo-loggedout.png"> </div>
|
<form id="nav_search" class="navbar-index-search pull-right" action="/jsearch/">
|
||||||
</a>
|
<span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span>
|
||||||
|
<input name="q" id="tipue_search_input" type="text" class="search_input search-query ui-autocomplete-input" placeholder="Search the Docs" autocomplete="off">
|
||||||
|
</form>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class=""><a href="http://www.docker.com/whatisdocker/" title="What is Docker">What is Docker?</a></li>
|
<li class=""><a href="http://www.docker.com/whatisdocker/" title="What is Docker">What is Docker?</a></li>
|
||||||
<li class=""><a href="http://www.docker.com/resources/usecases/" title="Use Cases">Use Cases</a></li>
|
<li class=""><a href="http://www.docker.com/resources/usecases/" title="Use Cases">Use Cases</a></li>
|
||||||
<li class=""><a href="http://www.docker.com/tryit/" title="Try It!">Try It!</a></li>
|
<li class=""><a href="http://www.docker.com/tryit/" title="Try It!">Try It!</a></li>
|
||||||
<li class="active"><a href="https://docs.docker.com" title="Install & Docs">Install & Docs</a></li>
|
|
||||||
<li><a href="https://registry.hub.docker.com" title="Browse">Browse</a></li>
|
<li><a href="https://registry.hub.docker.com" title="Browse">Browse</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div id="usernav" class="pull-right">
|
<div id="usernav" class="pull-right">
|
||||||
@@ -18,9 +19,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="topmostnav" class="topmostnav_loggedin navbar navbar-static-top">
|
<div id="topmostnav" class="topmostnav_loggedin navbar navbar-static-top">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a href="http://www.docker.com/" title="Docker Docs Home"><div class="brand logo"><img src="/img/nav/docker-logo-loggedin.png"> </div></a>
|
<a href="/" title="Docker Docs Home"><div class="brand logo"><img src="/img/nav/docker-logo-loggedin.png"> </div></a>
|
||||||
<form id="search_box_header" class="navbar-index-search pull-right" action="https://registry.hub.docker.com/search">
|
<form id="nav_search" class="navbar-index-search pull-right" action="/jsearch/">
|
||||||
<span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" class="search-query ui-autocomplete-input" placeholder="Search..." name="q" value="" autocomplete="off">
|
<span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span>
|
||||||
|
<input name="q" id="tipue_search_input" type="text" class="search_input search-query ui-autocomplete-input" placeholder="Search the Docs" autocomplete="off">
|
||||||
</form>
|
</form>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li><a href="https://registry.hub.docker.com" title="Browse Repos">Browse Repos</a></li>
|
<li><a href="https://registry.hub.docker.com" title="Browse Repos">Browse Repos</a></li>
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 2.7 KiB |
Vendored
+13
-7
@@ -1,12 +1,6 @@
|
|||||||
$(document).ready(function ()
|
$(document).ready(function ()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Tipue Search activation
|
|
||||||
$('#tipue_search_input').tipuesearch({
|
|
||||||
'mode': 'json',
|
|
||||||
'contentLocation': '/search_content.json'
|
|
||||||
});
|
|
||||||
|
|
||||||
prettyPrint();
|
prettyPrint();
|
||||||
|
|
||||||
// Resizing
|
// Resizing
|
||||||
@@ -51,6 +45,18 @@ $(document).ready(function ()
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getURLP(name)
|
||||||
|
{
|
||||||
|
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, '%20')) || null;
|
||||||
|
}
|
||||||
|
if (getURLP("q")) {
|
||||||
|
// Tipue Search activation
|
||||||
|
$('#tipue_search_input').tipuesearch({
|
||||||
|
'mode': 'json',
|
||||||
|
'contentLocation': '/search_content.json.gz'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function resizeMenuDropdown ()
|
function resizeMenuDropdown ()
|
||||||
@@ -92,4 +98,4 @@ function getCookie(cname) {
|
|||||||
if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
|
if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
-4
@@ -18,9 +18,5 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<form id="nav_search" class="pull-right" action="/jsearch/">
|
|
||||||
<span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span>
|
|
||||||
<input name="q" id="tipue_search_input" type="text" class="search_input search-query ui-autocomplete-input" placeholder="Search the Docs" autocomplete="off">
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRegister(t *testing.T) {
|
func TestRegister(t *testing.T) {
|
||||||
@@ -150,3 +152,85 @@ func TestCatchallEmptyName(t *testing.T) {
|
|||||||
t.Fatalf("Engine.Job(\"\").Run() should return an error")
|
t.Fatalf("Engine.Job(\"\").Run() should return an error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure that a job within a job both using the same underlying standard
|
||||||
|
// output writer does not close the output of the outer job when the inner
|
||||||
|
// job's stdout is wrapped with a NopCloser. When not wrapped, it should
|
||||||
|
// close the outer job's output.
|
||||||
|
func TestNestedJobSharedOutput(t *testing.T) {
|
||||||
|
var (
|
||||||
|
outerHandler Handler
|
||||||
|
innerHandler Handler
|
||||||
|
wrapOutput bool
|
||||||
|
)
|
||||||
|
|
||||||
|
outerHandler = func(job *Job) Status {
|
||||||
|
job.Stdout.Write([]byte("outer1"))
|
||||||
|
|
||||||
|
innerJob := job.Eng.Job("innerJob")
|
||||||
|
|
||||||
|
if wrapOutput {
|
||||||
|
innerJob.Stdout.Add(ioutils.NopWriteCloser(job.Stdout))
|
||||||
|
} else {
|
||||||
|
innerJob.Stdout.Add(job.Stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := innerJob.Run(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If wrapOutput was *false* this write will do nothing.
|
||||||
|
// FIXME (jlhawn): It should cause an error to write to
|
||||||
|
// closed output.
|
||||||
|
job.Stdout.Write([]byte(" outer2"))
|
||||||
|
|
||||||
|
return StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
innerHandler = func(job *Job) Status {
|
||||||
|
job.Stdout.Write([]byte(" inner"))
|
||||||
|
|
||||||
|
return StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
eng := New()
|
||||||
|
eng.Register("outerJob", outerHandler)
|
||||||
|
eng.Register("innerJob", innerHandler)
|
||||||
|
|
||||||
|
// wrapOutput starts *false* so the expected
|
||||||
|
// output of running the outer job will be:
|
||||||
|
//
|
||||||
|
// "outer1 inner"
|
||||||
|
//
|
||||||
|
outBuf := new(bytes.Buffer)
|
||||||
|
outerJob := eng.Job("outerJob")
|
||||||
|
outerJob.Stdout.Add(outBuf)
|
||||||
|
|
||||||
|
if err := outerJob.Run(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedOutput := "outer1 inner"
|
||||||
|
if outBuf.String() != expectedOutput {
|
||||||
|
t.Fatalf("expected job output to be %q, got %q", expectedOutput, outBuf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set wrapOutput to true so that the expected
|
||||||
|
// output of running the outer job will be:
|
||||||
|
//
|
||||||
|
// "outer1 inner outer2"
|
||||||
|
//
|
||||||
|
wrapOutput = true
|
||||||
|
outBuf.Reset()
|
||||||
|
outerJob = eng.Job("outerJob")
|
||||||
|
outerJob.Stdout.Add(outBuf)
|
||||||
|
|
||||||
|
if err := outerJob.Run(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedOutput = "outer1 inner outer2"
|
||||||
|
if outBuf.String() != expectedOutput {
|
||||||
|
t.Fatalf("expected job output to be %q, got %q", expectedOutput, outBuf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -223,6 +223,28 @@ func (graph *Graph) Mktemp(id string) (string, error) {
|
|||||||
return dir, nil
|
return dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (graph *Graph) newTempFile() (*os.File, error) {
|
||||||
|
tmp, err := graph.Mktemp("")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ioutil.TempFile(tmp, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func bufferToFile(f *os.File, src io.Reader) (int64, error) {
|
||||||
|
n, err := io.Copy(f, src)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
if err = f.Sync(); err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
if _, err := f.Seek(0, 0); err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
// setupInitLayer populates a directory with mountpoints suitable
|
// setupInitLayer populates a directory with mountpoints suitable
|
||||||
// for bind-mounting dockerinit into the container. The mountpoint is simply an
|
// for bind-mounting dockerinit into the container. The mountpoint is simply an
|
||||||
// empty file at /.dockerinit
|
// empty file at /.dockerinit
|
||||||
|
|||||||
+38
-52
@@ -3,11 +3,9 @@ package graph
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path"
|
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/engine"
|
"github.com/docker/docker/engine"
|
||||||
@@ -17,35 +15,6 @@ import (
|
|||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *TagStore) CmdManifest(job *engine.Job) engine.Status {
|
|
||||||
if len(job.Args) != 1 {
|
|
||||||
return job.Errorf("usage: %s NAME", job.Name)
|
|
||||||
}
|
|
||||||
name := job.Args[0]
|
|
||||||
tag := job.Getenv("tag")
|
|
||||||
if tag == "" {
|
|
||||||
tag = "latest"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve the Repository name from fqn to endpoint + name
|
|
||||||
repoInfo, err := registry.ParseRepositoryInfo(name)
|
|
||||||
if err != nil {
|
|
||||||
return job.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
manifestBytes, err := s.newManifest(name, repoInfo.RemoteName, tag)
|
|
||||||
if err != nil {
|
|
||||||
return job.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = job.Stdout.Write(manifestBytes)
|
|
||||||
if err != nil {
|
|
||||||
return job.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return engine.StatusOK
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error) {
|
func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error) {
|
||||||
manifest := ®istry.ManifestData{
|
manifest := ®istry.ManifestData{
|
||||||
Name: remoteName,
|
Name: remoteName,
|
||||||
@@ -71,14 +40,13 @@ func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if layer.Config == nil {
|
|
||||||
return nil, errors.New("Missing layer configuration")
|
|
||||||
}
|
|
||||||
manifest.Architecture = layer.Architecture
|
manifest.Architecture = layer.Architecture
|
||||||
manifest.FSLayers = make([]*registry.FSLayer, 0, 4)
|
manifest.FSLayers = make([]*registry.FSLayer, 0, 4)
|
||||||
manifest.History = make([]*registry.ManifestHistory, 0, 4)
|
manifest.History = make([]*registry.ManifestHistory, 0, 4)
|
||||||
var metadata runconfig.Config
|
var metadata runconfig.Config
|
||||||
metadata = *layer.Config
|
if layer.Config != nil {
|
||||||
|
metadata = *layer.Config
|
||||||
|
}
|
||||||
|
|
||||||
for ; layer != nil; layer, err = layer.GetParent() {
|
for ; layer != nil; layer, err = layer.GetParent() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -95,28 +63,41 @@ func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
archive, err := layer.TarLayer()
|
checksum, err := layer.GetCheckSum(s.graph.ImageRoot(layer.ID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("Error getting image checksum: %s", err)
|
||||||
|
}
|
||||||
|
if tarsum.VersionLabelForChecksum(checksum) != tarsum.Version1.String() {
|
||||||
|
archive, err := layer.TarLayer()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tarSum, err := tarsum.NewTarSum(archive, true, tarsum.Version1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = tarSum.Sum(nil)
|
||||||
|
|
||||||
|
// Save checksum value
|
||||||
|
if err := layer.SaveCheckSum(s.graph.ImageRoot(layer.ID), checksum); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tarSum, err := tarsum.NewTarSum(archive, true, tarsum.Version1)
|
jsonData, err := layer.RawJson()
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tarId := tarSum.Sum(nil)
|
|
||||||
|
|
||||||
manifest.FSLayers = append(manifest.FSLayers, ®istry.FSLayer{BlobSum: tarId})
|
|
||||||
|
|
||||||
layersSeen[layer.ID] = true
|
|
||||||
jsonData, err := ioutil.ReadFile(path.Join(s.graph.Root, layer.ID, "json"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Cannot retrieve the path for {%s}: %s", layer.ID, err)
|
return nil, fmt.Errorf("Cannot retrieve the path for {%s}: %s", layer.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
manifest.FSLayers = append(manifest.FSLayers, ®istry.FSLayer{BlobSum: checksum})
|
||||||
|
|
||||||
|
layersSeen[layer.ID] = true
|
||||||
|
|
||||||
manifest.History = append(manifest.History, ®istry.ManifestHistory{V1Compatibility: string(jsonData)})
|
manifest.History = append(manifest.History, ®istry.ManifestHistory{V1Compatibility: string(jsonData)})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +109,12 @@ func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error
|
|||||||
return manifestBytes, nil
|
return manifestBytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TagStore) verifyManifest(eng *engine.Engine, manifestBytes []byte) (*registry.ManifestData, bool, error) {
|
// loadManifest loads a manifest from a byte array and verifies its content.
|
||||||
|
// The signature must be verified or an error is returned. If the manifest
|
||||||
|
// contains no signatures by a trusted key for the name in the manifest, the
|
||||||
|
// image is not considered verified. The parsed manifest object and a boolean
|
||||||
|
// for whether the manifest is verified is returned.
|
||||||
|
func (s *TagStore) loadManifest(eng *engine.Engine, manifestBytes []byte) (*registry.ManifestData, bool, error) {
|
||||||
sig, err := libtrust.ParsePrettySignature(manifestBytes, "signatures")
|
sig, err := libtrust.ParsePrettySignature(manifestBytes, "signatures")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, fmt.Errorf("error parsing payload: %s", err)
|
return nil, false, fmt.Errorf("error parsing payload: %s", err)
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package graph
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/image"
|
||||||
|
"github.com/docker/docker/registry"
|
||||||
|
"github.com/docker/docker/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testManifestImageName = "testapp"
|
||||||
|
testManifestImageID = "d821b739e8834ec89ac4469266c3d11515da88fdcbcbdddcbcddb636f54fdde9"
|
||||||
|
testManifestImageIDShort = "d821b739e883"
|
||||||
|
testManifestTag = "manifesttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestManifestTarsumCache(t *testing.T) {
|
||||||
|
tmp, err := utils.TestDirectory("")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmp)
|
||||||
|
store := mkTestTagStore(tmp, t)
|
||||||
|
defer store.graph.driver.Cleanup()
|
||||||
|
|
||||||
|
archive, err := fakeTar()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
img := &image.Image{ID: testManifestImageID}
|
||||||
|
if err := store.graph.Register(img, archive); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := store.Set(testManifestImageName, testManifestTag, testManifestImageID, false); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cs, err := img.GetCheckSum(store.graph.ImageRoot(testManifestImageID)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if cs != "" {
|
||||||
|
t.Fatalf("Non-empty checksum file after register")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate manifest
|
||||||
|
payload, err := store.newManifest(testManifestImageName, testManifestImageName, testManifestTag)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestChecksum, err := img.GetCheckSum(store.graph.ImageRoot(testManifestImageID))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var manifest registry.ManifestData
|
||||||
|
if err := json.Unmarshal(payload, &manifest); err != nil {
|
||||||
|
t.Fatalf("error unmarshalling manifest: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(manifest.FSLayers) != 1 {
|
||||||
|
t.Fatalf("Unexpected number of layers, expecting 1: %d", len(manifest.FSLayers))
|
||||||
|
}
|
||||||
|
|
||||||
|
if manifest.FSLayers[0].BlobSum != manifestChecksum {
|
||||||
|
t.Fatalf("Unexpected blob sum, expecting %q, got %q", manifestChecksum, manifest.FSLayers[0].BlobSum)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(manifest.History) != 1 {
|
||||||
|
t.Fatalf("Unexpected number of layer history, expecting 1: %d", len(manifest.History))
|
||||||
|
}
|
||||||
|
|
||||||
|
v1compat, err := img.RawJson()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if manifest.History[0].V1Compatibility != string(v1compat) {
|
||||||
|
t.Fatalf("Unexpected json value\nExpected:\n%s\nActual:\n%s", v1compat, manifest.History[0].V1Compatibility)
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-3
@@ -72,7 +72,7 @@ func (s *TagStore) CmdPull(job *engine.Job) engine.Status {
|
|||||||
logName += ":" + tag
|
logName += ":" + tag
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(repoInfo.Index.Mirrors) == 0 && (repoInfo.Index.Official || endpoint.Version == registry.APIVersion2) {
|
if len(repoInfo.Index.Mirrors) == 0 && ((repoInfo.Official && repoInfo.Index.Official) || endpoint.Version == registry.APIVersion2) {
|
||||||
j := job.Eng.Job("trust_update_base")
|
j := job.Eng.Job("trust_update_base")
|
||||||
if err = j.Run(); err != nil {
|
if err = j.Run(); err != nil {
|
||||||
log.Errorf("error updating trust base graph: %s", err)
|
log.Errorf("error updating trust base graph: %s", err)
|
||||||
@@ -153,7 +153,6 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, repoInfo *
|
|||||||
for _, image := range repoData.ImgList {
|
for _, image := range repoData.ImgList {
|
||||||
downloadImage := func(img *registry.ImgData) {
|
downloadImage := func(img *registry.ImgData) {
|
||||||
if askedTag != "" && img.Tag != askedTag {
|
if askedTag != "" && img.Tag != askedTag {
|
||||||
log.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.ID)
|
|
||||||
if parallel {
|
if parallel {
|
||||||
errors <- nil
|
errors <- nil
|
||||||
}
|
}
|
||||||
@@ -388,6 +387,9 @@ func (s *TagStore) pullV2Repository(eng *engine.Engine, r *registry.Session, out
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(tags) == 0 {
|
||||||
|
return registry.ErrDoesNotExist
|
||||||
|
}
|
||||||
for _, t := range tags {
|
for _, t := range tags {
|
||||||
if downloaded, err := s.pullV2Tag(eng, r, out, endpoint, repoInfo, t, sf, parallel, auth); err != nil {
|
if downloaded, err := s.pullV2Tag(eng, r, out, endpoint, repoInfo, t, sf, parallel, auth); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -418,7 +420,7 @@ func (s *TagStore) pullV2Tag(eng *engine.Engine, r *registry.Session, out io.Wri
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest, verified, err := s.verifyManifest(eng, manifestBytes)
|
manifest, verified, err := s.loadManifest(eng, manifestBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("error verifying manifest: %s", err)
|
return false, fmt.Errorf("error verifying manifest: %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+119
-67
@@ -2,6 +2,7 @@ package graph
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -18,6 +19,8 @@ import (
|
|||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrV2RegistryUnavailable = errors.New("error v2 registry unavailable")
|
||||||
|
|
||||||
// Retrieve the all the images to be uploaded in the correct order
|
// Retrieve the all the images to be uploaded in the correct order
|
||||||
func (s *TagStore) getImageList(localRepo map[string]string, requestedTag string) ([]string, map[string][]string, error) {
|
func (s *TagStore) getImageList(localRepo map[string]string, requestedTag string) ([]string, map[string][]string, error) {
|
||||||
var (
|
var (
|
||||||
@@ -65,6 +68,25 @@ func (s *TagStore) getImageList(localRepo map[string]string, requestedTag string
|
|||||||
return imageList, tagsByImage, nil
|
return imageList, tagsByImage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TagStore) getImageTags(localName, askedTag string) ([]string, error) {
|
||||||
|
localRepo, err := s.Get(localName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("Checking %s against %#v", askedTag, localRepo)
|
||||||
|
if len(askedTag) > 0 {
|
||||||
|
if _, ok := localRepo[askedTag]; !ok {
|
||||||
|
return nil, fmt.Errorf("Tag does not exist for %s:%s", localName, askedTag)
|
||||||
|
}
|
||||||
|
return []string{askedTag}, nil
|
||||||
|
}
|
||||||
|
var tags []string
|
||||||
|
for tag := range localRepo {
|
||||||
|
tags = append(tags, tag)
|
||||||
|
}
|
||||||
|
return tags, nil
|
||||||
|
}
|
||||||
|
|
||||||
// createImageIndex returns an index of an image's layer IDs and tags.
|
// createImageIndex returns an index of an image's layer IDs and tags.
|
||||||
func (s *TagStore) createImageIndex(images []string, tags map[string][]string) []*registry.ImgData {
|
func (s *TagStore) createImageIndex(images []string, tags map[string][]string) []*registry.ImgData {
|
||||||
var imageIndex []*registry.ImgData
|
var imageIndex []*registry.ImgData
|
||||||
@@ -236,10 +258,7 @@ func (s *TagStore) pushImage(r *registry.Session, out io.Writer, imgID, ep strin
|
|||||||
// Send the layer
|
// Send the layer
|
||||||
log.Debugf("rendered layer for %s of [%d] size", imgData.ID, layerData.Size)
|
log.Debugf("rendered layer for %s of [%d] size", imgData.ID, layerData.Size)
|
||||||
|
|
||||||
prgRd := utils.ProgressReader(layerData, int(layerData.Size), out, sf, false, utils.TruncateID(imgData.ID), "Pushing")
|
checksum, checksumPayload, err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf, false, utils.TruncateID(imgData.ID), "Pushing"), ep, token, jsonRaw)
|
||||||
defer prgRd.Close()
|
|
||||||
|
|
||||||
checksum, checksumPayload, err := r.PushImageLayerRegistry(imgData.ID, prgRd, ep, token, jsonRaw)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -254,7 +273,7 @@ func (s *TagStore) pushImage(r *registry.Session, out io.Writer, imgID, ep strin
|
|||||||
return imgData.Checksum, nil
|
return imgData.Checksum, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TagStore) pushV2Repository(r *registry.Session, eng *engine.Engine, out io.Writer, repoInfo *registry.RepositoryInfo, manifestBytes, tag string, sf *utils.StreamFormatter) error {
|
func (s *TagStore) pushV2Repository(r *registry.Session, eng *engine.Engine, out io.Writer, repoInfo *registry.RepositoryInfo, tag string, sf *utils.StreamFormatter) error {
|
||||||
if repoInfo.Official {
|
if repoInfo.Official {
|
||||||
j := eng.Job("trust_update_base")
|
j := eng.Job("trust_update_base")
|
||||||
if err := j.Run(); err != nil {
|
if err := j.Run(); err != nil {
|
||||||
@@ -264,15 +283,28 @@ func (s *TagStore) pushV2Repository(r *registry.Session, eng *engine.Engine, out
|
|||||||
|
|
||||||
endpoint, err := r.V2RegistryEndpoint(repoInfo.Index)
|
endpoint, err := r.V2RegistryEndpoint(repoInfo.Index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if repoInfo.Index.Official {
|
||||||
|
log.Infof("Unable to push to V2 registry, falling back to v1: %s", err)
|
||||||
|
return ErrV2RegistryUnavailable
|
||||||
|
}
|
||||||
return fmt.Errorf("error getting registry endpoint: %s", err)
|
return fmt.Errorf("error getting registry endpoint: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tags, err := s.getImageTags(repoInfo.LocalName, tag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(tags) == 0 {
|
||||||
|
return fmt.Errorf("No tags to push for %s", repoInfo.LocalName)
|
||||||
|
}
|
||||||
|
|
||||||
auth, err := r.GetV2Authorization(endpoint, repoInfo.RemoteName, false)
|
auth, err := r.GetV2Authorization(endpoint, repoInfo.RemoteName, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error getting authorization: %s", err)
|
return fmt.Errorf("error getting authorization: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no manifest is given, generate and sign with the key associated with the local tag store
|
for _, tag := range tags {
|
||||||
if len(manifestBytes) == 0 {
|
log.Debugf("Pushing %s:%s to v2 repository", repoInfo.LocalName, tag)
|
||||||
mBytes, err := s.newManifest(repoInfo.LocalName, repoInfo.RemoteName, tag)
|
mBytes, err := s.newManifest(repoInfo.LocalName, repoInfo.RemoteName, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -290,75 +322,99 @@ func (s *TagStore) pushV2Repository(r *registry.Session, eng *engine.Engine, out
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Infof("Signed manifest using daemon's key: %s", s.trustKey.KeyID())
|
log.Infof("Signed manifest for %s:%s using daemon's key: %s", repoInfo.LocalName, tag, s.trustKey.KeyID())
|
||||||
|
|
||||||
manifestBytes = string(signedBody)
|
manifestBytes := string(signedBody)
|
||||||
}
|
|
||||||
|
|
||||||
manifest, verified, err := s.verifyManifest(eng, []byte(manifestBytes))
|
manifest, verified, err := s.loadManifest(eng, signedBody)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error verifying manifest: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := checkValidManifest(manifest); err != nil {
|
|
||||||
return fmt.Errorf("invalid manifest: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !verified {
|
|
||||||
log.Debugf("Pushing unverified image")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := len(manifest.FSLayers) - 1; i >= 0; i-- {
|
|
||||||
var (
|
|
||||||
sumStr = manifest.FSLayers[i].BlobSum
|
|
||||||
imgJSON = []byte(manifest.History[i].V1Compatibility)
|
|
||||||
)
|
|
||||||
|
|
||||||
sumParts := strings.SplitN(sumStr, ":", 2)
|
|
||||||
if len(sumParts) < 2 {
|
|
||||||
return fmt.Errorf("Invalid checksum: %s", sumStr)
|
|
||||||
}
|
|
||||||
manifestSum := sumParts[1]
|
|
||||||
|
|
||||||
img, err := image.NewImgJSON(imgJSON)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to parse json: %s", err)
|
return fmt.Errorf("error verifying manifest: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
img, err = s.graph.Get(img.ID)
|
if err := checkValidManifest(manifest); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("invalid manifest: %s", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
arch, err := img.TarLayer()
|
if verified {
|
||||||
if err != nil {
|
log.Infof("Pushing verified image, key %s is registered for %q", s.trustKey.KeyID(), repoInfo.RemoteName)
|
||||||
return fmt.Errorf("Could not get tar layer: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call mount blob
|
for i := len(manifest.FSLayers) - 1; i >= 0; i-- {
|
||||||
exists, err := r.HeadV2ImageBlob(endpoint, repoInfo.RemoteName, sumParts[0], manifestSum, auth)
|
var (
|
||||||
if err != nil {
|
sumStr = manifest.FSLayers[i].BlobSum
|
||||||
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image push failed", nil))
|
imgJSON = []byte(manifest.History[i].V1Compatibility)
|
||||||
return err
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if !exists {
|
sumParts := strings.SplitN(sumStr, ":", 2)
|
||||||
prgRd := utils.ProgressReader(arch, int(img.Size), out, sf, false, utils.TruncateID(img.ID), "Pushing")
|
if len(sumParts) < 2 {
|
||||||
defer prgRd.Close()
|
return fmt.Errorf("Invalid checksum: %s", sumStr)
|
||||||
|
}
|
||||||
|
manifestSum := sumParts[1]
|
||||||
|
|
||||||
err = r.PutV2ImageBlob(endpoint, repoInfo.RemoteName, sumParts[0], manifestSum, prgRd, auth)
|
img, err := image.NewImgJSON(imgJSON)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to parse json: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call mount blob
|
||||||
|
exists, err := r.HeadV2ImageBlob(endpoint, repoInfo.RemoteName, sumParts[0], manifestSum, auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image push failed", nil))
|
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image push failed", nil))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image successfully pushed", nil))
|
|
||||||
} else {
|
if !exists {
|
||||||
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image already exists", nil))
|
if err := s.pushV2Image(r, img, endpoint, repoInfo.RemoteName, sumParts[0], manifestSum, sf, out, auth); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image already exists", nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// push the manifest
|
||||||
|
if err := r.PutV2ImageManifest(endpoint, repoInfo.RemoteName, tag, bytes.NewReader([]byte(manifestBytes)), auth); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// push the manifest
|
// PushV2Image pushes the image content to the v2 registry, first buffering the contents to disk
|
||||||
return r.PutV2ImageManifest(endpoint, repoInfo.RemoteName, tag, bytes.NewReader([]byte(manifestBytes)), auth)
|
func (s *TagStore) pushV2Image(r *registry.Session, img *image.Image, endpoint *registry.Endpoint, imageName, sumType, sumStr string, sf *utils.StreamFormatter, out io.Writer, auth *registry.RequestAuthorization) error {
|
||||||
|
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Buffering to Disk", nil))
|
||||||
|
|
||||||
|
image, err := s.graph.Get(img.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
arch, err := image.TarLayer()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tf, err := s.graph.newTempFile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
tf.Close()
|
||||||
|
os.Remove(tf.Name())
|
||||||
|
}()
|
||||||
|
|
||||||
|
size, err := bufferToFile(tf, arch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the layer
|
||||||
|
log.Debugf("rendered layer for %s of [%d] size", img.ID, size)
|
||||||
|
|
||||||
|
if err := r.PutV2ImageBlob(endpoint, imageName, sumType, sumStr, utils.ProgressReader(tf, int(size), out, sf, false, utils.TruncateID(img.ID), "Pushing"), auth); err != nil {
|
||||||
|
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image push failed", nil))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image successfully pushed", nil))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Allow to interrupt current push when new push of same image is done.
|
// FIXME: Allow to interrupt current push when new push of same image is done.
|
||||||
@@ -380,7 +436,6 @@ func (s *TagStore) CmdPush(job *engine.Job) engine.Status {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tag := job.Getenv("tag")
|
tag := job.Getenv("tag")
|
||||||
manifestBytes := job.Getenv("manifest")
|
|
||||||
job.GetenvJson("authConfig", authConfig)
|
job.GetenvJson("authConfig", authConfig)
|
||||||
job.GetenvJson("metaHeaders", &metaHeaders)
|
job.GetenvJson("metaHeaders", &metaHeaders)
|
||||||
|
|
||||||
@@ -400,18 +455,15 @@ func (s *TagStore) CmdPush(job *engine.Job) engine.Status {
|
|||||||
return job.Error(err2)
|
return job.Error(err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(tag) == 0 {
|
if endpoint.Version == registry.APIVersion2 {
|
||||||
tag = DEFAULTTAG
|
err := s.pushV2Repository(r, job.Eng, job.Stdout, repoInfo, tag, sf)
|
||||||
}
|
|
||||||
|
|
||||||
if repoInfo.Index.Official || endpoint.Version == registry.APIVersion2 {
|
|
||||||
err := s.pushV2Repository(r, job.Eng, job.Stdout, repoInfo, manifestBytes, tag, sf)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return engine.StatusOK
|
return engine.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
// error out, no fallback to V1
|
if err != ErrV2RegistryUnavailable {
|
||||||
return job.Error(err)
|
return job.Errorf("Error pushing to registry: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ func (s *TagStore) Install(eng *engine.Engine) error {
|
|||||||
"import": s.CmdImport,
|
"import": s.CmdImport,
|
||||||
"pull": s.CmdPull,
|
"pull": s.CmdPull,
|
||||||
"push": s.CmdPush,
|
"push": s.CmdPush,
|
||||||
"image_manifest": s.CmdManifest,
|
|
||||||
} {
|
} {
|
||||||
if err := eng.Register(name, handler); err != nil {
|
if err := eng.Register(name, handler); err != nil {
|
||||||
return fmt.Errorf("Could not register %q: %v", name, err)
|
return fmt.Errorf("Could not register %q: %v", name, err)
|
||||||
@@ -151,7 +150,6 @@ func (s *TagStore) CmdLookup(job *engine.Job) engine.Status {
|
|||||||
out.Set("Os", image.OS)
|
out.Set("Os", image.OS)
|
||||||
out.SetInt64("Size", image.Size)
|
out.SetInt64("Size", image.Size)
|
||||||
out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
|
out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
|
||||||
out.Set("Checksum", image.Checksum)
|
|
||||||
if _, err = out.WriteTo(job.Stdout); err != nil {
|
if _, err = out.WriteTo(job.Stdout); err != nil {
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-38
@@ -9,9 +9,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
"github.com/docker/docker/pkg/tarsum"
|
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
)
|
)
|
||||||
@@ -33,7 +31,6 @@ type Image struct {
|
|||||||
Config *runconfig.Config `json:"config,omitempty"`
|
Config *runconfig.Config `json:"config,omitempty"`
|
||||||
Architecture string `json:"architecture,omitempty"`
|
Architecture string `json:"architecture,omitempty"`
|
||||||
OS string `json:"os,omitempty"`
|
OS string `json:"os,omitempty"`
|
||||||
Checksum string `json:"checksum"`
|
|
||||||
Size int64
|
Size int64
|
||||||
|
|
||||||
graph Graph
|
graph Graph
|
||||||
@@ -81,45 +78,15 @@ func LoadImage(root string) (*Image, error) {
|
|||||||
|
|
||||||
// StoreImage stores file system layer data for the given image to the
|
// StoreImage stores file system layer data for the given image to the
|
||||||
// image's registered storage driver. Image metadata is stored in a file
|
// image's registered storage driver. Image metadata is stored in a file
|
||||||
// at the specified root directory. This function also computes the TarSum
|
// at the specified root directory.
|
||||||
// of `layerData` (currently using tarsum.dev).
|
func StoreImage(img *Image, layerData archive.ArchiveReader, root string) (err error) {
|
||||||
func StoreImage(img *Image, layerData archive.ArchiveReader, root string) error {
|
// Store the layer. If layerData is not nil, unpack it into the new layer
|
||||||
// Store the layer
|
|
||||||
var (
|
|
||||||
size int64
|
|
||||||
err error
|
|
||||||
driver = img.graph.Driver()
|
|
||||||
layerTarSum tarsum.TarSum
|
|
||||||
)
|
|
||||||
|
|
||||||
// If layerData is not nil, unpack it into the new layer
|
|
||||||
if layerData != nil {
|
if layerData != nil {
|
||||||
// If the image doesn't have a checksum, we should add it. The layer
|
if img.Size, err = img.graph.Driver().ApplyDiff(img.ID, img.Parent, layerData); err != nil {
|
||||||
// checksums are verified when they are pulled from a remote, but when
|
|
||||||
// a container is committed it should be added here.
|
|
||||||
if img.Checksum == "" {
|
|
||||||
layerDataDecompressed, err := archive.DecompressStream(layerData)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer layerDataDecompressed.Close()
|
|
||||||
|
|
||||||
if layerTarSum, err = tarsum.NewTarSum(layerDataDecompressed, true, tarsum.VersionDev); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if size, err = driver.ApplyDiff(img.ID, img.Parent, layerTarSum); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
img.Checksum = layerTarSum.Sum(nil)
|
|
||||||
} else if size, err = driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
img.Size = size
|
|
||||||
if err := img.SaveSize(root); err != nil {
|
if err := img.SaveSize(root); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -146,6 +113,24 @@ func (img *Image) SaveSize(root string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (img *Image) SaveCheckSum(root, checksum string) error {
|
||||||
|
if err := ioutil.WriteFile(path.Join(root, "checksum"), []byte(checksum), 0600); err != nil {
|
||||||
|
return fmt.Errorf("Error storing checksum in %s/checksum: %s", root, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (img *Image) GetCheckSum(root string) (string, error) {
|
||||||
|
cs, err := ioutil.ReadFile(path.Join(root, "checksum"))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(cs), err
|
||||||
|
}
|
||||||
|
|
||||||
func jsonPath(root string) string {
|
func jsonPath(root string) string {
|
||||||
return path.Join(root, "json")
|
return path.Join(root, "json")
|
||||||
}
|
}
|
||||||
@@ -274,7 +259,6 @@ func (img *Image) CheckDepth() error {
|
|||||||
func NewImgJSON(src []byte) (*Image, error) {
|
func NewImgJSON(src []byte) (*Image, error) {
|
||||||
ret := &Image{}
|
ret := &Image{}
|
||||||
|
|
||||||
log.Debugf("Json string: {%s}", src)
|
|
||||||
// FIXME: Is there a cleaner way to "purify" the input json?
|
// FIXME: Is there a cleaner way to "purify" the input json?
|
||||||
if err := json.Unmarshal(src, ret); err != nil {
|
if err := json.Unmarshal(src, ret); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -299,3 +299,70 @@ func TestGetContainerStats(t *testing.T) {
|
|||||||
}
|
}
|
||||||
logDone("container REST API - check GET containers/stats")
|
logDone("container REST API - check GET containers/stats")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildApiDockerfilePath(t *testing.T) {
|
||||||
|
// Test to make sure we stop people from trying to leave the
|
||||||
|
// build context when specifying the path to the dockerfile
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
tw := tar.NewWriter(buffer)
|
||||||
|
defer tw.Close()
|
||||||
|
|
||||||
|
dockerfile := []byte("FROM busybox")
|
||||||
|
if err := tw.WriteHeader(&tar.Header{
|
||||||
|
Name: "Dockerfile",
|
||||||
|
Size: int64(len(dockerfile)),
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("failed to write tar file header: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := tw.Write(dockerfile); err != nil {
|
||||||
|
t.Fatalf("failed to write tar file content: %v", err)
|
||||||
|
}
|
||||||
|
if err := tw.Close(); err != nil {
|
||||||
|
t.Fatalf("failed to close tar archive: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := sockRequestRaw("POST", "/build?dockerfile=../Dockerfile", buffer, "application/x-tar")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Build was supposed to fail: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(string(out), "must be within the build context") {
|
||||||
|
t.Fatalf("Didn't complain about leaving build context: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("container REST API - check build w/bad Dockerfile path")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildApiDockerfileSymlink(t *testing.T) {
|
||||||
|
// Test to make sure we stop people from trying to leave the
|
||||||
|
// build context when specifying a symlink as the path to the dockerfile
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
tw := tar.NewWriter(buffer)
|
||||||
|
defer tw.Close()
|
||||||
|
|
||||||
|
if err := tw.WriteHeader(&tar.Header{
|
||||||
|
Name: "Dockerfile",
|
||||||
|
Typeflag: tar.TypeSymlink,
|
||||||
|
Linkname: "/etc/passwd",
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("failed to write tar file header: %v", err)
|
||||||
|
}
|
||||||
|
if err := tw.Close(); err != nil {
|
||||||
|
t.Fatalf("failed to close tar archive: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Build was supposed to fail: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The reason the error is "Cannot locate specified Dockerfile" is because
|
||||||
|
// in the builder, the symlink is resolved within the context, therefore
|
||||||
|
// Dockerfile -> /etc/passwd becomes etc/passwd from the context which is
|
||||||
|
// a nonexistent file.
|
||||||
|
if !strings.Contains(string(out), "Cannot locate specified Dockerfile: Dockerfile") {
|
||||||
|
t.Fatalf("Didn't complain about leaving build context: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("container REST API - check build w/bad Dockerfile symlink path")
|
||||||
|
}
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ func TestAttachAfterDetach(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
if err := waitRun(name); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
cpty.Write([]byte{16})
|
cpty.Write([]byte{16})
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
cpty.Write([]byte{17})
|
cpty.Write([]byte{17})
|
||||||
|
|||||||
@@ -4592,3 +4592,67 @@ func TestBuildFromOfficialNames(t *testing.T) {
|
|||||||
}
|
}
|
||||||
logDone("build - from official names")
|
logDone("build - from official names")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildDockerfileOutsideContext(t *testing.T) {
|
||||||
|
name := "testbuilddockerfileoutsidecontext"
|
||||||
|
tmpdir, err := ioutil.TempDir("", name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
ctx := filepath.Join(tmpdir, "context")
|
||||||
|
if err := os.MkdirAll(ctx, 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(ctx, "Dockerfile"), []byte("FROM busybox"), 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Chdir(wd)
|
||||||
|
if err := os.Chdir(ctx); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(tmpdir, "outsideDockerfile"), []byte("FROM busbox"), 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.Symlink("../outsideDockerfile", filepath.Join(ctx, "dockerfile1")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.Symlink(filepath.Join(tmpdir, "outsideDockerfile"), filepath.Join(ctx, "dockerfile2")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.Link("../outsideDockerfile", filepath.Join(ctx, "dockerfile3")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.Link(filepath.Join(tmpdir, "outsideDockerfile"), filepath.Join(ctx, "dockerfile4")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, dockerfilePath := range []string{
|
||||||
|
"../outsideDockerfile",
|
||||||
|
filepath.Join(ctx, "dockerfile1"),
|
||||||
|
filepath.Join(ctx, "dockerfile2"),
|
||||||
|
filepath.Join(ctx, "dockerfile3"),
|
||||||
|
filepath.Join(ctx, "dockerfile4"),
|
||||||
|
} {
|
||||||
|
out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "build", "-t", name, "--no-cache", "-f", dockerfilePath, "."))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error with %s. Out: %s", dockerfilePath, out)
|
||||||
|
}
|
||||||
|
deleteImages(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Chdir(tmpdir)
|
||||||
|
|
||||||
|
// Path to Dockerfile should be resolved relative to working directory, not relative to context.
|
||||||
|
// There is a Dockerfile in the context, but since there is no Dockerfile in the current directory, the following should fail
|
||||||
|
out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "build", "-t", name, "--no-cache", "-f", "Dockerfile", ctx))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error. Out: %s", out)
|
||||||
|
}
|
||||||
|
deleteImages(name)
|
||||||
|
|
||||||
|
logDone("build - Dockerfile outside context")
|
||||||
|
}
|
||||||
|
|||||||
@@ -383,6 +383,9 @@ func TestDaemonKeyMigration(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error generating private key: %s", err)
|
t.Fatalf("Error generating private key: %s", err)
|
||||||
}
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Join(os.Getenv("HOME"), ".docker"), 0755); err != nil {
|
||||||
|
t.Fatalf("Error creating .docker directory: %s", err)
|
||||||
|
}
|
||||||
if err := libtrust.SaveKey(filepath.Join(os.Getenv("HOME"), ".docker", "key.json"), k1); err != nil {
|
if err := libtrust.SaveKey(filepath.Join(os.Getenv("HOME"), ".docker", "key.json"), k1); err != nil {
|
||||||
t.Fatalf("Error saving private key: %s", err)
|
t.Fatalf("Error saving private key: %s", err)
|
||||||
}
|
}
|
||||||
@@ -403,3 +406,77 @@ func TestDaemonKeyMigration(t *testing.T) {
|
|||||||
|
|
||||||
logDone("daemon - key migration")
|
logDone("daemon - key migration")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simulate an older daemon (pre 1.3) coming up with volumes specified in containers
|
||||||
|
// without corrosponding volume json
|
||||||
|
func TestDaemonUpgradeWithVolumes(t *testing.T) {
|
||||||
|
d := NewDaemon(t)
|
||||||
|
|
||||||
|
graphDir := filepath.Join(os.TempDir(), "docker-test")
|
||||||
|
defer os.RemoveAll(graphDir)
|
||||||
|
if err := d.StartWithBusybox("-g", graphDir); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpDir := filepath.Join(os.TempDir(), "test")
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
if out, err := d.Cmd("create", "-v", tmpDir+":/foo", "--name=test", "busybox"); err != nil {
|
||||||
|
t.Fatal(err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Stop(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove this since we're expecting the daemon to re-create it too
|
||||||
|
if err := os.RemoveAll(tmpDir); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configDir := filepath.Join(graphDir, "volumes")
|
||||||
|
|
||||||
|
if err := os.RemoveAll(configDir); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Start("-g", graphDir); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||||
|
t.Fatalf("expected volume path %s to exist but it does not", tmpDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := ioutil.ReadDir(configDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(dir) == 0 {
|
||||||
|
t.Fatalf("expected volumes config dir to contain data for new volume")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now with just removing the volume config and not the volume data
|
||||||
|
if err := d.Stop(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.RemoveAll(configDir); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Start("-g", graphDir); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err = ioutil.ReadDir(configDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dir) == 0 {
|
||||||
|
t.Fatalf("expected volumes config dir to contain data for new volume")
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("daemon - volumes from old(pre 1.3) daemon work")
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ func TestPullImageWithAliases(t *testing.T) {
|
|||||||
if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "tag", "busybox", repo)); err != nil {
|
if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "tag", "busybox", repo)); err != nil {
|
||||||
t.Fatalf("Failed to tag image %v: error %v, output %q", repos, err, out)
|
t.Fatalf("Failed to tag image %v: error %v, output %q", repos, err, out)
|
||||||
}
|
}
|
||||||
|
defer deleteImages(repo)
|
||||||
if out, err := exec.Command(dockerBinary, "push", repo).CombinedOutput(); err != nil {
|
if out, err := exec.Command(dockerBinary, "push", repo).CombinedOutput(); err != nil {
|
||||||
t.Fatalf("Failed to push image %v: error %v, output %q", err, string(out))
|
t.Fatalf("Failed to push image %v: error %v, output %q", err, string(out))
|
||||||
}
|
}
|
||||||
@@ -40,7 +41,6 @@ func TestPullImageWithAliases(t *testing.T) {
|
|||||||
if out, _, err := runCommandWithOutput(pullCmd); err != nil {
|
if out, _, err := runCommandWithOutput(pullCmd); err != nil {
|
||||||
t.Fatalf("Failed to pull %v: error %v, output %q", repoName, err, out)
|
t.Fatalf("Failed to pull %v: error %v, output %q", repoName, err, out)
|
||||||
}
|
}
|
||||||
defer deleteImages(repos[0])
|
|
||||||
if err := exec.Command(dockerBinary, "inspect", repos[0]).Run(); err != nil {
|
if err := exec.Command(dockerBinary, "inspect", repos[0]).Run(); err != nil {
|
||||||
t.Fatalf("Image %v was not pulled down", repos[0])
|
t.Fatalf("Image %v was not pulled down", repos[0])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,14 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
||||||
)
|
)
|
||||||
|
|
||||||
// pulling an image from the central registry should work
|
// pulling an image from the central registry should work
|
||||||
@@ -41,7 +45,7 @@ func TestPushUntagged(t *testing.T) {
|
|||||||
|
|
||||||
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
|
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
|
||||||
|
|
||||||
expected := "does not exist"
|
expected := "No tags to push"
|
||||||
pushCmd := exec.Command(dockerBinary, "push", repoName)
|
pushCmd := exec.Command(dockerBinary, "push", repoName)
|
||||||
if out, _, err := runCommandWithOutput(pushCmd); err == nil {
|
if out, _, err := runCommandWithOutput(pushCmd); err == nil {
|
||||||
t.Fatalf("pushing the image to the private registry should have failed: outuput %q", out)
|
t.Fatalf("pushing the image to the private registry should have failed: outuput %q", out)
|
||||||
@@ -51,6 +55,46 @@ func TestPushUntagged(t *testing.T) {
|
|||||||
logDone("push - untagged image")
|
logDone("push - untagged image")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPushBadTag(t *testing.T) {
|
||||||
|
defer setupRegistry(t)()
|
||||||
|
|
||||||
|
repoName := fmt.Sprintf("%v/dockercli/busybox:latest", privateRegistryURL)
|
||||||
|
|
||||||
|
expected := "does not exist"
|
||||||
|
pushCmd := exec.Command(dockerBinary, "push", repoName)
|
||||||
|
if out, _, err := runCommandWithOutput(pushCmd); err == nil {
|
||||||
|
t.Fatalf("pushing the image to the private registry should have failed: outuput %q", out)
|
||||||
|
} else if !strings.Contains(out, expected) {
|
||||||
|
t.Fatalf("pushing the image failed with an unexpected message: expected %q, got %q", expected, out)
|
||||||
|
}
|
||||||
|
logDone("push - image with bad tag")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPushMultipleTags(t *testing.T) {
|
||||||
|
defer setupRegistry(t)()
|
||||||
|
|
||||||
|
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
|
||||||
|
repoTag1 := fmt.Sprintf("%v/dockercli/busybox:t1", privateRegistryURL)
|
||||||
|
repoTag2 := fmt.Sprintf("%v/dockercli/busybox:t2", privateRegistryURL)
|
||||||
|
// tag the image to upload it tot he private registry
|
||||||
|
tagCmd1 := exec.Command(dockerBinary, "tag", "busybox", repoTag1)
|
||||||
|
if out, _, err := runCommandWithOutput(tagCmd1); err != nil {
|
||||||
|
t.Fatalf("image tagging failed: %s, %v", out, err)
|
||||||
|
}
|
||||||
|
defer deleteImages(repoTag1)
|
||||||
|
tagCmd2 := exec.Command(dockerBinary, "tag", "busybox", repoTag2)
|
||||||
|
if out, _, err := runCommandWithOutput(tagCmd2); err != nil {
|
||||||
|
t.Fatalf("image tagging failed: %s, %v", out, err)
|
||||||
|
}
|
||||||
|
defer deleteImages(repoTag2)
|
||||||
|
|
||||||
|
pushCmd := exec.Command(dockerBinary, "push", repoName)
|
||||||
|
if out, _, err := runCommandWithOutput(pushCmd); err != nil {
|
||||||
|
t.Fatalf("pushing the image to the private registry has failed: %s, %v", out, err)
|
||||||
|
}
|
||||||
|
logDone("push - multiple tags to private registry")
|
||||||
|
}
|
||||||
|
|
||||||
func TestPushInterrupt(t *testing.T) {
|
func TestPushInterrupt(t *testing.T) {
|
||||||
defer setupRegistry(t)()
|
defer setupRegistry(t)()
|
||||||
|
|
||||||
@@ -80,3 +124,35 @@ func TestPushInterrupt(t *testing.T) {
|
|||||||
|
|
||||||
logDone("push - interrupted")
|
logDone("push - interrupted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPushEmptyLayer(t *testing.T) {
|
||||||
|
defer setupRegistry(t)()
|
||||||
|
repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL)
|
||||||
|
emptyTarball, err := ioutil.TempFile("", "empty_tarball")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to create test file: %v", err)
|
||||||
|
}
|
||||||
|
tw := tar.NewWriter(emptyTarball)
|
||||||
|
err = tw.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating empty tarball: %v", err)
|
||||||
|
}
|
||||||
|
freader, err := os.Open(emptyTarball.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not open test tarball: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
importCmd := exec.Command(dockerBinary, "import", "-", repoName)
|
||||||
|
importCmd.Stdin = freader
|
||||||
|
out, _, err := runCommandWithOutput(importCmd)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("import failed with errors: %v, output: %q", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now verify we can push it
|
||||||
|
pushCmd := exec.Command(dockerBinary, "push", repoName)
|
||||||
|
if out, _, err := runCommandWithOutput(pushCmd); err != nil {
|
||||||
|
t.Fatalf("pushing the image to the private registry has failed: %s, %v", out, err)
|
||||||
|
}
|
||||||
|
logDone("push - empty layer config to private registry")
|
||||||
|
}
|
||||||
|
|||||||
@@ -274,6 +274,15 @@ func daemonHost() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sockRequest(method, endpoint string, data interface{}) ([]byte, error) {
|
func sockRequest(method, endpoint string, data interface{}) ([]byte, error) {
|
||||||
|
jsonData := bytes.NewBuffer(nil)
|
||||||
|
if err := json.NewEncoder(jsonData).Encode(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sockRequestRaw(method, endpoint, jsonData, "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func sockRequestRaw(method, endpoint string, data io.Reader, ct string) ([]byte, error) {
|
||||||
daemon := daemonHost()
|
daemon := daemonHost()
|
||||||
daemonUrl, err := url.Parse(daemon)
|
daemonUrl, err := url.Parse(daemon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -296,17 +305,16 @@ func sockRequest(method, endpoint string, data interface{}) ([]byte, error) {
|
|||||||
client := httputil.NewClientConn(c, nil)
|
client := httputil.NewClientConn(c, nil)
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
jsonData := bytes.NewBuffer(nil)
|
req, err := http.NewRequest(method, endpoint, data)
|
||||||
if err := json.NewEncoder(jsonData).Encode(data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest(method, endpoint, jsonData)
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not create new request: %v", err)
|
return nil, fmt.Errorf("could not create new request: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ct == "" {
|
||||||
|
ct = "application/json"
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", ct)
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not perform request: %v", err)
|
return nil, fmt.Errorf("could not perform request: %v", err)
|
||||||
@@ -870,5 +878,18 @@ func setupRegistry(t *testing.T) func() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for registry to be ready to serve requests.
|
||||||
|
for i := 0; i != 5; i++ {
|
||||||
|
if err = reg.Ping(); err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Timeout waiting for test registry to become available")
|
||||||
|
}
|
||||||
|
|
||||||
return func() { reg.Close() }
|
return func() { reg.Close() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -52,6 +53,18 @@ http:
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *testRegistryV2) Ping() error {
|
||||||
|
// We always ping through HTTP for our test registry.
|
||||||
|
resp, err := http.Get(fmt.Sprintf("http://%s/v2/", privateRegistryURL))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("registry ping replied with an unexpected status code %s", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *testRegistryV2) Close() {
|
func (r *testRegistryV2) Close() {
|
||||||
r.cmd.Process.Kill()
|
r.cmd.Process.Kill()
|
||||||
os.RemoveAll(r.dir)
|
os.RemoveAll(r.dir)
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
"github.com/docker/libtrust"
|
|
||||||
"github.com/kr/pty"
|
"github.com/kr/pty"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -122,12 +121,7 @@ func TestRunDetach(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := libtrust.GenerateECP256PrivateKey()
|
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
|
||||||
defer cleanup(globalEngine, t)
|
defer cleanup(globalEngine, t)
|
||||||
|
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
@@ -177,12 +171,7 @@ func TestAttachDetach(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := libtrust.GenerateECP256PrivateKey()
|
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
|
||||||
defer cleanup(globalEngine, t)
|
defer cleanup(globalEngine, t)
|
||||||
|
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
@@ -219,7 +208,7 @@ func TestAttachDetach(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cli = client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
cli = client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||||
|
|
||||||
ch = make(chan struct{})
|
ch = make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
@@ -270,12 +259,7 @@ func TestAttachDetachTruncatedID(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := libtrust.GenerateECP256PrivateKey()
|
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
|
||||||
defer cleanup(globalEngine, t)
|
defer cleanup(globalEngine, t)
|
||||||
|
|
||||||
// Discard the CmdRun output
|
// Discard the CmdRun output
|
||||||
@@ -297,7 +281,7 @@ func TestAttachDetachTruncatedID(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cli = client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
cli = client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||||
|
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
@@ -347,12 +331,7 @@ func TestAttachDisconnect(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := libtrust.GenerateECP256PrivateKey()
|
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
|
||||||
defer cleanup(globalEngine, t)
|
defer cleanup(globalEngine, t)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -421,11 +400,8 @@ func TestAttachDisconnect(t *testing.T) {
|
|||||||
func TestRunAutoRemove(t *testing.T) {
|
func TestRunAutoRemove(t *testing.T) {
|
||||||
t.Skip("Fixme. Skipping test for now, race condition")
|
t.Skip("Fixme. Skipping test for now, race condition")
|
||||||
stdout, stdoutPipe := io.Pipe()
|
stdout, stdoutPipe := io.Pipe()
|
||||||
key, err := libtrust.GenerateECP256PrivateKey()
|
|
||||||
if err != nil {
|
cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
|
||||||
defer cleanup(globalEngine, t)
|
defer cleanup(globalEngine, t)
|
||||||
|
|
||||||
c := make(chan struct{})
|
c := make(chan struct{})
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/client"
|
"github.com/docker/docker/api/client"
|
||||||
"github.com/docker/libtrust"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -38,11 +37,7 @@ func getTlsConfig(certFile, keyFile string, t *testing.T) *tls.Config {
|
|||||||
|
|
||||||
// TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint
|
// TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint
|
||||||
func TestHttpsInfo(t *testing.T) {
|
func TestHttpsInfo(t *testing.T) {
|
||||||
key, err := libtrust.GenerateECP256PrivateKey()
|
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, "", testDaemonProto,
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto,
|
|
||||||
testDaemonHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t))
|
testDaemonHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t))
|
||||||
|
|
||||||
setTimeout(t, "Reading command output time out", 10*time.Second, func() {
|
setTimeout(t, "Reading command output time out", 10*time.Second, func() {
|
||||||
@@ -55,11 +50,7 @@ func TestHttpsInfo(t *testing.T) {
|
|||||||
// TestHttpsInfoRogueCert connects via two-way authenticated HTTPS to the info endpoint
|
// TestHttpsInfoRogueCert connects via two-way authenticated HTTPS to the info endpoint
|
||||||
// by using a rogue client certificate and checks that it fails with the expected error.
|
// by using a rogue client certificate and checks that it fails with the expected error.
|
||||||
func TestHttpsInfoRogueCert(t *testing.T) {
|
func TestHttpsInfoRogueCert(t *testing.T) {
|
||||||
key, err := libtrust.GenerateECP256PrivateKey()
|
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, "", testDaemonProto,
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto,
|
|
||||||
testDaemonHttpsAddr, getTlsConfig("client-rogue-cert.pem", "client-rogue-key.pem", t))
|
testDaemonHttpsAddr, getTlsConfig("client-rogue-cert.pem", "client-rogue-key.pem", t))
|
||||||
|
|
||||||
setTimeout(t, "Reading command output time out", 10*time.Second, func() {
|
setTimeout(t, "Reading command output time out", 10*time.Second, func() {
|
||||||
@@ -76,11 +67,7 @@ func TestHttpsInfoRogueCert(t *testing.T) {
|
|||||||
// TestHttpsInfoRogueServerCert connects via two-way authenticated HTTPS to the info endpoint
|
// TestHttpsInfoRogueServerCert connects via two-way authenticated HTTPS to the info endpoint
|
||||||
// which provides a rogue server certificate and checks that it fails with the expected error
|
// which provides a rogue server certificate and checks that it fails with the expected error
|
||||||
func TestHttpsInfoRogueServerCert(t *testing.T) {
|
func TestHttpsInfoRogueServerCert(t *testing.T) {
|
||||||
key, err := libtrust.GenerateECP256PrivateKey()
|
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, "", testDaemonProto,
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto,
|
|
||||||
testDaemonRogueHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t))
|
testDaemonRogueHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t))
|
||||||
|
|
||||||
setTimeout(t, "Reading command output time out", 10*time.Second, func() {
|
setTimeout(t, "Reading command output time out", 10*time.Second, func() {
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("[tar autodetect] n: %v", bs)
|
|
||||||
|
|
||||||
compression := DetectCompression(bs)
|
compression := DetectCompression(bs)
|
||||||
switch compression {
|
switch compression {
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ type tHashConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// NOTE: DO NOT include MD5 or SHA1, which are considered insecure.
|
||||||
standardHashConfigs = map[string]tHashConfig{
|
standardHashConfigs = map[string]tHashConfig{
|
||||||
"sha256": {name: "sha256", hash: crypto.SHA256},
|
"sha256": {name: "sha256", hash: crypto.SHA256},
|
||||||
"sha512": {name: "sha512", hash: crypto.SHA512},
|
"sha512": {name: "sha512", hash: crypto.SHA512},
|
||||||
|
|||||||
@@ -22,6 +22,18 @@ const (
|
|||||||
VersionDev
|
VersionDev
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// VersionLabelForChecksum returns the label for the given tarsum
|
||||||
|
// checksum, i.e., everything before the first `+` character in
|
||||||
|
// the string or an empty string if no label separator is found.
|
||||||
|
func VersionLabelForChecksum(checksum string) string {
|
||||||
|
// Checksums are in the form: {versionLabel}+{hashID}:{hex}
|
||||||
|
sepIndex := strings.Index(checksum, "+")
|
||||||
|
if sepIndex < 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return checksum[:sepIndex]
|
||||||
|
}
|
||||||
|
|
||||||
// Get a list of all known tarsum Version
|
// Get a list of all known tarsum Version
|
||||||
func GetVersions() []Version {
|
func GetVersions() []Version {
|
||||||
v := []Version{}
|
v := []Version{}
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ cd "$(dirname "$(readlink -f "$BASH_SOURCE")")/.."
|
|||||||
# For how it is generated, see `project/generate-authors.sh`.
|
# For how it is generated, see `project/generate-authors.sh`.
|
||||||
EOH
|
EOH
|
||||||
echo
|
echo
|
||||||
git log --format='%aN <%aE>' | sort -uf
|
git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf
|
||||||
} > AUTHORS
|
} > AUTHORS
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ echo
|
|||||||
DEFAULT_BUNDLES=(
|
DEFAULT_BUNDLES=(
|
||||||
validate-dco
|
validate-dco
|
||||||
validate-gofmt
|
validate-gofmt
|
||||||
|
validate-toml
|
||||||
|
|
||||||
binary
|
binary
|
||||||
|
|
||||||
|
|||||||
+20
-5
@@ -2,11 +2,26 @@
|
|||||||
|
|
||||||
DEST=$1
|
DEST=$1
|
||||||
|
|
||||||
PKGVERSION="$VERSION"
|
PKGVERSION="${VERSION//-/'~'}"
|
||||||
if [ -n "$(git status --porcelain)" ]; then
|
# if we have a "-dev" suffix or have change in Git, let's make this package version more complex so it works better
|
||||||
PKGVERSION="$PKGVERSION-$(date +%Y%m%d%H%M%S)-$GITCOMMIT"
|
if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then
|
||||||
|
GIT_UNIX="$(git log -1 --pretty='%at')"
|
||||||
|
GIT_DATE="$(date --date "@$GIT_UNIX" +'%Y%m%d.%H%M%S')"
|
||||||
|
GIT_COMMIT="$(git log -1 --pretty='%h')"
|
||||||
|
GIT_VERSION="git${GIT_DATE}.0.${GIT_COMMIT}"
|
||||||
|
# GIT_VERSION is now something like 'git20150128.112847.0.17e840a'
|
||||||
|
PKGVERSION="$PKGVERSION~$GIT_VERSION"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# $ dpkg --compare-versions 1.5.0 gt 1.5.0~rc1 && echo true || echo false
|
||||||
|
# true
|
||||||
|
# $ dpkg --compare-versions 1.5.0~rc1 gt 1.5.0~git20150128.112847.17e840a && echo true || echo false
|
||||||
|
# true
|
||||||
|
# $ dpkg --compare-versions 1.5.0~git20150128.112847.17e840a gt 1.5.0~dev~git20150128.112847.17e840a && echo true || echo false
|
||||||
|
# true
|
||||||
|
|
||||||
|
# ie, 1.5.0 > 1.5.0~rc1 > 1.5.0~git20150128.112847.17e840a > 1.5.0~dev~git20150128.112847.17e840a
|
||||||
|
|
||||||
PACKAGE_ARCHITECTURE="$(dpkg-architecture -qDEB_HOST_ARCH)"
|
PACKAGE_ARCHITECTURE="$(dpkg-architecture -qDEB_HOST_ARCH)"
|
||||||
PACKAGE_URL="http://www.docker.com/"
|
PACKAGE_URL="http://www.docker.com/"
|
||||||
PACKAGE_MAINTAINER="support@docker.com"
|
PACKAGE_MAINTAINER="support@docker.com"
|
||||||
@@ -124,7 +139,7 @@ EOF
|
|||||||
|
|
||||||
# create lxc-docker-VERSION package
|
# create lxc-docker-VERSION package
|
||||||
fpm -s dir -C $DIR \
|
fpm -s dir -C $DIR \
|
||||||
--name lxc-docker-$VERSION --version $PKGVERSION \
|
--name lxc-docker-$VERSION --version "$PKGVERSION" \
|
||||||
--after-install $DEST/postinst \
|
--after-install $DEST/postinst \
|
||||||
--before-remove $DEST/prerm \
|
--before-remove $DEST/prerm \
|
||||||
--after-remove $DEST/postrm \
|
--after-remove $DEST/postrm \
|
||||||
@@ -157,7 +172,7 @@ EOF
|
|||||||
|
|
||||||
# create empty lxc-docker wrapper package
|
# create empty lxc-docker wrapper package
|
||||||
fpm -s empty \
|
fpm -s empty \
|
||||||
--name lxc-docker --version $PKGVERSION \
|
--name lxc-docker --version "$PKGVERSION" \
|
||||||
--architecture "$PACKAGE_ARCHITECTURE" \
|
--architecture "$PACKAGE_ARCHITECTURE" \
|
||||||
--depends lxc-docker-$VERSION \
|
--depends lxc-docker-$VERSION \
|
||||||
--description "$PACKAGE_DESCRIPTION" \
|
--description "$PACKAGE_DESCRIPTION" \
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
source "$(dirname "$BASH_SOURCE")/.validate"
|
||||||
|
|
||||||
|
IFS=$'\n'
|
||||||
|
files=( $(validate_diff --diff-filter=ACMR --name-only -- 'MAINTAINERS' || true) )
|
||||||
|
unset IFS
|
||||||
|
|
||||||
|
badFiles=()
|
||||||
|
for f in "${files[@]}"; do
|
||||||
|
# we use "git show" here to validate that what's committed is formatted
|
||||||
|
if [ "$(git show "$VALIDATE_HEAD:$f" | tomlv)" ]; then
|
||||||
|
badFiles+=( "$f" )
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${#badFiles[@]} -eq 0 ]; then
|
||||||
|
echo 'Congratulations! All toml source files have valid syntax.'
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "These files are not valid toml:"
|
||||||
|
for f in "${badFiles[@]}"; do
|
||||||
|
echo " - $f"
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
echo 'Please reformat the above files as valid toml'
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
false
|
||||||
|
fi
|
||||||
@@ -231,10 +231,13 @@ func (e *Endpoint) pingV2() (RegistryInfo, error) {
|
|||||||
// Ensure it supports the v2 Registry API.
|
// Ensure it supports the v2 Registry API.
|
||||||
var supportsV2 bool
|
var supportsV2 bool
|
||||||
|
|
||||||
for _, versionName := range resp.Header[http.CanonicalHeaderKey("Docker-Distribution-API-Version")] {
|
HeaderLoop:
|
||||||
if versionName == "registry/2.0" {
|
for _, supportedVersions := range resp.Header[http.CanonicalHeaderKey("Docker-Distribution-API-Version")] {
|
||||||
supportsV2 = true
|
for _, versionName := range strings.Fields(supportedVersions) {
|
||||||
break
|
if versionName == "registry/2.0" {
|
||||||
|
supportsV2 = true
|
||||||
|
break HeaderLoop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ func TestValidateEndpointAmbiguousAPIVersion(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
requireBasicAuthHandlerV2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
requireBasicAuthHandlerV2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Add("Docker-Distribution-API-Version", "registry/2.0")
|
// This mock server supports v2.0, v2.1, v42.0, and v100.0
|
||||||
|
w.Header().Add("Docker-Distribution-API-Version", "registry/100.0 registry/42.0")
|
||||||
|
w.Header().Add("Docker-Distribution-API-Version", "registry/2.0 registry/2.1")
|
||||||
requireBasicAuthHandler.ServeHTTP(w, r)
|
requireBasicAuthHandler.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+23
-10
@@ -124,15 +124,18 @@ func (r *Session) HeadV2ImageBlob(ep *Endpoint, imageName, sumType, sum string,
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
res.Body.Close() // close early, since we're not needing a body on this call .. yet?
|
res.Body.Close() // close early, since we're not needing a body on this call .. yet?
|
||||||
switch res.StatusCode {
|
switch {
|
||||||
case 200:
|
case res.StatusCode >= 200 && res.StatusCode < 400:
|
||||||
// return something indicating no push needed
|
// return something indicating no push needed
|
||||||
return true, nil
|
return true, nil
|
||||||
case 404:
|
case res.StatusCode == 401:
|
||||||
|
return false, errLoginRequired
|
||||||
|
case res.StatusCode == 404:
|
||||||
// return something indicating blob push needed
|
// return something indicating blob push needed
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return false, fmt.Errorf("Failed to mount %q - %s:%s : %d", imageName, sumType, sum, res.StatusCode)
|
|
||||||
|
return false, utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying head request for %s - %s:%s", res.StatusCode, imageName, sumType, sum), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Session) GetV2ImageBlob(ep *Endpoint, imageName, sumType, sum string, blobWrtr io.Writer, auth *RequestAuthorization) error {
|
func (r *Session) GetV2ImageBlob(ep *Endpoint, imageName, sumType, sum string, blobWrtr io.Writer, auth *RequestAuthorization) error {
|
||||||
@@ -189,7 +192,7 @@ func (r *Session) GetV2ImageBlobReader(ep *Endpoint, imageName, sumType, sum str
|
|||||||
if res.StatusCode == 401 {
|
if res.StatusCode == 401 {
|
||||||
return nil, 0, errLoginRequired
|
return nil, 0, errLoginRequired
|
||||||
}
|
}
|
||||||
return nil, 0, utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to pull %s blob", res.StatusCode, imageName), res)
|
return nil, 0, utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to pull %s blob - %s:%s", res.StatusCode, imageName, sumType, sum), res)
|
||||||
}
|
}
|
||||||
lenStr := res.Header.Get("Content-Length")
|
lenStr := res.Header.Get("Content-Length")
|
||||||
l, err := strconv.ParseInt(lenStr, 10, 64)
|
l, err := strconv.ParseInt(lenStr, 10, 64)
|
||||||
@@ -246,7 +249,12 @@ func (r *Session) PutV2ImageBlob(ep *Endpoint, imageName, sumType, sumStr string
|
|||||||
if res.StatusCode == 401 {
|
if res.StatusCode == 401 {
|
||||||
return errLoginRequired
|
return errLoginRequired
|
||||||
}
|
}
|
||||||
return utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to push %s blob", res.StatusCode, imageName), res)
|
errBody, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("Unexpected response from server: %q %#v", errBody, res.Header)
|
||||||
|
return utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to push %s blob - %s:%s", res.StatusCode, imageName, sumType, sumStr), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -272,13 +280,18 @@ func (r *Session) PutV2ImageManifest(ep *Endpoint, imageName, tagName string, ma
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b, _ := ioutil.ReadAll(res.Body)
|
defer res.Body.Close()
|
||||||
res.Body.Close()
|
|
||||||
if res.StatusCode != 200 {
|
// All 2xx and 3xx responses can be accepted for a put.
|
||||||
|
if res.StatusCode >= 400 {
|
||||||
if res.StatusCode == 401 {
|
if res.StatusCode == 401 {
|
||||||
return errLoginRequired
|
return errLoginRequired
|
||||||
}
|
}
|
||||||
log.Debugf("Unexpected response from server: %q %#v", b, res.Header)
|
errBody, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("Unexpected response from server: %q %#v", errBody, res.Header)
|
||||||
return utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to push %s:%s manifest", res.StatusCode, imageName, tagName), res)
|
return utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to push %s:%s manifest", res.StatusCode, imageName, tagName), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -51,10 +51,12 @@ func getToken(username, password string, params map[string]string, registryEndpo
|
|||||||
reqParams.Add("scope", scopeField)
|
reqParams.Add("scope", scopeField)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqParams.Add("account", username)
|
if username != "" {
|
||||||
|
reqParams.Add("account", username)
|
||||||
|
req.SetBasicAuth(username, password)
|
||||||
|
}
|
||||||
|
|
||||||
req.URL.RawQuery = reqParams.Encode()
|
req.URL.RawQuery = reqParams.Encode()
|
||||||
req.SetBasicAuth(username, password)
|
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+20
-5
@@ -128,13 +128,28 @@ func (ub *URLBuilder) BuildBlobUploadChunkURL(name, uuid string, values ...url.V
|
|||||||
|
|
||||||
// clondedRoute returns a clone of the named route from the router. Routes
|
// clondedRoute returns a clone of the named route from the router. Routes
|
||||||
// must be cloned to avoid modifying them during url generation.
|
// must be cloned to avoid modifying them during url generation.
|
||||||
func (ub *URLBuilder) cloneRoute(name string) *mux.Route {
|
func (ub *URLBuilder) cloneRoute(name string) clonedRoute {
|
||||||
route := new(mux.Route)
|
route := new(mux.Route)
|
||||||
*route = *ub.router.GetRoute(name) // clone the route
|
root := new(url.URL)
|
||||||
|
|
||||||
return route.
|
*route = *ub.router.GetRoute(name) // clone the route
|
||||||
Schemes(ub.root.Scheme).
|
*root = *ub.root
|
||||||
Host(ub.root.Host)
|
|
||||||
|
return clonedRoute{Route: route, root: root}
|
||||||
|
}
|
||||||
|
|
||||||
|
type clonedRoute struct {
|
||||||
|
*mux.Route
|
||||||
|
root *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr clonedRoute) URL(pairs ...string) (*url.URL, error) {
|
||||||
|
routeURL, err := cr.Route.URL(pairs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cr.root.ResolveReference(routeURL), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendValuesURL appends the parameters to the url.
|
// appendValuesURL appends the parameters to the url.
|
||||||
|
|||||||
+53
-40
@@ -6,62 +6,58 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type urlBuilderTestCase struct {
|
type urlBuilderTestCase struct {
|
||||||
description string
|
description string
|
||||||
expected string
|
expectedPath string
|
||||||
build func() (string, error)
|
build func() (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestURLBuilder tests the various url building functions, ensuring they are
|
// TestURLBuilder tests the various url building functions, ensuring they are
|
||||||
// returning the expected values.
|
// returning the expected values.
|
||||||
func TestURLBuilder(t *testing.T) {
|
func TestURLBuilder(t *testing.T) {
|
||||||
|
var (
|
||||||
|
urlBuilder *URLBuilder
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
root := "http://localhost:5000/"
|
testCases := []urlBuilderTestCase{
|
||||||
urlBuilder, err := NewURLBuilderFromString(root)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error creating urlbuilder: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, testcase := range []struct {
|
|
||||||
description string
|
|
||||||
expected string
|
|
||||||
build func() (string, error)
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
description: "test base url",
|
description: "test base url",
|
||||||
expected: "http://localhost:5000/v2/",
|
expectedPath: "/v2/",
|
||||||
build: urlBuilder.BuildBaseURL,
|
build: func() (string, error) {
|
||||||
|
return urlBuilder.BuildBaseURL()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "test tags url",
|
description: "test tags url",
|
||||||
expected: "http://localhost:5000/v2/foo/bar/tags/list",
|
expectedPath: "/v2/foo/bar/tags/list",
|
||||||
build: func() (string, error) {
|
build: func() (string, error) {
|
||||||
return urlBuilder.BuildTagsURL("foo/bar")
|
return urlBuilder.BuildTagsURL("foo/bar")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "test manifest url",
|
description: "test manifest url",
|
||||||
expected: "http://localhost:5000/v2/foo/bar/manifests/tag",
|
expectedPath: "/v2/foo/bar/manifests/tag",
|
||||||
build: func() (string, error) {
|
build: func() (string, error) {
|
||||||
return urlBuilder.BuildManifestURL("foo/bar", "tag")
|
return urlBuilder.BuildManifestURL("foo/bar", "tag")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "build blob url",
|
description: "build blob url",
|
||||||
expected: "http://localhost:5000/v2/foo/bar/blobs/tarsum.v1+sha256:abcdef0123456789",
|
expectedPath: "/v2/foo/bar/blobs/tarsum.v1+sha256:abcdef0123456789",
|
||||||
build: func() (string, error) {
|
build: func() (string, error) {
|
||||||
return urlBuilder.BuildBlobURL("foo/bar", "tarsum.v1+sha256:abcdef0123456789")
|
return urlBuilder.BuildBlobURL("foo/bar", "tarsum.v1+sha256:abcdef0123456789")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "build blob upload url",
|
description: "build blob upload url",
|
||||||
expected: "http://localhost:5000/v2/foo/bar/blobs/uploads/",
|
expectedPath: "/v2/foo/bar/blobs/uploads/",
|
||||||
build: func() (string, error) {
|
build: func() (string, error) {
|
||||||
return urlBuilder.BuildBlobUploadURL("foo/bar")
|
return urlBuilder.BuildBlobUploadURL("foo/bar")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "build blob upload url with digest and size",
|
description: "build blob upload url with digest and size",
|
||||||
expected: "http://localhost:5000/v2/foo/bar/blobs/uploads/?digest=tarsum.v1%2Bsha256%3Aabcdef0123456789&size=10000",
|
expectedPath: "/v2/foo/bar/blobs/uploads/?digest=tarsum.v1%2Bsha256%3Aabcdef0123456789&size=10000",
|
||||||
build: func() (string, error) {
|
build: func() (string, error) {
|
||||||
return urlBuilder.BuildBlobUploadURL("foo/bar", url.Values{
|
return urlBuilder.BuildBlobUploadURL("foo/bar", url.Values{
|
||||||
"size": []string{"10000"},
|
"size": []string{"10000"},
|
||||||
@@ -70,15 +66,15 @@ func TestURLBuilder(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "build blob upload chunk url",
|
description: "build blob upload chunk url",
|
||||||
expected: "http://localhost:5000/v2/foo/bar/blobs/uploads/uuid-part",
|
expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part",
|
||||||
build: func() (string, error) {
|
build: func() (string, error) {
|
||||||
return urlBuilder.BuildBlobUploadChunkURL("foo/bar", "uuid-part")
|
return urlBuilder.BuildBlobUploadChunkURL("foo/bar", "uuid-part")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "build blob upload chunk url with digest and size",
|
description: "build blob upload chunk url with digest and size",
|
||||||
expected: "http://localhost:5000/v2/foo/bar/blobs/uploads/uuid-part?digest=tarsum.v1%2Bsha256%3Aabcdef0123456789&size=10000",
|
expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part?digest=tarsum.v1%2Bsha256%3Aabcdef0123456789&size=10000",
|
||||||
build: func() (string, error) {
|
build: func() (string, error) {
|
||||||
return urlBuilder.BuildBlobUploadChunkURL("foo/bar", "uuid-part", url.Values{
|
return urlBuilder.BuildBlobUploadChunkURL("foo/bar", "uuid-part", url.Values{
|
||||||
"size": []string{"10000"},
|
"size": []string{"10000"},
|
||||||
@@ -86,15 +82,32 @@ func TestURLBuilder(t *testing.T) {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} {
|
|
||||||
u, err := testcase.build()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%s: error building url: %v", testcase.description, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u != testcase.expected {
|
|
||||||
t.Fatalf("%s: %q != %q", testcase.description, u, testcase.expected)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
roots := []string{
|
||||||
|
"http://example.com",
|
||||||
|
"https://example.com",
|
||||||
|
"http://localhost:5000",
|
||||||
|
"https://localhost:5443",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, root := range roots {
|
||||||
|
urlBuilder, err = NewURLBuilderFromString(root)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error creating urlbuilder: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
url, err := testCase.build()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: error building url: %v", testCase.description, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedURL := root + testCase.expectedPath
|
||||||
|
|
||||||
|
if url != expectedURL {
|
||||||
|
t.Fatalf("%s: %q != %q", testCase.description, url, expectedURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-10
@@ -57,9 +57,10 @@ func (r *Repository) newVolume(path string, writable bool) (*Volume, error) {
|
|||||||
}
|
}
|
||||||
path = filepath.Clean(path)
|
path = filepath.Clean(path)
|
||||||
|
|
||||||
path, err = filepath.EvalSymlinks(path)
|
// Ignore the error here since the path may not exist
|
||||||
if err != nil {
|
// Really just want to make sure the path we are using is real(or non-existant)
|
||||||
return nil, err
|
if cleanPath, err := filepath.EvalSymlinks(path); err == nil {
|
||||||
|
path = cleanPath
|
||||||
}
|
}
|
||||||
|
|
||||||
v := &Volume{
|
v := &Volume{
|
||||||
@@ -169,13 +170,11 @@ func (r *Repository) Delete(path string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if volume.IsBindMount {
|
if !volume.IsBindMount {
|
||||||
return nil
|
if err := r.driver.Remove(volume.ID); err != nil {
|
||||||
}
|
if !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
if err := r.driver.Remove(volume.ID); err != nil {
|
}
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user