mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-19 12:16:30 +00:00
Compare commits
73 Commits
master
...
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_exec_test.go
|
||||
# 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
|
||||
|
||||
@@ -6,6 +6,24 @@
|
||||
#
|
||||
# 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>
|
||||
<daniel.mizyrycki@dotcloud.com> <daniel@dotcloud.com>
|
||||
<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> <crosby.michael@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>
|
||||
<dano@spotify.com> <daniel.norberg@gmail.com>
|
||||
<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@users.noreply.github.com>
|
||||
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>
|
||||
Alexandr Morozov <lk4d4math@gmail.com>
|
||||
<git.nivoc@neverbox.com> <kuehnle@online.de>
|
||||
|
||||
@@ -12,41 +12,46 @@ Adam Singer <financeCoding@gmail.com>
|
||||
Aditya <aditya@netroy.in>
|
||||
Adrian Mouat <adrian.mouat@gmail.com>
|
||||
Adrien Folie <folie.adrien@gmail.com>
|
||||
Ahmed Kamal <email.ahmedkamal@googlemail.com>
|
||||
Ahmet Alp Balkan <ahmetb@microsoft.com>
|
||||
Aidan Hobson Sayers <aidanhs@cantab.net>
|
||||
AJ Bowen <aj@gandi.net>
|
||||
Al Tobey <al@ooyala.com>
|
||||
alambike <alambike@gmail.com>
|
||||
Alan Thompson <cloojure@gmail.com>
|
||||
Albert Callarisa <shark234@gmail.com>
|
||||
Albert Zhang <zhgwenming@gmail.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 Morozov <lk4d4@docker.com>
|
||||
Alexander Shopov <ash@kambanaria.org>
|
||||
Alexandr Morozov <lk4d4@docker.com>
|
||||
Alexey Kotlyarov <alexey@infoxchange.net.au>
|
||||
Alexey Shamrin <shamrin@gmail.com>
|
||||
Alex Gaynor <alex.gaynor@gmail.com>
|
||||
Alexis THOMAS <fr.alexisthomas@gmail.com>
|
||||
Alex Warhawk <ax.warhawk@gmail.com>
|
||||
almoehi <almoehi@users.noreply.github.com>
|
||||
Al Tobey <al@ooyala.com>
|
||||
Álvaro Lázaro <alvaro.lazaro.g@gmail.com>
|
||||
amangoel <amangoel@gmail.com>
|
||||
Amit Bakshi <ambakshi@gmail.com>
|
||||
AnandkumarPatel <anandkumarpatel@gmail.com>
|
||||
Anand Patil <anand.prabhakar.patil@gmail.com>
|
||||
AnandkumarPatel <anandkumarpatel@gmail.com>
|
||||
Andre Dublin <81dublin@gmail.com>
|
||||
Andrea Luzzardi <aluzzardi@gmail.com>
|
||||
Andrea Turli <andrea.turli@gmail.com>
|
||||
Andreas Köhler <andi5.py@gmx.net>
|
||||
Andreas Savvides <andreas@editd.com>
|
||||
Andreas Tiefenthaler <at@an-ti.eu>
|
||||
Andrea Turli <andrea.turli@gmail.com>
|
||||
Andre Dublin <81dublin@gmail.com>
|
||||
Andrew C. Bodine <acbodine@us.ibm.com>
|
||||
Andrew Duckworth <grillopress@gmail.com>
|
||||
Andrew France <andrew@avito.co.uk>
|
||||
Andrew Macgregor <andrew.macgregor@agworld.com.au>
|
||||
Andrew Munsell <andrew@wizardapps.net>
|
||||
Andrews Medina <andrewsmedina@gmail.com>
|
||||
Andrew Weiss <andrew.weiss@outlook.com>
|
||||
Andrew Williams <williams.andrew@gmail.com>
|
||||
Andrews Medina <andrewsmedina@gmail.com>
|
||||
Andrey Petrov <andrey.petrov@shazow.net>
|
||||
Andrey Stolbovsky <andrey.stolbovsky@gmail.com>
|
||||
Andy Chambers <anchambers@paypal.com>
|
||||
@@ -56,6 +61,8 @@ Andy Kipp <andy@rstudio.com>
|
||||
Andy Rothfusz <github@developersupport.net>
|
||||
Andy Smith <github@anarkystic.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>
|
||||
Anton Löfgren <anton.lofgren@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>
|
||||
bdevloed <boris.de.vloed@gmail.com>
|
||||
Ben Firshman <ben@firshman.co.uk>
|
||||
Benjamin Atkin <ben@benatkin.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
Ben Sargent <ben@brokendigits.com>
|
||||
Ben Toews <mastahyeti@gmail.com>
|
||||
Ben Wiklund <ben@daisyowl.com>
|
||||
Benjamin Atkin <ben@benatkin.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
Bernerd Schaefer <bj.schaefer@gmail.com>
|
||||
Bert Goethals <bert@bertg.be>
|
||||
Bhiraj Butala <abhiraj.butala@gmail.com>
|
||||
@@ -113,18 +120,22 @@ Charles Hooper <charles.hooper@dotcloud.com>
|
||||
Charles Lindsay <chaz@chazomatic.us>
|
||||
Charles Merriam <charles.merriam@gmail.com>
|
||||
Charlie Lewis <charliel@lab41.org>
|
||||
Chen Chao <cc272309126@gmail.com>
|
||||
Chewey <prosto-chewey@users.noreply.github.com>
|
||||
Chia-liang Kao <clkao@clkao.org>
|
||||
Chris Alfonso <calfonso@redhat.com>
|
||||
Chris Armstrong <chris@opdemand.com>
|
||||
chrismckinnel <chris.mckinnel@tangentlabs.co.uk>
|
||||
Chris Snow <chsnow123@gmail.com>
|
||||
Chris St. Pierre <chris.a.st.pierre@gmail.com>
|
||||
chrismckinnel <chris.mckinnel@tangentlabs.co.uk>
|
||||
Christian Berendt <berendt@b1-systems.de>
|
||||
Christian Stefanescu <st.chris@gmail.com>
|
||||
ChristoperBiscardi <biscarch@sketcht.com>
|
||||
Christopher Currie <codemonkey+github@gmail.com>
|
||||
Christopher Rigor <crigor@gmail.com>
|
||||
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>
|
||||
Clayton Coleman <ccoleman@redhat.com>
|
||||
Colin Dunklau <colin.dunklau@gmail.com>
|
||||
@@ -132,15 +143,20 @@ Colin Rice <colin@daedrum.net>
|
||||
Colin Walters <walters@verbum.org>
|
||||
Cory Forsyth <cory.forsyth@gmail.com>
|
||||
cressie176 <github@stephen-cresswell.net>
|
||||
Cristian Staretu <cristian.staretu@gmail.com>
|
||||
Cruceru Calin-Cristian <crucerucalincristian@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>
|
||||
Dan Buch <d.buch@modcloth.com>
|
||||
Dan Cotora <dan@bluevision.ro>
|
||||
Dan Griffin <dgriffin@peer1.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 Farrell <dfarrell@redhat.com>
|
||||
Daniel Garcia <daniel@danielgarcia.info>
|
||||
@@ -152,29 +168,27 @@ Daniel Nordberg <dnordberg@gmail.com>
|
||||
Daniel Robinson <gottagetmac@gmail.com>
|
||||
Daniel Von Fange <daniel@leancoder.com>
|
||||
Daniel YC Lin <dlin.tw@gmail.com>
|
||||
Dan Keder <dan.keder@gmail.com>
|
||||
Dan McPherson <dmcphers@redhat.com>
|
||||
Daniel, Dao Quang Minh <dqminh89@gmail.com>
|
||||
Danny Berger <dpb587@gmail.com>
|
||||
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 Shepherd <darren.s.shepherd@gmail.com>
|
||||
David Anderson <dave@natulte.net>
|
||||
David Calavera <david.calavera@gmail.com>
|
||||
David Corking <dmc-source@dcorking.com>
|
||||
Davide Ceretti <davide.ceretti@hogarthww.com>
|
||||
David Gageot <david@gageot.net>
|
||||
David Gebler <davidgebler@gmail.com>
|
||||
David Mat <david@davidmat.com>
|
||||
David Mcanulty <github@hellspark.com>
|
||||
David Pelaez <pelaez89@gmail.com>
|
||||
David Röthlisberger <david@rothlis.net>
|
||||
David Sissitka <me@dsissitka.com>
|
||||
Davide Ceretti <davide.ceretti@hogarthww.com>
|
||||
Dawn Chen <dawnchen@google.com>
|
||||
decadent <decadent@users.noreply.github.com>
|
||||
Deni Bertovic <deni@kset.org>
|
||||
Derek <crq@kernel.org>
|
||||
Derek <crquan@gmail.com>
|
||||
Derek McGowan <derek@mcgstyle.net>
|
||||
Deric Crago <deric.crago@gmail.com>
|
||||
Deshi Xiao <dxiao@redhat.com>
|
||||
@@ -182,14 +196,16 @@ Dinesh Subhraveti <dineshs@altiscale.com>
|
||||
Djibril Koné <kone.djibril@gmail.com>
|
||||
dkumor <daniel@dkumor.com>
|
||||
Dmitry Demeshchuk <demeshchuk@gmail.com>
|
||||
Dmitry V. Krivenok <krivenok.dmitry@gmail.com>
|
||||
Dolph Mathews <dolph.mathews@gmail.com>
|
||||
Dominik Honnef <dominik@honnef.co>
|
||||
Don Kjer <don.kjer@gmail.com>
|
||||
Don Spaulding <donspauldingii@gmail.com>
|
||||
Doug Davis <dug@us.ibm.com>
|
||||
doug tangren <d.tangren@gmail.com>
|
||||
Dr Nic Williams <drnicwilliams@gmail.com>
|
||||
dragon788 <dragon788@users.noreply.github.com>
|
||||
Dražen Lučanin <kermit666@gmail.com>
|
||||
Dr Nic Williams <drnicwilliams@gmail.com>
|
||||
Dustin Sallings <dustin@spy.net>
|
||||
Edmund Wagner <edmund-wagner@web.de>
|
||||
Eiichi Tsukata <devel@etsukata.com>
|
||||
@@ -197,19 +213,22 @@ Eike Herzbach <eike@herzbach.net>
|
||||
Eivind Uggedal <eivind@uggedal.com>
|
||||
Elias Probst <mail@eliasprobst.eu>
|
||||
Emil Hernvall <emil@quench.at>
|
||||
Emily Maier <emily@emilymaier.net>
|
||||
Emily Rose <emily@contactvibe.com>
|
||||
Eric Hanchrow <ehanchrow@ine.com>
|
||||
Eric Lee <thenorthsecedes@gmail.com>
|
||||
Eric Myhre <hash@exultant.us>
|
||||
Eric Paris <eparis@redhat.com>
|
||||
Eric Windisch <ewindisch@docker.com>
|
||||
Erik Dubbelboer <erik@dubbelboer.com>
|
||||
Erik Hollensbe <github@hollensbe.org>
|
||||
Erik Inge Bolsø <knan@redpill-linpro.com>
|
||||
Erik Kristensen <erik@erikkristensen.com>
|
||||
Erno Hopearuoho <erno.hopearuoho@gmail.com>
|
||||
Erwin van der Koogh <info@erronis.nl>
|
||||
Eugene Yakubovich <eugene.yakubovich@coreos.com>
|
||||
eugenkrizo <eugen.krizo@gmail.com>
|
||||
evanderkoogh <info@erronis.nl>
|
||||
Evan Carmi <carmi@users.noreply.github.com>
|
||||
Evan Hazlett <ejhazlett@gmail.com>
|
||||
Evan Krall <krall@yelp.com>
|
||||
Evan Phoenix <evan@fallingsnow.net>
|
||||
@@ -230,9 +249,9 @@ Francisco Carriedo <fcarriedo@gmail.com>
|
||||
Francisco Souza <f@souza.cc>
|
||||
Frank Macreery <frank@macreery.com>
|
||||
Frank Rosquin <frank.rosquin+github@gmail.com>
|
||||
Fred Lifton <fred.lifton@docker.com>
|
||||
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
|
||||
Frederik Loeffert <frederik@zitrusmedia.de>
|
||||
Fred Lifton <fred.lifton@docker.com>
|
||||
Freek Kalter <freek@kalteronline.org>
|
||||
Gabe Rosenhouse <gabe@missionst.com>
|
||||
Gabor Nagy <mail@aigeruth.hu>
|
||||
@@ -266,13 +285,15 @@ Hector Castro <hectcastro@gmail.com>
|
||||
Henning Sprang <henning.sprang@gmail.com>
|
||||
Hobofan <goisser94@gmail.com>
|
||||
Hollie Teal <hollie@docker.com>
|
||||
Hu Keping <hukeping@huawei.com>
|
||||
Hu Tao <hutao@cn.fujitsu.com>
|
||||
Huayi Zhang <irachex@gmail.com>
|
||||
Hugo Duncan <hugo@hugoduncan.org>
|
||||
Hunter Blanks <hunter@twilio.com>
|
||||
Hu Tao <hutao@cn.fujitsu.com>
|
||||
Huu Nguyen <huu@prismskylabs.com>
|
||||
hyeongkyu.lee <hyeongkyu.lee@navercorp.com>
|
||||
Ian Babrou <ibobrik@gmail.com>
|
||||
Ian Bishop <ianbishop@pace7.com>
|
||||
Ian Bull <irbull@gmail.com>
|
||||
Ian Main <imain@redhat.com>
|
||||
Ian Truslove <ian.truslove@gmail.com>
|
||||
@@ -284,8 +305,10 @@ Isabel Jimenez <contact.isabeljimenez@gmail.com>
|
||||
Isao Jonas <isao.jonas@gmail.com>
|
||||
Ivan Fraixedes <ifcdev@gmail.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>
|
||||
jakedt <jake@devtable.com>
|
||||
James Allen <jamesallen0108@gmail.com>
|
||||
James Carr <james.r.carr@gmail.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 Dalido <jeanbaptiste@appgratis.com>
|
||||
Jean-Paul Calderone <exarkun@twistedmatrix.com>
|
||||
Jean-Tiare Le Bigot <jt@yadutaf.fr>
|
||||
Jeff Anderson <jeff@docker.com>
|
||||
Jeff Lindsay <progrium@gmail.com>
|
||||
Jeffrey Bolle <jeffreybolle@gmail.com>
|
||||
Jeff Welch <whatthejeff@gmail.com>
|
||||
Jeffrey Bolle <jeffreybolle@gmail.com>
|
||||
Jeremy Grosser <jeremy@synack.me>
|
||||
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
|
||||
Jesse Dubay <jesse@thefortytwo.net>
|
||||
Jessica Frazelle <jess@docker.com>
|
||||
Jezeniel Zapanta <jpzapanta22@gmail.com>
|
||||
Jilles Oldenbeuving <ojilles@gmail.com>
|
||||
Jim Alateras <jima@comware.com.au>
|
||||
Jimmy Cuadra <jimmy@jimmycuadra.com>
|
||||
Jim Perrin <jperrin@centos.org>
|
||||
Jimmy Cuadra <jimmy@jimmycuadra.com>
|
||||
Jiří Župka <jzupka@redhat.com>
|
||||
Joe Beda <joe.github@bedafamily.com>
|
||||
Joe Ferguson <joe@infosiftr.com>
|
||||
Joel Handwell <joelhandwell@gmail.com>
|
||||
Joe Shaw <joe@joeshaw.org>
|
||||
Joe Van Dyk <joe@tanga.com>
|
||||
Joel Friedly <joelfriedly@gmail.com>
|
||||
Joel Handwell <joelhandwell@gmail.com>
|
||||
Joffrey F <joffrey@docker.com>
|
||||
Johan Euphrosine <proppy@google.com>
|
||||
Johannes 'fish' Ziemke <github@freigeist.org>
|
||||
Johan Rydberg <johan.rydberg@gmail.com>
|
||||
Johannes 'fish' Ziemke <github@freigeist.org>
|
||||
John Costa <john.costa@gmail.com>
|
||||
John Feminella <jxf@jxf.me>
|
||||
John Gardiner Myers <jgmyers@proofpoint.com>
|
||||
John Gossman <johngos@microsoft.com>
|
||||
John OBrien III <jobrieniii@yahoo.com>
|
||||
John Warwick <jwarwick@gmail.com>
|
||||
Jon Wedaman <jweede@gmail.com>
|
||||
Jonas Pfenniger <jonas@pfenniger.name>
|
||||
Jonathan A. Sternberg <jonathansternberg@gmail.com>
|
||||
Jonathan Boulle <jonathanboulle@gmail.com>
|
||||
Jonathan Camp <jonathan@irondojo.com>
|
||||
Jonathan McCrohan <jmccrohan@gmail.com>
|
||||
Jonathan Mueller <j.mueller@apoveda.ch>
|
||||
Jonathan Pares <jonathanpa@users.noreply.github.com>
|
||||
Jonathan Rudenberg <jonathan@titanous.com>
|
||||
Jon Wedaman <jweede@gmail.com>
|
||||
Joost Cassee <joost@cassee.net>
|
||||
Jordan Arentsen <blissdev@gmail.com>
|
||||
Jordan Sissel <jls@semicomplete.com>
|
||||
Joseph Anthony Pasquale Holsten <joseph@josephholsten.com>
|
||||
Joseph Hager <ajhager@gmail.com>
|
||||
Josh Hawn <josh.hawn@docker.com>
|
||||
Josh <jokajak@gmail.com>
|
||||
Josh Hawn <josh.hawn@docker.com>
|
||||
Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
Josiah Kiehl <jkiehl@riotgames.com>
|
||||
JP <jpellerin@leapfrogonline.com>
|
||||
@@ -360,6 +386,9 @@ Justin Force <justin.force@gmail.com>
|
||||
Justin Plock <jplock@users.noreply.github.com>
|
||||
Justin Simonelis <justin.p.simonelis@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>
|
||||
Karl Grzeszczak <karlgrz@gmail.com>
|
||||
Kato Kazuyoshi <kato.kazuyoshi@gmail.com>
|
||||
@@ -367,14 +396,13 @@ Kawsar Saiyeed <kawsar.saiyeed@projiris.com>
|
||||
Keli Hu <dev@keli.hu>
|
||||
Ken Cochrane <kencochrane@gmail.com>
|
||||
Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com>
|
||||
Kevin "qwazerty" Houdebert <kevin.houdebert@gmail.com>
|
||||
Kevin Clark <kevin.clark@gmail.com>
|
||||
Kevin J. Lynagh <kevin@keminglabs.com>
|
||||
Kevin Menard <kevin@nirvdrum.com>
|
||||
Kevin "qwazerty" Houdebert <kevin.houdebert@gmail.com>
|
||||
Kevin Wallace <kevin@pentabarf.net>
|
||||
Keyvan Fatehi <keyvanfatehi@gmail.com>
|
||||
kies <lleelm@gmail.com>
|
||||
kim0 <email.ahmedkamal@googlemail.com>
|
||||
Kim BKC Carlbacker <kim.carlbacker@gmail.com>
|
||||
Kimbro Staken <kstaken@kstaken.com>
|
||||
Kiran Gangadharan <kiran.daredevil@gmail.com>
|
||||
@@ -382,6 +410,7 @@ knappe <tyler.knappe@gmail.com>
|
||||
Kohei Tsuruta <coheyxyz@gmail.com>
|
||||
Konrad Kleine <konrad.wilhelm.kleine@gmail.com>
|
||||
Konstantin Pelykh <kpelykh@zettaset.com>
|
||||
Krasimir Georgiev <support@vip-consult.co.uk>
|
||||
krrg <krrgithub@gmail.com>
|
||||
Kyle Conroy <kyle.j.conroy@gmail.com>
|
||||
kyu <leehk1227@gmail.com>
|
||||
@@ -397,13 +426,16 @@ Lei Jitang <leijitang@huawei.com>
|
||||
Len Weincier <len@cloudafrica.net>
|
||||
Leszek Kowalski <github@leszekkowalski.pl>
|
||||
Levi Gross <levi@levigross.com>
|
||||
Lewis Marshall <lewis@lmars.net>
|
||||
Lewis Peckover <lew+github@lew.io>
|
||||
Liang-Chi Hsieh <viirya@gmail.com>
|
||||
limsy <seongyeol37@gmail.com>
|
||||
Lokesh Mandvekar <lsm5@fedoraproject.org>
|
||||
Lorenz Leutgeb <lorenz.leutgeb@gmail.com>
|
||||
Louis Opter <kalessin@kalessin.fr>
|
||||
lukaspustina <lukas.pustina@centerdevice.com>
|
||||
lukemarsden <luke@digital-crocus.com>
|
||||
Lénaïc Huard <lhuard@amadeus.com>
|
||||
Madhu Venugopal <madhu@socketplane.io>
|
||||
Mahesh Tiyyagura <tmahesh@gmail.com>
|
||||
Malte Janduda <mail@janduda.net>
|
||||
@@ -412,12 +444,13 @@ Manuel Meurer <manuel@krautcomputing.com>
|
||||
Manuel Woelker <github@manuel.woelker.org>
|
||||
Marc Abramowitz <marc@marc-abramowitz.com>
|
||||
Marc Kuo <kuomarc2@gmail.com>
|
||||
Marco Hennings <marco.hennings@freiheit.com>
|
||||
Marc Tamsky <mtamsky@gmail.com>
|
||||
Marco Hennings <marco.hennings@freiheit.com>
|
||||
Marcus Farkas <toothlessgear@finitebox.com>
|
||||
marcuslinke <marcus.linke@gmx.de>
|
||||
Marcus Linke <marcus.linke@gmx.de>
|
||||
Marcus Ramberg <marcus@nordaaker.com>
|
||||
Marek Goldmann <marek.goldmann@gmail.com>
|
||||
Marianna <mtesselh@gmail.com>
|
||||
Marius Voila <marius.voila@gmail.com>
|
||||
Mark Allen <mrallen1@yahoo.com>
|
||||
Mark McGranaghan <mmcgrana@gmail.com>
|
||||
@@ -425,7 +458,9 @@ Marko Mikulicic <mmikulicic@gmail.com>
|
||||
Marko Tibold <marko@tibold.nl>
|
||||
Markus Fix <lispmeister@gmail.com>
|
||||
Martijn van Oosterhout <kleptog@svana.org>
|
||||
Martin Honermeyer <maze@strahlungsfrei.de>
|
||||
Martin Redmond <martin@tinychat.com>
|
||||
Mary Anthony <moxieandmore@gmail.com>
|
||||
Mason Malone <mason.malone@gmail.com>
|
||||
Mateusz Sulima <sulima.mateusz@gmail.com>
|
||||
Mathias Monnerville <mathias@monnerville.com>
|
||||
@@ -435,14 +470,14 @@ Matt Bachmann <bachmann.matt@gmail.com>
|
||||
Matt Haggard <haggardii@gmail.com>
|
||||
Matthew Heon <mheon@redhat.com>
|
||||
Matthew Mueller <mattmuelle@gmail.com>
|
||||
Matthew Riley <mattdr@google.com>
|
||||
Matthias Klumpp <matthias@tenstral.net>
|
||||
Matthias Kühnle <git.nivoc@neverbox.com>
|
||||
mattymo <raytrac3r@gmail.com>
|
||||
mattyw <mattyw@me.com>
|
||||
Maxime Petazzoni <max@signalfuse.com>
|
||||
Maxim Treskin <zerthurd@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>
|
||||
Mengdi Gao <usrgdd@gmail.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 Hudson-Doyle <michael.hudson@linaro.org>
|
||||
Michael Neale <michael.neale@gmail.com>
|
||||
Michaël Pailloncy <mpapo.dev@gmail.com>
|
||||
Michael Prokop <github@michael-prokop.at>
|
||||
Michael Scharf <github@scharf.gr>
|
||||
Michael Stapelberg <michael+gh@stapelberg.de>
|
||||
Michael Steinert <mike.steinert@gmail.com>
|
||||
Michael Thies <michaelthies78@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>
|
||||
Miguel Angel Fernández <elmendalerenda@gmail.com>
|
||||
Mike Chelen <michael.chelen@gmail.com>
|
||||
@@ -471,12 +508,15 @@ Morten Siebuhr <sbhr@sbhr.dk>
|
||||
Mrunal Patel <mrunalp@gmail.com>
|
||||
mschurenko <matt.schurenko@gmail.com>
|
||||
Mustafa Akın <mustafa91@gmail.com>
|
||||
Médi-Rémi Hashim <medimatrix@users.noreply.github.com>
|
||||
Nan Monnand Deng <monnand@gmail.com>
|
||||
Naoki Orii <norii@cs.cmu.edu>
|
||||
Nate Eagleson <nate@nateeag.com>
|
||||
Nate Jones <nate@endot.org>
|
||||
Nathan Hsieh <hsieh.nathan@gmail.com>
|
||||
Nathan Kleyn <nathan@nathankleyn.com>
|
||||
Nathan LeClaire <nathan.leclaire@docker.com>
|
||||
Neal McBurnett <neal@mcburnett.org>
|
||||
Nelson Chen <crazysim@gmail.com>
|
||||
Niall O'Higgins <niallo@unworkable.org>
|
||||
Nicholas E. Rabenau <nerab@gmx.at>
|
||||
@@ -491,18 +531,21 @@ NikolaMandic <mn080202@gmail.com>
|
||||
noducks <onemannoducks@gmail.com>
|
||||
Nolan Darilek <nolan@thewordnerd.info>
|
||||
nzwsch <hi@nzwsch.com>
|
||||
O.S. Tezer <ostezer@gmail.com>
|
||||
OddBloke <daniel@daniel-watkins.co.uk>
|
||||
odk- <github@odkurzacz.org>
|
||||
Oguz Bilgic <fisyonet@gmail.com>
|
||||
Oh Jinkyun <tintypemolly@gmail.com>
|
||||
Ole Reifschneider <mail@ole-reifschneider.de>
|
||||
Olivier Gambier <dmp42@users.noreply.github.com>
|
||||
O.S. Tezer <ostezer@gmail.com>
|
||||
pandrew <letters@paulnotcom.se>
|
||||
panticz <mail@konczalski.de>
|
||||
Pascal Borreli <pascal@borreli.com>
|
||||
Pascal Hartig <phartig@rdrei.net>
|
||||
Patrick Hemmer <patrick.hemmer@gmail.com>
|
||||
Patrick Stapleton <github@gdi2290.com>
|
||||
pattichen <craftsbear@gmail.com>
|
||||
Paul <paul9869@gmail.com>
|
||||
Paul Annesley <paul@annesley.cc>
|
||||
Paul Bowsher <pbowsher@globalpersonals.co.uk>
|
||||
Paul Hammond <paul@paulhammond.org>
|
||||
@@ -510,7 +553,6 @@ Paul Jimenez <pj@place.org>
|
||||
Paul Lietar <paul@lietar.net>
|
||||
Paul Morie <pmorie@gmail.com>
|
||||
Paul Nasrat <pnasrat@gmail.com>
|
||||
Paul <paul9869@gmail.com>
|
||||
Paul Weaver <pauweave@cisco.com>
|
||||
Pavlos Ratis <dastergon@gentoo.org>
|
||||
Peter Bourgon <peter@bourgon.org>
|
||||
@@ -518,16 +560,18 @@ Peter Braden <peterbraden@peterbraden.co.uk>
|
||||
Peter Ericson <pdericson@gmail.com>
|
||||
Peter Salvatore <peter@psftw.com>
|
||||
Peter Waller <p@pwaller.net>
|
||||
Phil <underscorephil@gmail.com>
|
||||
Phil Estes <estesp@linux.vnet.ibm.com>
|
||||
Phil Spitler <pspitler@gmail.com>
|
||||
Philipp Weissensteiner <mail@philippweissensteiner.com>
|
||||
Phillip Alexander <git@phillipalexander.io>
|
||||
Phil Spitler <pspitler@gmail.com>
|
||||
Phil <underscorephil@gmail.com>
|
||||
Piergiuliano Bossi <pgbossi@gmail.com>
|
||||
Pierre-Alain RIVIERE <pariviere@ippon.fr>
|
||||
Pierre <py@poujade.org>
|
||||
Pierre Wacrenier <pierre.wacrenier@gmail.com>
|
||||
Pierre-Alain RIVIERE <pariviere@ippon.fr>
|
||||
Piotr Bogdan <ppbogdan@gmail.com>
|
||||
pixelistik <pixelistik@users.noreply.github.com>
|
||||
Porjo <porjo38@yahoo.com.au>
|
||||
Prasanna Gautam <prasannagautam@gmail.com>
|
||||
Przemek Hejman <przemyslaw.hejman@gmail.com>
|
||||
pysqz <randomq@126.com>
|
||||
@@ -547,6 +591,7 @@ Renato Riccieri Santos Zannon <renato.riccieri@gmail.com>
|
||||
rgstephens <greg@udon.org>
|
||||
Rhys Hiltner <rhys@twitch.tv>
|
||||
Richard Harvey <richard@squarecows.com>
|
||||
Richard Metzler <richard@paadee.com>
|
||||
Richo Healey <richo@psych0tik.net>
|
||||
Rick Bradley <rick@users.noreply.github.com>
|
||||
Rick van de Loo <rickvandeloo@gmail.com>
|
||||
@@ -572,25 +617,26 @@ Ryan Fowler <rwfowler@gmail.com>
|
||||
Ryan O'Donnell <odonnellryanc@gmail.com>
|
||||
Ryan Seto <ryanseto@yak.net>
|
||||
Ryan Thomas <rthomas@atlassian.com>
|
||||
Rémy Greinhofer <remy.greinhofer@livelovely.com>
|
||||
Sam Alba <sam.alba@gmail.com>
|
||||
Sam Bailey <cyprix@cyprix.com.au>
|
||||
Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
|
||||
Sam Reis <sreis@atlassian.com>
|
||||
Sam Rijs <srijs@airpost.net>
|
||||
Sami Wagiaalla <swagiaal@redhat.com>
|
||||
Samuel Andaya <samuel@andaya.net>
|
||||
Samuel PHAN <samuel-phan@users.noreply.github.com>
|
||||
Satnam Singh <satnam@raintown.org>
|
||||
satoru <satorulogic@gmail.com>
|
||||
Satoshi Amemiya <satoshi_amemiya@voyagegroup.com>
|
||||
Scott Bessler <scottbessler@gmail.com>
|
||||
Scott Collier <emailscottcollier@gmail.com>
|
||||
Scott Johnston <scott@docker.com>
|
||||
Scott Stamp <scottstamp851@gmail.com>
|
||||
Scott Walls <sawalls@umich.edu>
|
||||
Sean Cronin <seancron@gmail.com>
|
||||
Sean P. Kane <skane@newrelic.com>
|
||||
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>
|
||||
SeongJae Park <sj38.park@gmail.com>
|
||||
Shane Canon <scanon@lbl.gov>
|
||||
@@ -598,12 +644,12 @@ shaunol <shaunol@gmail.com>
|
||||
Shawn Landden <shawn@churchofgit.com>
|
||||
Shawn Siefkas <shawn.siefkas@meredith.com>
|
||||
Shih-Yuan Lee <fourdollars@gmail.com>
|
||||
Shishir Mahajan <shishir.mahajan@redhat.com>
|
||||
shuai-z <zs.broccoli@gmail.com>
|
||||
Silas Sewell <silas@sewell.org>
|
||||
Simon Taranto <simon.taranto@gmail.com>
|
||||
Sindhu S <sindhus@live.in>
|
||||
Sjoerd Langkemper <sjoerd-github@linuxonly.nl>
|
||||
s-ko <aleks@s-ko.net>
|
||||
Solomon Hykes <solomon@docker.com>
|
||||
Song Gao <song@gao.io>
|
||||
Soulou <leo@unbekandt.eu>
|
||||
@@ -611,18 +657,23 @@ soulshake <amy@gandi.net>
|
||||
Sridatta Thatipamala <sthatipamala@gmail.com>
|
||||
Sridhar Ratnakumar <sridharr@activestate.com>
|
||||
Srini Brahmaroutu <sbrahma@us.ibm.com>
|
||||
Srini Brahmaroutu <srbrahma@us.ibm.com>
|
||||
Steeve Morin <steeve.morin@gmail.com>
|
||||
Stefan Praszalowicz <stefan@greplin.com>
|
||||
Stephen Crosby <stevecrozz@gmail.com>
|
||||
Steven Burgess <steven.a.burgess@hotmail.com>
|
||||
Steven Merrill <steven.merrill@gmail.com>
|
||||
sudosurootdev <sudosurootdev@gmail.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au>
|
||||
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>
|
||||
Tatsuki Sugiura <sugi@nemui.org>
|
||||
Tatsushi Inagaki <e29253@jp.ibm.com>
|
||||
Ted M. Young <tedyoung@gmail.com>
|
||||
Tehmasp Chaudhri <tehmasp@gmail.com>
|
||||
Tejesh Mehta <tejesh.mehta@gmail.com>
|
||||
Thatcher Peskens <thatcher@docker.com>
|
||||
Thermionix <bond711@gmail.com>
|
||||
Thijs Terlouw <thijsterlouw@gmail.com>
|
||||
@@ -636,11 +687,10 @@ Tianon Gravi <admwiggin@gmail.com>
|
||||
Tibor Vass <teabee89@gmail.com>
|
||||
Tim Bosse <taim@bosboot.org>
|
||||
Tim Hockin <thockin@google.com>
|
||||
Timothy Hobbs <timothyhobbs@seznam.cz>
|
||||
Tim Ruffles <oi@truffles.me.uk>
|
||||
Tim Smith <timbot@google.com>
|
||||
Tim Terhorst <mynamewastaken+git@gmail.com>
|
||||
tjmehta <tj@init.me>
|
||||
Timothy Hobbs <timothyhobbs@seznam.cz>
|
||||
tjwebb123 <tjwebb123@users.noreply.github.com>
|
||||
tobe <tobegit3hub@gmail.com>
|
||||
Tobias Bieniek <Tobias.Bieniek@gmx.de>
|
||||
@@ -648,10 +698,12 @@ Tobias Gesellchen <tobias@gesellix.de>
|
||||
Tobias Schmidt <ts@soundcloud.com>
|
||||
Tobias Schwab <tobias.schwab@dynport.de>
|
||||
Todd Lunter <tlunter@gmail.com>
|
||||
Tomasz Lipinski <tlipinski@users.noreply.github.com>
|
||||
Tom Fotherby <tom+github@peopleperhour.com>
|
||||
Tom Hulihan <hulihan.tom159@gmail.com>
|
||||
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>
|
||||
Tonis Tiigi <tonistiigi@gmail.com>
|
||||
Tony Daws <tony@daws.ca>
|
||||
@@ -662,7 +714,8 @@ Trent Ogren <tedwardo2@gmail.com>
|
||||
Tyler Brock <tyler.brock@gmail.com>
|
||||
Tzu-Jung Lee <roylee17@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>
|
||||
Victor Coisne <victor.coisne@dotcloud.com>
|
||||
Victor Lyuboslavsky <victor@victoreda.com>
|
||||
@@ -691,15 +744,15 @@ Walter Leibbrandt <github@wrl.co.za>
|
||||
Walter Stanish <walter@pratyeka.org>
|
||||
Ward Vandewege <ward@jhvc.com>
|
||||
WarheadsSE <max@warheads.net>
|
||||
Wayne Chang <wayne@neverfear.org>
|
||||
Wes Morgan <cap10morgan@gmail.com>
|
||||
Will Dietz <w@wdtz.org>
|
||||
Will Rouesnel <w.rouesnel@gmail.com>
|
||||
Will Weaver <monkey@buildingbananas.com>
|
||||
William Delanoue <william.delanoue@gmail.com>
|
||||
William Henry <whenry@redhat.com>
|
||||
William Riancho <wr.wllm@gmail.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>
|
||||
xuzhaokui <cynicholas@gmail.com>
|
||||
Yang Bai <hamo.by@gmail.com>
|
||||
@@ -715,4 +768,6 @@ Zilin Du <zilin.du@gmail.com>
|
||||
zimbatm <zimbatm@zimbatm.com>
|
||||
Zoltan Tombol <zoltan.tombol@gmail.com>
|
||||
zqh <zqhxuyuan@gmail.com>
|
||||
Álex González <agonzalezro@gmail.com>
|
||||
Álvaro Lázaro <alvaro.lazaro.g@gmail.com>
|
||||
尹吉峰 <jifeng.yin@gmail.com>
|
||||
|
||||
@@ -1,5 +1,44 @@
|
||||
# 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)
|
||||
|
||||
#### Runtime
|
||||
|
||||
+8
-2
@@ -84,8 +84,10 @@ RUN cd /usr/local/go/src && ./make.bash --no-clean 2>&1
|
||||
ENV DOCKER_CROSSPLATFORMS \
|
||||
linux/386 linux/arm \
|
||||
darwin/amd64 darwin/386 \
|
||||
freebsd/amd64 freebsd/386 freebsd/arm \
|
||||
windows/amd64 windows/386
|
||||
freebsd/amd64 freebsd/386 freebsd/arm
|
||||
|
||||
# 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)
|
||||
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 \
|
||||
&& 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
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ test-docker-py: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary test-docker-py
|
||||
|
||||
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
|
||||
$(DOCKER_RUN_DOCKER) bash
|
||||
|
||||
+3
-4
@@ -17,7 +17,6 @@ import (
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
type DockerCli struct {
|
||||
@@ -27,7 +26,7 @@ type DockerCli struct {
|
||||
in io.ReadCloser
|
||||
out io.Writer
|
||||
err io.Writer
|
||||
key libtrust.PrivateKey
|
||||
keyFile string
|
||||
tlsConfig *tls.Config
|
||||
scheme string
|
||||
// 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
|
||||
}
|
||||
|
||||
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 (
|
||||
inFd uintptr
|
||||
outFd uintptr
|
||||
@@ -177,7 +176,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, key libtrust.PrivateKey,
|
||||
in: in,
|
||||
out: out,
|
||||
err: err,
|
||||
key: key,
|
||||
keyFile: keyFile,
|
||||
inFd: inFd,
|
||||
outFd: outFd,
|
||||
isTerminalIn: isTerminalIn,
|
||||
|
||||
+60
-58
@@ -39,6 +39,7 @@ import (
|
||||
"github.com/docker/docker/pkg/parsers/filters"
|
||||
"github.com/docker/docker/pkg/promise"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/docker/pkg/timeutils"
|
||||
"github.com/docker/docker/pkg/units"
|
||||
@@ -46,7 +47,6 @@ import (
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -148,36 +148,36 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var filename string // path to Dockerfile
|
||||
var origDockerfile string // used for error msg
|
||||
filename := *dockerfileName // path to Dockerfile
|
||||
|
||||
if *dockerfileName == "" {
|
||||
// No -f/--file was specified so use the default
|
||||
origDockerfile = api.DefaultDockerfileName
|
||||
*dockerfileName = origDockerfile
|
||||
*dockerfileName = api.DefaultDockerfileName
|
||||
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) {
|
||||
return fmt.Errorf("Can not locate Dockerfile: %s", origDockerfile)
|
||||
origDockerfile := *dockerfileName // used for error msg
|
||||
|
||||
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"))
|
||||
if err != nil {
|
||||
@@ -1216,25 +1216,6 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
||||
v := url.Values{}
|
||||
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 {
|
||||
buf, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
@@ -1244,7 +1225,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
||||
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,
|
||||
})
|
||||
}
|
||||
@@ -2634,7 +2615,12 @@ type containerStats struct {
|
||||
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()
|
||||
var (
|
||||
previousCpu uint64
|
||||
@@ -2714,28 +2700,44 @@ func (cli *DockerCli) CmdStats(args ...string) error {
|
||||
|
||||
names := cmd.Args()
|
||||
sort.Strings(names)
|
||||
var cStats []*containerStats
|
||||
for _, n := range names {
|
||||
s := &containerStats{Name: n}
|
||||
cStats = append(cStats, s)
|
||||
stream, _, err := cli.call("GET", "/containers/"+n+"/stats", nil, false)
|
||||
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) {
|
||||
var (
|
||||
cStats []*containerStats
|
||||
w = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||
)
|
||||
printHeader := func() {
|
||||
fmt.Fprint(cli.out, "\033[2J")
|
||||
fmt.Fprint(cli.out, "\033[H")
|
||||
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{}
|
||||
for i, s := range cStats {
|
||||
if err := s.Display(w); err != nil {
|
||||
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:]...)
|
||||
}
|
||||
if len(cStats) == 0 {
|
||||
|
||||
@@ -72,6 +72,15 @@ func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Con
|
||||
if err != nil {
|
||||
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, ":")
|
||||
if colonPos == -1 {
|
||||
@@ -140,6 +149,15 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
||||
req.Host = cli.addr
|
||||
|
||||
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 strings.Contains(err.Error(), "connection refused") {
|
||||
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"mime"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
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,
|
||||
// otherwise generates a new one
|
||||
func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
|
||||
err := os.MkdirAll(path.Dir(trustKeyPath), 0700)
|
||||
err := os.MkdirAll(filepath.Dir(trustKeyPath), 0700)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -69,7 +69,7 @@ func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
|
||||
return nil, fmt.Errorf("Error saving key file: %s", err)
|
||||
}
|
||||
} 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
|
||||
}
|
||||
|
||||
@@ -621,18 +621,6 @@ func getImagesSearch(eng *engine.Engine, version version.Version, w http.Respons
|
||||
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 {
|
||||
if vars == nil {
|
||||
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.SetenvJson("metaHeaders", metaHeaders)
|
||||
job.SetenvJson("authConfig", authConfig)
|
||||
job.Setenv("manifest", string(manifest))
|
||||
job.Setenv("tag", r.Form.Get("tag"))
|
||||
if version.GreaterThan("1.0") {
|
||||
job.SetenvBool("json", true)
|
||||
@@ -1325,7 +1307,6 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
||||
"/images/viz": getImagesViz,
|
||||
"/images/search": getImagesSearch,
|
||||
"/images/get": getImagesGet,
|
||||
"/images/{name:.*}/manifest": getImageManifest,
|
||||
"/images/{name:.*}/get": getImagesGet,
|
||||
"/images/{name:.*}/history": getImagesHistory,
|
||||
"/images/{name:.*}/json": getImagesByName,
|
||||
|
||||
+21
-21
@@ -6,21 +6,21 @@ import "time"
|
||||
|
||||
type ThrottlingData struct {
|
||||
// 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.
|
||||
ThrottledPeriods uint64 `json:"throttled_periods,omitempty"`
|
||||
ThrottledPeriods uint64 `json:"throttled_periods"`
|
||||
// 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.
|
||||
type CpuUsage struct {
|
||||
// Total CPU time consumed.
|
||||
// Units: nanoseconds.
|
||||
TotalUsage uint64 `json:"total_usage,omitempty"`
|
||||
TotalUsage uint64 `json:"total_usage"`
|
||||
// Total CPU time consumed per core.
|
||||
// Units: nanoseconds.
|
||||
PercpuUsage []uint64 `json:"percpu_usage,omitempty"`
|
||||
PercpuUsage []uint64 `json:"percpu_usage"`
|
||||
// Time spent by tasks of the cgroup in kernel mode.
|
||||
// Units: nanoseconds.
|
||||
UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
||||
@@ -30,41 +30,41 @@ type CpuUsage struct {
|
||||
}
|
||||
|
||||
type CpuStats struct {
|
||||
CpuUsage CpuUsage `json:"cpu_usage,omitempty"`
|
||||
CpuUsage CpuUsage `json:"cpu_usage"`
|
||||
SystemUsage uint64 `json:"system_cpu_usage"`
|
||||
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
||||
}
|
||||
|
||||
type MemoryStats struct {
|
||||
// current res_counter usage for memory
|
||||
Usage uint64 `json:"usage,omitempty"`
|
||||
Usage uint64 `json:"usage"`
|
||||
// maximum usage ever recorded.
|
||||
MaxUsage uint64 `json:"max_usage,omitempty"`
|
||||
MaxUsage uint64 `json:"max_usage"`
|
||||
// TODO(vishh): Export these as stronger types.
|
||||
// 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.
|
||||
Failcnt uint64 `json:"failcnt"`
|
||||
Limit uint64 `json:"limit"`
|
||||
}
|
||||
|
||||
type BlkioStatEntry struct {
|
||||
Major uint64 `json:"major,omitempty"`
|
||||
Minor uint64 `json:"minor,omitempty"`
|
||||
Op string `json:"op,omitempty"`
|
||||
Value uint64 `json:"value,omitempty"`
|
||||
Major uint64 `json:"major"`
|
||||
Minor uint64 `json:"minor"`
|
||||
Op string `json:"op"`
|
||||
Value uint64 `json:"value"`
|
||||
}
|
||||
|
||||
type BlkioStats struct {
|
||||
// number of bytes tranferred to and from the block device
|
||||
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"`
|
||||
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive,omitempty"`
|
||||
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,omitempty"`
|
||||
IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive,omitempty"`
|
||||
IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive,omitempty"`
|
||||
IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive,omitempty"`
|
||||
IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive,omitempty"`
|
||||
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive,omitempty"`
|
||||
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive"`
|
||||
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive"`
|
||||
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive"`
|
||||
IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive"`
|
||||
IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive"`
|
||||
IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive"`
|
||||
IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive"`
|
||||
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"`
|
||||
}
|
||||
|
||||
type Network struct {
|
||||
|
||||
+12
-8
@@ -24,7 +24,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/engine"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/tarsum"
|
||||
"github.com/docker/docker/registry"
|
||||
"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
|
||||
// 'filename' is a relative path from the root of the context
|
||||
func (b *Builder) readDockerfile(filename string) error {
|
||||
filename = path.Join(b.contextPath, filename)
|
||||
func (b *Builder) readDockerfile(origFile string) error {
|
||||
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) {
|
||||
return fmt.Errorf("Cannot build a directory without a Dockerfile")
|
||||
return fmt.Errorf("Cannot locate specified Dockerfile: %s", origFile)
|
||||
}
|
||||
if fi.Size() == 0 {
|
||||
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
|
||||
// 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 {
|
||||
os.Remove(path.Join(b.contextPath, ".dockerignore"))
|
||||
os.Remove(filepath.Join(b.contextPath, ".dockerignore"))
|
||||
b.context.(tarsum.BuilderContext).Remove(".dockerignore")
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
imagepkg "github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"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("parallel", true)
|
||||
job.SetenvJson("authConfig", pullRegistryAuth)
|
||||
job.Stdout.Add(b.OutOld)
|
||||
job.Stdout.Add(ioutils.NopWriteCloser(b.OutOld))
|
||||
if err := job.Run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -104,6 +104,22 @@ __docker_pos_first_nonflag() {
|
||||
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() {
|
||||
command -v host >/dev/null 2>&1 || return
|
||||
COMPREPLY=( $(host 2>/dev/null "${cur%:}" | awk '/has address/ {print $4}') )
|
||||
@@ -154,15 +170,47 @@ __docker_capabilities() {
|
||||
}
|
||||
|
||||
_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
|
||||
-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
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-H" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "$boolean_options $main_options_with_args" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=( $( compgen -W "${commands[*]} help" -- "$cur" ) )
|
||||
@@ -561,6 +609,8 @@ _docker_run() {
|
||||
--sig-proxy
|
||||
"
|
||||
|
||||
local options_with_args_glob=$(__docker_to_extglob "$options_with_args")
|
||||
|
||||
case "$prev" in
|
||||
--add-host)
|
||||
case "$cur" in
|
||||
@@ -677,7 +727,7 @@ _docker_run() {
|
||||
__docker_containers_all
|
||||
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
|
||||
;;
|
||||
esac
|
||||
@@ -687,7 +737,7 @@ _docker_run() {
|
||||
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
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
@@ -739,6 +789,10 @@ _docker_start() {
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_stats() {
|
||||
__docker_containers_running
|
||||
}
|
||||
|
||||
_docker_stop() {
|
||||
case "$prev" in
|
||||
--time|-t)
|
||||
@@ -801,6 +855,9 @@ _docker_wait() {
|
||||
}
|
||||
|
||||
_docker() {
|
||||
local previous_extglob_setting=$(shopt -p extglob)
|
||||
shopt -s extglob
|
||||
|
||||
local commands=(
|
||||
attach
|
||||
build
|
||||
@@ -833,6 +890,7 @@ _docker() {
|
||||
save
|
||||
search
|
||||
start
|
||||
stats
|
||||
stop
|
||||
tag
|
||||
top
|
||||
@@ -841,6 +899,33 @@ _docker() {
|
||||
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=()
|
||||
local cur prev words cword
|
||||
_get_comp_words_by_ref -n : cur prev words cword
|
||||
@@ -849,7 +934,7 @@ _docker() {
|
||||
local counter=1
|
||||
while [ $counter -lt $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
-H)
|
||||
$main_options_with_args_glob )
|
||||
(( counter++ ))
|
||||
;;
|
||||
-*)
|
||||
@@ -867,6 +952,7 @@ _docker() {
|
||||
local completions_func=_docker_${command}
|
||||
declare -F $completions_func >/dev/null && $completions_func
|
||||
|
||||
eval "$previous_extglob_setting"
|
||||
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-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' -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 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' -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 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 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' -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' -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 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 tlscert -d 'Path to TLS certificate 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
|
||||
# attach
|
||||
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 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"
|
||||
|
||||
# build
|
||||
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 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 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' -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'
|
||||
@@ -92,12 +101,14 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s t -l tag -d '
|
||||
# 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 -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 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"
|
||||
|
||||
# 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 -A -f -n '__fish_seen_subcommand_from cp' -l help -d 'Print usage'
|
||||
|
||||
# create
|
||||
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 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 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-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' -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 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' -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' -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' -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 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 -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 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 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'
|
||||
@@ -136,26 +153,32 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from create' -a '(__fish_pri
|
||||
|
||||
# diff
|
||||
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"
|
||||
|
||||
# events
|
||||
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 until -d 'Stream events until this timestamp'
|
||||
|
||||
# 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' -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 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"
|
||||
|
||||
# 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 -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"
|
||||
|
||||
# history
|
||||
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' -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"
|
||||
@@ -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 -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' -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' -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"
|
||||
|
||||
# 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 -A -f -n '__fish_seen_subcommand_from import' -l help -d 'Print usage'
|
||||
|
||||
# info
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a info -d 'Display system-wide information'
|
||||
|
||||
# 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' -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_containers all)' -d "Container"
|
||||
|
||||
# kill
|
||||
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' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# load
|
||||
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'
|
||||
|
||||
# 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 -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 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
|
||||
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' -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' -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"
|
||||
|
||||
# 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"
|
||||
|
||||
# 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' -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' -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 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' -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.'
|
||||
|
||||
# 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 -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_repositories)' -d "Repository"
|
||||
|
||||
# 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 -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_repositories)' -d "Repository"
|
||||
|
||||
# rename
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a rename -d 'Rename an existing container'
|
||||
|
||||
# restart
|
||||
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' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# rm
|
||||
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' -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 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"
|
||||
@@ -251,6 +290,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -a '(__fish_print_d
|
||||
# rmi
|
||||
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' -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' -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 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' -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-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' -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 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' -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' -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' -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 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 -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 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 rm -d 'Automatically remove the container when it exits (incompatible with -d)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l security-opt -d 'Security Options'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l sig-proxy -d 'Proxy received signals to the process (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 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)'
|
||||
@@ -294,32 +340,43 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from run' -a '(__fish_print_
|
||||
|
||||
# save
|
||||
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"
|
||||
|
||||
# search
|
||||
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 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' -s s -l stars -d 'Only displays with at least x stars'
|
||||
|
||||
# start
|
||||
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' -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' -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
|
||||
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' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# tag
|
||||
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' -l help -d 'Print usage'
|
||||
|
||||
# top
|
||||
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"
|
||||
|
||||
# unpause
|
||||
@@ -331,6 +388,7 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -a version -d 'Show the D
|
||||
|
||||
# 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 -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"
|
||||
|
||||
|
||||
|
||||
+2
-6
@@ -820,7 +820,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error)
|
||||
if os.Geteuid() != 0 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1205,11 +1205,7 @@ func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*i
|
||||
return match, nil
|
||||
}
|
||||
|
||||
func checkKernelAndArch() 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)
|
||||
}
|
||||
func checkKernel() error {
|
||||
// Check for unsupported kernel versions
|
||||
// FIXME: it would be cleaner to not test for specific versions, but rather
|
||||
// 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
|
||||
# for security. See: http://man7.org/linux/man-pages/man5/lxc.container.conf.5.html
|
||||
lxc.mount.auto = proc sys
|
||||
{{if .AppArmor}}
|
||||
lxc.aa_profile = .AppArmorProfile
|
||||
{{if .AppArmorProfile}}
|
||||
lxc.aa_profile = {{.AppArmorProfile}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -248,7 +248,8 @@ func TestCustomLxcConfigMisc(t *testing.T) {
|
||||
}
|
||||
defer os.RemoveAll(root)
|
||||
os.MkdirAll(path.Join(root, "containers", "1"), 0777)
|
||||
driver, err := NewDriver(root, "", false)
|
||||
driver, err := NewDriver(root, "", true)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -271,9 +272,10 @@ func TestCustomLxcConfigMisc(t *testing.T) {
|
||||
Bridge: "docker0",
|
||||
},
|
||||
},
|
||||
ProcessConfig: processConfig,
|
||||
CapAdd: []string{"net_admin", "syslog"},
|
||||
CapDrop: []string{"kill", "mknod"},
|
||||
ProcessConfig: processConfig,
|
||||
CapAdd: []string{"net_admin", "syslog"},
|
||||
CapDrop: []string{"kill", "mknod"},
|
||||
AppArmorProfile: "lxc-container-default-with-nesting",
|
||||
}
|
||||
|
||||
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.gateway = 10.10.10.1")
|
||||
grepFile(t, p, "lxc.network.flags = up")
|
||||
|
||||
grepFile(t, p, "lxc.aa_profile = lxc-container-default-with-nesting")
|
||||
// hostname
|
||||
grepFile(t, p, "lxc.utsname = testhost")
|
||||
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
|
||||
}
|
||||
|
||||
@@ -173,7 +188,6 @@ func InitDriver(job *engine.Job) engine.Status {
|
||||
|
||||
networkv4 = addrv4.(*net.IPNet)
|
||||
|
||||
log.Infof("enableIPv6 = %t", enableIPv6)
|
||||
if enableIPv6 {
|
||||
if len(addrsv6) == 0 {
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if enableIPv6 {
|
||||
// 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: %s\n", err)
|
||||
}
|
||||
|
||||
ipAddr6, ipNet6, err := net.ParseCIDR(bridgeIPv6)
|
||||
if err != nil {
|
||||
log.Errorf("BridgeIPv6 parsing failed")
|
||||
if err := setupIPv6Bridge(bridgeIPv6); err != nil {
|
||||
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 {
|
||||
@@ -424,6 +426,30 @@ func configureBridge(bridgeIP string, bridgeIPv6 string, enableIPv6 bool) error
|
||||
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 {
|
||||
kv, err := kernel.GetKernelVersion()
|
||||
// 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 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) {
|
||||
iface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
|
||||
+2
-7
@@ -79,11 +79,6 @@ func main() {
|
||||
}
|
||||
protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
|
||||
|
||||
trustKey, err := api.LoadOrCreateTrustKey(*flTrustKey)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var (
|
||||
cli *client.DockerCli
|
||||
tlsConfig tls.Config
|
||||
@@ -125,9 +120,9 @@ func main() {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ func init() {
|
||||
{"images", "List images"},
|
||||
{"import", "Create a new filesystem image from the contents of a tarball"},
|
||||
{"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"},
|
||||
{"load", "Load an image from a tar archive"},
|
||||
{"login", "Register or log in to a Docker registry server"},
|
||||
|
||||
@@ -59,7 +59,7 @@ as context.
|
||||
|
||||
# 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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
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 .
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ unix://[/path/to/socket] to use.
|
||||
Display system-wide information
|
||||
|
||||
**docker-inspect(1)**
|
||||
Return low-level information on a container
|
||||
Return low-level information on a container or image
|
||||
|
||||
**docker-kill(1)**
|
||||
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/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/index.md', '**HIDDEN**']
|
||||
- ['examples/nodejs_web_app.md', 'Examples', 'Dockerizing a Node.js web application']
|
||||
@@ -86,6 +91,7 @@ pages:
|
||||
- ['articles/networking.md', 'Articles', 'Advanced networking']
|
||||
- ['articles/security.md', 'Articles', 'Security']
|
||||
- ['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/baseimages.md', 'Articles', 'Creating a base image']
|
||||
- ['articles/dockerfile_best-practices.md', 'Articles', 'Best practices for writing Dockerfiles']
|
||||
|
||||
+6
-2
@@ -72,6 +72,9 @@ setup_s3() {
|
||||
|
||||
build_current_documentation() {
|
||||
mkdocs build
|
||||
cd site/
|
||||
gzip -9k -f search_content.json
|
||||
cd ..
|
||||
}
|
||||
|
||||
upload_current_documentation() {
|
||||
@@ -87,8 +90,6 @@ upload_current_documentation() {
|
||||
echo "Uploading $src"
|
||||
echo " to $dst"
|
||||
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
|
||||
# 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
|
||||
|
||||
# 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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
> **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.
|
||||
|
||||
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:
|
||||
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
|
||||
Enter pass phrase for ca-key.pem:
|
||||
You are about to be asked to enter information that will be incorporated
|
||||
into your certificate request.
|
||||
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
|
||||
For some fields there will be a default value,
|
||||
If you enter '.', the field will be left blank.
|
||||
-----
|
||||
Country Name (2 letter code) [AU]:
|
||||
State or Province Name (full name) [Some-State]:Queensland
|
||||
Locality Name (eg, city) []:Brisbane
|
||||
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc
|
||||
Organizational Unit Name (eg, section) []:Boot2Docker
|
||||
Common Name (e.g. server FQDN or YOUR name) []:$HOST
|
||||
Email Address []:Sven@home.org.au
|
||||
You are about to be asked to enter information that will be incorporated
|
||||
into your certificate request.
|
||||
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
|
||||
For some fields there will be a default value,
|
||||
If you enter '.', the field will be left blank.
|
||||
-----
|
||||
Country Name (2 letter code) [AU]:
|
||||
State or Province Name (full name) [Some-State]:Queensland
|
||||
Locality Name (eg, city) []:Brisbane
|
||||
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc
|
||||
Organizational Unit Name (eg, section) []:Boot2Docker
|
||||
Common Name (e.g. server FQDN or YOUR name) []:$HOST
|
||||
Email Address []:Sven@home.org.au
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
$ 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)
|
||||
$ 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 \
|
||||
-CAcreateserial -out server-cert.pem
|
||||
@@ -93,7 +93,7 @@ config file:
|
||||
|
||||
$ 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 \
|
||||
-CAcreateserial -out cert.pem -extfile extfile.cnf
|
||||
@@ -102,6 +102,24 @@ Now sign the key:
|
||||
Getting CA Private Key
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
$ 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
|
||||
|
||||
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
|
||||
`-H=tcp://:2376` and `--tlsverify` on every call).
|
||||
`-H=tcp://$HOST:2376` and `--tlsverify` on every call).
|
||||
|
||||
$ mkdir -p ~/.docker
|
||||
$ cp ca.pem ~/.docker/ca.pem
|
||||
$ cp cert.pem ~/.docker/cert.pem
|
||||
$ cp key.pem ~/.docker/key.pem
|
||||
$ export DOCKER_HOST=tcp://:2376
|
||||
$ export DOCKER_TLS_VERIFY=1
|
||||
$ mkdir -pv ~/.docker
|
||||
$ cp -v {ca,cert,key}.pem ~/.docker
|
||||
$ export DOCKER_HOST=tcp://$HOST:2376 DOCKER_TLS_VERIFY=1
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
location using the environment variable `DOCKER_CERT_PATH`.
|
||||
|
||||
$ export DOCKER_CERT_PATH=${HOME}/.docker/zone1/
|
||||
$ export DOCKER_CERT_PATH=~/.docker/zone1/
|
||||
$ docker --tlsverify ps
|
||||
|
||||
### Connecting to the Secure Docker port using `curl`
|
||||
|
||||
@@ -13,11 +13,12 @@ cert: build
|
||||
certs: cert
|
||||
|
||||
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:
|
||||
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 --tls --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=$(HOST):6666 version
|
||||
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:
|
||||
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
|
||||
|
||||
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 ip link set dev docker0 down
|
||||
$ sudo brctl delbr docker0
|
||||
$ sudo iptables -t nat -F POSTROUTING
|
||||
|
||||
Then, before starting the Docker service, create your own bridge and
|
||||
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
|
||||
$ 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
|
||||
now prepared to bind containers to the new bridge. After pausing to
|
||||
verify the bridge's configuration, try creating a container — you will
|
||||
|
||||
@@ -39,7 +39,7 @@ our container.
|
||||
|
||||
Here we're installing the `openssh-server`,
|
||||
`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.
|
||||
|
||||
## 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
|
||||
binaries, go environment, go dependencies, etc.
|
||||
|
||||
**Things you need:**
|
||||
|
||||
* Docker
|
||||
* git
|
||||
* make
|
||||
|
||||
## Install Docker
|
||||
|
||||
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
|
||||
`Dockerfile` in the current directory. Essentially, it installs all
|
||||
the build and runtime dependencies necessary to build and test Docker.
|
||||
Your first build will take some time to complete. On Linux systems:
|
||||
|
||||
$ sudo make build
|
||||
|
||||
On Mac OS X, from within the `boot2docker` shell:
|
||||
Your first build will take some time to complete. On Linux systems and on Mac
|
||||
OS X from within the `boot2docker` shell:
|
||||
|
||||
$ make build
|
||||
|
||||
|
||||
> **Note**:
|
||||
> 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
|
||||
> 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
|
||||
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:
|
||||
|
||||
$ sudo make binary
|
||||
$ make binary
|
||||
|
||||
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`
|
||||
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`
|
||||
or `make BINDDIR=. cross`.
|
||||
@@ -91,7 +97,7 @@ on ubuntu:
|
||||
|
||||
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
|
||||
something like this
|
||||
@@ -138,7 +144,7 @@ is recommended.
|
||||
|
||||
You can run an interactive session in the newly built container:
|
||||
|
||||
$ sudo make shell
|
||||
$ make shell
|
||||
|
||||
# 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
|
||||
by:
|
||||
|
||||
$ sudo make docs
|
||||
$ make docs
|
||||
|
||||
# when its done, you can point your browser to http://yourdockerhost:8000
|
||||
# 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
|
||||
> 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 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
|
||||
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:*
|
||||
|
||||
```
|
||||
@@ -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"
|
||||
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 allow you to chain calls to multiple services. For example,
|
||||
|
||||
@@ -385,6 +385,41 @@ Status Codes:
|
||||
- **404** – no such container
|
||||
- **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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
|
||||
@@ -385,6 +385,41 @@ Status Codes:
|
||||
- **404** – no such container
|
||||
- **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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
|
||||
@@ -35,7 +35,7 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -45,7 +45,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -55,7 +55,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0",
|
||||
@@ -65,7 +65,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0",
|
||||
@@ -119,7 +119,7 @@ Create a container
|
||||
"Cmd":[
|
||||
"date"
|
||||
],
|
||||
"Image":"base",
|
||||
"Image":"ubuntu",
|
||||
"Volumes":{
|
||||
"/tmp": {}
|
||||
},
|
||||
@@ -194,7 +194,7 @@ Return low-level information on the container `id`
|
||||
"Cmd": [
|
||||
"date"
|
||||
],
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {},
|
||||
"WorkingDir":""
|
||||
|
||||
@@ -539,6 +539,41 @@ Status Codes:
|
||||
4. Read the extracted size and output it on the correct output
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
@@ -667,7 +702,7 @@ Create an image, either by pull it from the registry or by importing
|
||||
|
||||
**Example request**:
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -739,7 +774,7 @@ Return low-level information on the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/ubuntu/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -766,7 +801,7 @@ Return low-level information on the image `name`
|
||||
"StdinOnce":false,
|
||||
"Env":null,
|
||||
"Cmd": ["/bin/bash"]
|
||||
"Image":"base",
|
||||
"Image":"ubuntu",
|
||||
"Volumes":null,
|
||||
"WorkingDir":""
|
||||
},
|
||||
@@ -787,7 +822,7 @@ Return the history of the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/ubuntu/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -1194,10 +1229,10 @@ and Docker images will report:
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||
|
||||
Query Parameters:
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -45,7 +45,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -55,7 +55,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0",
|
||||
@@ -65,7 +65,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0",
|
||||
@@ -119,7 +119,7 @@ Create a container
|
||||
"Cmd":[
|
||||
"date"
|
||||
],
|
||||
"Image":"base",
|
||||
"Image":"ubuntu",
|
||||
"Volumes":{
|
||||
"/tmp": {}
|
||||
},
|
||||
@@ -195,7 +195,7 @@ Return low-level information on the container `id`
|
||||
"date"
|
||||
],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -574,6 +574,41 @@ Status Codes:
|
||||
4. Read the extracted size and output it on the correct output
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
@@ -701,7 +736,7 @@ Create an image, either by pull it from the registry or by importing i
|
||||
|
||||
**Example request**:
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -742,7 +777,7 @@ Return low-level information on the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/ubuntu/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -770,7 +805,7 @@ Return low-level information on the image `name`
|
||||
"Env":null,
|
||||
"Cmd": ["/bin/bash"],
|
||||
"Dns":null,
|
||||
"Image":"base",
|
||||
"Image":"ubuntu",
|
||||
"Volumes":null,
|
||||
"VolumesFrom":"",
|
||||
"WorkingDir":""
|
||||
@@ -792,7 +827,7 @@ Return the history of the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/ubuntu/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -1224,10 +1259,10 @@ and Docker images will report:
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||
|
||||
Query Parameters:
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -46,7 +46,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -56,7 +56,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0",
|
||||
@@ -66,7 +66,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0",
|
||||
@@ -127,7 +127,7 @@ Create a container
|
||||
"Cmd":[
|
||||
"date"
|
||||
],
|
||||
"Image":"base",
|
||||
"Image":"ubuntu",
|
||||
"Volumes":{
|
||||
"/tmp": {}
|
||||
},
|
||||
@@ -204,7 +204,7 @@ Return low-level information on the container `id`
|
||||
"date"
|
||||
],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -622,6 +622,41 @@ Status Codes:
|
||||
4. Read the extracted size and output it on the correct output
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
@@ -760,7 +795,7 @@ Create an image, either by pull it from the registry or by importing i
|
||||
|
||||
**Example request**:
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -803,7 +838,7 @@ Return low-level information on the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/ubuntu/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -829,7 +864,7 @@ Return low-level information on the image `name`
|
||||
"Env": null,
|
||||
"Cmd": ["/bin/bash"],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": null,
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -853,7 +888,7 @@ Return the history of the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/ubuntu/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -1290,10 +1325,10 @@ and Docker images will report:
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||
|
||||
Query Parameters:
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -46,7 +46,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -56,7 +56,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0",
|
||||
@@ -66,7 +66,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0",
|
||||
@@ -121,7 +121,7 @@ Create a container
|
||||
"Cmd":[
|
||||
"date"
|
||||
],
|
||||
"Image":"base",
|
||||
"Image":"ubuntu",
|
||||
"Volumes":{
|
||||
"/tmp": {}
|
||||
},
|
||||
@@ -198,7 +198,7 @@ Return low-level information on the container `id`
|
||||
"date"
|
||||
],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -615,6 +615,41 @@ Status Codes:
|
||||
4. Read the extracted size and output it on the correct output
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
@@ -749,7 +784,7 @@ Create an image, either by pulling it from the registry or by importing it
|
||||
|
||||
**Example request**:
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -792,7 +827,7 @@ Return low-level information on the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/ubuntu/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -818,7 +853,7 @@ Return low-level information on the image `name`
|
||||
"Env": null,
|
||||
"Cmd": ["/bin/bash"],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": null,
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -842,7 +877,7 @@ Return the history of the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/ubuntu/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -1279,10 +1314,10 @@ and Docker images will report:
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||
|
||||
Query Parameters:
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -46,7 +46,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -56,7 +56,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0",
|
||||
@@ -66,7 +66,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0",
|
||||
@@ -124,7 +124,7 @@ Create a container
|
||||
"Cmd":[
|
||||
"date"
|
||||
],
|
||||
"Image":"base",
|
||||
"Image":"ubuntu",
|
||||
"Volumes":{
|
||||
"/tmp": {}
|
||||
},
|
||||
@@ -207,7 +207,7 @@ Return low-level information on the container `id`
|
||||
"date"
|
||||
],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -625,6 +625,41 @@ Status Codes:
|
||||
4. Read the extracted size and output it on the correct output
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
@@ -759,7 +794,7 @@ Create an image, either by pulling it from the registry or by importing it
|
||||
|
||||
**Example request**:
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -802,7 +837,7 @@ Return low-level information on the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/ubuntu/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -828,7 +863,7 @@ Return low-level information on the image `name`
|
||||
"Env": null,
|
||||
"Cmd": ["/bin/bash"],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": null,
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -852,7 +887,7 @@ Return the history of the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/ubuntu/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -1289,10 +1324,10 @@ and Docker images will report:
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||
|
||||
Query Parameters:
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -46,7 +46,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -56,7 +56,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0",
|
||||
@@ -66,7 +66,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0",
|
||||
@@ -128,7 +128,7 @@ Create a container
|
||||
"date"
|
||||
],
|
||||
"Entrypoint": "",
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {
|
||||
"/tmp": {}
|
||||
},
|
||||
@@ -148,6 +148,7 @@ Create a container
|
||||
"Privileged": false,
|
||||
"Dns": ["8.8.8.8"],
|
||||
"DnsSearch": [""],
|
||||
"ExtraHosts": null,
|
||||
"VolumesFrom": ["parent", "other:ro"],
|
||||
"CapAdd": ["NET_ADMIN"],
|
||||
"CapDrop": ["MKNOD"],
|
||||
@@ -220,6 +221,8 @@ Json Parameters:
|
||||
a boolean value.
|
||||
- **Dns** - A list of dns servers for the container to use.
|
||||
- **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.
|
||||
Specified in the form `<container name>[:<ro|rw>]`
|
||||
- **CapAdd** - A list of kernel capabilties to add to the container.
|
||||
@@ -288,7 +291,7 @@ Return low-level information on the container `id`
|
||||
"date"
|
||||
],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -767,6 +770,41 @@ Status Codes:
|
||||
4. Read the extracted size and output it on the correct output
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
@@ -901,7 +939,7 @@ Create an image, either by pulling it from the registry or by importing it
|
||||
|
||||
**Example request**:
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -945,7 +983,7 @@ Return low-level information on the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/ubuntu/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -971,7 +1009,7 @@ Return low-level information on the image `name`
|
||||
"Env": null,
|
||||
"Cmd": ["/bin/bash"],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": null,
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -995,7 +1033,7 @@ Return the history of the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/ubuntu/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -1433,10 +1471,10 @@ and Docker images will report:
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||
|
||||
Query Parameters:
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -46,7 +46,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -56,7 +56,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0",
|
||||
@@ -66,7 +66,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0",
|
||||
@@ -128,7 +128,7 @@ Create a container
|
||||
"date"
|
||||
],
|
||||
"Entrypoint": "",
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {
|
||||
"/tmp": {}
|
||||
},
|
||||
@@ -148,6 +148,7 @@ Create a container
|
||||
"Privileged": false,
|
||||
"Dns": ["8.8.8.8"],
|
||||
"DnsSearch": [""],
|
||||
"ExtraHosts": null,
|
||||
"VolumesFrom": ["parent", "other:ro"],
|
||||
"CapAdd": ["NET_ADMIN"],
|
||||
"CapDrop": ["MKNOD"],
|
||||
@@ -220,6 +221,8 @@ Json Parameters:
|
||||
a boolean value.
|
||||
- **Dns** - A list of dns servers for the container to use.
|
||||
- **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.
|
||||
Specified in the form `<container name>[:<ro|rw>]`
|
||||
- **CapAdd** - A list of kernel capabilties to add to the container.
|
||||
@@ -288,7 +291,7 @@ Return low-level information on the container `id`
|
||||
"date"
|
||||
],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -713,6 +716,41 @@ Status Codes:
|
||||
4. Read the extracted size and output it on the correct output
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
@@ -847,7 +885,7 @@ Create an image, either by pulling it from the registry or by importing it
|
||||
|
||||
**Example request**:
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -891,7 +929,7 @@ Return low-level information on the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/ubuntu/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -917,7 +955,7 @@ Return low-level information on the image `name`
|
||||
"Env": null,
|
||||
"Cmd": ["/bin/bash"],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": null,
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -941,7 +979,7 @@ Return the history of the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/ubuntu/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -1389,10 +1427,10 @@ and Docker images will report:
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||
|
||||
Query Parameters:
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -46,7 +46,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -56,7 +56,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0",
|
||||
@@ -66,7 +66,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0",
|
||||
@@ -128,7 +128,7 @@ Create a container
|
||||
"date"
|
||||
],
|
||||
"Entrypoint": "",
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {
|
||||
"/tmp": {}
|
||||
},
|
||||
@@ -149,6 +149,7 @@ Create a container
|
||||
"ReadonlyRootfs": false,
|
||||
"Dns": ["8.8.8.8"],
|
||||
"DnsSearch": [""],
|
||||
"ExtraHosts": null,
|
||||
"VolumesFrom": ["parent", "other:ro"],
|
||||
"CapAdd": ["NET_ADMIN"],
|
||||
"CapDrop": ["MKNOD"],
|
||||
@@ -223,6 +224,8 @@ Json Parameters:
|
||||
Specified as a boolean value.
|
||||
- **Dns** - A list of dns servers for the container to use.
|
||||
- **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.
|
||||
Specified in the form `<container name>[:<ro|rw>]`
|
||||
- **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
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
@@ -1058,7 +1096,7 @@ Create an image, either by pulling it from the registry or by importing it
|
||||
|
||||
**Example request**:
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -1102,7 +1140,7 @@ Return low-level information on the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/ubuntu/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -1128,7 +1166,7 @@ Return low-level information on the image `name`
|
||||
"Env": null,
|
||||
"Cmd": ["/bin/bash"],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": null,
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir": ""
|
||||
@@ -1152,7 +1190,7 @@ Return the history of the image `name`
|
||||
|
||||
**Example request**:
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/ubuntu/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -1551,10 +1589,10 @@ and Docker images will report:
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}
|
||||
{"status": "create", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "start", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067924}
|
||||
{"status": "stop", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067966}
|
||||
{"status": "destroy", "id": "dfdf82bd3881","from": "ubuntu:latest", "time":1374067970}
|
||||
|
||||
Query Parameters:
|
||||
|
||||
|
||||
@@ -397,6 +397,41 @@ Status Codes:
|
||||
- **404** – no such container
|
||||
- **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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
|
||||
@@ -445,6 +445,41 @@ Status Codes:
|
||||
- **404** – no such container
|
||||
- **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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
|
||||
@@ -460,6 +460,41 @@ Status Codes:
|
||||
- **404** – no such container
|
||||
- **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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
|
||||
@@ -458,6 +458,41 @@ Status Codes:
|
||||
- **404** – no such container
|
||||
- **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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
|
||||
@@ -564,6 +564,41 @@ Status Codes:
|
||||
4. Read the extracted size and output it on the correct output
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
|
||||
@@ -509,6 +509,41 @@ Status Codes:
|
||||
4. Read the extracted size and output it on the correct output
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
|
||||
@@ -557,6 +557,41 @@ Status Codes:
|
||||
4. Read the extracted size and output it on the correct output
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
|
||||
@@ -561,6 +561,41 @@ Status Codes:
|
||||
4. Read the extracted size and output it on the correct output
|
||||
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
|
||||
|
||||
`POST /containers/(id)/wait`
|
||||
|
||||
@@ -64,7 +64,7 @@ This will mount the host directory, `/src/webapp`, into the container at
|
||||
`/opt/webapp`.
|
||||
|
||||
> **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
|
||||
> 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
|
||||
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
|
||||
Python applications: both bound to port 5000 inside your container.
|
||||
Without Docker's port mapping you could only access one at a time.
|
||||
Python applications: both bound to port 5000 inside their own containers.
|
||||
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
|
||||
see the application.
|
||||
|
||||
Vendored
+1
@@ -60,6 +60,7 @@ pre {
|
||||
/* Main Navigation */
|
||||
#nav_menu > #docsnav {
|
||||
max-width: 940px;
|
||||
width: 940px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#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;
|
||||
width: 140px;
|
||||
}
|
||||
#topmostnav .navbar-index-search .search-query:focus,
|
||||
#topmostnav .navbar-index-search .search-query.focused {
|
||||
width: 200px;
|
||||
}
|
||||
#topmostnav.public {
|
||||
border-bottom: none;
|
||||
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">
|
||||
<a href="http://www.docker.com/" title="Homepage">
|
||||
<div class="brand logo"><img src="/img/nav/docker-logo-loggedout.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="nav_search" class="navbar-index-search 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>
|
||||
<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/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="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>
|
||||
</ul>
|
||||
<div id="usernav" class="pull-right">
|
||||
@@ -18,9 +19,10 @@
|
||||
</div>
|
||||
<div id="topmostnav" class="topmostnav_loggedin navbar navbar-static-top">
|
||||
<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>
|
||||
<form id="search_box_header" class="navbar-index-search pull-right" action="https://registry.hub.docker.com/search">
|
||||
<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">
|
||||
<a href="/" title="Docker Docs Home"><div class="brand logo"><img src="/img/nav/docker-logo-loggedin.png"> </div></a>
|
||||
<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 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">
|
||||
<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 ()
|
||||
{
|
||||
|
||||
// Tipue Search activation
|
||||
$('#tipue_search_input').tipuesearch({
|
||||
'mode': 'json',
|
||||
'contentLocation': '/search_content.json'
|
||||
});
|
||||
|
||||
prettyPrint();
|
||||
|
||||
// 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 ()
|
||||
@@ -92,4 +98,4 @@ function getCookie(cname) {
|
||||
if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
-4
@@ -18,9 +18,5 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</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>
|
||||
</nav>
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
)
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
@@ -150,3 +152,85 @@ func TestCatchallEmptyName(t *testing.T) {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
// for bind-mounting dockerinit into the container. The mountpoint is simply an
|
||||
// empty file at /.dockerinit
|
||||
|
||||
+38
-52
@@ -3,11 +3,9 @@ package graph
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/engine"
|
||||
@@ -17,35 +15,6 @@ import (
|
||||
"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) {
|
||||
manifest := ®istry.ManifestData{
|
||||
Name: remoteName,
|
||||
@@ -71,14 +40,13 @@ func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if layer.Config == nil {
|
||||
return nil, errors.New("Missing layer configuration")
|
||||
}
|
||||
manifest.Architecture = layer.Architecture
|
||||
manifest.FSLayers = make([]*registry.FSLayer, 0, 4)
|
||||
manifest.History = make([]*registry.ManifestHistory, 0, 4)
|
||||
var metadata runconfig.Config
|
||||
metadata = *layer.Config
|
||||
if layer.Config != nil {
|
||||
metadata = *layer.Config
|
||||
}
|
||||
|
||||
for ; layer != nil; layer, err = layer.GetParent() {
|
||||
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 {
|
||||
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)
|
||||
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"))
|
||||
jsonData, err := layer.RawJson()
|
||||
if err != nil {
|
||||
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)})
|
||||
}
|
||||
|
||||
@@ -128,7 +109,12 @@ func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error
|
||||
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")
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
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")
|
||||
if err = j.Run(); err != nil {
|
||||
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 {
|
||||
downloadImage := func(img *registry.ImgData) {
|
||||
if askedTag != "" && img.Tag != askedTag {
|
||||
log.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.ID)
|
||||
if parallel {
|
||||
errors <- nil
|
||||
}
|
||||
@@ -388,6 +387,9 @@ func (s *TagStore) pullV2Repository(eng *engine.Engine, r *registry.Session, out
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(tags) == 0 {
|
||||
return registry.ErrDoesNotExist
|
||||
}
|
||||
for _, t := range tags {
|
||||
if downloaded, err := s.pullV2Tag(eng, r, out, endpoint, repoInfo, t, sf, parallel, auth); err != nil {
|
||||
return err
|
||||
@@ -418,7 +420,7 @@ func (s *TagStore) pullV2Tag(eng *engine.Engine, r *registry.Session, out io.Wri
|
||||
return false, err
|
||||
}
|
||||
|
||||
manifest, verified, err := s.verifyManifest(eng, manifestBytes)
|
||||
manifest, verified, err := s.loadManifest(eng, manifestBytes)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error verifying manifest: %s", err)
|
||||
}
|
||||
|
||||
+119
-67
@@ -2,6 +2,7 @@ package graph
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -18,6 +19,8 @@ import (
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
var ErrV2RegistryUnavailable = errors.New("error v2 registry unavailable")
|
||||
|
||||
// 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) {
|
||||
var (
|
||||
@@ -65,6 +68,25 @@ func (s *TagStore) getImageList(localRepo map[string]string, requestedTag string
|
||||
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.
|
||||
func (s *TagStore) createImageIndex(images []string, tags map[string][]string) []*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
|
||||
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")
|
||||
defer prgRd.Close()
|
||||
|
||||
checksum, checksumPayload, err := r.PushImageLayerRegistry(imgData.ID, prgRd, ep, token, jsonRaw)
|
||||
checksum, checksumPayload, err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf, false, utils.TruncateID(imgData.ID), "Pushing"), ep, token, jsonRaw)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -254,7 +273,7 @@ func (s *TagStore) pushImage(r *registry.Session, out io.Writer, imgID, ep strin
|
||||
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 {
|
||||
j := eng.Job("trust_update_base")
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
if len(manifestBytes) == 0 {
|
||||
for _, tag := range tags {
|
||||
log.Debugf("Pushing %s:%s to v2 repository", repoInfo.LocalName, tag)
|
||||
mBytes, err := s.newManifest(repoInfo.LocalName, repoInfo.RemoteName, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -290,75 +322,99 @@ func (s *TagStore) pushV2Repository(r *registry.Session, eng *engine.Engine, out
|
||||
if err != nil {
|
||||
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))
|
||||
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)
|
||||
manifest, verified, err := s.loadManifest(eng, signedBody)
|
||||
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 != nil {
|
||||
return err
|
||||
if err := checkValidManifest(manifest); err != nil {
|
||||
return fmt.Errorf("invalid manifest: %s", err)
|
||||
}
|
||||
|
||||
arch, err := img.TarLayer()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not get tar layer: %s", err)
|
||||
if verified {
|
||||
log.Infof("Pushing verified image, key %s is registered for %q", s.trustKey.KeyID(), repoInfo.RemoteName)
|
||||
}
|
||||
|
||||
// Call mount blob
|
||||
exists, err := r.HeadV2ImageBlob(endpoint, repoInfo.RemoteName, sumParts[0], manifestSum, auth)
|
||||
if err != nil {
|
||||
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image push failed", nil))
|
||||
return err
|
||||
}
|
||||
for i := len(manifest.FSLayers) - 1; i >= 0; i-- {
|
||||
var (
|
||||
sumStr = manifest.FSLayers[i].BlobSum
|
||||
imgJSON = []byte(manifest.History[i].V1Compatibility)
|
||||
)
|
||||
|
||||
if !exists {
|
||||
prgRd := utils.ProgressReader(arch, int(img.Size), out, sf, false, utils.TruncateID(img.ID), "Pushing")
|
||||
defer prgRd.Close()
|
||||
sumParts := strings.SplitN(sumStr, ":", 2)
|
||||
if len(sumParts) < 2 {
|
||||
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 {
|
||||
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))
|
||||
} else {
|
||||
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Image already exists", nil))
|
||||
|
||||
if !exists {
|
||||
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
|
||||
return r.PutV2ImageManifest(endpoint, repoInfo.RemoteName, tag, bytes.NewReader([]byte(manifestBytes)), auth)
|
||||
// PushV2Image pushes the image content to the v2 registry, first buffering the contents to disk
|
||||
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.
|
||||
@@ -380,7 +436,6 @@ func (s *TagStore) CmdPush(job *engine.Job) engine.Status {
|
||||
}
|
||||
|
||||
tag := job.Getenv("tag")
|
||||
manifestBytes := job.Getenv("manifest")
|
||||
job.GetenvJson("authConfig", authConfig)
|
||||
job.GetenvJson("metaHeaders", &metaHeaders)
|
||||
|
||||
@@ -400,18 +455,15 @@ func (s *TagStore) CmdPush(job *engine.Job) engine.Status {
|
||||
return job.Error(err2)
|
||||
}
|
||||
|
||||
if len(tag) == 0 {
|
||||
tag = DEFAULTTAG
|
||||
}
|
||||
|
||||
if repoInfo.Index.Official || endpoint.Version == registry.APIVersion2 {
|
||||
err := s.pushV2Repository(r, job.Eng, job.Stdout, repoInfo, manifestBytes, tag, sf)
|
||||
if endpoint.Version == registry.APIVersion2 {
|
||||
err := s.pushV2Repository(r, job.Eng, job.Stdout, repoInfo, tag, sf)
|
||||
if err == nil {
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
||||
// error out, no fallback to V1
|
||||
return job.Error(err)
|
||||
if err != ErrV2RegistryUnavailable {
|
||||
return job.Errorf("Error pushing to registry: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -25,7 +25,6 @@ func (s *TagStore) Install(eng *engine.Engine) error {
|
||||
"import": s.CmdImport,
|
||||
"pull": s.CmdPull,
|
||||
"push": s.CmdPush,
|
||||
"image_manifest": s.CmdManifest,
|
||||
} {
|
||||
if err := eng.Register(name, handler); err != nil {
|
||||
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.SetInt64("Size", image.Size)
|
||||
out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
|
||||
out.Set("Checksum", image.Checksum)
|
||||
if _, err = out.WriteTo(job.Stdout); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
+22
-38
@@ -9,9 +9,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/tarsum"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
)
|
||||
@@ -33,7 +31,6 @@ type Image struct {
|
||||
Config *runconfig.Config `json:"config,omitempty"`
|
||||
Architecture string `json:"architecture,omitempty"`
|
||||
OS string `json:"os,omitempty"`
|
||||
Checksum string `json:"checksum"`
|
||||
Size int64
|
||||
|
||||
graph Graph
|
||||
@@ -81,45 +78,15 @@ func LoadImage(root string) (*Image, error) {
|
||||
|
||||
// StoreImage stores file system layer data for the given image to the
|
||||
// image's registered storage driver. Image metadata is stored in a file
|
||||
// at the specified root directory. This function also computes the TarSum
|
||||
// of `layerData` (currently using tarsum.dev).
|
||||
func StoreImage(img *Image, layerData archive.ArchiveReader, root string) error {
|
||||
// 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
|
||||
// at the specified root directory.
|
||||
func StoreImage(img *Image, layerData archive.ArchiveReader, root string) (err error) {
|
||||
// Store the layer. If layerData is not nil, unpack it into the new layer
|
||||
if layerData != nil {
|
||||
// If the image doesn't have a checksum, we should add it. The layer
|
||||
// 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 {
|
||||
if img.Size, err = img.graph.Driver().ApplyDiff(img.ID, img.Parent, layerData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
img.Size = size
|
||||
if err := img.SaveSize(root); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -146,6 +113,24 @@ func (img *Image) SaveSize(root string) error {
|
||||
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 {
|
||||
return path.Join(root, "json")
|
||||
}
|
||||
@@ -274,7 +259,6 @@ func (img *Image) CheckDepth() error {
|
||||
func NewImgJSON(src []byte) (*Image, error) {
|
||||
ret := &Image{}
|
||||
|
||||
log.Debugf("Json string: {%s}", src)
|
||||
// FIXME: Is there a cleaner way to "purify" the input json?
|
||||
if err := json.Unmarshal(src, ret); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -299,3 +299,70 @@ func TestGetContainerStats(t *testing.T) {
|
||||
}
|
||||
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)
|
||||
if err := waitRun(name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cpty.Write([]byte{16})
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
cpty.Write([]byte{17})
|
||||
|
||||
@@ -4592,3 +4592,67 @@ func TestBuildFromOfficialNames(t *testing.T) {
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
t.Fatalf("Error saving private key: %s", err)
|
||||
}
|
||||
@@ -403,3 +406,77 @@ func TestDaemonKeyMigration(t *testing.T) {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
t.Fatalf("Image %v was not pulled down", repos[0])
|
||||
}
|
||||
|
||||
@@ -2,10 +2,14 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"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
|
||||
@@ -41,7 +45,7 @@ func TestPushUntagged(t *testing.T) {
|
||||
|
||||
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
|
||||
|
||||
expected := "does not exist"
|
||||
expected := "No tags to push"
|
||||
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)
|
||||
@@ -51,6 +55,46 @@ func TestPushUntagged(t *testing.T) {
|
||||
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) {
|
||||
defer setupRegistry(t)()
|
||||
|
||||
@@ -80,3 +124,35 @@ func TestPushInterrupt(t *testing.T) {
|
||||
|
||||
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) {
|
||||
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()
|
||||
daemonUrl, err := url.Parse(daemon)
|
||||
if err != nil {
|
||||
@@ -296,17 +305,16 @@ func sockRequest(method, endpoint string, data interface{}) ([]byte, error) {
|
||||
client := httputil.NewClientConn(c, nil)
|
||||
defer client.Close()
|
||||
|
||||
jsonData := bytes.NewBuffer(nil)
|
||||
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")
|
||||
req, err := http.NewRequest(method, endpoint, data)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not perform request: %v", err)
|
||||
@@ -870,5 +878,18 @@ func setupRegistry(t *testing.T) func() {
|
||||
if err != nil {
|
||||
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() }
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -52,6 +53,18 @@ http:
|
||||
}, 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() {
|
||||
r.cmd.Process.Kill()
|
||||
os.RemoveAll(r.dir)
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/kr/pty"
|
||||
)
|
||||
|
||||
@@ -122,12 +121,7 @@ func TestRunDetach(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
key, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||
defer cleanup(globalEngine, t)
|
||||
|
||||
ch := make(chan struct{})
|
||||
@@ -177,12 +171,7 @@ func TestAttachDetach(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
key, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||
defer cleanup(globalEngine, t)
|
||||
|
||||
ch := make(chan struct{})
|
||||
@@ -219,7 +208,7 @@ func TestAttachDetach(t *testing.T) {
|
||||
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{})
|
||||
go func() {
|
||||
@@ -270,12 +259,7 @@ func TestAttachDetachTruncatedID(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
key, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||
defer cleanup(globalEngine, t)
|
||||
|
||||
// Discard the CmdRun output
|
||||
@@ -297,7 +281,7 @@ func TestAttachDetachTruncatedID(t *testing.T) {
|
||||
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{})
|
||||
go func() {
|
||||
@@ -347,12 +331,7 @@ func TestAttachDisconnect(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
key, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
||||
cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||
defer cleanup(globalEngine, t)
|
||||
|
||||
go func() {
|
||||
@@ -421,11 +400,8 @@ func TestAttachDisconnect(t *testing.T) {
|
||||
func TestRunAutoRemove(t *testing.T) {
|
||||
t.Skip("Fixme. Skipping test for now, race condition")
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
key, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil)
|
||||
|
||||
cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, "", testDaemonProto, testDaemonAddr, nil)
|
||||
defer cleanup(globalEngine, t)
|
||||
|
||||
c := make(chan struct{})
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/client"
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
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
|
||||
func TestHttpsInfo(t *testing.T) {
|
||||
key, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto,
|
||||
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, "", testDaemonProto,
|
||||
testDaemonHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t))
|
||||
|
||||
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
|
||||
// by using a rogue client certificate and checks that it fails with the expected error.
|
||||
func TestHttpsInfoRogueCert(t *testing.T) {
|
||||
key, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto,
|
||||
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, "", testDaemonProto,
|
||||
testDaemonHttpsAddr, getTlsConfig("client-rogue-cert.pem", "client-rogue-key.pem", t))
|
||||
|
||||
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
|
||||
// which provides a rogue server certificate and checks that it fails with the expected error
|
||||
func TestHttpsInfoRogueServerCert(t *testing.T) {
|
||||
key, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto,
|
||||
cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, "", testDaemonProto,
|
||||
testDaemonRogueHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t))
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("[tar autodetect] n: %v", bs)
|
||||
|
||||
compression := DetectCompression(bs)
|
||||
switch compression {
|
||||
|
||||
@@ -122,6 +122,7 @@ type tHashConfig struct {
|
||||
}
|
||||
|
||||
var (
|
||||
// NOTE: DO NOT include MD5 or SHA1, which are considered insecure.
|
||||
standardHashConfigs = map[string]tHashConfig{
|
||||
"sha256": {name: "sha256", hash: crypto.SHA256},
|
||||
"sha512": {name: "sha512", hash: crypto.SHA512},
|
||||
|
||||
@@ -22,6 +22,18 @@ const (
|
||||
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
|
||||
func GetVersions() []Version {
|
||||
v := []Version{}
|
||||
|
||||
@@ -11,5 +11,5 @@ cd "$(dirname "$(readlink -f "$BASH_SOURCE")")/.."
|
||||
# For how it is generated, see `project/generate-authors.sh`.
|
||||
EOH
|
||||
echo
|
||||
git log --format='%aN <%aE>' | sort -uf
|
||||
git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf
|
||||
} > AUTHORS
|
||||
|
||||
@@ -44,6 +44,7 @@ echo
|
||||
DEFAULT_BUNDLES=(
|
||||
validate-dco
|
||||
validate-gofmt
|
||||
validate-toml
|
||||
|
||||
binary
|
||||
|
||||
|
||||
+20
-5
@@ -2,11 +2,26 @@
|
||||
|
||||
DEST=$1
|
||||
|
||||
PKGVERSION="$VERSION"
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
PKGVERSION="$PKGVERSION-$(date +%Y%m%d%H%M%S)-$GITCOMMIT"
|
||||
PKGVERSION="${VERSION//-/'~'}"
|
||||
# if we have a "-dev" suffix or have change in Git, let's make this package version more complex so it works better
|
||||
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
|
||||
|
||||
# $ 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_URL="http://www.docker.com/"
|
||||
PACKAGE_MAINTAINER="support@docker.com"
|
||||
@@ -124,7 +139,7 @@ EOF
|
||||
|
||||
# create lxc-docker-VERSION package
|
||||
fpm -s dir -C $DIR \
|
||||
--name lxc-docker-$VERSION --version $PKGVERSION \
|
||||
--name lxc-docker-$VERSION --version "$PKGVERSION" \
|
||||
--after-install $DEST/postinst \
|
||||
--before-remove $DEST/prerm \
|
||||
--after-remove $DEST/postrm \
|
||||
@@ -157,7 +172,7 @@ EOF
|
||||
|
||||
# create empty lxc-docker wrapper package
|
||||
fpm -s empty \
|
||||
--name lxc-docker --version $PKGVERSION \
|
||||
--name lxc-docker --version "$PKGVERSION" \
|
||||
--architecture "$PACKAGE_ARCHITECTURE" \
|
||||
--depends lxc-docker-$VERSION \
|
||||
--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.
|
||||
var supportsV2 bool
|
||||
|
||||
for _, versionName := range resp.Header[http.CanonicalHeaderKey("Docker-Distribution-API-Version")] {
|
||||
if versionName == "registry/2.0" {
|
||||
supportsV2 = true
|
||||
break
|
||||
HeaderLoop:
|
||||
for _, supportedVersions := range resp.Header[http.CanonicalHeaderKey("Docker-Distribution-API-Version")] {
|
||||
for _, versionName := range strings.Fields(supportedVersions) {
|
||||
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) {
|
||||
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)
|
||||
})
|
||||
|
||||
|
||||
+23
-10
@@ -124,15 +124,18 @@ func (r *Session) HeadV2ImageBlob(ep *Endpoint, imageName, sumType, sum string,
|
||||
return false, err
|
||||
}
|
||||
res.Body.Close() // close early, since we're not needing a body on this call .. yet?
|
||||
switch res.StatusCode {
|
||||
case 200:
|
||||
switch {
|
||||
case res.StatusCode >= 200 && res.StatusCode < 400:
|
||||
// return something indicating no push needed
|
||||
return true, nil
|
||||
case 404:
|
||||
case res.StatusCode == 401:
|
||||
return false, errLoginRequired
|
||||
case res.StatusCode == 404:
|
||||
// return something indicating blob push needed
|
||||
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 {
|
||||
@@ -189,7 +192,7 @@ func (r *Session) GetV2ImageBlobReader(ep *Endpoint, imageName, sumType, sum str
|
||||
if res.StatusCode == 401 {
|
||||
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")
|
||||
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 {
|
||||
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
|
||||
@@ -272,13 +280,18 @@ func (r *Session) PutV2ImageManifest(ep *Endpoint, imageName, tagName string, ma
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, _ := ioutil.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
defer res.Body.Close()
|
||||
|
||||
// All 2xx and 3xx responses can be accepted for a put.
|
||||
if res.StatusCode >= 400 {
|
||||
if res.StatusCode == 401 {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -51,10 +51,12 @@ func getToken(username, password string, params map[string]string, registryEndpo
|
||||
reqParams.Add("scope", scopeField)
|
||||
}
|
||||
|
||||
reqParams.Add("account", username)
|
||||
if username != "" {
|
||||
reqParams.Add("account", username)
|
||||
req.SetBasicAuth(username, password)
|
||||
}
|
||||
|
||||
req.URL.RawQuery = reqParams.Encode()
|
||||
req.SetBasicAuth(username, password)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
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
|
||||
// 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 = *ub.router.GetRoute(name) // clone the route
|
||||
root := new(url.URL)
|
||||
|
||||
return route.
|
||||
Schemes(ub.root.Scheme).
|
||||
Host(ub.root.Host)
|
||||
*route = *ub.router.GetRoute(name) // clone the route
|
||||
*root = *ub.root
|
||||
|
||||
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.
|
||||
|
||||
+53
-40
@@ -6,62 +6,58 @@ import (
|
||||
)
|
||||
|
||||
type urlBuilderTestCase struct {
|
||||
description string
|
||||
expected string
|
||||
build func() (string, error)
|
||||
description string
|
||||
expectedPath string
|
||||
build func() (string, error)
|
||||
}
|
||||
|
||||
// TestURLBuilder tests the various url building functions, ensuring they are
|
||||
// returning the expected values.
|
||||
func TestURLBuilder(t *testing.T) {
|
||||
var (
|
||||
urlBuilder *URLBuilder
|
||||
err error
|
||||
)
|
||||
|
||||
root := "http://localhost:5000/"
|
||||
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)
|
||||
}{
|
||||
testCases := []urlBuilderTestCase{
|
||||
{
|
||||
description: "test base url",
|
||||
expected: "http://localhost:5000/v2/",
|
||||
build: urlBuilder.BuildBaseURL,
|
||||
description: "test base url",
|
||||
expectedPath: "/v2/",
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildBaseURL()
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "test tags url",
|
||||
expected: "http://localhost:5000/v2/foo/bar/tags/list",
|
||||
description: "test tags url",
|
||||
expectedPath: "/v2/foo/bar/tags/list",
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildTagsURL("foo/bar")
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "test manifest url",
|
||||
expected: "http://localhost:5000/v2/foo/bar/manifests/tag",
|
||||
description: "test manifest url",
|
||||
expectedPath: "/v2/foo/bar/manifests/tag",
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildManifestURL("foo/bar", "tag")
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "build blob url",
|
||||
expected: "http://localhost:5000/v2/foo/bar/blobs/tarsum.v1+sha256:abcdef0123456789",
|
||||
description: "build blob url",
|
||||
expectedPath: "/v2/foo/bar/blobs/tarsum.v1+sha256:abcdef0123456789",
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildBlobURL("foo/bar", "tarsum.v1+sha256:abcdef0123456789")
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "build blob upload url",
|
||||
expected: "http://localhost:5000/v2/foo/bar/blobs/uploads/",
|
||||
description: "build blob upload url",
|
||||
expectedPath: "/v2/foo/bar/blobs/uploads/",
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildBlobUploadURL("foo/bar")
|
||||
},
|
||||
},
|
||||
{
|
||||
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",
|
||||
description: "build blob upload url with digest and size",
|
||||
expectedPath: "/v2/foo/bar/blobs/uploads/?digest=tarsum.v1%2Bsha256%3Aabcdef0123456789&size=10000",
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildBlobUploadURL("foo/bar", url.Values{
|
||||
"size": []string{"10000"},
|
||||
@@ -70,15 +66,15 @@ func TestURLBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "build blob upload chunk url",
|
||||
expected: "http://localhost:5000/v2/foo/bar/blobs/uploads/uuid-part",
|
||||
description: "build blob upload chunk url",
|
||||
expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part",
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildBlobUploadChunkURL("foo/bar", "uuid-part")
|
||||
},
|
||||
},
|
||||
{
|
||||
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",
|
||||
description: "build blob upload chunk url with digest and size",
|
||||
expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part?digest=tarsum.v1%2Bsha256%3Aabcdef0123456789&size=10000",
|
||||
build: func() (string, error) {
|
||||
return urlBuilder.BuildBlobUploadChunkURL("foo/bar", "uuid-part", url.Values{
|
||||
"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, err = filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Ignore the error here since the path may not exist
|
||||
// Really just want to make sure the path we are using is real(or non-existant)
|
||||
if cleanPath, err := filepath.EvalSymlinks(path); err == nil {
|
||||
path = cleanPath
|
||||
}
|
||||
|
||||
v := &Volume{
|
||||
@@ -169,13 +170,11 @@ func (r *Repository) Delete(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if volume.IsBindMount {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := r.driver.Remove(volume.ID); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
if !volume.IsBindMount {
|
||||
if err := r.driver.Remove(volume.ID); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user