mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-20 04:36:37 +00:00
Compare commits
1 Commits
v1.1.1
..
v0.7.0-rc7
| Author | SHA1 | Date | |
|---|---|---|---|
| ea7811c83d |
@@ -1,2 +0,0 @@
|
||||
bundles
|
||||
.gopath
|
||||
@@ -1,6 +1,3 @@
|
||||
# Docker project generated files to ignore
|
||||
# if you want to ignore files created by your editor/tools,
|
||||
# please consider a global .gitignore https://help.github.com/articles/ignoring-files
|
||||
.vagrant*
|
||||
bin
|
||||
docker/docker
|
||||
@@ -22,8 +19,3 @@ bundles/
|
||||
.git/
|
||||
vendor/pkg/
|
||||
pyenv
|
||||
Vagrantfile
|
||||
docs/AWS_S3_BUCKET
|
||||
docs/GIT_BRANCH
|
||||
docs/VERSION
|
||||
docs/GITCOMMIT
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
# Generate AUTHORS: git log --format='%aN <%aE>' | sort -uf
|
||||
<charles.hooper@dotcloud.com> <chooper@plumata.com>
|
||||
# Generate AUTHORS: git log --format='%aN <%aE>' | sort -uf | grep -v vagrant-ubuntu-12
|
||||
<charles.hooper@dotcloud.com> <chooper@plumata.com>
|
||||
<daniel.mizyrycki@dotcloud.com> <daniel@dotcloud.com>
|
||||
<daniel.mizyrycki@dotcloud.com> <mzdaniel@glidelink.net>
|
||||
Guillaume J. Charmes <guillaume.charmes@docker.com> <charmes.guillaume@gmail.com>
|
||||
<guillaume.charmes@docker.com> <guillaume@dotcloud.com>
|
||||
<guillaume.charmes@docker.com> <guillaume@docker.com>
|
||||
<guillaume.charmes@docker.com> <guillaume.charmes@dotcloud.com>
|
||||
<guillaume.charmes@docker.com> <guillaume@charmes.net>
|
||||
Guillaume J. Charmes <guillaume.charmes@dotcloud.com> <charmes.guillaume@gmail.com>
|
||||
<guillaume.charmes@dotcloud.com> <guillaume@dotcloud.com>
|
||||
<kencochrane@gmail.com> <KenCochrane@gmail.com>
|
||||
Thatcher Peskens <thatcher@docker.com>
|
||||
Thatcher Peskens <thatcher@docker.com> <thatcher@dotcloud.com>
|
||||
Thatcher Peskens <thatcher@docker.com> dhrp <thatcher@gmx.net>
|
||||
<sridharr@activestate.com> <github@srid.name>
|
||||
Thatcher Peskens <thatcher@dotcloud.com> dhrp <thatcher@dotcloud.com>
|
||||
Thatcher Peskens <thatcher@dotcloud.com> dhrp <thatcher@gmx.net>
|
||||
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com> jpetazzo <jerome.petazzoni@dotcloud.com>
|
||||
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com> <jp@enix.org>
|
||||
Joffrey F <joffrey@docker.com>
|
||||
Joffrey F <joffrey@docker.com> <joffrey@dotcloud.com>
|
||||
Joffrey F <joffrey@docker.com> <f.joffrey@gmail.com>
|
||||
Joffrey F <joffrey@dotcloud.com>
|
||||
<joffrey@dotcloud.com> <f.joffrey@gmail.com>
|
||||
Tim Terhorst <mynamewastaken+git@gmail.com>
|
||||
Andy Smith <github@anarkystic.com>
|
||||
<kalessin@kalessin.fr> <louis@dotcloud.com>
|
||||
<victor.vieux@docker.com> <victor.vieux@dotcloud.com>
|
||||
<victor.vieux@docker.com> <victor@dotcloud.com>
|
||||
<victor.vieux@docker.com> <dev@vvieux.com>
|
||||
<victor.vieux@docker.com> <victor@docker.com>
|
||||
<victor.vieux@docker.com> <vieux@docker.com>
|
||||
<victor.vieux@dotcloud.com> <victor@dotcloud.com>
|
||||
<victor.vieux@dotcloud.com> <dev@vvieux.com>
|
||||
<dominik@honnef.co> <dominikh@fork-bomb.org>
|
||||
Thatcher Peskens <thatcher@dotcloud.com>
|
||||
<ehanchrow@ine.com> <eric.hanchrow@gmail.com>
|
||||
Walter Stanish <walter@pratyeka.org>
|
||||
<daniel@gasienica.ch> <dgasienica@zynga.com>
|
||||
@@ -44,37 +38,3 @@ Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
|
||||
Matthew Mueller <mattmuelle@gmail.com>
|
||||
<mosoni@ebay.com> <mohitsoni1989@gmail.com>
|
||||
Shih-Yuan Lee <fourdollars@gmail.com>
|
||||
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com> root <root@vagrant-ubuntu-12.10.vagrantup.com>
|
||||
Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
|
||||
<proppy@google.com> <proppy@aminche.com>
|
||||
<michael@crosbymichael.com> <crosby.michael@gmail.com>
|
||||
<github@metaliveblog.com> <github@developersupport.net>
|
||||
<brandon@ifup.org> <brandon@ifup.co>
|
||||
<dano@spotify.com> <daniel.norberg@gmail.com>
|
||||
<danny@codeaholics.org> <Danny.Yates@mailonline.co.uk>
|
||||
<gurjeet@singh.im> <singh.gurjeet@gmail.com>
|
||||
<shawn@churchofgit.com> <shawnlandden@gmail.com>
|
||||
<sjoerd-github@linuxonly.nl> <sjoerd@byte.nl>
|
||||
<solomon@docker.com> <solomon.hykes@dotcloud.com>
|
||||
<solomon@docker.com> <solomon@dotcloud.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@fosiki.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@docker.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <¨SvenDowideit@home.org.au¨>
|
||||
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>
|
||||
O.S. Tezer <ostezer@gmail.com>
|
||||
<ostezer@gmail.com> <ostezer@users.noreply.github.com>
|
||||
Roberto G. Hashioka <roberto.hashioka@docker.com> <roberto_hashioka@hotmail.com>
|
||||
<justin.p.simonelis@gmail.com> <justin.simonelis@PTS-JSIMON2.toronto.exclamation.com>
|
||||
<taim@bosboot.org> <maztaim@users.noreply.github.com>
|
||||
<viktor.vojnovski@amadeus.com> <vojnovski@gmail.com>
|
||||
<vbatts@redhat.com> <vbatts@hashbangbash.com>
|
||||
<altsysrq@gmail.com> <iamironbob@gmail.com>
|
||||
Sridhar Ratnakumar <sridharr@activestate.com>
|
||||
Sridhar Ratnakumar <sridharr@activestate.com> <github@srid.name>
|
||||
Liang-Chi Hsieh <viirya@gmail.com>
|
||||
Aleksa Sarai <cyphar@cyphar.com>
|
||||
Will Weaver <monkey@buildingbananas.com>
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
# Note: right now we don't use go-specific features of travis.
|
||||
# Later we might automate "go test" etc. (or do it inside a docker container...?)
|
||||
|
||||
language: go
|
||||
|
||||
go: 1.2
|
||||
|
||||
# Disable the normal go build.
|
||||
install: true
|
||||
|
||||
before_script:
|
||||
- env | sort
|
||||
|
||||
script:
|
||||
- hack/make.sh validate-dco
|
||||
- hack/make.sh validate-gofmt
|
||||
|
||||
# vim:set sw=2 ts=2:
|
||||
@@ -1,454 +1,197 @@
|
||||
# This file lists all individuals having contributed content to the repository.
|
||||
# For how it is generated, see `.mailmap`.
|
||||
|
||||
Aanand Prasad <aanand.prasad@gmail.com>
|
||||
Aaron Feng <aaron.feng@gmail.com>
|
||||
Aaron Huslage <huslage@gmail.com>
|
||||
Abel Muiño <amuino@gmail.com>
|
||||
Adam Miller <admiller@redhat.com>
|
||||
Adam Singer <financeCoding@gmail.com>
|
||||
Aditya <aditya@netroy.in>
|
||||
Adrian Mouat <adrian.mouat@gmail.com>
|
||||
alambike <alambike@gmail.com>
|
||||
Aleksa Sarai <cyphar@cyphar.com>
|
||||
Alexander Larsson <alexl@redhat.com>
|
||||
Alexandr Morozov <lk4d4math@gmail.com>
|
||||
Alexey Kotlyarov <alexey@infoxchange.net.au>
|
||||
Alexey Shamrin <shamrin@gmail.com>
|
||||
Alex Gaynor <alex.gaynor@gmail.com>
|
||||
Alexis THOMAS <fr.alexisthomas@gmail.com>
|
||||
almoehi <almoehi@users.noreply.github.com>
|
||||
# If you're submitting a patch, please add your name here in alphabetical order as part of the patch.
|
||||
#
|
||||
# For a list of active project maintainers, see the MAINTAINERS file.
|
||||
#
|
||||
Al Tobey <al@ooyala.com>
|
||||
amangoel <amangoel@gmail.com>
|
||||
Alex Gaynor <alex.gaynor@gmail.com>
|
||||
Alexander Larsson <alexl@redhat.com>
|
||||
Alexey Shamrin <shamrin@gmail.com>
|
||||
Andrea Luzzardi <aluzzardi@gmail.com>
|
||||
Andreas Savvides <andreas@editd.com>
|
||||
Andreas Tiefenthaler <at@an-ti.eu>
|
||||
Andrea Turli <andrea.turli@gmail.com>
|
||||
Andrew Duckworth <grillopress@gmail.com>
|
||||
Andrew Macgregor <andrew.macgregor@agworld.com.au>
|
||||
Andrew Munsell <andrew@wizardapps.net>
|
||||
Andrews Medina <andrewsmedina@gmail.com>
|
||||
Andrew Williams <williams.andrew@gmail.com>
|
||||
Andy Chambers <anchambers@paypal.com>
|
||||
andy diller <dillera@gmail.com>
|
||||
Andy Goldstein <agoldste@redhat.com>
|
||||
Andy Kipp <andy@rstudio.com>
|
||||
Andy Rothfusz <github@metaliveblog.com>
|
||||
Andy Smith <github@anarkystic.com>
|
||||
Anthony Bishopric <git@anthonybishopric.com>
|
||||
Anton Nikitin <anton.k.nikitin@gmail.com>
|
||||
Antony Messerli <amesserl@rackspace.com>
|
||||
apocas <petermdias@gmail.com>
|
||||
Arnaud Porterie <icecrime@gmail.com>
|
||||
Asbjørn Enge <asbjorn@hanafjedle.net>
|
||||
Barnaby Gray <barnaby@pickle.me.uk>
|
||||
Barry Allard <barry.allard@gmail.com>
|
||||
Bartłomiej Piotrowski <b@bpiotrowski.pl>
|
||||
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>
|
||||
Bernerd Schaefer <bj.schaefer@gmail.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
Bhiraj Butala <abhiraj.butala@gmail.com>
|
||||
bin liu <liubin0329@users.noreply.github.com>
|
||||
Bouke Haarsma <bouke@webatoom.nl>
|
||||
Brandon Liu <bdon@bdon.org>
|
||||
Brandon Philips <brandon@ifup.org>
|
||||
Brian Dorsey <brian@dorseys.org>
|
||||
Brian Flad <bflad417@gmail.com>
|
||||
Brian Goff <cpuguy83@gmail.com>
|
||||
Brandon Philips <brandon@ifup.co>
|
||||
Brian McCallister <brianm@skife.org>
|
||||
Brian Olsen <brian@maven-group.org>
|
||||
Brian Shumate <brian@couchbase.com>
|
||||
Briehan Lombaard <briehan.lombaard@gmail.com>
|
||||
Bruno Bigras <bigras.bruno@gmail.com>
|
||||
Bryan Matsuo <bryan.matsuo@gmail.com>
|
||||
Bryan Murphy <bmurphy1976@gmail.com>
|
||||
Caleb Spare <cespare@gmail.com>
|
||||
Calen Pennington <cale@edx.org>
|
||||
Cameron Boehmer <cameron.boehmer@gmail.com>
|
||||
Carl X. Su <bcbcarl@gmail.com>
|
||||
Charles Hooper <charles.hooper@dotcloud.com>
|
||||
Charles Lindsay <chaz@chazomatic.us>
|
||||
Charles Merriam <charles.merriam@gmail.com>
|
||||
Charlie Lewis <charliel@lab41.org>
|
||||
Chia-liang Kao <clkao@clkao.org>
|
||||
Chris St. Pierre <chris.a.st.pierre@gmail.com>
|
||||
Christopher Currie <codemonkey+github@gmail.com>
|
||||
Christopher Rigor <crigor@gmail.com>
|
||||
Christophe Troestler <christophe.Troestler@umons.ac.be>
|
||||
Clayton Coleman <ccoleman@redhat.com>
|
||||
Colin Dunklau <colin.dunklau@gmail.com>
|
||||
Colin Rice <colin@daedrum.net>
|
||||
Cory Forsyth <cory.forsyth@gmail.com>
|
||||
cressie176 <github@stephen-cresswell.net>
|
||||
Dafydd Crosby <dtcrsby@gmail.com>
|
||||
Dan Buch <d.buch@modcloth.com>
|
||||
Dan Hirsch <thequux@upstandinghackers.com>
|
||||
Daniel Exner <dex@dragonslave.de>
|
||||
Daniel Garcia <daniel@danielgarcia.info>
|
||||
Daniel Gasienica <daniel@gasienica.ch>
|
||||
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com>
|
||||
Daniel Norberg <dano@spotify.com>
|
||||
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>
|
||||
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 Gageot <david@gageot.net>
|
||||
David Mcanulty <github@hellspark.com>
|
||||
David Röthlisberger <david@rothlis.net>
|
||||
David Sissitka <me@dsissitka.com>
|
||||
Deni Bertovic <deni@kset.org>
|
||||
Dinesh Subhraveti <dineshs@altiscale.com>
|
||||
Djibril Koné <kone.djibril@gmail.com>
|
||||
dkumor <daniel@dkumor.com>
|
||||
Dmitry Demeshchuk <demeshchuk@gmail.com>
|
||||
Dolph Mathews <dolph.mathews@gmail.com>
|
||||
Dominik Honnef <dominik@honnef.co>
|
||||
Don Spaulding <donspauldingii@gmail.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>
|
||||
Eivind Uggedal <eivind@uggedal.com>
|
||||
Dražen Lučanin <kermit666@gmail.com>
|
||||
Elias Probst <mail@eliasprobst.eu>
|
||||
Emil Hernvall <emil@quench.at>
|
||||
Emily Rose <emily@contactvibe.com>
|
||||
Eric Hanchrow <ehanchrow@ine.com>
|
||||
Eric Lee <thenorthsecedes@gmail.com>
|
||||
Eric Myhre <hash@exultant.us>
|
||||
Erik Hollensbe <erik+github@hollensbe.org>
|
||||
Erno Hopearuoho <erno.hopearuoho@gmail.com>
|
||||
eugenkrizo <eugen.krizo@gmail.com>
|
||||
Evan Hazlett <ejhazlett@gmail.com>
|
||||
Evan Krall <krall@yelp.com>
|
||||
Evan Phoenix <evan@fallingsnow.net>
|
||||
Evan Wies <evan@neomantra.net>
|
||||
Eystein Måløy Stenberg <eystein.maloy.stenberg@cfengine.com>
|
||||
ezbercih <cem.ezberci@gmail.com>
|
||||
Fabio Falci <fabiofalci@gmail.com>
|
||||
Fabio Rehm <fgrehm@gmail.com>
|
||||
Fabrizio Regini <freegenie@gmail.com>
|
||||
Faiz Khan <faizkhan00@gmail.com>
|
||||
Fareed Dudhia <fareeddudhia@googlemail.com>
|
||||
Felix Rabe <felix@rabe.io>
|
||||
Fernando <fermayo@gmail.com>
|
||||
Flavio Castelli <fcastelli@suse.com>
|
||||
Francisco Souza <f@souza.cc>
|
||||
Frank Macreery <frank@macreery.com>
|
||||
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
|
||||
Frederik Loeffert <frederik@zitrusmedia.de>
|
||||
Freek Kalter <freek@kalteronline.org>
|
||||
Gabe Rosenhouse <gabe@missionst.com>
|
||||
Gabriel Monroy <gabriel@opdemand.com>
|
||||
Galen Sampson <galen.sampson@gmail.com>
|
||||
Gareth Rushgrove <gareth@morethanseven.net>
|
||||
Geoffrey Bachelet <grosfrais@gmail.com>
|
||||
Gereon Frey <gereon.frey@dynport.de>
|
||||
German DZ <germ@ndz.com.ar>
|
||||
Gert van Valkenhoef <g.h.m.van.valkenhoef@rug.nl>
|
||||
Goffert van Gool <goffert@phusion.nl>
|
||||
Graydon Hoare <graydon@pobox.com>
|
||||
Greg Thornton <xdissent@me.com>
|
||||
grunny <mwgrunny@gmail.com>
|
||||
Guillaume J. Charmes <guillaume.charmes@docker.com>
|
||||
Guillaume J. Charmes <guillaume.charmes@dotcloud.com>
|
||||
Gurjeet Singh <gurjeet@singh.im>
|
||||
Guruprasad <lgp171188@gmail.com>
|
||||
Harley Laue <losinggeneration@gmail.com>
|
||||
Hector Castro <hectcastro@gmail.com>
|
||||
Hobofan <goisser94@gmail.com>
|
||||
Hunter Blanks <hunter@twilio.com>
|
||||
Ian Truslove <ian.truslove@gmail.com>
|
||||
ILYA Khlopotov <ilya.khlopotov@gmail.com>
|
||||
inglesp <peter.inglesby@gmail.com>
|
||||
Isaac Dupree <antispam@idupree.com>
|
||||
Isabel Jimenez <contact.isabeljimenez@gmail.com>
|
||||
Isao Jonas <isao.jonas@gmail.com>
|
||||
Jack Danger Canty <jackdanger@squareup.com>
|
||||
jakedt <jake@devtable.com>
|
||||
Jake Moshenko <jake@devtable.com>
|
||||
James Allen <jamesallen0108@gmail.com>
|
||||
James Carr <james.r.carr@gmail.com>
|
||||
James DeFelice <james.defelice@ishisystems.com>
|
||||
James Harrison Fisher <jameshfisher@gmail.com>
|
||||
James Mills <prologic@shortcircuit.net.au>
|
||||
James Turnbull <james@lovedthanlost.net>
|
||||
jaseg <jaseg@jaseg.net>
|
||||
Jason McVetta <jason.mcvetta@gmail.com>
|
||||
Jason Plum <jplum@devonit.com>
|
||||
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
|
||||
Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
|
||||
Jeff Lindsay <progrium@gmail.com>
|
||||
Jeremy Grosser <jeremy@synack.me>
|
||||
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
|
||||
Jesse Dubay <jesse@thefortytwo.net>
|
||||
Jilles Oldenbeuving <ojilles@gmail.com>
|
||||
Jim Alateras <jima@comware.com.au>
|
||||
Jimmy Cuadra <jimmy@jimmycuadra.com>
|
||||
Joe Beda <joe.github@bedafamily.com>
|
||||
Joel Handwell <joelhandwell@gmail.com>
|
||||
Joe Shaw <joe@joeshaw.org>
|
||||
Joe Van Dyk <joe@tanga.com>
|
||||
Joffrey F <joffrey@docker.com>
|
||||
Joffrey F <joffrey@dotcloud.com>
|
||||
Johan Euphrosine <proppy@google.com>
|
||||
Johannes 'fish' Ziemke <github@freigeist.org>
|
||||
Johan Rydberg <johan.rydberg@gmail.com>
|
||||
John Costa <john.costa@gmail.com>
|
||||
John Feminella <jxf@jxf.me>
|
||||
John Gardiner Myers <jgmyers@proofpoint.com>
|
||||
John Warwick <jwarwick@gmail.com>
|
||||
Jonas Pfenniger <jonas@pfenniger.name>
|
||||
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>
|
||||
Jonas Pfenniger <jonas@pfenniger.name>
|
||||
Jonathan Mueller <j.mueller@apoveda.ch>
|
||||
Jonathan Rudenberg <jonathan@titanous.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 Poimboeuf <jpoimboe@redhat.com>
|
||||
JP <jpellerin@leapfrogonline.com>
|
||||
Julien Barbier <write0@gmail.com>
|
||||
Julien Dubois <julien.dubois@gmail.com>
|
||||
Justin Force <justin.force@gmail.com>
|
||||
Justin Plock <jplock@users.noreply.github.com>
|
||||
Justin Simonelis <justin.p.simonelis@gmail.com>
|
||||
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
|
||||
Karan Lyons <karan@karanlyons.com>
|
||||
Karl Grzeszczak <karlgrz@gmail.com>
|
||||
Kato Kazuyoshi <kato.kazuyoshi@gmail.com>
|
||||
Karl Grzeszczak <karl@karlgrz.com>
|
||||
Kawsar Saiyeed <kawsar.saiyeed@projiris.com>
|
||||
Keli Hu <dev@keli.hu>
|
||||
Ken Cochrane <kencochrane@gmail.com>
|
||||
Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com>
|
||||
Kevin Clark <kevin.clark@gmail.com>
|
||||
Kevin J. Lynagh <kevin@keminglabs.com>
|
||||
Kevin Menard <kevin@nirvdrum.com>
|
||||
Kevin Wallace <kevin@pentabarf.net>
|
||||
Keyvan Fatehi <keyvanfatehi@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>
|
||||
Konstantin Pelykh <kpelykh@zettaset.com>
|
||||
Kyle Conroy <kyle.j.conroy@gmail.com>
|
||||
lalyos <lalyos@yahoo.com>
|
||||
Lance Chen <cyen0312@gmail.com>
|
||||
Lars R. Damerow <lars@pixar.com>
|
||||
Laurie Voss <github@seldo.com>
|
||||
Lewis Peckover <lew+github@lew.io>
|
||||
Liang-Chi Hsieh <viirya@gmail.com>
|
||||
Lokesh Mandvekar <lsm5@redhat.com>
|
||||
Louis Opter <kalessin@kalessin.fr>
|
||||
lukaspustina <lukas.pustina@centerdevice.com>
|
||||
lukemarsden <luke@digital-crocus.com>
|
||||
Mahesh Tiyyagura <tmahesh@gmail.com>
|
||||
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>
|
||||
Marcus Farkas <toothlessgear@finitebox.com>
|
||||
Marcus Ramberg <marcus@nordaaker.com>
|
||||
Marek Goldmann <marek.goldmann@gmail.com>
|
||||
Mark Allen <mrallen1@yahoo.com>
|
||||
Mark McGranaghan <mmcgrana@gmail.com>
|
||||
Marko Mikulicic <mmikulicic@gmail.com>
|
||||
Markus Fix <lispmeister@gmail.com>
|
||||
Martijn van Oosterhout <kleptog@svana.org>
|
||||
Martin Redmond <martin@tinychat.com>
|
||||
Mason Malone <mason.malone@gmail.com>
|
||||
Mateusz Sulima <sulima.mateusz@gmail.com>
|
||||
Mathieu Le Marec - Pasquet <kiorky@cryptelium.net>
|
||||
Matt Apperson <me@mattapperson.com>
|
||||
Mathieu Le Marec - Pasquet <kiorky@cryptelium.net>
|
||||
Matt Bachmann <bachmann.matt@gmail.com>
|
||||
Matt Haggard <haggardii@gmail.com>
|
||||
Matthew Mueller <mattmuelle@gmail.com>
|
||||
Matthias Klumpp <matthias@tenstral.net>
|
||||
Matthias Kühnle <git.nivoc@neverbox.com>
|
||||
mattymo <raytrac3r@gmail.com>
|
||||
Maxime Petazzoni <max@signalfuse.com>
|
||||
Maxim Treskin <zerthurd@gmail.com>
|
||||
Max Shytikov <mshytikov@gmail.com>
|
||||
meejah <meejah@meejah.ca>
|
||||
Michael Brown <michael@netdirect.ca>
|
||||
Michael Crosby <michael@crosbymichael.com>
|
||||
Michael Crosby <crosby.michael@gmail.com>
|
||||
Michael Gorsuch <gorsuch@github.com>
|
||||
Michael Neale <michael.neale@gmail.com>
|
||||
Michael Stapelberg <michael+gh@stapelberg.de>
|
||||
Miguel Angel Fernández <elmendalerenda@gmail.com>
|
||||
Mike Gaffney <mike@uberu.com>
|
||||
Mike MacCana <mike.maccana@gmail.com>
|
||||
Mike Naberezny <mike@naberezny.com>
|
||||
Mikhail Sobolev <mss@mawhrin.net>
|
||||
Mohit Soni <mosoni@ebay.com>
|
||||
Morgante Pell <morgante.pell@morgante.net>
|
||||
Morten Siebuhr <sbhr@sbhr.dk>
|
||||
Nan Monnand Deng <monnand@gmail.com>
|
||||
Nate Jones <nate@endot.org>
|
||||
Nathan Kleyn <nathan@nathankleyn.com>
|
||||
Nelson Chen <crazysim@gmail.com>
|
||||
Niall O'Higgins <niallo@unworkable.org>
|
||||
Nick Payne <nick@kurai.co.uk>
|
||||
Nick Stenning <nick.stenning@digital.cabinet-office.gov.uk>
|
||||
Nick Stinemates <nick@stinemates.org>
|
||||
Nicolas Dudebout <nicolas.dudebout@gatech.edu>
|
||||
Nicolas Kaiser <nikai@nikai.net>
|
||||
noducks <onemannoducks@gmail.com>
|
||||
Nolan Darilek <nolan@thewordnerd.info>
|
||||
odk- <github@odkurzacz.org>
|
||||
Oguz Bilgic <fisyonet@gmail.com>
|
||||
Ole Reifschneider <mail@ole-reifschneider.de>
|
||||
O.S. Tezer <ostezer@gmail.com>
|
||||
pandrew <letters@paulnotcom.se>
|
||||
Pascal Borreli <pascal@borreli.com>
|
||||
pattichen <craftsbear@gmail.com>
|
||||
Paul Annesley <paul@annesley.cc>
|
||||
Paul Bowsher <pbowsher@globalpersonals.co.uk>
|
||||
Paul Hammond <paul@paulhammond.org>
|
||||
Paul Jimenez <pj@place.org>
|
||||
Paul Lietar <paul@lietar.net>
|
||||
Paul Morie <pmorie@gmail.com>
|
||||
Paul Nasrat <pnasrat@gmail.com>
|
||||
Paul <paul9869@gmail.com>
|
||||
Peter Braden <peterbraden@peterbraden.co.uk>
|
||||
Peter Waller <peter@scraperwiki.com>
|
||||
Phillip Alexander <git@phillipalexander.io>
|
||||
Phil Spitler <pspitler@gmail.com>
|
||||
Piergiuliano Bossi <pgbossi@gmail.com>
|
||||
Pierre-Alain RIVIERE <pariviere@ippon.fr>
|
||||
Piotr Bogdan <ppbogdan@gmail.com>
|
||||
pysqz <randomq@126.com>
|
||||
Quentin Brossard <qbrossard@gmail.com>
|
||||
Rafal Jeczalik <rjeczalik@gmail.com>
|
||||
Rajat Pandit <rp@rajatpandit.com>
|
||||
Ralph Bean <rbean@redhat.com>
|
||||
Ramkumar Ramachandra <artagnon@gmail.com>
|
||||
Ramon van Alteren <ramon@vanalteren.nl>
|
||||
Renato Riccieri Santos Zannon <renato.riccieri@gmail.com>
|
||||
rgstephens <greg@udon.org>
|
||||
Rhys Hiltner <rhys@twitch.tv>
|
||||
Richo Healey <richo@psych0tik.net>
|
||||
Rick Bradley <rick@users.noreply.github.com>
|
||||
Robert Obryk <robryk@gmail.com>
|
||||
Roberto G. Hashioka <roberto.hashioka@docker.com>
|
||||
robpc <rpcann@gmail.com>
|
||||
Rodrigo Vaz <rodrigo.vaz@gmail.com>
|
||||
Roel Van Nyen <roel.vannyen@gmail.com>
|
||||
Roger Peppe <rogpeppe@gmail.com>
|
||||
Rohit Jnagal <jnagal@google.com>
|
||||
Roland Moriz <rmoriz@users.noreply.github.com>
|
||||
Rovanion Luckey <rovanion.luckey@gmail.com>
|
||||
Ryan Aslett <github@mixologic.com>
|
||||
Roberto Hashioka <roberto_hashioka@hotmail.com>
|
||||
Ryan Fowler <rwfowler@gmail.com>
|
||||
Ryan O'Donnell <odonnellryanc@gmail.com>
|
||||
Ryan Seto <ryanseto@yak.net>
|
||||
Ryan Thomas <rthomas@atlassian.com>
|
||||
Sam Alba <sam.alba@gmail.com>
|
||||
Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
|
||||
Sam Rijs <srijs@airpost.net>
|
||||
Samuel Andaya <samuel@andaya.net>
|
||||
Scott Bessler <scottbessler@gmail.com>
|
||||
Scott Collier <emailscottcollier@gmail.com>
|
||||
Sean Cronin <seancron@gmail.com>
|
||||
Sean P. Kane <skane@newrelic.com>
|
||||
Sébastien Stormacq <sebsto@users.noreply.github.com>
|
||||
Shawn Landden <shawn@churchofgit.com>
|
||||
Shawn Siefkas <shawn.siefkas@meredith.com>
|
||||
Shih-Yuan Lee <fourdollars@gmail.com>
|
||||
Silas Sewell <silas@sewell.org>
|
||||
Simon Taranto <simon.taranto@gmail.com>
|
||||
Sindhu S <sindhus@live.in>
|
||||
Sjoerd Langkemper <sjoerd-github@linuxonly.nl>
|
||||
Solomon Hykes <solomon@docker.com>
|
||||
Solomon Hykes <solomon@dotcloud.com>
|
||||
Song Gao <song@gao.io>
|
||||
Soulou <leo@unbekandt.eu>
|
||||
Sridatta Thatipamala <sthatipamala@gmail.com>
|
||||
Sridhar Ratnakumar <sridharr@activestate.com>
|
||||
Steeve Morin <steeve.morin@gmail.com>
|
||||
Stefan Praszalowicz <stefan@greplin.com>
|
||||
Steven Burgess <steven.a.burgess@hotmail.com>
|
||||
sudosurootdev <sudosurootdev@gmail.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au>
|
||||
Sylvain Bellemare <sylvain.bellemare@ezeep.com>
|
||||
tang0th <tang0th@gmx.com>
|
||||
Tatsuki Sugiura <sugi@nemui.org>
|
||||
Tehmasp Chaudhri <tehmasp@gmail.com>
|
||||
Thatcher Peskens <thatcher@docker.com>
|
||||
Thatcher Peskens <thatcher@dotcloud.com>
|
||||
Thermionix <bond711@gmail.com>
|
||||
Thijs Terlouw <thijsterlouw@gmail.com>
|
||||
Thomas Bikeev <thomas.bikeev@mac.com>
|
||||
Thomas Frössman <thomasf@jossystem.se>
|
||||
Thomas Hansen <thomas.hansen@gmail.com>
|
||||
Thomas LEVEIL <thomasleveil@gmail.com>
|
||||
Thomas Schroeter <thomas@cliqz.com>
|
||||
Tianon Gravi <admwiggin@gmail.com>
|
||||
Tibor Vass <teabee89@gmail.com>
|
||||
Tim Bosse <taim@bosboot.org>
|
||||
Timothy Hobbs <timothyhobbs@seznam.cz>
|
||||
Tim Ruffles <oi@truffles.me.uk>
|
||||
Tim Terhorst <mynamewastaken+git@gmail.com>
|
||||
tjmehta <tj@init.me>
|
||||
Tobias Bieniek <Tobias.Bieniek@gmx.de>
|
||||
Tobias Schmidt <ts@soundcloud.com>
|
||||
Tobias Schwab <tobias.schwab@dynport.de>
|
||||
Todd Lunter <tlunter@gmail.com>
|
||||
Tom Fotherby <tom+github@peopleperhour.com>
|
||||
Tom Hulihan <hulihan.tom159@gmail.com>
|
||||
Tommaso Visconti <tommaso.visconti@gmail.com>
|
||||
Tony Daws <tony@daws.ca>
|
||||
Travis Cline <travis.cline@gmail.com>
|
||||
Tyler Brock <tyler.brock@gmail.com>
|
||||
Tzu-Jung Lee <roylee17@gmail.com>
|
||||
Ulysse Carion <ulyssecarion@gmail.com>
|
||||
unclejack <unclejacksons@gmail.com>
|
||||
vgeta <gopikannan.venugopalsamy@gmail.com>
|
||||
Victor Coisne <victor.coisne@dotcloud.com>
|
||||
Victor Lyuboslavsky <victor@victoreda.com>
|
||||
Victor Marmol <vmarmol@google.com>
|
||||
Victor Vieux <victor.vieux@docker.com>
|
||||
Viktor Vojnovski <viktor.vojnovski@amadeus.com>
|
||||
Vincent Batts <vbatts@redhat.com>
|
||||
Victor Vieux <victor.vieux@dotcloud.com>
|
||||
Vincent Bernat <bernat@luffy.cx>
|
||||
Vincent Mayers <vincent.mayers@inbloom.org>
|
||||
Vincent Woo <me@vincentwoo.com>
|
||||
Vinod Kulkarni <vinod.kulkarni@gmail.com>
|
||||
Vishnu Kannan <vishnuk@google.com>
|
||||
Vitor Monteiro <vmrmonteiro@gmail.com>
|
||||
Vivek Agarwal <me@vivek.im>
|
||||
Vladimir Bulyga <xx@ccxx.cc>
|
||||
Vladimir Kirillov <proger@wilab.org.ua>
|
||||
Vladimir Rutsky <altsysrq@gmail.com>
|
||||
Walter Leibbrandt <github@wrl.co.za>
|
||||
Walter Stanish <walter@pratyeka.org>
|
||||
WarheadsSE <max@warheads.net>
|
||||
Wes Morgan <cap10morgan@gmail.com>
|
||||
Will Dietz <w@wdtz.org>
|
||||
William Delanoue <william.delanoue@gmail.com>
|
||||
William Henry <whenry@redhat.com>
|
||||
Will Rouesnel <w.rouesnel@gmail.com>
|
||||
Will Weaver <monkey@buildingbananas.com>
|
||||
Xiuming Chen <cc@cxm.cc>
|
||||
Yang Bai <hamo.by@gmail.com>
|
||||
Yasunori Mahata <nori@mahata.net>
|
||||
Yurii Rashkovskii <yrashk@gmail.com>
|
||||
Zain Memon <zain@inzain.net>
|
||||
Zaiste! <oh@zaiste.net>
|
||||
Zilin Du <zilin.du@gmail.com>
|
||||
zimbatm <zimbatm@zimbatm.com>
|
||||
zqh <zqhxuyuan@gmail.com>
|
||||
|
||||
+10
-701
@@ -1,695 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 1.1.1 (2014-07-09)
|
||||
|
||||
#### Builder
|
||||
* Fix issue with ADD
|
||||
|
||||
## 1.1.0 (2014-07-03)
|
||||
|
||||
#### Notable features since 1.0.1
|
||||
+ Add `.dockerignore` support
|
||||
+ Pause containers during `docker commit`
|
||||
+ Add `--tail` to `docker logs`
|
||||
|
||||
#### Builder
|
||||
+ Allow a tar file as context for `docker build`
|
||||
* Fix issue with white-spaces and multi-lines in `Dockerfiles`
|
||||
|
||||
#### Runtime
|
||||
* Overall performance improvements
|
||||
* Allow `/` as source of `docker run -v`
|
||||
* Fix port allocation
|
||||
* Fix bug in `docker save`
|
||||
* Add links information to `docker inspect`
|
||||
|
||||
#### Client
|
||||
* Improve command line parsing for `docker commit`
|
||||
|
||||
#### Remote API
|
||||
* Improve status code for the `start` and `stop` endpoints
|
||||
|
||||
## 1.0.1 (2014-06-19)
|
||||
|
||||
#### Notable features since 1.0.0
|
||||
* Enhance security for the LXC driver
|
||||
|
||||
#### Builder
|
||||
* Fix `ONBUILD` instruction passed to grandchildren
|
||||
|
||||
#### Runtime
|
||||
* Fix events subscription
|
||||
* Fix /etc/hostname file with host networking
|
||||
* Allow `-h` and `--net=none`
|
||||
* Fix issue with hotplug devices in `--privileged`
|
||||
|
||||
#### Client
|
||||
* Fix artifacts with events
|
||||
* Fix a panic with empty flags
|
||||
* Fix `docker cp` on Mac OS X
|
||||
|
||||
#### Miscellaneous
|
||||
* Fix compilation on Mac OS X
|
||||
* Fix several races
|
||||
|
||||
## 1.0.0 (2014-06-09)
|
||||
|
||||
#### Notable features since 0.12.0
|
||||
* Production support
|
||||
|
||||
## 0.12.0 (2014-06-05)
|
||||
|
||||
#### Notable features since 0.11.0
|
||||
* 40+ various improvements to stability, performance and usability
|
||||
* New `COPY` Dockerfile instruction to allow copying a local file from the context into the container without ever extracting if the file is a tar file
|
||||
* Inherit file permissions from the host on `ADD`
|
||||
* New `pause` and `unpause` commands to allow pausing and unpausing of containers using cgroup freezer
|
||||
* The `images` command has a `-f`/`--filter` option to filter the list of images
|
||||
* Add `--force-rm` to clean up after a failed build
|
||||
* Standardize JSON keys in Remote API to CamelCase
|
||||
* Pull from a docker run now assumes `latest` tag if not specified
|
||||
* Enhance security on Linux capabilities and device nodes
|
||||
|
||||
## 0.11.1 (2014-05-07)
|
||||
|
||||
#### Registry
|
||||
- Fix push and pull to private registry
|
||||
|
||||
## 0.11.0 (2014-05-07)
|
||||
|
||||
#### Notable features since 0.10.0
|
||||
|
||||
* SELinux support for mount and process labels
|
||||
* Linked containers can be accessed by hostname
|
||||
* Use the net `--net` flag to allow advanced network configuration such as host networking so that containers can use the host's network interfaces
|
||||
* Add a ping endpoint to the Remote API to do healthchecks of your docker daemon
|
||||
* Logs can now be returned with an optional timestamp
|
||||
* Docker now works with registries that support SHA-512
|
||||
* Multiple registry endpoints are supported to allow registry mirrors
|
||||
|
||||
## 0.10.0 (2014-04-08)
|
||||
|
||||
#### Builder
|
||||
- Fix printing multiple messages on a single line. Fixes broken output during builds.
|
||||
- Follow symlinks inside container's root for ADD build instructions.
|
||||
- Fix EXPOSE caching.
|
||||
|
||||
#### Documentation
|
||||
- Add the new options of `docker ps` to the documentation.
|
||||
- Add the options of `docker restart` to the documentation.
|
||||
- Update daemon docs and help messages for --iptables and --ip-forward.
|
||||
- Updated apt-cacher-ng docs example.
|
||||
- Remove duplicate description of --mtu from docs.
|
||||
- Add missing -t and -v for `docker images` to the docs.
|
||||
- Add fixes to the cli docs.
|
||||
- Update libcontainer docs.
|
||||
- Update images in docs to remove references to AUFS and LXC.
|
||||
- Update the nodejs_web_app in the docs to use the new epel RPM address.
|
||||
- Fix external link on security of containers.
|
||||
- Update remote API docs.
|
||||
- Add image size to history docs.
|
||||
- Be explicit about binding to all interfaces in redis example.
|
||||
- Document DisableNetwork flag in the 1.10 remote api.
|
||||
- Document that `--lxc-conf` is lxc only.
|
||||
- Add chef usage documentation.
|
||||
- Add example for an image with multiple for `docker load`.
|
||||
- Explain what `docker run -a` does in the docs.
|
||||
|
||||
#### Contrib
|
||||
- Add variable for DOCKER_LOGFILE to sysvinit and use append instead of overwrite in opening the logfile.
|
||||
- Fix init script cgroup mounting workarounds to be more similar to cgroupfs-mount and thus work properly.
|
||||
- Remove inotifywait hack from the upstart host-integration example because it's not necessary any more.
|
||||
- Add check-config script to contrib.
|
||||
- Fix fish shell completion.
|
||||
|
||||
#### Hack
|
||||
* Clean up "go test" output from "make test" to be much more readable/scannable.
|
||||
* Excluse more "definitely not unit tested Go source code" directories from hack/make/test.
|
||||
+ Generate md5 and sha256 hashes when building, and upload them via hack/release.sh.
|
||||
- Include contributed completions in Ubuntu PPA.
|
||||
+ Add cli integration tests.
|
||||
* Add tweaks to the hack scripts to make them simpler.
|
||||
|
||||
#### Remote API
|
||||
+ Add TLS auth support for API.
|
||||
* Move git clone from daemon to client.
|
||||
- Fix content-type detection in docker cp.
|
||||
* Split API into 2 go packages.
|
||||
|
||||
#### Runtime
|
||||
* Support hairpin NAT without going through Docker server.
|
||||
- devicemapper: succeed immediately when removing non-existing devices.
|
||||
- devicemapper: improve handling of devicemapper devices (add per device lock, increase sleep time and unlock while sleeping).
|
||||
- devicemapper: increase timeout in waitClose to 10 seconds.
|
||||
- devicemapper: ensure we shut down thin pool cleanly.
|
||||
- devicemapper: pass info, rather than hash to activateDeviceIfNeeded, deactivateDevice, setInitialized, deleteDevice.
|
||||
- devicemapper: avoid AB-BA deadlock.
|
||||
- devicemapper: make shutdown better/faster.
|
||||
- improve alpha sorting in mflag.
|
||||
- Remove manual http cookie management because the cookiejar is being used.
|
||||
- Use BSD raw mode on Darwin. Fixes nano, tmux and others.
|
||||
- Add FreeBSD support for the client.
|
||||
- Merge auth package into registry.
|
||||
- Add deprecation warning for -t on `docker pull`.
|
||||
- Remove goroutine leak on error.
|
||||
- Update parseLxcInfo to comply with new lxc1.0 format.
|
||||
- Fix attach exit on darwin.
|
||||
- Improve deprecation message.
|
||||
- Retry to retrieve the layer metadata up to 5 times for `docker pull`.
|
||||
- Only unshare the mount namespace for execin.
|
||||
- Merge existing config when committing.
|
||||
- Disable daemon startup timeout.
|
||||
- Fix issue #4681: add loopback interface when networking is disabled.
|
||||
- Add failing test case for issue #4681.
|
||||
- Send SIGTERM to child, instead of SIGKILL.
|
||||
- Show the driver and the kernel version in `docker info` even when not in debug mode.
|
||||
- Always symlink /dev/ptmx for libcontainer. This fixes console related problems.
|
||||
- Fix issue caused by the absence of /etc/apparmor.d.
|
||||
- Don't leave empty cidFile behind when failing to create the container.
|
||||
- Mount cgroups automatically if they're not mounted already.
|
||||
- Use mock for search tests.
|
||||
- Update to double-dash everywhere.
|
||||
- Move .dockerenv parsing to lxc driver.
|
||||
- Move all bind-mounts in the container inside the namespace.
|
||||
- Don't use separate bind mount for container.
|
||||
- Always symlink /dev/ptmx for libcontainer.
|
||||
- Don't kill by pid for other drivers.
|
||||
- Add initial logging to libcontainer.
|
||||
* Sort by port in `docker ps`.
|
||||
- Move networking drivers into runtime top level package.
|
||||
+ Add --no-prune to `docker rmi`.
|
||||
+ Add time since exit in `docker ps`.
|
||||
- graphdriver: add build tags.
|
||||
- Prevent allocation of previously allocated ports & prevent improve port allocation.
|
||||
* Add support for --since/--before in `docker ps`.
|
||||
- Clean up container stop.
|
||||
+ Add support for configurable dns search domains.
|
||||
- Add support for relative WORKDIR instructions.
|
||||
- Add --output flag for docker save.
|
||||
- Remove duplication of DNS entries in config merging.
|
||||
- Add cpuset.cpus to cgroups and native driver options.
|
||||
- Remove docker-ci.
|
||||
- Promote btrfs. btrfs is no longer considered experimental.
|
||||
- Add --input flag to `docker load`.
|
||||
- Return error when existing bridge doesn't match IP address.
|
||||
- Strip comments before parsing line continuations to avoid interpreting instructions as comments.
|
||||
- Fix TestOnlyLoopbackExistsWhenUsingDisableNetworkOption to ignore "DOWN" interfaces.
|
||||
- Add systemd implementation of cgroups and make containers show up as systemd units.
|
||||
- Fix commit and import when no repository is specified.
|
||||
- Remount /var/lib/docker as --private to fix scaling issue.
|
||||
- Use the environment's proxy when pinging the remote registry.
|
||||
- Reduce error level from harmless errors.
|
||||
* Allow --volumes-from to be individual files.
|
||||
- Fix expanding buffer in StdCopy.
|
||||
- Set error regardless of attach or stdin. This fixes #3364.
|
||||
- Add support for --env-file to load environment variables from files.
|
||||
- Symlink /etc/mtab and /proc/mounts.
|
||||
- Allow pushing a single tag.
|
||||
- Shut down containers cleanly at shutdown and wait forever for the containers to shut down. This makes container shutdown on daemon shutdown work properly via SIGTERM.
|
||||
- Don't throw error when starting an already running container.
|
||||
- Fix dynamic port allocation limit.
|
||||
- remove setupDev from libcontainer.
|
||||
- Add API version to `docker version`.
|
||||
- Return correct exit code when receiving signal and make SIGQUIT quit without cleanup.
|
||||
- Fix --volumes-from mount failure.
|
||||
- Allow non-privileged containers to create device nodes.
|
||||
- Skip login tests because of external dependency on a hosted service.
|
||||
- Deprecate `docker images --tree` and `docker images --viz`.
|
||||
- Deprecate `docker insert`.
|
||||
- Include base abstraction for apparmor. This fixes some apparmor related problems on Ubuntu 14.04.
|
||||
- Add specific error message when hitting 401 over HTTP on push.
|
||||
- Fix absolute volume check.
|
||||
- Remove volumes-from from the config.
|
||||
- Move DNS options to hostconfig.
|
||||
- Update the apparmor profile for libcontainer.
|
||||
- Add deprecation notice for `docker commit -run`.
|
||||
|
||||
## 0.9.1 (2014-03-24)
|
||||
|
||||
#### Builder
|
||||
- Fix printing multiple messages on a single line. Fixes broken output during builds.
|
||||
|
||||
#### Documentation
|
||||
- Fix external link on security of containers.
|
||||
|
||||
#### Contrib
|
||||
- Fix init script cgroup mounting workarounds to be more similar to cgroupfs-mount and thus work properly.
|
||||
- Add variable for DOCKER_LOGFILE to sysvinit and use append instead of overwrite in opening the logfile.
|
||||
|
||||
#### Hack
|
||||
- Generate md5 and sha256 hashes when building, and upload them via hack/release.sh.
|
||||
|
||||
#### Remote API
|
||||
- Fix content-type detection in `docker cp`.
|
||||
|
||||
#### Runtime
|
||||
- Use BSD raw mode on Darwin. Fixes nano, tmux and others.
|
||||
- Only unshare the mount namespace for execin.
|
||||
- Retry to retrieve the layer metadata up to 5 times for `docker pull`.
|
||||
- Merge existing config when committing.
|
||||
- Fix panic in monitor.
|
||||
- Disable daemon startup timeout.
|
||||
- Fix issue #4681: add loopback interface when networking is disabled.
|
||||
- Add failing test case for issue #4681.
|
||||
- Send SIGTERM to child, instead of SIGKILL.
|
||||
- Show the driver and the kernel version in `docker info` even when not in debug mode.
|
||||
- Always symlink /dev/ptmx for libcontainer. This fixes console related problems.
|
||||
- Fix issue caused by the absence of /etc/apparmor.d.
|
||||
- Don't leave empty cidFile behind when failing to create the container.
|
||||
- Improve deprecation message.
|
||||
- Fix attach exit on darwin.
|
||||
- devicemapper: improve handling of devicemapper devices (add per device lock, increase sleep time, unlock while sleeping).
|
||||
- devicemapper: succeed immediately when removing non-existing devices.
|
||||
- devicemapper: increase timeout in waitClose to 10 seconds.
|
||||
- Remove goroutine leak on error.
|
||||
- Update parseLxcInfo to comply with new lxc1.0 format.
|
||||
|
||||
## 0.9.0 (2014-03-10)
|
||||
|
||||
#### Builder
|
||||
- Avoid extra mount/unmount during build. This fixes mount/unmount related errors during build.
|
||||
- Add error to docker build --rm. This adds missing error handling.
|
||||
- Forbid chained onbuild, `onbuild from` and `onbuild maintainer` triggers.
|
||||
- Make `--rm` the default for `docker build`.
|
||||
|
||||
#### Documentation
|
||||
- Download the docker client binary for Mac over https.
|
||||
- Update the titles of the install instructions & descriptions.
|
||||
* Add instructions for upgrading boot2docker.
|
||||
* Add port forwarding example in OS X install docs.
|
||||
- Attempt to disentangle repository and registry.
|
||||
- Update docs to explain more about `docker ps`.
|
||||
- Update sshd example to use a Dockerfile.
|
||||
- Rework some examples, including the Python examples.
|
||||
- Update docs to include instructions for a container's lifecycle.
|
||||
- Update docs documentation to discuss the docs branch.
|
||||
- Don't skip cert check for an example & use HTTPS.
|
||||
- Bring back the memory and swap accounting section which was lost when the kernel page was removed.
|
||||
- Explain DNS warnings and how to fix them on systems running and using a local nameserver.
|
||||
|
||||
#### Contrib
|
||||
- Add Tanglu support for mkimage-debootstrap.
|
||||
- Add SteamOS support for mkimage-debootstrap.
|
||||
|
||||
#### Hack
|
||||
- Get package coverage when running integration tests.
|
||||
- Remove the Vagrantfile. This is being replaced with boot2docker.
|
||||
- Fix tests on systems where aufs isn't available.
|
||||
- Update packaging instructions and remove the dependency on lxc.
|
||||
|
||||
#### Remote API
|
||||
* Move code specific to the API to the api package.
|
||||
- Fix header content type for the API. Makes all endpoints use proper content type.
|
||||
- Fix registry auth & remove ping calls from CmdPush and CmdPull.
|
||||
- Add newlines to the JSON stream functions.
|
||||
|
||||
#### Runtime
|
||||
* Do not ping the registry from the CLI. All requests to registres flow through the daemon.
|
||||
- Check for nil information return in the lxc driver. This fixes panics with older lxc versions.
|
||||
- Devicemapper: cleanups and fix for unmount. Fixes two problems which were causing unmount to fail intermittently.
|
||||
- Devicemapper: remove directory when removing device. Directories don't get left behind when removing the device.
|
||||
* Devicemapper: enable skip_block_zeroing. Improves performance by not zeroing blocks.
|
||||
- Devicemapper: fix shutdown warnings. Fixes shutdown warnings concerning pool device removal.
|
||||
- Ensure docker cp stream is closed properly. Fixes problems with files not being copied by `docker cp`.
|
||||
- Stop making `tcp://` default to `127.0.0.1:4243` and remove the default port for tcp.
|
||||
- Fix `--run` in `docker commit`. This makes `docker commit --run` work again.
|
||||
- Fix custom bridge related options. This makes custom bridges work again.
|
||||
+ Mount-bind the PTY as container console. This allows tmux/screen to run.
|
||||
+ Add the pure Go libcontainer library to make it possible to run containers using only features of the Linux kernel.
|
||||
+ Add native exec driver which uses libcontainer and make it the default exec driver.
|
||||
- Add support for handling extended attributes in archives.
|
||||
* Set the container MTU to be the same as the host MTU.
|
||||
+ Add simple sha256 checksums for layers to speed up `docker push`.
|
||||
* Improve kernel version parsing.
|
||||
* Allow flag grouping (`docker run -it`).
|
||||
- Remove chroot exec driver.
|
||||
- Fix divide by zero to fix panic.
|
||||
- Rewrite `docker rmi`.
|
||||
- Fix docker info with lxc 1.0.0.
|
||||
- Fix fedora tty with apparmor.
|
||||
* Don't always append env vars, replace defaults with vars from config.
|
||||
* Fix a goroutine leak.
|
||||
* Switch to Go 1.2.1.
|
||||
- Fix unique constraint error checks.
|
||||
* Handle symlinks for Docker's data directory and for TMPDIR.
|
||||
- Add deprecation warnings for flags (-flag is deprecated in favor of --flag)
|
||||
- Add apparmor profile for the native execution driver.
|
||||
* Move system specific code from archive to pkg/system.
|
||||
- Fix duplicate signal for `docker run -i -t` (issue #3336).
|
||||
- Return correct process pid for lxc.
|
||||
- Add a -G option to specify the group which unix sockets belong to.
|
||||
+ Add `-f` flag to `docker rm` to force removal of running containers.
|
||||
+ Kill ghost containers and restart all ghost containers when the docker daemon restarts.
|
||||
+ Add `DOCKER_RAMDISK` environment variable to make Docker work when the root is on a ramdisk.
|
||||
|
||||
## 0.8.1 (2014-02-18)
|
||||
|
||||
#### Builder
|
||||
|
||||
- Avoid extra mount/unmount during build. This removes an unneeded mount/unmount operation which was causing problems with devicemapper
|
||||
- Fix regression with ADD of tar files. This stops Docker from decompressing tarballs added via ADD from the local file system
|
||||
- Add error to `docker build --rm`. This adds a missing error check to ensure failures to remove containers are detected and reported
|
||||
|
||||
#### Documentation
|
||||
|
||||
* Update issue filing instructions
|
||||
* Warn against the use of symlinks for Docker's storage folder
|
||||
* Replace the Firefox example with an IceWeasel example
|
||||
* Rewrite the PostgresSQL example using a Dockerfile and add more details to it
|
||||
* Improve the OS X documentation
|
||||
|
||||
#### Remote API
|
||||
|
||||
- Fix broken images API for version less than 1.7
|
||||
- Use the right encoding for all API endpoints which return JSON
|
||||
- Move remote api client to api/
|
||||
- Queue calls to the API using generic socket wait
|
||||
|
||||
#### Runtime
|
||||
|
||||
- Fix the use of custom settings for bridges and custom bridges
|
||||
- Refactor the devicemapper code to avoid many mount/unmount race conditions and failures
|
||||
- Remove two panics which could make Docker crash in some situations
|
||||
- Don't ping registry from the CLI client
|
||||
- Enable skip_block_zeroing for devicemapper. This stops devicemapper from always zeroing entire blocks
|
||||
- Fix --run in `docker commit`. This makes docker commit store `--run` in the image configuration
|
||||
- Remove directory when removing devicemapper device. This cleans up leftover mount directories
|
||||
- Drop NET_ADMIN capability for non-privileged containers. Unprivileged containers can't change their network configuration
|
||||
- Ensure `docker cp` stream is closed properly
|
||||
- Avoid extra mount/unmount during container registration. This removes an unneeded mount/unmount operation which was causing problems with devicemapper
|
||||
- Stop allowing tcp:// as a default tcp bin address which binds to 127.0.0.1:4243 and remove the default port
|
||||
+ Mount-bind the PTY as container console. This allows tmux and screen to run in a container
|
||||
- Clean up archive closing. This fixes and improves archive handling
|
||||
- Fix engine tests on systems where temp directories are symlinked
|
||||
- Add test methods for save and load
|
||||
- Avoid temporarily unmounting the container when restarting it. This fixes a race for devicemapper during restart
|
||||
- Support submodules when building from a GitHub repository
|
||||
- Quote volume path to allow spaces
|
||||
- Fix remote tar ADD behavior. This fixes a regression which was causing Docker to extract tarballs
|
||||
|
||||
## 0.8.0 (2014-02-04)
|
||||
|
||||
#### Notable features since 0.7.0
|
||||
|
||||
* Images and containers can be removed much faster
|
||||
* Building an image from source with docker build is now much faster
|
||||
* The Docker daemon starts and stops much faster
|
||||
* The memory footprint of many common operations has been reduced, by streaming files instead of buffering them in memory, fixing memory leaks, and fixing various suboptimal memory allocations
|
||||
* Several race conditions were fixed, making Docker more stable under very high concurrency load. This makes Docker more stable and less likely to crash and reduces the memory footprint of many common operations
|
||||
* All packaging operations are now built on the Go language’s standard tar implementation, which is bundled with Docker itself. This makes packaging more portable across host distributions, and solves several issues caused by quirks and incompatibilities between different distributions of tar
|
||||
* Docker can now create, remove and modify larger numbers of containers and images graciously thanks to more aggressive releasing of system resources. For example the storage driver API now allows Docker to do reference counting on mounts created by the drivers
|
||||
With the ongoing changes to the networking and execution subsystems of docker testing these areas have been a focus of the refactoring. By moving these subsystems into separate packages we can test, analyze, and monitor coverage and quality of these packages
|
||||
* Many components have been separated into smaller sub-packages, each with a dedicated test suite. As a result the code is better-tested, more readable and easier to change
|
||||
|
||||
* The ADD instruction now supports caching, which avoids unnecessarily re-uploading the same source content again and again when it hasn’t changed
|
||||
* The new ONBUILD instruction adds to your image a “trigger” instruction to be executed at a later time, when the image is used as the base for another build
|
||||
* Docker now ships with an experimental storage driver which uses the BTRFS filesystem for copy-on-write
|
||||
* Docker is officially supported on Mac OSX
|
||||
* The Docker daemon supports systemd socket activation
|
||||
|
||||
## 0.7.6 (2014-01-14)
|
||||
|
||||
#### Builder
|
||||
|
||||
* Do not follow symlink outside of build context
|
||||
|
||||
#### Runtime
|
||||
|
||||
- Remount bind mounts when ro is specified
|
||||
* Use https for fetching docker version
|
||||
|
||||
#### Other
|
||||
|
||||
* Inline the test.docker.io fingerprint
|
||||
* Add ca-certificates to packaging documentation
|
||||
|
||||
## 0.7.5 (2014-01-09)
|
||||
|
||||
#### Builder
|
||||
|
||||
* Disable compression for build. More space usage but a much faster upload
|
||||
- Fix ADD caching for certain paths
|
||||
- Do not compress archive from git build
|
||||
|
||||
#### Documentation
|
||||
|
||||
- Fix error in GROUP add example
|
||||
* Make sure the GPG fingerprint is inline in the documentation
|
||||
* Give more specific advice on setting up signing of commits for DCO
|
||||
|
||||
#### Runtime
|
||||
|
||||
- Fix misspelled container names
|
||||
- Do not add hostname when networking is disabled
|
||||
* Return most recent image from the cache by date
|
||||
- Return all errors from docker wait
|
||||
* Add Content-Type Header "application/json" to GET /version and /info responses
|
||||
|
||||
#### Other
|
||||
|
||||
* Update DCO to version 1.1
|
||||
+ Update Makefile to use "docker:GIT_BRANCH" as the generated image name
|
||||
* Update Travis to check for new 1.1 DCO version
|
||||
|
||||
## 0.7.4 (2014-01-07)
|
||||
|
||||
#### Builder
|
||||
|
||||
- Fix ADD caching issue with . prefixed path
|
||||
- Fix docker build on devicemapper by reverting sparse file tar option
|
||||
- Fix issue with file caching and prevent wrong cache hit
|
||||
* Use same error handling while unmarshalling CMD and ENTRYPOINT
|
||||
|
||||
#### Documentation
|
||||
|
||||
* Simplify and streamline Amazon Quickstart
|
||||
* Install instructions use unprefixed fedora image
|
||||
* Update instructions for mtu flag for Docker on GCE
|
||||
+ Add Ubuntu Saucy to installation
|
||||
- Fix for wrong version warning on master instead of latest
|
||||
|
||||
#### Runtime
|
||||
|
||||
- Only get the image's rootfs when we need to calculate the image size
|
||||
- Correctly handle unmapping UDP ports
|
||||
* Make CopyFileWithTar use a pipe instead of a buffer to save memory on docker build
|
||||
- Fix login message to say pull instead of push
|
||||
- Fix "docker load" help by removing "SOURCE" prompt and mentioning STDIN
|
||||
* Make blank -H option default to the same as no -H was sent
|
||||
* Extract cgroups utilities to own submodule
|
||||
|
||||
#### Other
|
||||
|
||||
+ Add Travis CI configuration to validate DCO and gofmt requirements
|
||||
+ Add Developer Certificate of Origin Text
|
||||
* Upgrade VBox Guest Additions
|
||||
* Check standalone header when pinging a registry server
|
||||
|
||||
## 0.7.3 (2014-01-02)
|
||||
|
||||
#### Builder
|
||||
|
||||
+ Update ADD to use the image cache, based on a hash of the added content
|
||||
* Add error message for empty Dockerfile
|
||||
|
||||
#### Documentation
|
||||
|
||||
- Fix outdated link to the "Introduction" on www.docker.io
|
||||
+ Update the docs to get wider when the screen does
|
||||
- Add information about needing to install LXC when using raw binaries
|
||||
* Update Fedora documentation to disentangle the docker and docker.io conflict
|
||||
* Add a note about using the new `-mtu` flag in several GCE zones
|
||||
+ Add FrugalWare installation instructions
|
||||
+ Add a more complete example of `docker run`
|
||||
- Fix API documentation for creating and starting Privileged containers
|
||||
- Add missing "name" parameter documentation on "/containers/create"
|
||||
* Add a mention of `lxc-checkconfig` as a way to check for some of the necessary kernel configuration
|
||||
- Update the 1.8 API documentation with some additions that were added to the docs for 1.7
|
||||
|
||||
#### Hack
|
||||
|
||||
- Add missing libdevmapper dependency to the packagers documentation
|
||||
* Update minimum Go requirement to a hard line at Go 1.2+
|
||||
* Many minor improvements to the Vagrantfile
|
||||
+ Add ability to customize dockerinit search locations when compiling (to be used very sparingly only by packagers of platforms who require a nonstandard location)
|
||||
+ Add coverprofile generation reporting
|
||||
- Add `-a` to our Go build flags, removing the need for recompiling the stdlib manually
|
||||
* Update Dockerfile to be more canonical and have less spurious warnings during build
|
||||
- Fix some miscellaneous `docker pull` progress bar display issues
|
||||
* Migrate more miscellaneous packages under the "pkg" folder
|
||||
* Update TextMate highlighting to automatically be enabled for files named "Dockerfile"
|
||||
* Reorganize syntax highlighting files under a common "contrib/syntax" directory
|
||||
* Update install.sh script (https://get.docker.io/) to not fail if busybox fails to download or run at the end of the Ubuntu/Debian installation
|
||||
* Add support for container names in bash completion
|
||||
|
||||
#### Packaging
|
||||
|
||||
+ Add an official Docker client binary for Darwin (Mac OS X)
|
||||
* Remove empty "Vendor" string and added "License" on deb package
|
||||
+ Add a stubbed version of "/etc/default/docker" in the deb package
|
||||
|
||||
#### Runtime
|
||||
|
||||
* Update layer application to extract tars in place, avoiding file churn while handling whiteouts
|
||||
- Fix permissiveness of mtime comparisons in tar handling (since GNU tar and Go tar do not yet support sub-second mtime precision)
|
||||
* Reimplement `docker top` in pure Go to work more consistently, and even inside Docker-in-Docker (thus removing the shell injection vulnerability present in some versions of `lxc-ps`)
|
||||
+ Update `-H unix://` to work similarly to `-H tcp://` by inserting the default values for missing portions
|
||||
- Fix more edge cases regarding dockerinit and deleted or replaced docker or dockerinit files
|
||||
* Update container name validation to include '.'
|
||||
- Fix use of a symlink or non-absolute path as the argument to `-g` to work as expected
|
||||
* Update to handle external mounts outside of LXC, fixing many small mounting quirks and making future execution backends and other features simpler
|
||||
* Update to use proper box-drawing characters everywhere in `docker images -tree`
|
||||
* Move MTU setting from LXC configuration to directly use netlink
|
||||
* Add `-S` option to external tar invocation for more efficient spare file handling
|
||||
+ Add arch/os info to User-Agent string, especially for registry requests
|
||||
+ Add `-mtu` option to Docker daemon for configuring MTU
|
||||
- Fix `docker build` to exit with a non-zero exit code on error
|
||||
+ Add `DOCKER_HOST` environment variable to configure the client `-H` flag without specifying it manually for every invocation
|
||||
|
||||
## 0.7.2 (2013-12-16)
|
||||
|
||||
#### Runtime
|
||||
|
||||
+ Validate container names on creation with standard regex
|
||||
* Increase maximum image depth to 127 from 42
|
||||
* Continue to move api endpoints to the job api
|
||||
+ Add -bip flag to allow specification of dynamic bridge IP via CIDR
|
||||
- Allow bridge creation when ipv6 is not enabled on certain systems
|
||||
* Set hostname and IP address from within dockerinit
|
||||
* Drop capabilities from within dockerinit
|
||||
- Fix volumes on host when symlink is present the image
|
||||
- Prevent deletion of image if ANY container is depending on it even if the container is not running
|
||||
* Update docker push to use new progress display
|
||||
* Use os.Lstat to allow mounting unix sockets when inspecting volumes
|
||||
- Adjust handling of inactive user login
|
||||
- Add missing defines in devicemapper for older kernels
|
||||
- Allow untag operations with no container validation
|
||||
- Add auth config to docker build
|
||||
|
||||
#### Documentation
|
||||
|
||||
* Add more information about Docker logging
|
||||
+ Add RHEL documentation
|
||||
* Add a direct example for changing the CMD that is run in a container
|
||||
* Update Arch installation documentation
|
||||
+ Add section on Trusted Builds
|
||||
+ Add Network documentation page
|
||||
|
||||
#### Other
|
||||
|
||||
+ Add new cover bundle for providing code coverage reporting
|
||||
* Separate integration tests in bundles
|
||||
* Make Tianon the hack maintainer
|
||||
* Update mkimage-debootstrap with more tweaks for keeping images small
|
||||
* Use https to get the install script
|
||||
* Remove vendored dotcloud/tar now that Go 1.2 has been released
|
||||
|
||||
## 0.7.1 (2013-12-05)
|
||||
|
||||
#### Documentation
|
||||
|
||||
+ Add @SvenDowideit as documentation maintainer
|
||||
+ Add links example
|
||||
+ Add documentation regarding ambassador pattern
|
||||
+ Add Google Cloud Platform docs
|
||||
+ Add dockerfile best practices
|
||||
* Update doc for RHEL
|
||||
* Update doc for registry
|
||||
* Update Postgres examples
|
||||
* Update doc for Ubuntu install
|
||||
* Improve remote api doc
|
||||
|
||||
#### Runtime
|
||||
|
||||
+ Add hostconfig to docker inspect
|
||||
+ Implement `docker log -f` to stream logs
|
||||
+ Add env variable to disable kernel version warning
|
||||
+ Add -format to `docker inspect`
|
||||
+ Support bind-mount for files
|
||||
- Fix bridge creation on RHEL
|
||||
- Fix image size calculation
|
||||
- Make sure iptables are called even if the bridge already exists
|
||||
- Fix issue with stderr only attach
|
||||
- Remove init layer when destroying a container
|
||||
- Fix same port binding on different interfaces
|
||||
- `docker build` now returns the correct exit code
|
||||
- Fix `docker port` to display correct port
|
||||
- `docker build` now check that the dockerfile exists client side
|
||||
- `docker attach` now returns the correct exit code
|
||||
- Remove the name entry when the container does not exist
|
||||
|
||||
#### Registry
|
||||
|
||||
* Improve progress bars, add ETA for downloads
|
||||
* Simultaneous pulls now waits for the first to finish instead of failing
|
||||
- Tag only the top-layer image when pushing to registry
|
||||
- Fix issue with offline image transfer
|
||||
- Fix issue preventing using ':' in password for registry
|
||||
|
||||
#### Other
|
||||
|
||||
+ Add pprof handler for debug
|
||||
+ Create a Makefile
|
||||
* Use stdlib tar that now includes fix
|
||||
* Improve make.sh test script
|
||||
* Handle SIGQUIT on the daemon
|
||||
* Disable verbose during tests
|
||||
* Upgrade to go1.2 for official build
|
||||
* Improve unit tests
|
||||
* The test suite now runs all tests even if one fails
|
||||
* Refactor C in Go (Devmapper)
|
||||
- Fix OSX compilation
|
||||
|
||||
## 0.7.0 (2013-11-25)
|
||||
|
||||
#### Notable features since 0.6.0
|
||||
|
||||
* Storage drivers: choose from aufs, device-mapper, or vfs.
|
||||
* Standard Linux support: docker now runs on unmodified Linux kernels and all major distributions.
|
||||
* Links: compose complex software stacks by connecting containers to each other.
|
||||
* Container naming: organize your containers by giving them memorable names.
|
||||
* Advanced port redirects: specify port redirects per interface, or keep sensitive ports private.
|
||||
* Offline transfer: push and pull images to the filesystem without losing information.
|
||||
* Quality: numerous bugfixes and small usability improvements. Significant increase in test coverage.
|
||||
|
||||
## 0.6.7 (2013-11-21)
|
||||
|
||||
#### Runtime
|
||||
|
||||
* Improve stability, fixes some race conditons
|
||||
* Skip the volumes mounted when deleting the volumes of container.
|
||||
* Fix layer size computation: handle hard links correctly
|
||||
* Use the work Path for docker cp CONTAINER:PATH
|
||||
* Fix tmp dir never cleanup
|
||||
* Speedup docker ps
|
||||
* More informative error message on name collisions
|
||||
* Fix nameserver regex
|
||||
* Always return long id's
|
||||
* Fix container restart race condition
|
||||
* Keep published ports on docker stop;docker start
|
||||
* Fix container networking on Fedora
|
||||
* Correctly express "any address" to iptables
|
||||
* Fix network setup when reconnecting to ghost container
|
||||
* Prevent deletion if image is used by a running container
|
||||
* Lock around read operations in graph
|
||||
|
||||
#### RemoteAPI
|
||||
|
||||
* Return full ID on docker rmi
|
||||
|
||||
#### Client
|
||||
|
||||
+ Add -tree option to images
|
||||
+ Offline image transfer
|
||||
* Exit with status 2 on usage error and display usage on stderr
|
||||
* Do not forward SIGCHLD to container
|
||||
* Use string timestamp for docker events -since
|
||||
|
||||
#### Other
|
||||
|
||||
* Update to go 1.2rc5
|
||||
+ Add /etc/default/docker support to upstart
|
||||
|
||||
## 0.6.6 (2013-11-06)
|
||||
|
||||
#### Runtime
|
||||
@@ -699,7 +9,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
+ Add lock around write operations in graph
|
||||
* Check if port is valid
|
||||
* Fix restart runtime error with ghost container networking
|
||||
+ Add some more colors and animals to increase the pool of generated names
|
||||
+ Added some more colors and animals to increase the pool of generated names
|
||||
* Fix issues in docker inspect
|
||||
+ Escape apparmor confinement
|
||||
+ Set environment variables using a file.
|
||||
@@ -707,7 +17,6 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
+ Prevent DNS server conflicts in CreateBridgeIface
|
||||
+ Validate bind mounts on the server side
|
||||
+ Use parent image config in docker build
|
||||
* Fix regression in /etc/hosts
|
||||
|
||||
#### Client
|
||||
|
||||
@@ -853,7 +162,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
* Improve network performance for VirtualBox
|
||||
* Revamp install.sh to be usable by more people, and to use official install methods whenever possible (apt repo, portage tree, etc.)
|
||||
- Fix contrib/mkimage-debian.sh apt caching prevention
|
||||
+ Add Dockerfile.tmLanguage to contrib
|
||||
+ Added Dockerfile.tmLanguage to contrib
|
||||
* Configured FPM to make /etc/init/docker.conf a config file
|
||||
* Enable SSH Agent forwarding in Vagrant VM
|
||||
* Several small tweaks/fixes for contrib/mkimage-debian.sh
|
||||
@@ -967,7 +276,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
* Mount /dev/shm as a tmpfs
|
||||
- Switch from http to https for get.docker.io
|
||||
* Let userland proxy handle container-bound traffic
|
||||
* Update the Docker CLI to specify a value for the "Host" header.
|
||||
* Updated the Docker CLI to specify a value for the "Host" header.
|
||||
- Change network range to avoid conflict with EC2 DNS
|
||||
- Reduce connect and read timeout when pinging the registry
|
||||
* Parallel pull
|
||||
@@ -1163,7 +472,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
|
||||
+ Builder: 'docker build git://URL' fetches and builds a remote git repository
|
||||
* Runtime: 'docker ps -s' optionally prints container size
|
||||
* Tests: improved and simplified
|
||||
* Tests: Improved and simplified
|
||||
- Runtime: fix a regression introduced in 0.4.3 which caused the logs command to fail.
|
||||
- Builder: fix a regression when using ADD with single regular file.
|
||||
|
||||
@@ -1178,7 +487,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
+ ADD of a local file will detect tar archives and unpack them
|
||||
* ADD improvements: use tar for copy + automatically unpack local archives
|
||||
* ADD uses tar/untar for copies instead of calling 'cp -ar'
|
||||
* Fix the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented.
|
||||
* Fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented.
|
||||
- Fix a bug which caused builds to fail if ADD was the first command
|
||||
* Nicer output for 'docker build'
|
||||
|
||||
@@ -1223,7 +532,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
+ Detect faulty DNS configuration and replace it with a public default
|
||||
+ Allow docker run <name>:<id>
|
||||
+ You can now specify public port (ex: -p 80:4500)
|
||||
* Improve image removal to garbage-collect unreferenced parents
|
||||
* Improved image removal to garbage-collect unreferenced parents
|
||||
|
||||
#### Client
|
||||
|
||||
@@ -1277,7 +586,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
|
||||
#### Documentation
|
||||
|
||||
* Improve install instructions.
|
||||
* Improved install instructions.
|
||||
|
||||
## 0.3.3 (2013-05-23)
|
||||
|
||||
@@ -1362,7 +671,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
|
||||
+ Support for data volumes ('docker run -v=PATH')
|
||||
+ Share data volumes between containers ('docker run -volumes-from')
|
||||
+ Improve documentation
|
||||
+ Improved documentation
|
||||
* Upgrade to Go 1.0.3
|
||||
* Various upgrades to the dev environment for contributors
|
||||
|
||||
@@ -1418,7 +727,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
- Add debian packaging
|
||||
- Documentation: installing on Arch Linux
|
||||
- Documentation: running Redis on docker
|
||||
- Fix lxc 0.9 compatibility
|
||||
- Fixed lxc 0.9 compatibility
|
||||
- Automatically load aufs module
|
||||
- Various bugfixes and stability improvements
|
||||
|
||||
@@ -1453,7 +762,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
- Stabilize process management
|
||||
- Layers can include a commit message
|
||||
- Simplified 'docker attach'
|
||||
- Fix support for re-attaching
|
||||
- Fixed support for re-attaching
|
||||
- Various bugfixes and stability improvements
|
||||
- Auto-download at run
|
||||
- Auto-login on push
|
||||
|
||||
+16
-99
@@ -4,20 +4,11 @@ Want to hack on Docker? Awesome! Here are instructions to get you
|
||||
started. They are probably not perfect, please let us know if anything
|
||||
feels wrong or incomplete.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
When reporting [issues](https://github.com/dotcloud/docker/issues)
|
||||
on GitHub please include your host OS (Ubuntu 12.04, Fedora 19, etc),
|
||||
the output of `uname -a` and the output of `docker version` along with
|
||||
the output of `docker -D info`. Please include the steps required to reproduce
|
||||
the problem if possible and applicable.
|
||||
This information will help us review and fix your issue faster.
|
||||
|
||||
## Build Environment
|
||||
|
||||
For instructions on setting up your development environment, please
|
||||
see our dedicated [dev environment setup
|
||||
docs](http://docs.docker.com/contributing/devenvironment/).
|
||||
docs](http://docs.docker.io/en/latest/contributing/devenvironment/).
|
||||
|
||||
## Contribution guidelines
|
||||
|
||||
@@ -47,7 +38,7 @@ else is working on the same thing.
|
||||
|
||||
### Create issues...
|
||||
|
||||
Any significant improvement should be documented as [a GitHub
|
||||
Any significant improvement should be documented as [a github
|
||||
issue](https://github.com/dotcloud/docker/issues) before anybody
|
||||
starts working on it.
|
||||
|
||||
@@ -73,22 +64,21 @@ your branch before submitting a pull request.
|
||||
|
||||
Update the documentation when creating or modifying features. Test
|
||||
your documentation changes for clarity, concision, and correctness, as
|
||||
well as a clean documentation build. See ``docs/README.md`` for more
|
||||
well as a clean docmuent build. See ``docs/README.md`` for more
|
||||
information on building the docs and how docs get released.
|
||||
|
||||
Write clean code. Universally formatted code promotes ease of writing, reading,
|
||||
and maintenance. Always run `gofmt -s -w file.go` on each changed file before
|
||||
committing your changes. Most editors have plugins that do this automatically.
|
||||
and maintenance. Always run `go fmt` before committing your changes. Most
|
||||
editors have plugins that do this automatically, and there's also a git
|
||||
pre-commit hook:
|
||||
|
||||
```
|
||||
curl -o .git/hooks/pre-commit https://raw.github.com/edsrzf/gofmt-git-hook/master/fmt-check && chmod +x .git/hooks/pre-commit
|
||||
```
|
||||
|
||||
Pull requests descriptions should be as clear as possible and include a
|
||||
reference to all the issues that they address.
|
||||
|
||||
Pull requests must not contain commits from other users or branches.
|
||||
|
||||
Commit messages must start with a capitalized and short summary (max. 50
|
||||
chars) written in the imperative, followed by an optional, more detailed
|
||||
explanatory text which is separated from the summary by an empty line.
|
||||
|
||||
Code review comments may be added to your pull request. Discuss, then make the
|
||||
suggested modifications and push additional commits to your feature branch. Be
|
||||
sure to post a comment after pushing. The new commits will show up in the pull
|
||||
@@ -103,10 +93,12 @@ same commit so that a revert would remove all traces of the feature or fix.
|
||||
Commits that fix or close an issue should include a reference like `Closes #XXX`
|
||||
or `Fixes #XXX`, which will automatically close the issue when merged.
|
||||
|
||||
Please do not add yourself to the AUTHORS file, as it is regenerated
|
||||
regularly from the Git history.
|
||||
Add your name to the AUTHORS file, but make sure the list is sorted and your
|
||||
name and email address match your git configuration. The AUTHORS file is
|
||||
regenerated occasionally from the git commit history, so a mismatch may result
|
||||
in your changes being overwritten.
|
||||
|
||||
### Merge approval
|
||||
### Approval
|
||||
|
||||
Docker maintainers use LGTM (looks good to me) in comments on the code review
|
||||
to indicate acceptance.
|
||||
@@ -114,90 +106,15 @@ to indicate acceptance.
|
||||
A change requires LGTMs from an absolute majority of the maintainers of each
|
||||
component affected. For example, if a change affects docs/ and registry/, it
|
||||
needs an absolute majority from the maintainers of docs/ AND, separately, an
|
||||
absolute majority of the maintainers of registry.
|
||||
absolute majority of the maintainers of registry
|
||||
|
||||
For more details see [MAINTAINERS.md](hack/MAINTAINERS.md)
|
||||
|
||||
### Sign your work
|
||||
|
||||
The sign-off is a simple line at the end of the explanation for the
|
||||
patch, which certifies that you wrote it or otherwise have the right to
|
||||
pass it on as an open-source patch. The rules are pretty simple: if you
|
||||
can certify the below (from
|
||||
[developercertificate.org](http://developercertificate.org/)):
|
||||
|
||||
```
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
```
|
||||
|
||||
then you just add a line to every git commit message:
|
||||
|
||||
Docker-DCO-1.1-Signed-off-by: Joe Smith <joe.smith@email.com> (github: github_handle)
|
||||
|
||||
using your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
|
||||
One way to automate this, is customise your get ``commit.template`` by adding
|
||||
a ``prepare-commit-msg`` hook to your docker checkout:
|
||||
|
||||
```
|
||||
curl -o .git/hooks/prepare-commit-msg https://raw.githubusercontent.com/dotcloud/docker/master/contrib/prepare-commit-msg.hook && chmod +x .git/hooks/prepare-commit-msg
|
||||
```
|
||||
|
||||
* Note: the above script expects to find your GitHub user name in ``git config --get github.user``
|
||||
|
||||
#### Small patch exception
|
||||
|
||||
There are several exceptions to the signing requirement. Currently these are:
|
||||
|
||||
* Your patch fixes spelling or grammar errors.
|
||||
* Your patch is a single line change to documentation contained in the
|
||||
`docs` directory.
|
||||
* Your patch fixes Markdown formatting or syntax errors in the
|
||||
documentation contained in the `docs` directory.
|
||||
|
||||
If you have any questions, please refer to the FAQ in the [docs](http://docs.docker.com)
|
||||
|
||||
### How can I become a maintainer?
|
||||
|
||||
* Step 1: learn the component inside out
|
||||
* Step 2: make yourself useful by contributing code, bugfixes, support etc.
|
||||
* Step 3: volunteer on the irc channel (#docker@freenode)
|
||||
* Step 4: propose yourself at a scheduled docker meeting in #docker-dev
|
||||
|
||||
Don't forget: being a maintainer is a time investment. Make sure you will have time to make yourself available.
|
||||
You don't have to be a maintainer to make a difference on the project!
|
||||
|
||||
+39
-68
@@ -6,13 +6,13 @@
|
||||
# docker build -t docker .
|
||||
#
|
||||
# # Mount your source in an interactive container for quick testing:
|
||||
# docker run -v `pwd`:/go/src/github.com/dotcloud/docker --privileged -i -t docker bash
|
||||
# docker run -v `pwd`:/go/src/github.com/dotcloud/docker -privileged -i -t docker bash
|
||||
#
|
||||
# # Run the test suite:
|
||||
# docker run --privileged docker hack/make.sh test
|
||||
# docker run -privileged docker hack/make.sh test
|
||||
#
|
||||
# # Publish a release:
|
||||
# docker run --privileged \
|
||||
# docker run -privileged \
|
||||
# -e AWS_S3_BUCKET=baz \
|
||||
# -e AWS_ACCESS_KEY=foo \
|
||||
# -e AWS_SECRET_KEY=bar \
|
||||
@@ -24,81 +24,52 @@
|
||||
#
|
||||
|
||||
docker-version 0.6.1
|
||||
FROM ubuntu:14.04
|
||||
MAINTAINER Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
from ubuntu:12.04
|
||||
maintainer Solomon Hykes <solomon@dotcloud.com>
|
||||
|
||||
# Packaged dependencies
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq \
|
||||
apt-utils \
|
||||
aufs-tools \
|
||||
automake \
|
||||
btrfs-tools \
|
||||
build-essential \
|
||||
curl \
|
||||
dpkg-sig \
|
||||
git \
|
||||
iptables \
|
||||
libapparmor-dev \
|
||||
libcap-dev \
|
||||
libsqlite3-dev \
|
||||
lxc=1.0* \
|
||||
mercurial \
|
||||
pandoc \
|
||||
reprepro \
|
||||
ruby1.9.1 \
|
||||
ruby1.9.1-dev \
|
||||
s3cmd=1.1.0* \
|
||||
--no-install-recommends
|
||||
|
||||
# Get lvm2 source for compiling statically
|
||||
RUN git clone --no-checkout https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2 && cd /usr/local/lvm2 && git checkout -q v2_02_103
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
||||
# note: we don't use "git clone -b" above because it then spews big nasty warnings about 'detached HEAD' state that we can't silence as easily as we can silence them using "git checkout" directly
|
||||
|
||||
# Compile and install lvm2
|
||||
RUN cd /usr/local/lvm2 && ./configure --enable-static_link && make device-mapper && make install_device-mapper
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
# Build dependencies
|
||||
run echo 'deb http://archive.ubuntu.com/ubuntu precise main universe' > /etc/apt/sources.list
|
||||
run apt-get update
|
||||
run apt-get install -y -q curl
|
||||
run apt-get install -y -q git
|
||||
run apt-get install -y -q mercurial
|
||||
run apt-get install -y -q build-essential libsqlite3-dev
|
||||
|
||||
# Install Go
|
||||
RUN curl -s https://go.googlecode.com/files/go1.2.1.src.tar.gz | tar -v -C /usr/local -xz
|
||||
ENV PATH /usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go:/go/src/github.com/dotcloud/docker/vendor
|
||||
RUN cd /usr/local/go/src && ./make.bash --no-clean 2>&1
|
||||
run curl -s https://go.googlecode.com/files/go1.2rc5.src.tar.gz | tar -v -C /usr/local -xz
|
||||
env PATH /usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
|
||||
env GOPATH /go:/go/src/github.com/dotcloud/docker/vendor
|
||||
run cd /usr/local/go/src && ./make.bash && go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std
|
||||
|
||||
# Compile Go for cross compilation
|
||||
ENV DOCKER_CROSSPLATFORMS \
|
||||
linux/386 linux/arm \
|
||||
darwin/amd64 darwin/386 \
|
||||
freebsd/amd64 freebsd/386 freebsd/arm
|
||||
# (set an explicit GOARM of 5 for maximum compatibility)
|
||||
ENV GOARM 5
|
||||
RUN cd /usr/local/go/src && bash -xc 'for platform in $DOCKER_CROSSPLATFORMS; do GOOS=${platform%/*} GOARCH=${platform##*/} ./make.bash --no-clean 2>&1; done'
|
||||
# Ubuntu stuff
|
||||
run apt-get install -y -q ruby1.9.3 rubygems libffi-dev
|
||||
run gem install --no-rdoc --no-ri fpm
|
||||
run apt-get install -y -q reprepro dpkg-sig
|
||||
|
||||
# Grab Go's cover tool for dead-simple code coverage testing
|
||||
RUN go get code.google.com/p/go.tools/cmd/cover
|
||||
run apt-get install -y -q python-pip
|
||||
run pip install s3cmd==1.1.0-beta3
|
||||
run pip install python-magic==0.4.6
|
||||
run /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY\n' > /.s3cfg
|
||||
|
||||
# TODO replace FPM with some very minimal debhelper stuff
|
||||
RUN gem install --no-rdoc --no-ri fpm --version 1.0.2
|
||||
# Runtime dependencies
|
||||
run apt-get install -y -q iptables
|
||||
run apt-get install -y -q lxc
|
||||
run apt-get install -y -q aufs-tools
|
||||
|
||||
# Get the "busybox" image source so we can build locally instead of pulling
|
||||
RUN git clone -b buildroot-2014.02 https://github.com/jpetazzo/docker-busybox.git /docker-busybox
|
||||
# Get lvm2 source for compiling statically
|
||||
run git clone git://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2 && cd /usr/local/lvm2 && git checkout v2_02_103
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
||||
# note: we can't use "git clone -b" above because it requires at least git 1.7.10 to be able to use that on a tag instead of a branch and we only have 1.7.9.5
|
||||
|
||||
# Setup s3cmd config
|
||||
RUN /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY' > /.s3cfg
|
||||
# Compile and install lvm2
|
||||
run cd /usr/local/lvm2 && ./configure --enable-static_link && make device-mapper && make install_device-mapper
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
|
||||
# Set user.email so crosbymichael's in-container merge commits go smoothly
|
||||
RUN git config --global user.email 'docker-dummy@example.com'
|
||||
|
||||
# Add an unprivileged user to be used for tests which need it
|
||||
RUN groupadd -r docker
|
||||
RUN useradd --create-home --gid docker unprivilegeduser
|
||||
|
||||
VOLUME /var/lib/docker
|
||||
WORKDIR /go/src/github.com/dotcloud/docker
|
||||
ENV DOCKER_BUILDTAGS apparmor selinux
|
||||
volume /var/lib/docker
|
||||
workdir /go/src/github.com/dotcloud/docker
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
entrypoint ["hack/dind"]
|
||||
|
||||
# Upload docker source
|
||||
ADD . /go/src/github.com/dotcloud/docker
|
||||
add . /go/src/github.com/dotcloud/docker
|
||||
|
||||
@@ -11,14 +11,20 @@ They are just like FIXME comments in the source code, except we're not sure wher
|
||||
to put them - so we put them here :)
|
||||
|
||||
|
||||
* Merge Runtime, Server and Builder into Runtime
|
||||
* Run linter on codebase
|
||||
* Unify build commands and regular commands
|
||||
* Move source code into src/ subdir for clarity
|
||||
* docker build: on non-existent local path for ADD, don't show full absolute path on the host
|
||||
* docker tag foo REPO:TAG
|
||||
* use size header for progress bar in pull
|
||||
* Clean up context upload in build!!!
|
||||
* Parallel pull
|
||||
* Always generate a resolv.conf per container, to avoid changing resolv.conf under thne container's feet
|
||||
* Save metadata with import/export (#1974)
|
||||
* Upgrade dockerd without stopping containers
|
||||
* Simple command to remove all untagged images (`docker rmi $(docker images | awk '/^<none>/ { print $3 }')`)
|
||||
* Simple command to clean up containers for disk space
|
||||
* Caching after an ADD (#880)
|
||||
* Clean up the ProgressReader api, it's a PITA to use
|
||||
* Use netlink instead of iproute2/iptables (#925)
|
||||
|
||||
@@ -176,7 +176,18 @@
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2014 Docker, Inc.
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
+5
-8
@@ -1,9 +1,6 @@
|
||||
Solomon Hykes <solomon@docker.com> (@shykes)
|
||||
Victor Vieux <vieux@docker.com> (@vieux)
|
||||
Solomon Hykes <solomon@dotcloud.com> (@shykes)
|
||||
Guillaume Charmes <guillaume@dotcloud.com> (@creack)
|
||||
Victor Vieux <victor@dotcloud.com> (@vieux)
|
||||
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
||||
.mailmap: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
.travis.yml: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
AUTHORS: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
Dockerfile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
Makefile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
.dockerignore: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
api.go: Victor Vieux <victor@dotcloud.com> (@vieux)
|
||||
Vagrantfile: Daniel Mizyrycki <daniel@dotcloud.com> (@mzdaniel)
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
.PHONY: all binary build cross default docs docs-build docs-shell shell test test-unit test-integration test-integration-cli validate
|
||||
|
||||
# to allow `make BINDDIR=. shell` or `make BINDDIR= test`
|
||||
BINDDIR := bundles
|
||||
# to allow `make DOCSPORT=9000 docs`
|
||||
DOCSPORT := 8000
|
||||
|
||||
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||
GITCOMMIT := $(shell git rev-parse --short HEAD 2>/dev/null)
|
||||
DOCKER_IMAGE := docker$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
||||
DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
||||
DOCKER_MOUNT := $(if $(BINDDIR),-v "$(CURDIR)/$(BINDDIR):/go/src/github.com/dotcloud/docker/$(BINDDIR)")
|
||||
|
||||
DOCKER_RUN_DOCKER := docker run --rm -it --privileged -e TESTFLAGS -e TESTDIRS -e DOCKER_GRAPHDRIVER -e DOCKER_EXECDRIVER $(DOCKER_MOUNT) "$(DOCKER_IMAGE)"
|
||||
# to allow `make DOCSDIR=docs docs-shell`
|
||||
DOCKER_RUN_DOCS := docker run --rm -it $(if $(DOCSDIR),-v $(CURDIR)/$(DOCSDIR):/$(DOCSDIR)) -e AWS_S3_BUCKET
|
||||
|
||||
default: binary
|
||||
|
||||
all: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh
|
||||
|
||||
binary: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary
|
||||
|
||||
cross: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary cross
|
||||
|
||||
docs: docs-build
|
||||
$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" mkdocs serve
|
||||
|
||||
docs-shell: docs-build
|
||||
$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" bash
|
||||
|
||||
docs-release: docs-build
|
||||
$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" ./release.sh
|
||||
|
||||
test: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary cross test-unit test-integration test-integration-cli
|
||||
|
||||
test-unit: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh test-unit
|
||||
|
||||
test-integration: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh test-integration
|
||||
|
||||
test-integration-cli: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary test-integration-cli
|
||||
|
||||
validate: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh validate-gofmt validate-dco
|
||||
|
||||
shell: build
|
||||
$(DOCKER_RUN_DOCKER) bash
|
||||
|
||||
build: bundles
|
||||
docker build -t "$(DOCKER_IMAGE)" .
|
||||
|
||||
docs-build:
|
||||
cp ./VERSION docs/VERSION
|
||||
echo "$(GIT_BRANCH)" > docs/GIT_BRANCH
|
||||
echo "$(AWS_S3_BUCKET)" > docs/AWS_S3_BUCKET
|
||||
echo "$(GITCOMMIT)" > docs/GITCOMMIT
|
||||
docker build -t "$(DOCKER_DOCS_IMAGE)" docs
|
||||
|
||||
bundles:
|
||||
mkdir bundles
|
||||
@@ -1,5 +1,5 @@
|
||||
Docker
|
||||
Copyright 2012-2014 Docker, Inc.
|
||||
Copyright 2012-2013 Docker, Inc.
|
||||
|
||||
This product includes software developed at Docker, Inc. (http://www.docker.com).
|
||||
|
||||
|
||||
@@ -4,36 +4,30 @@ Docker: the Linux container engine
|
||||
Docker is an open source project to pack, ship and run any application
|
||||
as a lightweight container
|
||||
|
||||
Docker containers are both *hardware-agnostic* and *platform-agnostic*.
|
||||
This means that they can run anywhere, from your laptop to the largest
|
||||
EC2 compute instance and everything in between - and they don't require
|
||||
that you use a particular language, framework or packaging system. That
|
||||
makes them great building blocks for deploying and scaling web apps,
|
||||
databases and backend services without depending on a particular stack
|
||||
or provider.
|
||||
Docker containers are both *hardware-agnostic* and
|
||||
*platform-agnostic*. This means that they can run anywhere, from your
|
||||
laptop to the largest EC2 compute instance and everything in between -
|
||||
and they don't require that you use a particular language, framework
|
||||
or packaging system. That makes them great building blocks for
|
||||
deploying and scaling web apps, databases and backend services without
|
||||
depending on a particular stack or provider.
|
||||
|
||||
Docker is an open-source implementation of the deployment engine which
|
||||
powers [dotCloud](http://dotcloud.com), a popular Platform-as-a-Service.
|
||||
It benefits directly from the experience accumulated over several years
|
||||
of large-scale operation and support of hundreds of thousands of
|
||||
applications and databases.
|
||||
powers [dotCloud](http://dotcloud.com), a popular
|
||||
Platform-as-a-Service. It benefits directly from the experience
|
||||
accumulated over several years of large-scale operation and support of
|
||||
hundreds of thousands of applications and databases.
|
||||
|
||||

|
||||
|
||||
## Security Disclosure
|
||||
|
||||
Security is very important to us. If you have any issue regarding security,
|
||||
please disclose the information responsibly by sending an email to
|
||||
security@docker.com and not by creating a github issue.
|
||||

|
||||
|
||||
## Better than VMs
|
||||
|
||||
A common method for distributing applications and sandboxing their
|
||||
A common method for distributing applications and sandbox their
|
||||
execution is to use virtual machines, or VMs. Typical VM formats are
|
||||
VMWare's vmdk, Oracle Virtualbox's vdi, and Amazon EC2's ami. In theory
|
||||
these formats should allow every developer to automatically package
|
||||
their application into a "machine" for easy distribution and deployment.
|
||||
In practice, that almost never happens, for a few reasons:
|
||||
VMWare's vmdk, Oracle Virtualbox's vdi, and Amazon EC2's ami. In
|
||||
theory these formats should allow every developer to automatically
|
||||
package their application into a "machine" for easy distribution and
|
||||
deployment. In practice, that almost never happens, for a few reasons:
|
||||
|
||||
* *Size*: VMs are very large which makes them impractical to store
|
||||
and transfer.
|
||||
@@ -53,37 +47,39 @@ In practice, that almost never happens, for a few reasons:
|
||||
service discovery.
|
||||
|
||||
By contrast, Docker relies on a different sandboxing method known as
|
||||
*containerization*. Unlike traditional virtualization, containerization
|
||||
takes place at the kernel level. Most modern operating system kernels
|
||||
now support the primitives necessary for containerization, including
|
||||
Linux with [openvz](http://openvz.org),
|
||||
*containerization*. Unlike traditional virtualization,
|
||||
containerization takes place at the kernel level. Most modern
|
||||
operating system kernels now support the primitives necessary for
|
||||
containerization, including Linux with [openvz](http://openvz.org),
|
||||
[vserver](http://linux-vserver.org) and more recently
|
||||
[lxc](http://lxc.sourceforge.net), Solaris with
|
||||
[zones](http://docs.oracle.com/cd/E26502_01/html/E29024/preface-1.html#scrolltoc)
|
||||
and FreeBSD with
|
||||
[Jails](http://www.freebsd.org/doc/handbook/jails.html).
|
||||
|
||||
Docker builds on top of these low-level primitives to offer developers a
|
||||
portable format and runtime environment that solves all 4 problems.
|
||||
Docker containers are small (and their transfer can be optimized with
|
||||
layers), they have basically zero memory and cpu overhead, they are
|
||||
completely portable and are designed from the ground up with an
|
||||
application-centric design.
|
||||
Docker builds on top of these low-level primitives to offer developers
|
||||
a portable format and runtime environment that solves all 4
|
||||
problems. Docker containers are small (and their transfer can be
|
||||
optimized with layers), they have basically zero memory and cpu
|
||||
overhead, they are completely portable and are designed from the
|
||||
ground up with an application-centric design.
|
||||
|
||||
The best part: because Docker operates at the OS level, it can still be
|
||||
run inside a VM!
|
||||
The best part: because ``docker`` operates at the OS level, it can
|
||||
still be run inside a VM!
|
||||
|
||||
## Plays well with others
|
||||
|
||||
Docker does not require that you buy into a particular programming
|
||||
language, framework, packaging system or configuration language.
|
||||
|
||||
Is your application a Unix process? Does it use files, tcp connections,
|
||||
environment variables, standard Unix streams and command-line arguments
|
||||
as inputs and outputs? Then Docker can run it.
|
||||
Is your application a Unix process? Does it use files, tcp
|
||||
connections, environment variables, standard Unix streams and
|
||||
command-line arguments as inputs and outputs? Then ``docker`` can run
|
||||
it.
|
||||
|
||||
Can your application's build be expressed as a sequence of such
|
||||
commands? Then Docker can build it.
|
||||
commands? Then ``docker`` can build it.
|
||||
|
||||
|
||||
## Escape dependency hell
|
||||
|
||||
@@ -130,11 +126,14 @@ build command inherits the result of the previous commands, the
|
||||
Here's a typical Docker build process:
|
||||
|
||||
```bash
|
||||
FROM ubuntu:12.04
|
||||
RUN apt-get update
|
||||
RUN apt-get install -q -y python python-pip curl
|
||||
RUN curl -L https://github.com/shykes/helloflask/archive/master.tar.gz | tar -xzv
|
||||
RUN cd helloflask-master && pip install -r requirements.txt
|
||||
from ubuntu:12.10
|
||||
run apt-get update
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -q -y python
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -q -y python-pip
|
||||
run pip install django
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -q -y curl
|
||||
run curl -L https://github.com/shykes/helloflask/archive/master.tar.gz | tar -xzv
|
||||
run cd helloflask-master && pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Note that Docker doesn't care *how* dependencies are built - as long
|
||||
@@ -144,24 +143,22 @@ as they can be built by running a Unix command in a container.
|
||||
Getting started
|
||||
===============
|
||||
|
||||
Docker can be installed on your local machine as well as servers - both
|
||||
bare metal and virtualized. It is available as a binary on most modern
|
||||
Linux systems, or as a VM on Windows, Mac and other systems.
|
||||
Docker can be installed on your local machine as well as servers - both bare metal and virtualized.
|
||||
It is available as a binary on most modern Linux systems, or as a VM on Windows, Mac and other systems.
|
||||
|
||||
We also offer an [interactive tutorial](http://www.docker.com/tryit/)
|
||||
for quickly learning the basics of using Docker.
|
||||
We also offer an interactive tutorial for quickly learning the basics of using Docker.
|
||||
|
||||
|
||||
For up-to-date install instructions and online tutorials, see the [Getting Started page](http://www.docker.io/gettingstarted/).
|
||||
|
||||
For up-to-date install instructions, see the [Docs](http://docs.docker.com).
|
||||
|
||||
Usage examples
|
||||
==============
|
||||
|
||||
Docker can be used to run short-lived commands, long-running daemons
|
||||
(app servers, databases etc.), interactive shell sessions, etc.
|
||||
Docker can be used to run short-lived commands, long-running daemons (app servers, databases etc.),
|
||||
interactive shell sessions, etc.
|
||||
|
||||
You can find a [list of real-world
|
||||
examples](http://docs.docker.com/examples/) in the
|
||||
documentation.
|
||||
You can find a [list of real-world examples](http://docs.docker.io/en/latest/examples/) in the documentation.
|
||||
|
||||
Under the hood
|
||||
--------------
|
||||
@@ -173,7 +170,13 @@ Under the hood, Docker is built on the following components:
|
||||
and
|
||||
[namespacing](http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part)
|
||||
capabilities of the Linux kernel;
|
||||
* The [Go](http://golang.org) programming language.
|
||||
* [AUFS](http://aufs.sourceforge.net/aufs.html), a powerful union
|
||||
filesystem with copy-on-write capabilities;
|
||||
* The [Go](http://golang.org) programming language;
|
||||
* [lxc](http://lxc.sourceforge.net/), a set of convenience scripts to
|
||||
simplify the creation of Linux containers.
|
||||
|
||||
|
||||
|
||||
Contributing to Docker
|
||||
======================
|
||||
@@ -184,6 +187,7 @@ started [here](CONTRIBUTING.md).
|
||||
They are probably not perfect, please let us know if anything feels
|
||||
wrong or incomplete.
|
||||
|
||||
|
||||
### Legal
|
||||
|
||||
*Brought to you courtesy of our legal counsel. For more context,
|
||||
@@ -195,9 +199,3 @@ It is your responsibility to ensure that your use and/or transfer does not
|
||||
violate applicable laws.
|
||||
|
||||
For more information, please see http://www.bis.doc.gov
|
||||
|
||||
|
||||
Licensing
|
||||
=========
|
||||
Docker is licensed under the Apache License, Version 2.0. See LICENSE for full license text.
|
||||
|
||||
|
||||
Vendored
+175
@@ -0,0 +1,175 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
BOX_NAME = ENV['BOX_NAME'] || "ubuntu"
|
||||
BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64.box"
|
||||
VF_BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64_vmware_fusion.box"
|
||||
AWS_BOX_URI = ENV['BOX_URI'] || "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
|
||||
AWS_REGION = ENV['AWS_REGION'] || "us-east-1"
|
||||
AWS_AMI = ENV['AWS_AMI'] || "ami-69f5a900"
|
||||
AWS_INSTANCE_TYPE = ENV['AWS_INSTANCE_TYPE'] || 't1.micro'
|
||||
|
||||
FORWARD_DOCKER_PORTS = ENV['FORWARD_DOCKER_PORTS']
|
||||
|
||||
SSH_PRIVKEY_PATH = ENV["SSH_PRIVKEY_PATH"]
|
||||
|
||||
# A script to upgrade from the 12.04 kernel to the raring backport kernel (3.8)
|
||||
# and install docker.
|
||||
$script = <<SCRIPT
|
||||
# The username to add to the docker group will be passed as the first argument
|
||||
# to the script. If nothing is passed, default to "vagrant".
|
||||
user="$1"
|
||||
if [ -z "$user" ]; then
|
||||
user=vagrant
|
||||
fi
|
||||
|
||||
# Adding an apt gpg key is idempotent.
|
||||
wget -q -O - https://get.docker.io/gpg | apt-key add -
|
||||
|
||||
# Creating the docker.list file is idempotent, but it may overrite desired
|
||||
# settings if it already exists. This could be solved with md5sum but it
|
||||
# doesn't seem worth it.
|
||||
echo 'deb http://get.docker.io/ubuntu docker main' > \
|
||||
/etc/apt/sources.list.d/docker.list
|
||||
|
||||
# Update remote package metadata. 'apt-get update' is idempotent.
|
||||
apt-get update -q
|
||||
|
||||
# Install docker. 'apt-get install' is idempotent.
|
||||
apt-get install -q -y lxc-docker
|
||||
|
||||
usermod -a -G docker "$user"
|
||||
|
||||
tmp=`mktemp -q` && {
|
||||
# Only install the backport kernel, don't bother upgrade if the backport is
|
||||
# already installed. We want parse the output of apt so we need to save it
|
||||
# with 'tee'. NOTE: The installation of the kernel will trigger dkms to
|
||||
# install vboxguest if needed.
|
||||
apt-get install -q -y --no-upgrade linux-image-generic-lts-raring | \
|
||||
tee "$tmp"
|
||||
|
||||
# Parse the number of installed packages from the output
|
||||
NUM_INST=`awk '$2 == "upgraded," && $4 == "newly" { print $3 }' "$tmp"`
|
||||
rm "$tmp"
|
||||
}
|
||||
|
||||
# If the number of installed packages is greater than 0, we want to reboot (the
|
||||
# backport kernel was installed but is not running).
|
||||
if [ "$NUM_INST" -gt 0 ];
|
||||
then
|
||||
echo "Rebooting down to activate new kernel."
|
||||
echo "/vagrant will not be mounted. Use 'vagrant halt' followed by"
|
||||
echo "'vagrant up' to ensure /vagrant is mounted."
|
||||
shutdown -r now
|
||||
fi
|
||||
SCRIPT
|
||||
|
||||
# We need to install the virtualbox guest additions *before* we do the normal
|
||||
# docker installation. As such this script is prepended to the common docker
|
||||
# install script above. This allows the install of the backport kernel to
|
||||
# trigger dkms to build the virtualbox guest module install.
|
||||
$vbox_script = <<VBOX_SCRIPT + $script
|
||||
# Install the VirtualBox guest additions if they aren't already installed.
|
||||
if [ ! -d /opt/VBoxGuestAdditions-4.3.2/ ]; then
|
||||
# Update remote package metadata. 'apt-get update' is idempotent.
|
||||
apt-get update -q
|
||||
|
||||
# Kernel Headers and dkms are required to build the vbox guest kernel
|
||||
# modules.
|
||||
apt-get install -q -y linux-headers-generic-lts-raring dkms
|
||||
|
||||
echo 'Downloading VBox Guest Additions...'
|
||||
wget -cq http://dlc.sun.com.edgesuite.net/virtualbox/4.3.2/VBoxGuestAdditions_4.3.2.iso
|
||||
|
||||
mount -o loop,ro /home/vagrant/VBoxGuestAdditions_4.3.2.iso /mnt
|
||||
/mnt/VBoxLinuxAdditions.run --nox11
|
||||
umount /mnt
|
||||
fi
|
||||
VBOX_SCRIPT
|
||||
|
||||
Vagrant::Config.run do |config|
|
||||
# Setup virtual machine box. This VM configuration code is always executed.
|
||||
config.vm.box = BOX_NAME
|
||||
config.vm.box_url = BOX_URI
|
||||
|
||||
# Use the specified private key path if it is specified and not empty.
|
||||
if SSH_PRIVKEY_PATH
|
||||
config.ssh.private_key_path = SSH_PRIVKEY_PATH
|
||||
end
|
||||
|
||||
config.ssh.forward_agent = true
|
||||
end
|
||||
|
||||
# Providers were added on Vagrant >= 1.1.0
|
||||
#
|
||||
# NOTE: The vagrant "vm.provision" appends its arguments to a list and executes
|
||||
# them in order. If you invoke "vm.provision :shell, :inline => $script"
|
||||
# twice then vagrant will run the script two times. Unfortunately when you use
|
||||
# providers and the override argument to set up provisioners (like the vbox
|
||||
# guest extensions) they 1) don't replace the other provisioners (they append
|
||||
# to the end of the list) and 2) you can't control the order the provisioners
|
||||
# are executed (you can only append to the list). If you want the virtualbox
|
||||
# only script to run before the other script, you have to jump through a lot of
|
||||
# hoops.
|
||||
#
|
||||
# Here is my only repeatable solution: make one script that is common ($script)
|
||||
# and another script that is the virtual box guest *prepended* to the common
|
||||
# script. Only ever use "vm.provision" *one time* per provider. That means
|
||||
# every single provider has an override, and every single one configures
|
||||
# "vm.provision". Much saddness, but such is life.
|
||||
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
||||
config.vm.provider :aws do |aws, override|
|
||||
username = "ubuntu"
|
||||
override.vm.box_url = AWS_BOX_URI
|
||||
override.vm.provision :shell, :inline => $script, :args => username
|
||||
aws.access_key_id = ENV["AWS_ACCESS_KEY"]
|
||||
aws.secret_access_key = ENV["AWS_SECRET_KEY"]
|
||||
aws.keypair_name = ENV["AWS_KEYPAIR_NAME"]
|
||||
override.ssh.username = username
|
||||
aws.region = AWS_REGION
|
||||
aws.ami = AWS_AMI
|
||||
aws.instance_type = AWS_INSTANCE_TYPE
|
||||
end
|
||||
|
||||
config.vm.provider :rackspace do |rs, override|
|
||||
override.vm.provision :shell, :inline => $script
|
||||
rs.username = ENV["RS_USERNAME"]
|
||||
rs.api_key = ENV["RS_API_KEY"]
|
||||
rs.public_key_path = ENV["RS_PUBLIC_KEY"]
|
||||
rs.flavor = /512MB/
|
||||
rs.image = /Ubuntu/
|
||||
end
|
||||
|
||||
config.vm.provider :vmware_fusion do |f, override|
|
||||
override.vm.box_url = VF_BOX_URI
|
||||
override.vm.synced_folder ".", "/vagrant", disabled: true
|
||||
override.vm.provision :shell, :inline => $script
|
||||
f.vmx["displayName"] = "docker"
|
||||
end
|
||||
|
||||
config.vm.provider :virtualbox do |vb, override|
|
||||
override.vm.provision :shell, :inline => $vbox_script
|
||||
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
|
||||
vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
|
||||
end
|
||||
end
|
||||
|
||||
# If this is a version 1 config, virtualbox is the only option. A version 2
|
||||
# config would have already been set in the above provider section.
|
||||
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
|
||||
config.vm.provision :shell, :inline => $vbox_script
|
||||
end
|
||||
|
||||
if !FORWARD_DOCKER_PORTS.nil?
|
||||
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
|
||||
(49000..49900).each do |port|
|
||||
config.vm.forward_port port, port
|
||||
end
|
||||
end
|
||||
|
||||
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
||||
(49000..49900).each do |port|
|
||||
config.vm.network :forwarded_port, :host => port, :guest => port
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1 +0,0 @@
|
||||
Victor Vieux <vieux@docker.com> (@vieux)
|
||||
@@ -1,5 +0,0 @@
|
||||
This directory contains code pertaining to the Docker API:
|
||||
|
||||
- Used by the docker client when comunicating with the docker deamon
|
||||
|
||||
- Used by third party tools wishing to interface with the docker deamon
|
||||
@@ -1,19 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJsonContentType(t *testing.T) {
|
||||
if !MatchesContentType("application/json", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if !MatchesContentType("application/json; charset=utf-8", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if MatchesContentType("dockerapplication/json", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
flag "github.com/dotcloud/docker/pkg/mflag"
|
||||
"github.com/dotcloud/docker/pkg/term"
|
||||
"github.com/dotcloud/docker/registry"
|
||||
)
|
||||
|
||||
var funcMap = template.FuncMap{
|
||||
"json": func(v interface{}) string {
|
||||
a, _ := json.Marshal(v)
|
||||
return string(a)
|
||||
},
|
||||
}
|
||||
|
||||
func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) {
|
||||
if len(name) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
|
||||
method := reflect.ValueOf(cli).MethodByName(methodName)
|
||||
if !method.IsValid() {
|
||||
return nil, false
|
||||
}
|
||||
return method.Interface().(func(...string) error), true
|
||||
}
|
||||
|
||||
func (cli *DockerCli) ParseCommands(args ...string) error {
|
||||
if len(args) > 0 {
|
||||
method, exists := cli.getMethod(args[0])
|
||||
if !exists {
|
||||
fmt.Println("Error: Command not found:", args[0])
|
||||
return cli.CmdHelp(args[1:]...)
|
||||
}
|
||||
return method(args[1:]...)
|
||||
}
|
||||
return cli.CmdHelp(args...)
|
||||
}
|
||||
|
||||
func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet {
|
||||
flags := flag.NewFlagSet(name, flag.ContinueOnError)
|
||||
flags.Usage = func() {
|
||||
fmt.Fprintf(cli.err, "\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
|
||||
flags.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
func (cli *DockerCli) LoadConfigFile() (err error) {
|
||||
cli.configFile, err = registry.LoadConfig(os.Getenv("HOME"))
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.err, "WARNING: %s\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsConfig *tls.Config) *DockerCli {
|
||||
var (
|
||||
isTerminal = false
|
||||
terminalFd uintptr
|
||||
scheme = "http"
|
||||
)
|
||||
|
||||
if tlsConfig != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
if in != nil {
|
||||
if file, ok := out.(*os.File); ok {
|
||||
terminalFd = file.Fd()
|
||||
isTerminal = term.IsTerminal(terminalFd)
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = out
|
||||
}
|
||||
return &DockerCli{
|
||||
proto: proto,
|
||||
addr: addr,
|
||||
in: in,
|
||||
out: out,
|
||||
err: err,
|
||||
isTerminal: isTerminal,
|
||||
terminalFd: terminalFd,
|
||||
tlsConfig: tlsConfig,
|
||||
scheme: scheme,
|
||||
}
|
||||
}
|
||||
|
||||
type DockerCli struct {
|
||||
proto string
|
||||
addr string
|
||||
configFile *registry.ConfigFile
|
||||
in io.ReadCloser
|
||||
out io.Writer
|
||||
err io.Writer
|
||||
isTerminal bool
|
||||
terminalFd uintptr
|
||||
tlsConfig *tls.Config
|
||||
scheme string
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,133 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/dotcloud/docker/api"
|
||||
"github.com/dotcloud/docker/dockerversion"
|
||||
"github.com/dotcloud/docker/pkg/term"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
)
|
||||
|
||||
func (cli *DockerCli) dial() (net.Conn, error) {
|
||||
if cli.tlsConfig != nil && cli.proto != "unix" {
|
||||
return tls.Dial(cli.proto, cli.addr, cli.tlsConfig)
|
||||
}
|
||||
return net.Dial(cli.proto, cli.addr)
|
||||
}
|
||||
|
||||
func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer) error {
|
||||
defer func() {
|
||||
if started != nil {
|
||||
close(started)
|
||||
}
|
||||
}()
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
||||
req.Header.Set("Content-Type", "plain/text")
|
||||
req.Host = cli.addr
|
||||
|
||||
dial, err := cli.dial()
|
||||
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?")
|
||||
}
|
||||
return err
|
||||
}
|
||||
clientconn := httputil.NewClientConn(dial, nil)
|
||||
defer clientconn.Close()
|
||||
|
||||
// Server hijacks the connection, error 'connection closed' expected
|
||||
clientconn.Do(req)
|
||||
|
||||
rwc, br := clientconn.Hijack()
|
||||
defer rwc.Close()
|
||||
|
||||
if started != nil {
|
||||
started <- rwc
|
||||
}
|
||||
|
||||
var receiveStdout chan error
|
||||
|
||||
var oldState *term.State
|
||||
|
||||
if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" {
|
||||
oldState, err = term.SetRawTerminal(cli.terminalFd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer term.RestoreTerminal(cli.terminalFd, oldState)
|
||||
}
|
||||
|
||||
if stdout != nil || stderr != nil {
|
||||
receiveStdout = utils.Go(func() (err error) {
|
||||
defer func() {
|
||||
if in != nil {
|
||||
if setRawTerminal && cli.isTerminal {
|
||||
term.RestoreTerminal(cli.terminalFd, oldState)
|
||||
}
|
||||
// For some reason this Close call blocks on darwin..
|
||||
// As the client exists right after, simply discard the close
|
||||
// until we find a better solution.
|
||||
if runtime.GOOS != "darwin" {
|
||||
in.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// When TTY is ON, use regular copy
|
||||
if setRawTerminal {
|
||||
_, err = io.Copy(stdout, br)
|
||||
} else {
|
||||
_, err = utils.StdCopy(stdout, stderr, br)
|
||||
}
|
||||
utils.Debugf("[hijack] End of stdout")
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
sendStdin := utils.Go(func() error {
|
||||
if in != nil {
|
||||
io.Copy(rwc, in)
|
||||
utils.Debugf("[hijack] End of stdin")
|
||||
}
|
||||
if tcpc, ok := rwc.(*net.TCPConn); ok {
|
||||
if err := tcpc.CloseWrite(); err != nil {
|
||||
utils.Debugf("Couldn't send EOF: %s\n", err)
|
||||
}
|
||||
} else if unixc, ok := rwc.(*net.UnixConn); ok {
|
||||
if err := unixc.CloseWrite(); err != nil {
|
||||
utils.Debugf("Couldn't send EOF: %s\n", err)
|
||||
}
|
||||
}
|
||||
// Discard errors due to pipe interruption
|
||||
return nil
|
||||
})
|
||||
|
||||
if stdout != nil || stderr != nil {
|
||||
if err := <-receiveStdout; err != nil {
|
||||
utils.Debugf("Error receiveStdout: %s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !cli.isTerminal {
|
||||
if err := <-sendStdin; err != nil {
|
||||
utils.Debugf("Error sendStdin: %s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
gosignal "os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/dotcloud/docker/api"
|
||||
"github.com/dotcloud/docker/dockerversion"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/pkg/term"
|
||||
"github.com/dotcloud/docker/registry"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||
)
|
||||
|
||||
func (cli *DockerCli) HTTPClient() *http.Client {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: cli.tlsConfig,
|
||||
Dial: func(network, addr string) (net.Conn, error) {
|
||||
return net.Dial(cli.proto, cli.addr)
|
||||
},
|
||||
}
|
||||
return &http.Client{Transport: tr}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) {
|
||||
params := bytes.NewBuffer(nil)
|
||||
if data != nil {
|
||||
if env, ok := data.(engine.Env); ok {
|
||||
if err := env.Encode(params); err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
} else {
|
||||
buf, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
if _, err := params.Write(buf); err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
if passAuthInfo {
|
||||
cli.LoadConfigFile()
|
||||
// Resolve the Auth config relevant for this server
|
||||
authConfig := cli.configFile.ResolveAuthConfig(registry.IndexServerAddress())
|
||||
getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) {
|
||||
buf, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
registryAuthHeader := []string{
|
||||
base64.URLEncoding.EncodeToString(buf),
|
||||
}
|
||||
return map[string][]string{"X-Registry-Auth": registryAuthHeader}, nil
|
||||
}
|
||||
if headers, err := getHeaders(authConfig); err == nil && headers != nil {
|
||||
for k, v := range headers {
|
||||
req.Header[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
||||
req.URL.Host = cli.addr
|
||||
req.URL.Scheme = cli.scheme
|
||||
if data != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
} else if method == "POST" {
|
||||
req.Header.Set("Content-Type", "plain/text")
|
||||
}
|
||||
resp, err := cli.HTTPClient().Do(req)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return nil, -1, ErrConnectionRefused
|
||||
}
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
if len(body) == 0 {
|
||||
return nil, resp.StatusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(resp.StatusCode), req.URL)
|
||||
}
|
||||
return nil, resp.StatusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
|
||||
}
|
||||
return resp.Body, resp.StatusCode, nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
|
||||
return cli.streamHelper(method, path, true, in, out, nil, headers)
|
||||
}
|
||||
|
||||
func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error {
|
||||
if (method == "POST" || method == "PUT") && in == nil {
|
||||
in = bytes.NewReader([]byte{})
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("http://v%s%s", api.APIVERSION, path), in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
||||
req.URL.Host = cli.addr
|
||||
req.URL.Scheme = cli.scheme
|
||||
if method == "POST" {
|
||||
req.Header.Set("Content-Type", "plain/text")
|
||||
}
|
||||
|
||||
if headers != nil {
|
||||
for k, v := range headers {
|
||||
req.Header[k] = v
|
||||
}
|
||||
}
|
||||
resp, err := cli.HTTPClient().Do(req)
|
||||
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?")
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(body) == 0 {
|
||||
return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode))
|
||||
}
|
||||
return fmt.Errorf("Error: %s", bytes.TrimSpace(body))
|
||||
}
|
||||
|
||||
if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
|
||||
return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.terminalFd, cli.isTerminal)
|
||||
}
|
||||
if stdout != nil || stderr != nil {
|
||||
// When TTY is ON, use regular copy
|
||||
if setRawTerminal {
|
||||
_, err = io.Copy(stdout, resp.Body)
|
||||
} else {
|
||||
_, err = utils.StdCopy(stdout, stderr, resp.Body)
|
||||
}
|
||||
utils.Debugf("[stream] End of stdout")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) resizeTty(id string) {
|
||||
height, width := cli.getTtySize()
|
||||
if height == 0 && width == 0 {
|
||||
return
|
||||
}
|
||||
v := url.Values{}
|
||||
v.Set("h", strconv.Itoa(height))
|
||||
v.Set("w", strconv.Itoa(width))
|
||||
if _, _, err := readBody(cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil, false)); err != nil {
|
||||
utils.Debugf("Error resize: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func waitForExit(cli *DockerCli, containerId string) (int, error) {
|
||||
stream, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil, false)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
var out engine.Env
|
||||
if err := out.Decode(stream); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return out.GetInt("StatusCode"), nil
|
||||
}
|
||||
|
||||
// getExitCode perform an inspect on the container. It returns
|
||||
// the running state and the exit code.
|
||||
func getExitCode(cli *DockerCli, containerId string) (bool, int, error) {
|
||||
steam, _, err := cli.call("GET", "/containers/"+containerId+"/json", nil, false)
|
||||
if err != nil {
|
||||
// If we can't connect, then the daemon probably died.
|
||||
if err != ErrConnectionRefused {
|
||||
return false, -1, err
|
||||
}
|
||||
return false, -1, nil
|
||||
}
|
||||
|
||||
var result engine.Env
|
||||
if err := result.Decode(steam); err != nil {
|
||||
return false, -1, err
|
||||
}
|
||||
|
||||
state := result.GetSubEnv("State")
|
||||
return state.GetBool("Running"), state.GetInt("ExitCode"), nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) monitorTtySize(id string) error {
|
||||
cli.resizeTty(id)
|
||||
|
||||
sigchan := make(chan os.Signal, 1)
|
||||
gosignal.Notify(sigchan, syscall.SIGWINCH)
|
||||
go func() {
|
||||
for _ = range sigchan {
|
||||
cli.resizeTty(id)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) getTtySize() (int, int) {
|
||||
if !cli.isTerminal {
|
||||
return 0, 0
|
||||
}
|
||||
ws, err := term.GetWinsize(cli.terminalFd)
|
||||
if err != nil {
|
||||
utils.Debugf("Error getting size: %s", err)
|
||||
if ws == nil {
|
||||
return 0, 0
|
||||
}
|
||||
}
|
||||
return int(ws.Height), int(ws.Width)
|
||||
}
|
||||
|
||||
func readBody(stream io.ReadCloser, statusCode int, err error) ([]byte, int, error) {
|
||||
if stream != nil {
|
||||
defer stream.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, statusCode, err
|
||||
}
|
||||
body, err := ioutil.ReadAll(stream)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
return body, statusCode, nil
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime"
|
||||
"strings"
|
||||
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/pkg/version"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
APIVERSION version.Version = "1.13"
|
||||
DEFAULTHTTPHOST = "127.0.0.1"
|
||||
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
|
||||
)
|
||||
|
||||
func ValidateHost(val string) (string, error) {
|
||||
host, err := utils.ParseHost(DEFAULTHTTPHOST, DEFAULTUNIXSOCKET, val)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
return host, nil
|
||||
}
|
||||
|
||||
//TODO remove, used on < 1.5 in getContainersJSON
|
||||
func DisplayablePorts(ports *engine.Table) string {
|
||||
result := []string{}
|
||||
ports.SetKey("PublicPort")
|
||||
ports.Sort()
|
||||
for _, port := range ports.Data {
|
||||
if port.Get("IP") == "" {
|
||||
result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PrivatePort"), port.Get("Type")))
|
||||
} else {
|
||||
result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.Get("IP"), port.GetInt("PublicPort"), port.GetInt("PrivatePort"), port.Get("Type")))
|
||||
}
|
||||
}
|
||||
return strings.Join(result, ", ")
|
||||
}
|
||||
|
||||
func MatchesContentType(contentType, expectedType string) bool {
|
||||
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
|
||||
}
|
||||
return err == nil && mimetype == expectedType
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
Victor Vieux <vieux@docker.com> (@vieux)
|
||||
Johan Euphrosine <proppy@google.com> (@proppy)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,353 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/dotcloud/docker/api"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
)
|
||||
|
||||
func TestGetBoolParam(t *testing.T) {
|
||||
if ret, err := getBoolParam("true"); err != nil || !ret {
|
||||
t.Fatalf("true -> true, nil | got %t %s", ret, err)
|
||||
}
|
||||
if ret, err := getBoolParam("True"); err != nil || !ret {
|
||||
t.Fatalf("True -> true, nil | got %t %s", ret, err)
|
||||
}
|
||||
if ret, err := getBoolParam("1"); err != nil || !ret {
|
||||
t.Fatalf("1 -> true, nil | got %t %s", ret, err)
|
||||
}
|
||||
if ret, err := getBoolParam(""); err != nil || ret {
|
||||
t.Fatalf("\"\" -> false, nil | got %t %s", ret, err)
|
||||
}
|
||||
if ret, err := getBoolParam("false"); err != nil || ret {
|
||||
t.Fatalf("false -> false, nil | got %t %s", ret, err)
|
||||
}
|
||||
if ret, err := getBoolParam("0"); err != nil || ret {
|
||||
t.Fatalf("0 -> false, nil | got %t %s", ret, err)
|
||||
}
|
||||
if ret, err := getBoolParam("faux"); err == nil || ret {
|
||||
t.Fatalf("faux -> false, err | got %t %s", ret, err)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TesthttpError(t *testing.T) {
|
||||
r := httptest.NewRecorder()
|
||||
|
||||
httpError(r, fmt.Errorf("No such method"))
|
||||
if r.Code != http.StatusNotFound {
|
||||
t.Fatalf("Expected %d, got %d", http.StatusNotFound, r.Code)
|
||||
}
|
||||
|
||||
httpError(r, fmt.Errorf("This accound hasn't been activated"))
|
||||
if r.Code != http.StatusForbidden {
|
||||
t.Fatalf("Expected %d, got %d", http.StatusForbidden, r.Code)
|
||||
}
|
||||
|
||||
httpError(r, fmt.Errorf("Some error"))
|
||||
if r.Code != http.StatusInternalServerError {
|
||||
t.Fatalf("Expected %d, got %d", http.StatusInternalServerError, r.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVersion(t *testing.T) {
|
||||
eng := engine.New()
|
||||
var called bool
|
||||
eng.Register("version", func(job *engine.Job) engine.Status {
|
||||
called = true
|
||||
v := &engine.Env{}
|
||||
v.SetJson("Version", "42.1")
|
||||
v.Set("ApiVersion", "1.1.1.1.1")
|
||||
v.Set("GoVersion", "2.42")
|
||||
v.Set("Os", "Linux")
|
||||
v.Set("Arch", "x86_64")
|
||||
if _, err := v.WriteTo(job.Stdout); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return engine.StatusOK
|
||||
})
|
||||
r := serveRequest("GET", "/version", nil, eng, t)
|
||||
if !called {
|
||||
t.Fatalf("handler was not called")
|
||||
}
|
||||
v := readEnv(r.Body, t)
|
||||
if v.Get("Version") != "42.1" {
|
||||
t.Fatalf("%#v\n", v)
|
||||
}
|
||||
if r.HeaderMap.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("%#v\n", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInfo(t *testing.T) {
|
||||
eng := engine.New()
|
||||
var called bool
|
||||
eng.Register("info", func(job *engine.Job) engine.Status {
|
||||
called = true
|
||||
v := &engine.Env{}
|
||||
v.SetInt("Containers", 1)
|
||||
v.SetInt("Images", 42000)
|
||||
if _, err := v.WriteTo(job.Stdout); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return engine.StatusOK
|
||||
})
|
||||
r := serveRequest("GET", "/info", nil, eng, t)
|
||||
if !called {
|
||||
t.Fatalf("handler was not called")
|
||||
}
|
||||
v := readEnv(r.Body, t)
|
||||
if v.GetInt("Images") != 42000 {
|
||||
t.Fatalf("%#v\n", v)
|
||||
}
|
||||
if v.GetInt("Containers") != 1 {
|
||||
t.Fatalf("%#v\n", v)
|
||||
}
|
||||
if r.HeaderMap.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("%#v\n", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetContainersByName(t *testing.T) {
|
||||
eng := engine.New()
|
||||
name := "container_name"
|
||||
var called bool
|
||||
eng.Register("container_inspect", func(job *engine.Job) engine.Status {
|
||||
called = true
|
||||
if job.Args[0] != name {
|
||||
t.Fatalf("name != '%s': %#v", name, job.Args[0])
|
||||
}
|
||||
if api.APIVERSION.LessThan("1.12") && !job.GetenvBool("dirty") {
|
||||
t.Fatal("dirty env variable not set")
|
||||
} else if api.APIVERSION.GreaterThanOrEqualTo("1.12") && job.GetenvBool("dirty") {
|
||||
t.Fatal("dirty env variable set when it shouldn't")
|
||||
}
|
||||
v := &engine.Env{}
|
||||
v.SetBool("dirty", true)
|
||||
if _, err := v.WriteTo(job.Stdout); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return engine.StatusOK
|
||||
})
|
||||
r := serveRequest("GET", "/containers/"+name+"/json", nil, eng, t)
|
||||
if !called {
|
||||
t.Fatal("handler was not called")
|
||||
}
|
||||
if r.HeaderMap.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("%#v\n", r)
|
||||
}
|
||||
var stdoutJson interface{}
|
||||
if err := json.Unmarshal(r.Body.Bytes(), &stdoutJson); err != nil {
|
||||
t.Fatalf("%#v", err)
|
||||
}
|
||||
if stdoutJson.(map[string]interface{})["dirty"].(float64) != 1 {
|
||||
t.Fatalf("%#v", stdoutJson)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEvents(t *testing.T) {
|
||||
eng := engine.New()
|
||||
var called bool
|
||||
eng.Register("events", func(job *engine.Job) engine.Status {
|
||||
called = true
|
||||
since := job.Getenv("since")
|
||||
if since != "1" {
|
||||
t.Fatalf("'since' should be 1, found %#v instead", since)
|
||||
}
|
||||
until := job.Getenv("until")
|
||||
if until != "0" {
|
||||
t.Fatalf("'until' should be 0, found %#v instead", until)
|
||||
}
|
||||
v := &engine.Env{}
|
||||
v.Set("since", since)
|
||||
v.Set("until", until)
|
||||
if _, err := v.WriteTo(job.Stdout); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return engine.StatusOK
|
||||
})
|
||||
r := serveRequest("GET", "/events?since=1&until=0", nil, eng, t)
|
||||
if !called {
|
||||
t.Fatal("handler was not called")
|
||||
}
|
||||
if r.HeaderMap.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("%#v\n", r)
|
||||
}
|
||||
var stdout_json struct {
|
||||
Since int
|
||||
Until int
|
||||
}
|
||||
if err := json.Unmarshal(r.Body.Bytes(), &stdout_json); err != nil {
|
||||
t.Fatalf("%#v", err)
|
||||
}
|
||||
if stdout_json.Since != 1 {
|
||||
t.Fatalf("since != 1: %#v", stdout_json.Since)
|
||||
}
|
||||
if stdout_json.Until != 0 {
|
||||
t.Fatalf("until != 0: %#v", stdout_json.Until)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogs(t *testing.T) {
|
||||
eng := engine.New()
|
||||
var inspect bool
|
||||
var logs bool
|
||||
eng.Register("container_inspect", func(job *engine.Job) engine.Status {
|
||||
inspect = true
|
||||
if len(job.Args) == 0 {
|
||||
t.Fatal("Job arguments is empty")
|
||||
}
|
||||
if job.Args[0] != "test" {
|
||||
t.Fatalf("Container name %s, must be test", job.Args[0])
|
||||
}
|
||||
return engine.StatusOK
|
||||
})
|
||||
expected := "logs"
|
||||
eng.Register("logs", func(job *engine.Job) engine.Status {
|
||||
logs = true
|
||||
if len(job.Args) == 0 {
|
||||
t.Fatal("Job arguments is empty")
|
||||
}
|
||||
if job.Args[0] != "test" {
|
||||
t.Fatalf("Container name %s, must be test", job.Args[0])
|
||||
}
|
||||
follow := job.Getenv("follow")
|
||||
if follow != "1" {
|
||||
t.Fatalf("follow: %s, must be 1", follow)
|
||||
}
|
||||
stdout := job.Getenv("stdout")
|
||||
if stdout != "1" {
|
||||
t.Fatalf("stdout %s, must be 1", stdout)
|
||||
}
|
||||
stderr := job.Getenv("stderr")
|
||||
if stderr != "" {
|
||||
t.Fatalf("stderr %s, must be empty", stderr)
|
||||
}
|
||||
timestamps := job.Getenv("timestamps")
|
||||
if timestamps != "1" {
|
||||
t.Fatalf("timestamps %s, must be 1", timestamps)
|
||||
}
|
||||
job.Stdout.Write([]byte(expected))
|
||||
return engine.StatusOK
|
||||
})
|
||||
r := serveRequest("GET", "/containers/test/logs?follow=1&stdout=1×tamps=1", nil, eng, t)
|
||||
if r.Code != http.StatusOK {
|
||||
t.Fatalf("Got status %d, expected %d", r.Code, http.StatusOK)
|
||||
}
|
||||
if !inspect {
|
||||
t.Fatal("container_inspect job was not called")
|
||||
}
|
||||
if !logs {
|
||||
t.Fatal("logs job was not called")
|
||||
}
|
||||
res := r.Body.String()
|
||||
if res != expected {
|
||||
t.Fatalf("Output %s, expected %s", res, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogsNoStreams(t *testing.T) {
|
||||
eng := engine.New()
|
||||
var inspect bool
|
||||
var logs bool
|
||||
eng.Register("container_inspect", func(job *engine.Job) engine.Status {
|
||||
inspect = true
|
||||
if len(job.Args) == 0 {
|
||||
t.Fatal("Job arguments is empty")
|
||||
}
|
||||
if job.Args[0] != "test" {
|
||||
t.Fatalf("Container name %s, must be test", job.Args[0])
|
||||
}
|
||||
return engine.StatusOK
|
||||
})
|
||||
eng.Register("logs", func(job *engine.Job) engine.Status {
|
||||
logs = true
|
||||
return engine.StatusOK
|
||||
})
|
||||
r := serveRequest("GET", "/containers/test/logs", nil, eng, t)
|
||||
if r.Code != http.StatusBadRequest {
|
||||
t.Fatalf("Got status %d, expected %d", r.Code, http.StatusBadRequest)
|
||||
}
|
||||
if inspect {
|
||||
t.Fatal("container_inspect job was called, but it shouldn't")
|
||||
}
|
||||
if logs {
|
||||
t.Fatal("logs job was called, but it shouldn't")
|
||||
}
|
||||
res := strings.TrimSpace(r.Body.String())
|
||||
expected := "Bad parameters: you must choose at least one stream"
|
||||
if !strings.Contains(res, expected) {
|
||||
t.Fatalf("Output %s, expected %s in it", res, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetImagesHistory(t *testing.T) {
|
||||
eng := engine.New()
|
||||
imageName := "docker-test-image"
|
||||
var called bool
|
||||
eng.Register("history", func(job *engine.Job) engine.Status {
|
||||
called = true
|
||||
if len(job.Args) == 0 {
|
||||
t.Fatal("Job arguments is empty")
|
||||
}
|
||||
if job.Args[0] != imageName {
|
||||
t.Fatalf("name != '%s': %#v", imageName, job.Args[0])
|
||||
}
|
||||
v := &engine.Env{}
|
||||
if _, err := v.WriteTo(job.Stdout); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return engine.StatusOK
|
||||
})
|
||||
r := serveRequest("GET", "/images/"+imageName+"/history", nil, eng, t)
|
||||
if !called {
|
||||
t.Fatalf("handler was not called")
|
||||
}
|
||||
if r.Code != http.StatusOK {
|
||||
t.Fatalf("Got status %d, expected %d", r.Code, http.StatusOK)
|
||||
}
|
||||
if r.HeaderMap.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("%#v\n", r)
|
||||
}
|
||||
}
|
||||
|
||||
func serveRequest(method, target string, body io.Reader, eng *engine.Engine, t *testing.T) *httptest.ResponseRecorder {
|
||||
r := httptest.NewRecorder()
|
||||
req, err := http.NewRequest(method, target, body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ServeRequest(eng, api.APIVERSION, r, req); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func readEnv(src io.Reader, t *testing.T) *engine.Env {
|
||||
out := engine.NewOutput()
|
||||
v, err := out.AddEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := io.Copy(out, src); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out.Close()
|
||||
return v
|
||||
}
|
||||
|
||||
func toJson(data interface{}, t *testing.T) io.Reader {
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &buf
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
package docker
|
||||
|
||||
import "strings"
|
||||
|
||||
type (
|
||||
APIHistory struct {
|
||||
ID string `json:"Id"`
|
||||
Tags []string `json:",omitempty"`
|
||||
Created int64
|
||||
CreatedBy string `json:",omitempty"`
|
||||
Size int64
|
||||
}
|
||||
|
||||
APIImages struct {
|
||||
ID string `json:"Id"`
|
||||
RepoTags []string `json:",omitempty"`
|
||||
Created int64
|
||||
Size int64
|
||||
VirtualSize int64
|
||||
ParentId string `json:",omitempty"`
|
||||
}
|
||||
|
||||
APIImagesOld struct {
|
||||
Repository string `json:",omitempty"`
|
||||
Tag string `json:",omitempty"`
|
||||
ID string `json:"Id"`
|
||||
Created int64
|
||||
Size int64
|
||||
VirtualSize int64
|
||||
}
|
||||
|
||||
APIInfo struct {
|
||||
Debug bool
|
||||
Containers int
|
||||
Images int
|
||||
Driver string `json:",omitempty"`
|
||||
DriverStatus [][2]string `json:",omitempty"`
|
||||
NFd int `json:",omitempty"`
|
||||
NGoroutines int `json:",omitempty"`
|
||||
MemoryLimit bool `json:",omitempty"`
|
||||
SwapLimit bool `json:",omitempty"`
|
||||
IPv4Forwarding bool `json:",omitempty"`
|
||||
LXCVersion string `json:",omitempty"`
|
||||
NEventsListener int `json:",omitempty"`
|
||||
KernelVersion string `json:",omitempty"`
|
||||
IndexServerAddress string `json:",omitempty"`
|
||||
}
|
||||
|
||||
APITop struct {
|
||||
Titles []string
|
||||
Processes [][]string
|
||||
}
|
||||
|
||||
APIRmi struct {
|
||||
Deleted string `json:",omitempty"`
|
||||
Untagged string `json:",omitempty"`
|
||||
}
|
||||
|
||||
APIContainers struct {
|
||||
ID string `json:"Id"`
|
||||
Image string
|
||||
Command string
|
||||
Created int64
|
||||
Status string
|
||||
Ports []APIPort
|
||||
SizeRw int64
|
||||
SizeRootFs int64
|
||||
Names []string
|
||||
}
|
||||
|
||||
APIContainersOld struct {
|
||||
ID string `json:"Id"`
|
||||
Image string
|
||||
Command string
|
||||
Created int64
|
||||
Status string
|
||||
Ports string
|
||||
SizeRw int64
|
||||
SizeRootFs int64
|
||||
}
|
||||
|
||||
APIID struct {
|
||||
ID string `json:"Id"`
|
||||
}
|
||||
|
||||
APIRun struct {
|
||||
ID string `json:"Id"`
|
||||
Warnings []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
APIPort struct {
|
||||
PrivatePort int64
|
||||
PublicPort int64
|
||||
Type string
|
||||
IP string
|
||||
}
|
||||
|
||||
APIVersion struct {
|
||||
Version string
|
||||
GitCommit string `json:",omitempty"`
|
||||
GoVersion string `json:",omitempty"`
|
||||
}
|
||||
|
||||
APIWait struct {
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
APIAuth struct {
|
||||
Status string
|
||||
}
|
||||
|
||||
APIImageConfig struct {
|
||||
ID string `json:"Id"`
|
||||
*Config
|
||||
}
|
||||
|
||||
APICopy struct {
|
||||
Resource string
|
||||
HostPath string
|
||||
}
|
||||
)
|
||||
|
||||
func (api APIImages) ToLegacy() []APIImagesOld {
|
||||
outs := []APIImagesOld{}
|
||||
for _, repotag := range api.RepoTags {
|
||||
components := strings.SplitN(repotag, ":", 2)
|
||||
outs = append(outs, APIImagesOld{
|
||||
ID: api.ID,
|
||||
Repository: components[0],
|
||||
Tag: components[1],
|
||||
Created: api.Created,
|
||||
Size: api.Size,
|
||||
VirtualSize: api.VirtualSize,
|
||||
})
|
||||
}
|
||||
return outs
|
||||
}
|
||||
|
||||
func (api APIContainers) ToLegacy() *APIContainersOld {
|
||||
return &APIContainersOld{
|
||||
ID: api.ID,
|
||||
Image: api.Image,
|
||||
Command: api.Command,
|
||||
Created: api.Created,
|
||||
Status: api.Status,
|
||||
Ports: displayablePorts(api.Ports),
|
||||
SizeRw: api.SizeRw,
|
||||
SizeRootFs: api.SizeRootFs,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJsonContentType(t *testing.T) {
|
||||
if !matchesContentType("application/json", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if !matchesContentType("application/json; charset=utf-8", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if matchesContentType("dockerapplication/json", "application/json") {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
This code provides helper functions for dealing with archive files.
|
||||
|
||||
**TODO**: Move this to either `pkg` or (if not possible) to `utils`.
|
||||
+140
-400
@@ -1,41 +1,29 @@
|
||||
package archive
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/dotcloud/docker/pkg/system"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
||||
)
|
||||
|
||||
type (
|
||||
Archive io.ReadCloser
|
||||
ArchiveReader io.Reader
|
||||
Compression int
|
||||
TarOptions struct {
|
||||
Includes []string
|
||||
Excludes []string
|
||||
Compression Compression
|
||||
NoLchown bool
|
||||
}
|
||||
)
|
||||
type Archive io.Reader
|
||||
|
||||
var (
|
||||
ErrNotImplemented = errors.New("Function not implemented")
|
||||
)
|
||||
type Compression int
|
||||
|
||||
type TarOptions struct {
|
||||
Includes []string
|
||||
Excludes []string
|
||||
Recursive bool
|
||||
Compression Compression
|
||||
CreateFiles []string
|
||||
}
|
||||
|
||||
const (
|
||||
Uncompressed Compression = iota
|
||||
@@ -44,77 +32,43 @@ const (
|
||||
Xz
|
||||
)
|
||||
|
||||
func IsArchive(header []byte) bool {
|
||||
compression := DetectCompression(header)
|
||||
if compression != Uncompressed {
|
||||
return true
|
||||
}
|
||||
r := tar.NewReader(bytes.NewBuffer(header))
|
||||
_, err := r.Next()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func DetectCompression(source []byte) Compression {
|
||||
sourceLen := len(source)
|
||||
for compression, m := range map[Compression][]byte{
|
||||
Bzip2: {0x42, 0x5A, 0x68},
|
||||
Gzip: {0x1F, 0x8B, 0x08},
|
||||
Xz: {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00},
|
||||
} {
|
||||
if len(source) < len(m) {
|
||||
fail := false
|
||||
if len(m) > sourceLen {
|
||||
utils.Debugf("Len too short")
|
||||
continue
|
||||
}
|
||||
if bytes.Compare(m, source[:len(m)]) == 0 {
|
||||
i := 0
|
||||
for _, b := range m {
|
||||
if b != source[i] {
|
||||
fail = true
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
if !fail {
|
||||
return compression
|
||||
}
|
||||
}
|
||||
return Uncompressed
|
||||
}
|
||||
|
||||
func xzDecompress(archive io.Reader) (io.ReadCloser, error) {
|
||||
args := []string{"xz", "-d", "-c", "-q"}
|
||||
|
||||
return CmdStream(exec.Command(args[0], args[1:]...), archive)
|
||||
}
|
||||
|
||||
func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
|
||||
buf := bufio.NewReader(archive)
|
||||
bs, err := buf.Peek(10)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
utils.Debugf("[tar autodetect] n: %v", bs)
|
||||
|
||||
compression := DetectCompression(bs)
|
||||
|
||||
switch compression {
|
||||
case Uncompressed:
|
||||
return ioutil.NopCloser(buf), nil
|
||||
case Gzip:
|
||||
return gzip.NewReader(buf)
|
||||
func (compression *Compression) Flag() string {
|
||||
switch *compression {
|
||||
case Bzip2:
|
||||
return ioutil.NopCloser(bzip2.NewReader(buf)), nil
|
||||
case Xz:
|
||||
return xzDecompress(buf)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
|
||||
}
|
||||
}
|
||||
|
||||
func CompressStream(dest io.WriteCloser, compression Compression) (io.WriteCloser, error) {
|
||||
|
||||
switch compression {
|
||||
case Uncompressed:
|
||||
return utils.NopWriteCloser(dest), nil
|
||||
return "j"
|
||||
case Gzip:
|
||||
return gzip.NewWriter(dest), nil
|
||||
case Bzip2, Xz:
|
||||
// archive/bzip2 does not support writing, and there is no xz support at all
|
||||
// However, this is not a problem as docker only currently generates gzipped tars
|
||||
return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
|
||||
return "z"
|
||||
case Xz:
|
||||
return "J"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (compression *Compression) Extension() string {
|
||||
@@ -131,163 +85,10 @@ func (compression *Compression) Extension() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func addTarFile(path, name string, tw *tar.Writer) error {
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
link := ""
|
||||
if fi.Mode()&os.ModeSymlink != 0 {
|
||||
if link, err = os.Readlink(path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
hdr, err := tar.FileInfoHeader(fi, link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fi.IsDir() && !strings.HasSuffix(name, "/") {
|
||||
name = name + "/"
|
||||
}
|
||||
|
||||
hdr.Name = name
|
||||
|
||||
stat, ok := fi.Sys().(*syscall.Stat_t)
|
||||
if ok {
|
||||
// Currently go does not fill in the major/minors
|
||||
if stat.Mode&syscall.S_IFBLK == syscall.S_IFBLK ||
|
||||
stat.Mode&syscall.S_IFCHR == syscall.S_IFCHR {
|
||||
hdr.Devmajor = int64(major(uint64(stat.Rdev)))
|
||||
hdr.Devminor = int64(minor(uint64(stat.Rdev)))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
capability, _ := system.Lgetxattr(path, "security.capability")
|
||||
if capability != nil {
|
||||
hdr.Xattrs = make(map[string]string)
|
||||
hdr.Xattrs["security.capability"] = string(capability)
|
||||
}
|
||||
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hdr.Typeflag == tar.TypeReg {
|
||||
if file, err := os.Open(path); err != nil {
|
||||
return err
|
||||
} else {
|
||||
_, err := io.Copy(tw, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool) error {
|
||||
// hdr.Mode is in linux format, which we can use for sycalls,
|
||||
// but for os.Foo() calls we need the mode converted to os.FileMode,
|
||||
// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
|
||||
hdrInfo := hdr.FileInfo()
|
||||
|
||||
switch hdr.Typeflag {
|
||||
case tar.TypeDir:
|
||||
// Create directory unless it exists as a directory already.
|
||||
// In that case we just want to merge the two
|
||||
if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {
|
||||
if err := os.Mkdir(path, hdrInfo.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case tar.TypeReg, tar.TypeRegA:
|
||||
// Source is regular file
|
||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(file, reader); err != nil {
|
||||
file.Close()
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
|
||||
case tar.TypeBlock, tar.TypeChar, tar.TypeFifo:
|
||||
mode := uint32(hdr.Mode & 07777)
|
||||
switch hdr.Typeflag {
|
||||
case tar.TypeBlock:
|
||||
mode |= syscall.S_IFBLK
|
||||
case tar.TypeChar:
|
||||
mode |= syscall.S_IFCHR
|
||||
case tar.TypeFifo:
|
||||
mode |= syscall.S_IFIFO
|
||||
}
|
||||
|
||||
if err := syscall.Mknod(path, mode, int(mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case tar.TypeLink:
|
||||
if err := os.Link(filepath.Join(extractDir, hdr.Linkname), path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case tar.TypeSymlink:
|
||||
if err := os.Symlink(hdr.Linkname, path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case tar.TypeXGlobalHeader:
|
||||
utils.Debugf("PAX Global Extended Headers found and ignored")
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Unhandled tar header type %d\n", hdr.Typeflag)
|
||||
}
|
||||
|
||||
if err := os.Lchown(path, hdr.Uid, hdr.Gid); err != nil && Lchown {
|
||||
return err
|
||||
}
|
||||
|
||||
for key, value := range hdr.Xattrs {
|
||||
if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// There is no LChmod, so ignore mode for symlink. Also, this
|
||||
// must happen after chown, as that can modify the file mode
|
||||
if hdr.Typeflag != tar.TypeSymlink {
|
||||
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
|
||||
// syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and
|
||||
if hdr.Typeflag != tar.TypeSymlink {
|
||||
if err := system.UtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := system.LUtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tar creates an archive from the directory at `path`, and returns it as a
|
||||
// stream of bytes.
|
||||
func Tar(path string, compression Compression) (io.ReadCloser, error) {
|
||||
return TarWithOptions(path, &TarOptions{Compression: compression})
|
||||
func Tar(path string, compression Compression) (io.Reader, error) {
|
||||
return TarFilter(path, &TarOptions{Recursive: true, Compression: compression})
|
||||
}
|
||||
|
||||
func escapeName(name string) string {
|
||||
@@ -306,75 +107,59 @@ func escapeName(name string) string {
|
||||
return string(escaped)
|
||||
}
|
||||
|
||||
// TarWithOptions creates an archive from the directory at `path`, only including files whose relative
|
||||
// paths are included in `options.Includes` (if non-nil) or not in `options.Excludes`.
|
||||
func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
// Tar creates an archive from the directory at `path`, only including files whose relative
|
||||
// paths are included in `filter`. If `filter` is nil, then all files are included.
|
||||
func TarFilter(path string, options *TarOptions) (io.Reader, error) {
|
||||
args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path, "-T", "-"}
|
||||
if options.Includes == nil {
|
||||
options.Includes = []string{"."}
|
||||
}
|
||||
args = append(args, "-c"+options.Compression.Flag())
|
||||
|
||||
compressWriter, err := CompressStream(pipeWriter, options.Compression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, exclude := range options.Excludes {
|
||||
args = append(args, fmt.Sprintf("--exclude=%s", exclude))
|
||||
}
|
||||
|
||||
tw := tar.NewWriter(compressWriter)
|
||||
if !options.Recursive {
|
||||
args = append(args, "--no-recursion")
|
||||
}
|
||||
|
||||
go func() {
|
||||
// In general we log errors here but ignore them because
|
||||
// during e.g. a diff operation the container can continue
|
||||
// mutating the filesystem and we can see transient errors
|
||||
// from this
|
||||
files := ""
|
||||
for _, f := range options.Includes {
|
||||
files = files + escapeName(f) + "\n"
|
||||
}
|
||||
|
||||
if options.Includes == nil {
|
||||
options.Includes = []string{"."}
|
||||
tmpDir := ""
|
||||
|
||||
if options.CreateFiles != nil {
|
||||
var err error // Can't use := here or we override the outer tmpDir
|
||||
tmpDir, err = ioutil.TempDir("", "docker-tar")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, include := range options.Includes {
|
||||
filepath.Walk(filepath.Join(srcPath, include), func(filePath string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
utils.Debugf("Tar: Can't stat file %s to tar: %s\n", srcPath, err)
|
||||
return nil
|
||||
}
|
||||
files = files + "-C" + tmpDir + "\n"
|
||||
for _, f := range options.CreateFiles {
|
||||
path := filepath.Join(tmpDir, f)
|
||||
err := os.MkdirAll(filepath.Dir(path), 0600)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
relFilePath, err := filepath.Rel(srcPath, filePath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, exclude := range options.Excludes {
|
||||
matched, err := filepath.Match(exclude, relFilePath)
|
||||
if err != nil {
|
||||
utils.Errorf("Error matching: %s (pattern: %s)", relFilePath, exclude)
|
||||
return err
|
||||
}
|
||||
if matched {
|
||||
utils.Debugf("Skipping excluded path: %s", relFilePath)
|
||||
if f.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := addTarFile(filePath, relFilePath, tw); err != nil {
|
||||
utils.Debugf("Can't add file %s to tar: %s\n", srcPath, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if file, err := os.OpenFile(path, os.O_CREATE, 0600); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
file.Close()
|
||||
}
|
||||
files = files + escapeName(f) + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure to check the error on Close.
|
||||
if err := tw.Close(); err != nil {
|
||||
utils.Debugf("Can't close tar writer: %s\n", err)
|
||||
return CmdStream(exec.Command(args[0], args[1:]...), &files, func() {
|
||||
if tmpDir != "" {
|
||||
_ = os.RemoveAll(tmpDir)
|
||||
}
|
||||
if err := compressWriter.Close(); err != nil {
|
||||
utils.Debugf("Can't close compress writer: %s\n", err)
|
||||
}
|
||||
if err := pipeWriter.Close(); err != nil {
|
||||
utils.Debugf("Can't close pipe writer: %s\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return pipeReader, nil
|
||||
})
|
||||
}
|
||||
|
||||
// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
|
||||
@@ -382,107 +167,66 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
||||
// The archive may be compressed with one of the following algorithms:
|
||||
// identity (uncompressed), gzip, bzip2, xz.
|
||||
// FIXME: specify behavior when target path exists vs. doesn't exist.
|
||||
func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
||||
func Untar(archive io.Reader, path string, options *TarOptions) error {
|
||||
if archive == nil {
|
||||
return fmt.Errorf("Empty archive")
|
||||
}
|
||||
|
||||
decompressedArchive, err := DecompressStream(archive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer decompressedArchive.Close()
|
||||
|
||||
tr := tar.NewReader(decompressedArchive)
|
||||
|
||||
var dirs []*tar.Header
|
||||
|
||||
// Iterate through the files in the archive.
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
// end of tar archive
|
||||
break
|
||||
}
|
||||
buf := make([]byte, 10)
|
||||
totalN := 0
|
||||
for totalN < 10 {
|
||||
n, err := archive.Read(buf[totalN:])
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return fmt.Errorf("Tarball too short")
|
||||
}
|
||||
return err
|
||||
}
|
||||
totalN += n
|
||||
utils.Debugf("[tar autodetect] n: %d", n)
|
||||
}
|
||||
|
||||
// Normalize name, for safety and for a simple is-root check
|
||||
hdr.Name = filepath.Clean(hdr.Name)
|
||||
compression := DetectCompression(buf)
|
||||
|
||||
if !strings.HasSuffix(hdr.Name, "/") {
|
||||
// Not the root directory, ensure that the parent directory exists
|
||||
parent := filepath.Dir(hdr.Name)
|
||||
parentPath := filepath.Join(dest, parent)
|
||||
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
||||
err = os.MkdirAll(parentPath, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
utils.Debugf("Archive compression detected: %s", compression.Extension())
|
||||
args := []string{"--numeric-owner", "-f", "-", "-C", path, "-x" + compression.Flag()}
|
||||
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
|
||||
// If path exits we almost always just want to remove and replace it
|
||||
// The only exception is when it is a directory *and* the file from
|
||||
// the layer is also a directory. Then we want to merge them (i.e.
|
||||
// just apply the metadata from the layer).
|
||||
if fi, err := os.Lstat(path); err == nil {
|
||||
if fi.IsDir() && hdr.Name == "." {
|
||||
continue
|
||||
}
|
||||
if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := createTarFile(path, dest, hdr, tr, options == nil || !options.NoLchown); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Directory mtimes must be handled at the end to avoid further
|
||||
// file creation in them to modify the directory mtime
|
||||
if hdr.Typeflag == tar.TypeDir {
|
||||
dirs = append(dirs, hdr)
|
||||
if options != nil {
|
||||
for _, exclude := range options.Excludes {
|
||||
args = append(args, fmt.Sprintf("--exclude=%s", exclude))
|
||||
}
|
||||
}
|
||||
|
||||
for _, hdr := range dirs {
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
|
||||
if err := syscall.UtimesNano(path, ts); err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := exec.Command("tar", args...)
|
||||
cmd.Stdin = io.MultiReader(bytes.NewReader(buf), archive)
|
||||
// Hardcode locale environment for predictable outcome regardless of host configuration.
|
||||
// (see https://github.com/dotcloud/docker/issues/355)
|
||||
cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TarUntar is a convenience function which calls Tar and Untar, with
|
||||
// the output of one piped into the other. If either Tar or Untar fails,
|
||||
// TarUntar aborts and returns the error.
|
||||
func TarUntar(src string, dst string) error {
|
||||
utils.Debugf("TarUntar(%s %s)", src, dst)
|
||||
archive, err := TarWithOptions(src, &TarOptions{Compression: Uncompressed})
|
||||
func TarUntar(src string, filter []string, dst string) error {
|
||||
utils.Debugf("TarUntar(%s %s %s)", src, filter, dst)
|
||||
archive, err := TarFilter(src, &TarOptions{Compression: Uncompressed, Includes: filter, Recursive: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
return Untar(archive, dst, nil)
|
||||
}
|
||||
|
||||
// UntarPath is a convenience function which looks for an archive
|
||||
// at filesystem path `src`, and unpacks it at `dst`.
|
||||
func UntarPath(src, dst string) error {
|
||||
archive, err := os.Open(src)
|
||||
if err != nil {
|
||||
if archive, err := os.Open(src); err != nil {
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
if err := Untar(archive, dst, nil); err != nil {
|
||||
} else if err := Untar(archive, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -507,7 +251,7 @@ func CopyWithTar(src, dst string) error {
|
||||
return err
|
||||
}
|
||||
utils.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
||||
return TarUntar(src, dst)
|
||||
return TarUntar(src, nil, dst)
|
||||
}
|
||||
|
||||
// CopyFileWithTar emulates the behavior of the 'cp' command-line
|
||||
@@ -516,7 +260,7 @@ func CopyWithTar(src, dst string) error {
|
||||
//
|
||||
// If `dst` ends with a trailing slash '/', the final destination path
|
||||
// will be `dst/base(src)`.
|
||||
func CopyFileWithTar(src, dst string) (err error) {
|
||||
func CopyFileWithTar(src, dst string) error {
|
||||
utils.Debugf("CopyFileWithTar(%s, %s)", src, dst)
|
||||
srcSt, err := os.Stat(src)
|
||||
if err != nil {
|
||||
@@ -533,61 +277,57 @@ func CopyFileWithTar(src, dst string) (err error) {
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
r, w := io.Pipe()
|
||||
errC := utils.Go(func() error {
|
||||
defer w.Close()
|
||||
|
||||
srcF, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcF.Close()
|
||||
|
||||
tw := tar.NewWriter(w)
|
||||
hdr, err := tar.FileInfoHeader(srcSt, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Name = filepath.Base(dst)
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(tw, srcF); err != nil {
|
||||
return err
|
||||
}
|
||||
tw.Close()
|
||||
return nil
|
||||
})
|
||||
defer func() {
|
||||
if er := <-errC; err != nil {
|
||||
err = er
|
||||
}
|
||||
}()
|
||||
return Untar(r, filepath.Dir(dst), nil)
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
hdr, err := tar.FileInfoHeader(srcSt, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Name = filepath.Base(dst)
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
srcF, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(tw, srcF); err != nil {
|
||||
return err
|
||||
}
|
||||
tw.Close()
|
||||
return Untar(buf, filepath.Dir(dst), nil)
|
||||
}
|
||||
|
||||
// CmdStream executes a command, and returns its stdout as a stream.
|
||||
// If the command fails to run or doesn't complete successfully, an error
|
||||
// will be returned, including anything written on stderr.
|
||||
func CmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, error) {
|
||||
func CmdStream(cmd *exec.Cmd, input *string, atEnd func()) (io.Reader, error) {
|
||||
if input != nil {
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
if atEnd != nil {
|
||||
atEnd()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
// Write stdin if any
|
||||
go func() {
|
||||
io.Copy(stdin, input)
|
||||
_, _ = stdin.Write([]byte(*input))
|
||||
stdin.Close()
|
||||
}()
|
||||
}
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
if atEnd != nil {
|
||||
atEnd()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
if atEnd != nil {
|
||||
atEnd()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
pipeR, pipeW := io.Pipe()
|
||||
@@ -612,6 +352,9 @@ func CmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, error) {
|
||||
} else {
|
||||
pipeW.Close()
|
||||
}
|
||||
if atEnd != nil {
|
||||
atEnd()
|
||||
}
|
||||
}()
|
||||
// Run the command and return the pipe
|
||||
if err := cmd.Start(); err != nil {
|
||||
@@ -631,9 +374,6 @@ func NewTempArchive(src Archive, dir string) (*TempArchive, error) {
|
||||
if _, err := io.Copy(f, src); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = f.Sync(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+16
-99
@@ -10,13 +10,11 @@ import (
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
||||
)
|
||||
|
||||
func TestCmdStreamLargeStderr(t *testing.T) {
|
||||
cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
|
||||
out, err := CmdStream(cmd, nil)
|
||||
out, err := CmdStream(cmd, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start command: %s", err)
|
||||
}
|
||||
@@ -37,7 +35,7 @@ func TestCmdStreamLargeStderr(t *testing.T) {
|
||||
|
||||
func TestCmdStreamBad(t *testing.T) {
|
||||
badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
|
||||
out, err := CmdStream(badCmd, nil)
|
||||
out, err := CmdStream(badCmd, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start command: %s", err)
|
||||
}
|
||||
@@ -52,7 +50,7 @@ func TestCmdStreamBad(t *testing.T) {
|
||||
|
||||
func TestCmdStreamGood(t *testing.T) {
|
||||
cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0")
|
||||
out, err := CmdStream(cmd, nil)
|
||||
out, err := CmdStream(cmd, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -63,38 +61,35 @@ func TestCmdStreamGood(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func tarUntar(t *testing.T, origin string, options *TarOptions) ([]Change, error) {
|
||||
archive, err := TarWithOptions(origin, options)
|
||||
func tarUntar(t *testing.T, origin string, compression Compression) error {
|
||||
archive, err := Tar(origin, compression)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
buf := make([]byte, 10)
|
||||
if _, err := archive.Read(buf); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
wrap := io.MultiReader(bytes.NewReader(buf), archive)
|
||||
archive = io.MultiReader(bytes.NewReader(buf), archive)
|
||||
|
||||
detectedCompression := DetectCompression(buf)
|
||||
compression := options.Compression
|
||||
if detectedCompression.Extension() != compression.Extension() {
|
||||
return nil, fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
|
||||
return fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
|
||||
}
|
||||
|
||||
tmp, err := ioutil.TempDir("", "docker-test-untar")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
if err := Untar(wrap, tmp, nil); err != nil {
|
||||
return nil, err
|
||||
if err := Untar(archive, tmp, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := os.Stat(tmp); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
return ChangesDirs(origin, tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestTarUntar(t *testing.T) {
|
||||
@@ -113,89 +108,11 @@ func TestTarUntar(t *testing.T) {
|
||||
for _, c := range []Compression{
|
||||
Uncompressed,
|
||||
Gzip,
|
||||
Bzip2,
|
||||
Xz,
|
||||
} {
|
||||
changes, err := tarUntar(t, origin, &TarOptions{
|
||||
Compression: c,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if err := tarUntar(t, origin, c); err != nil {
|
||||
t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
|
||||
}
|
||||
|
||||
if len(changes) != 0 {
|
||||
t.Fatalf("Unexpected differences after tarUntar: %v", changes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTarWithOptions(t *testing.T) {
|
||||
origin, err := ioutil.TempDir("", "docker-test-untar-origin")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(origin)
|
||||
if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
opts *TarOptions
|
||||
numChanges int
|
||||
}{
|
||||
{&TarOptions{Includes: []string{"1"}}, 1},
|
||||
{&TarOptions{Excludes: []string{"2"}}, 1},
|
||||
}
|
||||
for _, testCase := range cases {
|
||||
changes, err := tarUntar(t, origin, testCase.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Error tar/untar when testing inclusion/exclusion: %s", err)
|
||||
}
|
||||
if len(changes) != testCase.numChanges {
|
||||
t.Errorf("Expected %d changes, got %d for %+v:",
|
||||
testCase.numChanges, len(changes), testCase.opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some tar archives such as http://haproxy.1wt.eu/download/1.5/src/devel/haproxy-1.5-dev21.tar.gz
|
||||
// use PAX Global Extended Headers.
|
||||
// Failing prevents the archives from being uncompressed during ADD
|
||||
func TestTypeXGlobalHeaderDoesNotFail(t *testing.T) {
|
||||
hdr := tar.Header{Typeflag: tar.TypeXGlobalHeader}
|
||||
err := createTarFile("pax_global_header", "some_dir", &hdr, nil, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Some tar have both GNU specific (huge uid) and Ustar specific (long name) things.
|
||||
// Not supposed to happen (should use PAX instead of Ustar for long name) but it does and it should still work.
|
||||
func TestUntarUstarGnuConflict(t *testing.T) {
|
||||
f, err := os.Open("testdata/broken.tar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
found := false
|
||||
tr := tar.NewReader(f)
|
||||
// Iterate through the files in the archive.
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
// end of tar archive
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if hdr.Name == "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("%s not found in the archive", "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm")
|
||||
}
|
||||
}
|
||||
|
||||
+30
-94
@@ -1,18 +1,11 @@
|
||||
package archive
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/dotcloud/docker/pkg/system"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
||||
)
|
||||
|
||||
type ChangeType int
|
||||
@@ -41,21 +34,6 @@ func (change *Change) String() string {
|
||||
return fmt.Sprintf("%s %s", kind, change.Path)
|
||||
}
|
||||
|
||||
// Gnu tar and the go tar writer don't have sub-second mtime
|
||||
// precision, which is problematic when we apply changes via tar
|
||||
// files, we handle this by comparing for exact times, *or* same
|
||||
// second count and either a or b having exactly 0 nanoseconds
|
||||
func sameFsTime(a, b time.Time) bool {
|
||||
return a == b ||
|
||||
(a.Unix() == b.Unix() &&
|
||||
(a.Nanosecond() == 0 || b.Nanosecond() == 0))
|
||||
}
|
||||
|
||||
func sameFsTimeSpec(a, b syscall.Timespec) bool {
|
||||
return a.Sec == b.Sec &&
|
||||
(a.Nsec == b.Nsec || a.Nsec == 0 || b.Nsec == 0)
|
||||
}
|
||||
|
||||
func Changes(layers []string, rw string) ([]Change, error) {
|
||||
var changes []Change
|
||||
err := filepath.Walk(rw, func(path string, f os.FileInfo, err error) error {
|
||||
@@ -107,7 +85,7 @@ func Changes(layers []string, rw string) ([]Change, error) {
|
||||
// However, if it's a directory, maybe it wasn't actually modified.
|
||||
// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
|
||||
if stat.IsDir() && f.IsDir() {
|
||||
if f.Size() == stat.Size() && f.Mode() == stat.Mode() && sameFsTime(f.ModTime(), stat.ModTime()) {
|
||||
if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
|
||||
// Both directories are the same, don't record the change
|
||||
return nil
|
||||
}
|
||||
@@ -129,11 +107,10 @@ func Changes(layers []string, rw string) ([]Change, error) {
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
parent *FileInfo
|
||||
name string
|
||||
stat syscall.Stat_t
|
||||
children map[string]*FileInfo
|
||||
capability []byte
|
||||
parent *FileInfo
|
||||
name string
|
||||
stat syscall.Stat_t
|
||||
children map[string]*FileInfo
|
||||
}
|
||||
|
||||
func (root *FileInfo) LookUp(path string) *FileInfo {
|
||||
@@ -204,8 +181,7 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
|
||||
oldStat.Rdev != newStat.Rdev ||
|
||||
// Don't look at size for dirs, its not a good measure of change
|
||||
(oldStat.Size != newStat.Size && oldStat.Mode&syscall.S_IFDIR != syscall.S_IFDIR) ||
|
||||
!sameFsTimeSpec(system.GetLastModification(oldStat), system.GetLastModification(newStat)) ||
|
||||
bytes.Compare(oldChild.capability, newChild.capability) != 0 {
|
||||
oldStat.Mtim != newStat.Mtim {
|
||||
change := Change{
|
||||
Path: newChild.path(),
|
||||
Kind: ChangeModify,
|
||||
@@ -280,8 +256,6 @@ func collectFileInfo(sourceDir string) (*FileInfo, error) {
|
||||
return err
|
||||
}
|
||||
|
||||
info.capability, _ = system.Lgetxattr(path, "security.capability")
|
||||
|
||||
parent.children[info.name] = info
|
||||
|
||||
return nil
|
||||
@@ -294,23 +268,13 @@ func collectFileInfo(sourceDir string) (*FileInfo, error) {
|
||||
|
||||
// Compare two directories and generate an array of Change objects describing the changes
|
||||
func ChangesDirs(newDir, oldDir string) ([]Change, error) {
|
||||
var (
|
||||
oldRoot, newRoot *FileInfo
|
||||
err1, err2 error
|
||||
errs = make(chan error, 2)
|
||||
)
|
||||
go func() {
|
||||
oldRoot, err1 = collectFileInfo(oldDir)
|
||||
errs <- err1
|
||||
}()
|
||||
go func() {
|
||||
newRoot, err2 = collectFileInfo(newDir)
|
||||
errs <- err2
|
||||
}()
|
||||
for i := 0; i < 2; i++ {
|
||||
if err := <-errs; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oldRoot, err := collectFileInfo(oldDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newRoot, err := collectFileInfo(newDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newRoot.Changes(oldRoot), nil
|
||||
@@ -330,52 +294,24 @@ func ChangesSize(newDir string, changes []Change) int64 {
|
||||
return size
|
||||
}
|
||||
|
||||
func major(device uint64) uint64 {
|
||||
return (device >> 8) & 0xfff
|
||||
}
|
||||
|
||||
func minor(device uint64) uint64 {
|
||||
return (device & 0xff) | ((device >> 12) & 0xfff00)
|
||||
}
|
||||
|
||||
func ExportChanges(dir string, changes []Change) (Archive, error) {
|
||||
reader, writer := io.Pipe()
|
||||
tw := tar.NewWriter(writer)
|
||||
|
||||
go func() {
|
||||
// In general we log errors here but ignore them because
|
||||
// during e.g. a diff operation the container can continue
|
||||
// mutating the filesystem and we can see transient errors
|
||||
// from this
|
||||
for _, change := range changes {
|
||||
if change.Kind == ChangeDelete {
|
||||
whiteOutDir := filepath.Dir(change.Path)
|
||||
whiteOutBase := filepath.Base(change.Path)
|
||||
whiteOut := filepath.Join(whiteOutDir, ".wh."+whiteOutBase)
|
||||
timestamp := time.Now()
|
||||
hdr := &tar.Header{
|
||||
Name: whiteOut[1:],
|
||||
Size: 0,
|
||||
ModTime: timestamp,
|
||||
AccessTime: timestamp,
|
||||
ChangeTime: timestamp,
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
utils.Debugf("Can't write whiteout header: %s\n", err)
|
||||
}
|
||||
} else {
|
||||
path := filepath.Join(dir, change.Path)
|
||||
if err := addTarFile(path, change.Path[1:], tw); err != nil {
|
||||
utils.Debugf("Can't add file %s to tar: %s\n", path, err)
|
||||
}
|
||||
}
|
||||
files := make([]string, 0)
|
||||
deletions := make([]string, 0)
|
||||
for _, change := range changes {
|
||||
if change.Kind == ChangeModify || change.Kind == ChangeAdd {
|
||||
files = append(files, change.Path)
|
||||
}
|
||||
|
||||
// Make sure to check the error on Close.
|
||||
if err := tw.Close(); err != nil {
|
||||
utils.Debugf("Can't close layer: %s\n", err)
|
||||
if change.Kind == ChangeDelete {
|
||||
base := filepath.Base(change.Path)
|
||||
dir := filepath.Dir(change.Path)
|
||||
deletions = append(deletions, filepath.Join(dir, ".wh."+base))
|
||||
}
|
||||
writer.Close()
|
||||
}()
|
||||
return reader, nil
|
||||
}
|
||||
// FIXME: Why do we create whiteout files inside Tar code ?
|
||||
return TarFilter(dir, &TarOptions{
|
||||
Compression: Uncompressed,
|
||||
Includes: files,
|
||||
Recursive: false,
|
||||
CreateFiles: deletions,
|
||||
})
|
||||
}
|
||||
|
||||
+23
-26
@@ -71,27 +71,17 @@ func createSampleDir(t *testing.T, root string) {
|
||||
{Symlink, "symlink1", "target1", 0666},
|
||||
{Symlink, "symlink2", "target2", 0666},
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
for _, info := range files {
|
||||
p := path.Join(root, info.path)
|
||||
if info.filetype == Dir {
|
||||
if err := os.MkdirAll(p, info.permissions); err != nil {
|
||||
if err := os.MkdirAll(path.Join(root, info.path), info.permissions); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else if info.filetype == Regular {
|
||||
if err := ioutil.WriteFile(p, []byte(info.contents), info.permissions); err != nil {
|
||||
if err := ioutil.WriteFile(path.Join(root, info.path), []byte(info.contents), info.permissions); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else if info.filetype == Symlink {
|
||||
if err := os.Symlink(info.contents, p); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if info.filetype != Symlink {
|
||||
// Set a consistent ctime, atime for all files and dirs
|
||||
if err := os.Chtimes(p, now, now); err != nil {
|
||||
if err := os.Symlink(info.contents, path.Join(root, info.path)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -138,7 +128,7 @@ func mutateSampleDir(t *testing.T, root string) {
|
||||
}
|
||||
|
||||
// Rewrite a file
|
||||
if err := ioutil.WriteFile(path.Join(root, "file2"), []byte("fileNN\n"), 0777); err != nil {
|
||||
if err := ioutil.WriteFile(path.Join(root, "file2"), []byte("fileN\n"), 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -146,12 +136,12 @@ func mutateSampleDir(t *testing.T, root string) {
|
||||
if err := os.RemoveAll(path.Join(root, "file3")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(path.Join(root, "file3"), []byte("fileMM\n"), 0404); err != nil {
|
||||
if err := ioutil.WriteFile(path.Join(root, "file3"), []byte("fileM\n"), 0404); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Touch file
|
||||
if err := os.Chtimes(path.Join(root, "file4"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
|
||||
if err := os.Chtimes(path.Join(root, "file4"), time.Now(), time.Now()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -195,7 +185,7 @@ func mutateSampleDir(t *testing.T, root string) {
|
||||
}
|
||||
|
||||
// Touch dir
|
||||
if err := os.Chtimes(path.Join(root, "dir3"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
|
||||
if err := os.Chtimes(path.Join(root, "dir3"), time.Now(), time.Now()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -210,9 +200,6 @@ func TestChangesDirsMutated(t *testing.T) {
|
||||
if err := copyDir(src, dst); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(src)
|
||||
defer os.RemoveAll(dst)
|
||||
|
||||
mutateSampleDir(t, dst)
|
||||
|
||||
changes, err := ChangesDirs(dst, src)
|
||||
@@ -238,7 +225,8 @@ func TestChangesDirsMutated(t *testing.T) {
|
||||
{"/symlinknew", ChangeAdd},
|
||||
}
|
||||
|
||||
for i := 0; i < max(len(changes), len(expectedChanges)); i++ {
|
||||
i := 0
|
||||
for ; i < max(len(changes), len(expectedChanges)); i++ {
|
||||
if i >= len(expectedChanges) {
|
||||
t.Fatalf("unexpected change %s\n", changes[i].String())
|
||||
}
|
||||
@@ -247,29 +235,35 @@ func TestChangesDirsMutated(t *testing.T) {
|
||||
}
|
||||
if changes[i].Path == expectedChanges[i].Path {
|
||||
if changes[i] != expectedChanges[i] {
|
||||
t.Fatalf("Wrong change for %s, expected %s, got %s\n", changes[i].Path, changes[i].String(), expectedChanges[i].String())
|
||||
t.Fatalf("Wrong change for %s, expected %s, got %d\n", changes[i].Path, changes[i].String(), expectedChanges[i].String())
|
||||
}
|
||||
} else if changes[i].Path < expectedChanges[i].Path {
|
||||
t.Fatalf("unexpected change %s\n", changes[i].String())
|
||||
} else {
|
||||
t.Fatalf("no change for expected change %s != %s\n", expectedChanges[i].String(), changes[i].String())
|
||||
t.Fatalf("no change for expected change %s\n", expectedChanges[i].String())
|
||||
}
|
||||
}
|
||||
for ; i < len(expectedChanges); i++ {
|
||||
}
|
||||
|
||||
os.RemoveAll(src)
|
||||
os.RemoveAll(dst)
|
||||
}
|
||||
|
||||
func TestApplyLayer(t *testing.T) {
|
||||
t.Skip("Skipping TestApplyLayer due to known failures") // Disable this for now as it is broken
|
||||
return
|
||||
|
||||
src, err := ioutil.TempDir("", "docker-changes-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
createSampleDir(t, src)
|
||||
defer os.RemoveAll(src)
|
||||
dst := src + "-copy"
|
||||
if err := copyDir(src, dst); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mutateSampleDir(t, dst)
|
||||
defer os.RemoveAll(dst)
|
||||
|
||||
changes, err := ChangesDirs(dst, src)
|
||||
if err != nil {
|
||||
@@ -296,6 +290,9 @@ func TestApplyLayer(t *testing.T) {
|
||||
}
|
||||
|
||||
if len(changes2) != 0 {
|
||||
t.Fatalf("Unexpected differences after reapplying mutation: %v", changes2)
|
||||
t.Fatalf("Unexpected differences after re applying mutation: %v", changes)
|
||||
}
|
||||
|
||||
os.RemoveAll(src)
|
||||
os.RemoveAll(dst)
|
||||
}
|
||||
|
||||
+72
-128
@@ -1,148 +1,92 @@
|
||||
package archive
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes.
|
||||
// They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major,
|
||||
// then the top 12 bits of the minor
|
||||
func mkdev(major int64, minor int64) uint32 {
|
||||
return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff))
|
||||
}
|
||||
|
||||
// ApplyLayer parses a diff in the standard layer format from `layer`, and
|
||||
// applies it to the directory `dest`.
|
||||
func ApplyLayer(dest string, layer ArchiveReader) error {
|
||||
// We need to be able to set any perms
|
||||
oldmask := syscall.Umask(0)
|
||||
defer syscall.Umask(oldmask)
|
||||
func ApplyLayer(dest string, layer Archive) error {
|
||||
// Poor man's diff applyer in 2 steps:
|
||||
|
||||
layer, err := DecompressStream(layer)
|
||||
// Step 1: untar everything in place
|
||||
if err := Untar(layer, dest, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
modifiedDirs := make(map[string]*syscall.Stat_t)
|
||||
addDir := func(file string) {
|
||||
d := filepath.Dir(file)
|
||||
if _, exists := modifiedDirs[d]; !exists {
|
||||
if s, err := os.Lstat(d); err == nil {
|
||||
if sys := s.Sys(); sys != nil {
|
||||
if stat, ok := sys.(*syscall.Stat_t); ok {
|
||||
modifiedDirs[d] = stat
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: walk for whiteouts and apply them, removing them in the process
|
||||
err := filepath.Walk(dest, func(fullPath string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// This happens in the case of whiteouts in parent dir removing a directory
|
||||
// We just ignore it
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Rebase path
|
||||
path, err := filepath.Rel(dest, fullPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path = filepath.Join("/", path)
|
||||
|
||||
// Skip AUFS metadata
|
||||
if matched, err := filepath.Match("/.wh..wh.*", path); err != nil {
|
||||
return err
|
||||
} else if matched {
|
||||
addDir(fullPath)
|
||||
if err := os.RemoveAll(fullPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
filename := filepath.Base(path)
|
||||
if strings.HasPrefix(filename, ".wh.") {
|
||||
rmTargetName := filename[len(".wh."):]
|
||||
rmTargetPath := filepath.Join(filepath.Dir(fullPath), rmTargetName)
|
||||
|
||||
// Remove the file targeted by the whiteout
|
||||
addDir(rmTargetPath)
|
||||
if err := os.RemoveAll(rmTargetPath); err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove the whiteout itself
|
||||
addDir(fullPath)
|
||||
if err := os.RemoveAll(fullPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tr := tar.NewReader(layer)
|
||||
for k, v := range modifiedDirs {
|
||||
aTime := time.Unix(v.Atim.Unix())
|
||||
mTime := time.Unix(v.Mtim.Unix())
|
||||
|
||||
var dirs []*tar.Header
|
||||
|
||||
aufsTempdir := ""
|
||||
aufsHardlinks := make(map[string]*tar.Header)
|
||||
|
||||
// Iterate through the files in the archive.
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
// end of tar archive
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Normalize name, for safety and for a simple is-root check
|
||||
hdr.Name = filepath.Clean(hdr.Name)
|
||||
|
||||
if !strings.HasSuffix(hdr.Name, "/") {
|
||||
// Not the root directory, ensure that the parent directory exists.
|
||||
// This happened in some tests where an image had a tarfile without any
|
||||
// parent directories.
|
||||
parent := filepath.Dir(hdr.Name)
|
||||
parentPath := filepath.Join(dest, parent)
|
||||
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
||||
err = os.MkdirAll(parentPath, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip AUFS metadata dirs
|
||||
if strings.HasPrefix(hdr.Name, ".wh..wh.") {
|
||||
// Regular files inside /.wh..wh.plnk can be used as hardlink targets
|
||||
// We don't want this directory, but we need the files in them so that
|
||||
// such hardlinks can be resolved.
|
||||
if strings.HasPrefix(hdr.Name, ".wh..wh.plnk") && hdr.Typeflag == tar.TypeReg {
|
||||
basename := filepath.Base(hdr.Name)
|
||||
aufsHardlinks[basename] = hdr
|
||||
if aufsTempdir == "" {
|
||||
if aufsTempdir, err = ioutil.TempDir("", "dockerplnk"); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(aufsTempdir)
|
||||
}
|
||||
if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
base := filepath.Base(path)
|
||||
if strings.HasPrefix(base, ".wh.") {
|
||||
originalBase := base[len(".wh."):]
|
||||
originalPath := filepath.Join(filepath.Dir(path), originalBase)
|
||||
if err := os.RemoveAll(originalPath); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// If path exits we almost always just want to remove and replace it.
|
||||
// The only exception is when it is a directory *and* the file from
|
||||
// the layer is also a directory. Then we want to merge them (i.e.
|
||||
// just apply the metadata from the layer).
|
||||
if fi, err := os.Lstat(path); err == nil {
|
||||
if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srcData := io.Reader(tr)
|
||||
srcHdr := hdr
|
||||
|
||||
// Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so
|
||||
// we manually retarget these into the temporary files we extracted them into
|
||||
if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), ".wh..wh.plnk") {
|
||||
linkBasename := filepath.Base(hdr.Linkname)
|
||||
srcHdr = aufsHardlinks[linkBasename]
|
||||
if srcHdr == nil {
|
||||
return fmt.Errorf("Invalid aufs hardlink")
|
||||
}
|
||||
tmpFile, err := os.Open(filepath.Join(aufsTempdir, linkBasename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tmpFile.Close()
|
||||
srcData = tmpFile
|
||||
}
|
||||
|
||||
if err := createTarFile(path, dest, srcHdr, srcData, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Directory mtimes must be handled at the end to avoid further
|
||||
// file creation in them to modify the directory mtime
|
||||
if hdr.Typeflag == tar.TypeDir {
|
||||
dirs = append(dirs, hdr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, hdr := range dirs {
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
|
||||
if err := syscall.UtimesNano(path, ts); err != nil {
|
||||
if err := os.Chtimes(k, aTime, mTime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -1,16 +0,0 @@
|
||||
package archive
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func timeToTimespec(time time.Time) (ts syscall.Timespec) {
|
||||
if time.IsZero() {
|
||||
// Return UTIME_OMIT special value
|
||||
ts.Sec = 0
|
||||
ts.Nsec = ((1 << 30) - 2)
|
||||
return
|
||||
}
|
||||
return syscall.NsecToTimespec(time.UnixNano())
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
package archive
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func timeToTimespec(time time.Time) (ts syscall.Timespec) {
|
||||
nsec := int64(0)
|
||||
if !time.IsZero() {
|
||||
nsec = time.UnixNano()
|
||||
}
|
||||
return syscall.NsecToTimespec(nsec)
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package archive
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Generate generates a new archive from the content provided
|
||||
// as input.
|
||||
//
|
||||
// `files` is a sequence of path/content pairs. A new file is
|
||||
// added to the archive for each pair.
|
||||
// If the last pair is incomplete, the file is created with an
|
||||
// empty content. For example:
|
||||
//
|
||||
// Generate("foo.txt", "hello world", "emptyfile")
|
||||
//
|
||||
// The above call will return an archive with 2 files:
|
||||
// * ./foo.txt with content "hello world"
|
||||
// * ./empty with empty content
|
||||
//
|
||||
// FIXME: stream content instead of buffering
|
||||
// FIXME: specify permissions and other archive metadata
|
||||
func Generate(input ...string) (Archive, error) {
|
||||
files := parseStringPairs(input...)
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
for _, file := range files {
|
||||
name, content := file[0], file[1]
|
||||
hdr := &tar.Header{
|
||||
Name: name,
|
||||
Size: int64(len(content)),
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := tw.Write([]byte(content)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := tw.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.NopCloser(buf), nil
|
||||
}
|
||||
|
||||
func parseStringPairs(input ...string) (output [][2]string) {
|
||||
output = make([][2]string, 0, len(input)/2+1)
|
||||
for i := 0; i < len(input); i += 2 {
|
||||
var pair [2]string
|
||||
pair[0] = input[i]
|
||||
if i+1 < len(input) {
|
||||
pair[1] = input[i+1]
|
||||
}
|
||||
output = append(output, pair)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
Sam Alba <sam@dotcloud.com> (@samalba)
|
||||
Joffrey Fuhrer <joffrey@dotcloud.com> (@shin-)
|
||||
Ken Cochrane <ken@dotcloud.com> (@kencochrane)
|
||||
@@ -1,17 +1,16 @@
|
||||
package registry
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/dotcloud/docker/utils"
|
||||
)
|
||||
|
||||
// Where we store the config file
|
||||
@@ -64,7 +63,7 @@ func decodeAuth(authStr string) (string, string, error) {
|
||||
if n > decLen {
|
||||
return "", "", fmt.Errorf("Something went wrong decoding auth config")
|
||||
}
|
||||
arr := strings.SplitN(string(decoded), ":", 2)
|
||||
arr := strings.Split(string(decoded), ":")
|
||||
if len(arr) != 2 {
|
||||
return "", "", fmt.Errorf("Invalid auth configuration file")
|
||||
}
|
||||
@@ -152,28 +151,19 @@ func SaveConfig(configFile *ConfigFile) error {
|
||||
|
||||
// try to register/login to the registry server
|
||||
func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, error) {
|
||||
var (
|
||||
status string
|
||||
reqBody []byte
|
||||
err error
|
||||
client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
},
|
||||
CheckRedirect: AddRequiredHeadersToRedirectedRequests,
|
||||
}
|
||||
reqStatusCode = 0
|
||||
serverAddress = authConfig.ServerAddress
|
||||
)
|
||||
client := &http.Client{}
|
||||
reqStatusCode := 0
|
||||
var status string
|
||||
var reqBody []byte
|
||||
|
||||
serverAddress := authConfig.ServerAddress
|
||||
if serverAddress == "" {
|
||||
serverAddress = IndexServerAddress()
|
||||
}
|
||||
|
||||
loginAgainstOfficialIndex := serverAddress == IndexServerAddress()
|
||||
|
||||
// to avoid sending the server address to the server it should be removed before being marshalled
|
||||
// to avoid sending the server address to the server it should be removed before marshalled
|
||||
authCopy := *authConfig
|
||||
authCopy.ServerAddress = ""
|
||||
|
||||
@@ -202,6 +192,13 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
|
||||
} else {
|
||||
status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it."
|
||||
}
|
||||
} else if reqStatusCode == 403 {
|
||||
if loginAgainstOfficialIndex {
|
||||
return "", fmt.Errorf("Login: Your account hasn't been activated. " +
|
||||
"Please check your e-mail for a confirmation link.")
|
||||
}
|
||||
return "", fmt.Errorf("Login: Your account hasn't been activated. " +
|
||||
"Please see the documentation of the registry " + serverAddress + " for instructions how to activate it.")
|
||||
} else if reqStatusCode == 400 {
|
||||
if string(reqBody) == "\"Username or email already exists\"" {
|
||||
req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
|
||||
@@ -219,39 +216,13 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
|
||||
status = "Login Succeeded"
|
||||
} else if resp.StatusCode == 401 {
|
||||
return "", fmt.Errorf("Wrong login/password, please try again")
|
||||
} else if resp.StatusCode == 403 {
|
||||
if loginAgainstOfficialIndex {
|
||||
return "", fmt.Errorf("Login: Account is not Active. Please check your e-mail for a confirmation link.")
|
||||
}
|
||||
return "", fmt.Errorf("Login: Account is not Active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)
|
||||
} else {
|
||||
return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, resp.StatusCode, resp.Header)
|
||||
return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
|
||||
resp.StatusCode, resp.Header)
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("Registration: %s", reqBody)
|
||||
}
|
||||
} else if reqStatusCode == 401 {
|
||||
// This case would happen with private registries where /v1/users is
|
||||
// protected, so people can use `docker login` as an auth check.
|
||||
req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
|
||||
req.SetBasicAuth(authConfig.Username, authConfig.Password)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resp.StatusCode == 200 {
|
||||
status = "Login Succeeded"
|
||||
} else if resp.StatusCode == 401 {
|
||||
return "", fmt.Errorf("Wrong login/password, please try again")
|
||||
} else {
|
||||
return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
|
||||
resp.StatusCode, resp.Header)
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody)
|
||||
}
|
||||
@@ -259,39 +230,50 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
|
||||
}
|
||||
|
||||
// this method matches a auth configuration to a server address or a url
|
||||
func (config *ConfigFile) ResolveAuthConfig(hostname string) AuthConfig {
|
||||
if hostname == IndexServerAddress() || len(hostname) == 0 {
|
||||
func (config *ConfigFile) ResolveAuthConfig(registry string) AuthConfig {
|
||||
if registry == IndexServerAddress() || len(registry) == 0 {
|
||||
// default to the index server
|
||||
return config.Configs[IndexServerAddress()]
|
||||
}
|
||||
// if its not the index server there are three cases:
|
||||
//
|
||||
// 1. this is a full config url -> it should be used as is
|
||||
// 2. it could be a full url, but with the wrong protocol
|
||||
// 3. it can be the hostname optionally with a port
|
||||
//
|
||||
// as there is only one auth entry which is fully qualified we need to start
|
||||
// parsing and matching
|
||||
|
||||
// First try the happy case
|
||||
if c, found := config.Configs[hostname]; found {
|
||||
return c
|
||||
}
|
||||
|
||||
convertToHostname := func(url string) string {
|
||||
stripped := url
|
||||
if strings.HasPrefix(url, "http://") {
|
||||
stripped = strings.Replace(url, "http://", "", 1)
|
||||
} else if strings.HasPrefix(url, "https://") {
|
||||
stripped = strings.Replace(url, "https://", "", 1)
|
||||
swapProtocol := func(url string) string {
|
||||
if strings.HasPrefix(url, "http:") {
|
||||
return strings.Replace(url, "http:", "https:", 1)
|
||||
}
|
||||
|
||||
nameParts := strings.SplitN(stripped, "/", 2)
|
||||
|
||||
return nameParts[0]
|
||||
}
|
||||
|
||||
// Maybe they have a legacy config file, we will iterate the keys converting
|
||||
// them to the new format and testing
|
||||
normalizedHostename := convertToHostname(hostname)
|
||||
for registry, config := range config.Configs {
|
||||
if registryHostname := convertToHostname(registry); registryHostname == normalizedHostename {
|
||||
return config
|
||||
if strings.HasPrefix(url, "https:") {
|
||||
return strings.Replace(url, "https:", "http:", 1)
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
// When all else fails, return an empty auth config
|
||||
return AuthConfig{}
|
||||
resolveIgnoringProtocol := func(url string) AuthConfig {
|
||||
if c, found := config.Configs[url]; found {
|
||||
return c
|
||||
}
|
||||
registrySwappedProtocol := swapProtocol(url)
|
||||
// now try to match with the different protocol
|
||||
if c, found := config.Configs[registrySwappedProtocol]; found {
|
||||
return c
|
||||
}
|
||||
return AuthConfig{}
|
||||
}
|
||||
|
||||
// match both protocols as it could also be a server name like httpfoo
|
||||
if strings.HasPrefix(registry, "http:") || strings.HasPrefix(registry, "https:") {
|
||||
return resolveIgnoringProtocol(registry)
|
||||
}
|
||||
|
||||
url := "https://" + registry
|
||||
if !strings.Contains(registry, "/") {
|
||||
url = url + "/v1/"
|
||||
}
|
||||
return resolveIgnoringProtocol(url)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package registry
|
||||
package auth
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@@ -108,7 +108,6 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
|
||||
}
|
||||
configFile.Configs["https://registry.example.com/v1/"] = registryAuth
|
||||
configFile.Configs["http://localhost:8000/v1/"] = localAuth
|
||||
configFile.Configs["registry.com"] = registryAuth
|
||||
|
||||
validRegistries := map[string][]string{
|
||||
"https://registry.example.com/v1/": {
|
||||
@@ -123,12 +122,6 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
|
||||
"localhost:8000",
|
||||
"localhost:8000/v1/",
|
||||
},
|
||||
"registry.com": {
|
||||
"https://registry.com/v1/",
|
||||
"http://registry.com/v1/",
|
||||
"registry.com",
|
||||
"registry.com/v1/",
|
||||
},
|
||||
}
|
||||
|
||||
for configKey, registries := range validRegistries {
|
||||
+546
@@ -0,0 +1,546 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/archive"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type BuildFile interface {
|
||||
Build(io.Reader) (string, error)
|
||||
CmdFrom(string) error
|
||||
CmdRun(string) error
|
||||
}
|
||||
|
||||
type buildFile struct {
|
||||
runtime *Runtime
|
||||
srv *Server
|
||||
|
||||
image string
|
||||
maintainer string
|
||||
config *Config
|
||||
context string
|
||||
verbose bool
|
||||
utilizeCache bool
|
||||
rm bool
|
||||
|
||||
tmpContainers map[string]struct{}
|
||||
tmpImages map[string]struct{}
|
||||
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func (b *buildFile) clearTmp(containers map[string]struct{}) {
|
||||
for c := range containers {
|
||||
tmp := b.runtime.Get(c)
|
||||
b.runtime.Destroy(tmp)
|
||||
fmt.Fprintf(b.out, "Removing intermediate container %s\n", utils.TruncateID(c))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdFrom(name string) error {
|
||||
image, err := b.runtime.repositories.LookupImage(name)
|
||||
if err != nil {
|
||||
if b.runtime.graph.IsNotExist(err) {
|
||||
remote, tag := utils.ParseRepositoryTag(name)
|
||||
if err := b.srv.ImagePull(remote, tag, b.out, utils.NewStreamFormatter(false), nil, nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
image, err = b.runtime.repositories.LookupImage(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b.image = image.ID
|
||||
b.config = &Config{}
|
||||
if image.Config != nil {
|
||||
b.config = image.Config
|
||||
}
|
||||
if b.config.Env == nil || len(b.config.Env) == 0 {
|
||||
b.config.Env = append(b.config.Env, "HOME=/", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdMaintainer(name string) error {
|
||||
b.maintainer = name
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("MAINTAINER %s", name))
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdRun(args string) error {
|
||||
if b.image == "" {
|
||||
return fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||
}
|
||||
config, _, _, err := ParseRun([]string{b.image, "/bin/sh", "-c", args}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := b.config.Cmd
|
||||
b.config.Cmd = nil
|
||||
MergeConfig(b.config, config)
|
||||
|
||||
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
|
||||
|
||||
utils.Debugf("Command to be executed: %v", b.config.Cmd)
|
||||
|
||||
if b.utilizeCache {
|
||||
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
|
||||
return err
|
||||
} else if cache != nil {
|
||||
fmt.Fprintf(b.out, " ---> Using cache\n")
|
||||
utils.Debugf("[BUILDER] Use cached version")
|
||||
b.image = cache.ID
|
||||
return nil
|
||||
} else {
|
||||
utils.Debugf("[BUILDER] Cache miss")
|
||||
}
|
||||
}
|
||||
|
||||
cid, err := b.run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.commit(cid, cmd, "run"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) FindEnvKey(key string) int {
|
||||
for k, envVar := range b.config.Env {
|
||||
envParts := strings.SplitN(envVar, "=", 2)
|
||||
if key == envParts[0] {
|
||||
return k
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (b *buildFile) ReplaceEnvMatches(value string) (string, error) {
|
||||
exp, err := regexp.Compile("(\\\\\\\\+|[^\\\\]|\\b|\\A)\\$({?)([[:alnum:]_]+)(}?)")
|
||||
if err != nil {
|
||||
return value, err
|
||||
}
|
||||
matches := exp.FindAllString(value, -1)
|
||||
for _, match := range matches {
|
||||
match = match[strings.Index(match, "$"):]
|
||||
matchKey := strings.Trim(match, "${}")
|
||||
|
||||
for _, envVar := range b.config.Env {
|
||||
envParts := strings.SplitN(envVar, "=", 2)
|
||||
envKey := envParts[0]
|
||||
envValue := envParts[1]
|
||||
|
||||
if envKey == matchKey {
|
||||
value = strings.Replace(value, match, envValue, -1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdEnv(args string) error {
|
||||
tmp := strings.SplitN(args, " ", 2)
|
||||
if len(tmp) != 2 {
|
||||
return fmt.Errorf("Invalid ENV format")
|
||||
}
|
||||
key := strings.Trim(tmp[0], " \t")
|
||||
value := strings.Trim(tmp[1], " \t")
|
||||
|
||||
envKey := b.FindEnvKey(key)
|
||||
replacedValue, err := b.ReplaceEnvMatches(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
replacedVar := fmt.Sprintf("%s=%s", key, replacedValue)
|
||||
|
||||
if envKey >= 0 {
|
||||
b.config.Env[envKey] = replacedVar
|
||||
} else {
|
||||
b.config.Env = append(b.config.Env, replacedVar)
|
||||
}
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("ENV %s", replacedVar))
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdCmd(args string) error {
|
||||
var cmd []string
|
||||
if err := json.Unmarshal([]byte(args), &cmd); err != nil {
|
||||
utils.Debugf("Error unmarshalling: %s, setting cmd to /bin/sh -c", err)
|
||||
cmd = []string{"/bin/sh", "-c", args}
|
||||
}
|
||||
if err := b.commit("", cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
|
||||
return err
|
||||
}
|
||||
b.config.Cmd = cmd
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdExpose(args string) error {
|
||||
ports := strings.Split(args, " ")
|
||||
b.config.PortSpecs = append(ports, b.config.PortSpecs...)
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdUser(args string) error {
|
||||
b.config.User = args
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("USER %v", args))
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdInsert(args string) error {
|
||||
return fmt.Errorf("INSERT has been deprecated. Please use ADD instead")
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdCopy(args string) error {
|
||||
return fmt.Errorf("COPY has been deprecated. Please use ADD instead")
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdEntrypoint(args string) error {
|
||||
if args == "" {
|
||||
return fmt.Errorf("Entrypoint cannot be empty")
|
||||
}
|
||||
|
||||
var entrypoint []string
|
||||
if err := json.Unmarshal([]byte(args), &entrypoint); err != nil {
|
||||
b.config.Entrypoint = []string{"/bin/sh", "-c", args}
|
||||
} else {
|
||||
b.config.Entrypoint = entrypoint
|
||||
}
|
||||
if err := b.commit("", b.config.Cmd, fmt.Sprintf("ENTRYPOINT %s", args)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdWorkdir(workdir string) error {
|
||||
b.config.WorkingDir = workdir
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdVolume(args string) error {
|
||||
if args == "" {
|
||||
return fmt.Errorf("Volume cannot be empty")
|
||||
}
|
||||
|
||||
var volume []string
|
||||
if err := json.Unmarshal([]byte(args), &volume); err != nil {
|
||||
volume = []string{args}
|
||||
}
|
||||
if b.config.Volumes == nil {
|
||||
b.config.Volumes = NewPathOpts()
|
||||
}
|
||||
for _, v := range volume {
|
||||
b.config.Volumes[v] = struct{}{}
|
||||
}
|
||||
if err := b.commit("", b.config.Cmd, fmt.Sprintf("VOLUME %s", args)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) addRemote(container *Container, orig, dest string) error {
|
||||
file, err := utils.Download(orig, ioutil.Discard)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Body.Close()
|
||||
|
||||
// If the destination is a directory, figure out the filename.
|
||||
if strings.HasSuffix(dest, "/") {
|
||||
u, err := url.Parse(orig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := u.Path
|
||||
if strings.HasSuffix(path, "/") {
|
||||
path = path[:len(path)-1]
|
||||
}
|
||||
parts := strings.Split(path, "/")
|
||||
filename := parts[len(parts)-1]
|
||||
if filename == "" {
|
||||
return fmt.Errorf("cannot determine filename from url: %s", u)
|
||||
}
|
||||
dest = dest + filename
|
||||
}
|
||||
|
||||
return container.Inject(file.Body, dest)
|
||||
}
|
||||
|
||||
func (b *buildFile) addContext(container *Container, orig, dest string) error {
|
||||
origPath := path.Join(b.context, orig)
|
||||
destPath := path.Join(container.RootfsPath(), dest)
|
||||
// Preserve the trailing '/'
|
||||
if strings.HasSuffix(dest, "/") {
|
||||
destPath = destPath + "/"
|
||||
}
|
||||
if !strings.HasPrefix(origPath, b.context) {
|
||||
return fmt.Errorf("Forbidden path: %s", origPath)
|
||||
}
|
||||
fi, err := os.Stat(origPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: no such file or directory", orig)
|
||||
}
|
||||
if fi.IsDir() {
|
||||
if err := archive.CopyWithTar(origPath, destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
// First try to unpack the source as an archive
|
||||
} else if err := archive.UntarPath(origPath, destPath); err != nil {
|
||||
utils.Debugf("Couldn't untar %s to %s: %s", origPath, destPath, err)
|
||||
// If that fails, just copy it as a regular file
|
||||
if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := archive.CopyWithTar(origPath, destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdAdd(args string) error {
|
||||
if b.context == "" {
|
||||
return fmt.Errorf("No context given. Impossible to use ADD")
|
||||
}
|
||||
tmp := strings.SplitN(args, " ", 2)
|
||||
if len(tmp) != 2 {
|
||||
return fmt.Errorf("Invalid ADD format")
|
||||
}
|
||||
|
||||
orig, err := b.ReplaceEnvMatches(strings.Trim(tmp[0], " \t"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dest, err := b.ReplaceEnvMatches(strings.Trim(tmp[1], " \t"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := b.config.Cmd
|
||||
b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)}
|
||||
|
||||
b.config.Image = b.image
|
||||
// Create the container and start it
|
||||
container, _, err := b.runtime.Create(b.config, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.tmpContainers[container.ID] = struct{}{}
|
||||
|
||||
if err := container.EnsureMounted(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer container.Unmount()
|
||||
|
||||
if utils.IsURL(orig) {
|
||||
if err := b.addRemote(container, orig, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := b.addContext(container, orig, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := b.commit(container.ID, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil {
|
||||
return err
|
||||
}
|
||||
b.config.Cmd = cmd
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) run() (string, error) {
|
||||
if b.image == "" {
|
||||
return "", fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||
}
|
||||
b.config.Image = b.image
|
||||
|
||||
// Create the container and start it
|
||||
c, _, err := b.runtime.Create(b.config, "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b.tmpContainers[c.ID] = struct{}{}
|
||||
fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(c.ID))
|
||||
|
||||
// override the entry point that may have been picked up from the base image
|
||||
c.Path = b.config.Cmd[0]
|
||||
c.Args = b.config.Cmd[1:]
|
||||
|
||||
var errCh chan error
|
||||
|
||||
if b.verbose {
|
||||
errCh = utils.Go(func() error {
|
||||
return <-c.Attach(nil, nil, b.out, b.out)
|
||||
})
|
||||
}
|
||||
|
||||
//start the container
|
||||
if err := c.Start(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if errCh != nil {
|
||||
if err := <-errCh; err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for it to finish
|
||||
if ret := c.Wait(); ret != 0 {
|
||||
return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret)
|
||||
}
|
||||
|
||||
return c.ID, nil
|
||||
}
|
||||
|
||||
// Commit the container <id> with the autorun command <autoCmd>
|
||||
func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
|
||||
if b.image == "" {
|
||||
return fmt.Errorf("Please provide a source image with `from` prior to commit")
|
||||
}
|
||||
b.config.Image = b.image
|
||||
if id == "" {
|
||||
cmd := b.config.Cmd
|
||||
b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
|
||||
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
|
||||
|
||||
if b.utilizeCache {
|
||||
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
|
||||
return err
|
||||
} else if cache != nil {
|
||||
fmt.Fprintf(b.out, " ---> Using cache\n")
|
||||
utils.Debugf("[BUILDER] Use cached version")
|
||||
b.image = cache.ID
|
||||
return nil
|
||||
} else {
|
||||
utils.Debugf("[BUILDER] Cache miss")
|
||||
}
|
||||
}
|
||||
|
||||
container, warnings, err := b.runtime.Create(b.config, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, warning := range warnings {
|
||||
fmt.Fprintf(b.out, " ---> [Warning] %s\n", warning)
|
||||
}
|
||||
b.tmpContainers[container.ID] = struct{}{}
|
||||
fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(container.ID))
|
||||
id = container.ID
|
||||
if err := container.EnsureMounted(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer container.Unmount()
|
||||
}
|
||||
|
||||
container := b.runtime.Get(id)
|
||||
if container == nil {
|
||||
return fmt.Errorf("An error occured while creating the container")
|
||||
}
|
||||
|
||||
// Note: Actually copy the struct
|
||||
autoConfig := *b.config
|
||||
autoConfig.Cmd = autoCmd
|
||||
// Commit the container
|
||||
image, err := b.runtime.Commit(container, "", "", "", b.maintainer, &autoConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.tmpImages[image.ID] = struct{}{}
|
||||
b.image = image.ID
|
||||
return nil
|
||||
}
|
||||
|
||||
// Long lines can be split with a backslash
|
||||
var lineContinuation = regexp.MustCompile(`\s*\\\s*\n`)
|
||||
|
||||
func (b *buildFile) Build(context io.Reader) (string, error) {
|
||||
// FIXME: @creack "name" is a terrible variable name
|
||||
name, err := ioutil.TempDir("", "docker-build")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := archive.Untar(context, name, nil); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.RemoveAll(name)
|
||||
b.context = name
|
||||
filename := path.Join(name, "Dockerfile")
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("Can't build a directory with no Dockerfile")
|
||||
}
|
||||
fileBytes, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dockerfile := string(fileBytes)
|
||||
dockerfile = lineContinuation.ReplaceAllString(dockerfile, "")
|
||||
stepN := 0
|
||||
for _, line := range strings.Split(dockerfile, "\n") {
|
||||
line = strings.Trim(strings.Replace(line, "\t", " ", -1), " \t\r\n")
|
||||
// Skip comments and empty line
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
tmp := strings.SplitN(line, " ", 2)
|
||||
if len(tmp) != 2 {
|
||||
return "", fmt.Errorf("Invalid Dockerfile format")
|
||||
}
|
||||
instruction := strings.ToLower(strings.Trim(tmp[0], " "))
|
||||
arguments := strings.Trim(tmp[1], " ")
|
||||
|
||||
method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
|
||||
if !exists {
|
||||
fmt.Fprintf(b.out, "# Skipping unknown instruction %s\n", strings.ToUpper(instruction))
|
||||
continue
|
||||
}
|
||||
|
||||
stepN += 1
|
||||
fmt.Fprintf(b.out, "Step %d : %s %s\n", stepN, strings.ToUpper(instruction), arguments)
|
||||
|
||||
ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
|
||||
if ret != nil {
|
||||
return "", ret.(error)
|
||||
}
|
||||
|
||||
fmt.Fprintf(b.out, " ---> %v\n", utils.TruncateID(b.image))
|
||||
}
|
||||
if b.image != "" {
|
||||
fmt.Fprintf(b.out, "Successfully built %s\n", utils.TruncateID(b.image))
|
||||
if b.rm {
|
||||
b.clearTmp(b.tmpContainers)
|
||||
}
|
||||
return b.image, nil
|
||||
}
|
||||
return "", fmt.Errorf("An error occurred during the build\n")
|
||||
}
|
||||
|
||||
func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache, rm bool) BuildFile {
|
||||
return &buildFile{
|
||||
runtime: srv.runtime,
|
||||
srv: srv,
|
||||
config: &Config{},
|
||||
out: out,
|
||||
tmpContainers: make(map[string]struct{}),
|
||||
tmpImages: make(map[string]struct{}),
|
||||
verbose: verbose,
|
||||
utilizeCache: utilizeCache,
|
||||
rm: rm,
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/dotcloud/docker/api"
|
||||
apiserver "github.com/dotcloud/docker/api/server"
|
||||
"github.com/dotcloud/docker/daemon/networkdriver/bridge"
|
||||
"github.com/dotcloud/docker/dockerversion"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/registry"
|
||||
"github.com/dotcloud/docker/server"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
)
|
||||
|
||||
func Register(eng *engine.Engine) error {
|
||||
if err := daemon(eng); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := remote(eng); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := eng.Register("version", dockerVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
return registry.NewService().Install(eng)
|
||||
}
|
||||
|
||||
// remote: a RESTful api for cross-docker communication
|
||||
func remote(eng *engine.Engine) error {
|
||||
if err := eng.Register("serveapi", apiserver.ServeApi); err != nil {
|
||||
return err
|
||||
}
|
||||
return eng.Register("acceptconnections", apiserver.AcceptConnections)
|
||||
}
|
||||
|
||||
// daemon: a default execution and storage backend for Docker on Linux,
|
||||
// with the following underlying components:
|
||||
//
|
||||
// * Pluggable storage drivers including aufs, vfs, lvm and btrfs.
|
||||
// * Pluggable execution drivers including lxc and chroot.
|
||||
//
|
||||
// In practice `daemon` still includes most core Docker components, including:
|
||||
//
|
||||
// * The reference registry client implementation
|
||||
// * Image management
|
||||
// * The build facility
|
||||
// * Logging
|
||||
//
|
||||
// These components should be broken off into plugins of their own.
|
||||
//
|
||||
func daemon(eng *engine.Engine) error {
|
||||
if err := eng.Register("initserver", server.InitServer); err != nil {
|
||||
return err
|
||||
}
|
||||
return eng.Register("init_networkdriver", bridge.InitDriver)
|
||||
}
|
||||
|
||||
// builtins jobs independent of any subsystem
|
||||
func dockerVersion(job *engine.Job) engine.Status {
|
||||
v := &engine.Env{}
|
||||
v.SetJson("Version", dockerversion.VERSION)
|
||||
v.SetJson("ApiVersion", api.APIVERSION)
|
||||
v.Set("GitCommit", dockerversion.GITCOMMIT)
|
||||
v.Set("GoVersion", runtime.Version())
|
||||
v.Set("Os", runtime.GOOS)
|
||||
v.Set("Arch", runtime.GOARCH)
|
||||
if kernelVersion, err := utils.GetKernelVersion(); err == nil {
|
||||
v.Set("KernelVersion", kernelVersion.String())
|
||||
}
|
||||
if _, err := v.WriteTo(job.Stdout); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return engine.StatusOK
|
||||
}
|
||||
+2565
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"net"
|
||||
)
|
||||
|
||||
// FIXME: separate runtime configuration from http api configuration
|
||||
type DaemonConfig struct {
|
||||
Pidfile string
|
||||
Root string
|
||||
AutoRestart bool
|
||||
EnableCors bool
|
||||
Dns []string
|
||||
EnableIptables bool
|
||||
BridgeIface string
|
||||
DefaultIp net.IP
|
||||
InterContainerCommunication bool
|
||||
GraphDriver string
|
||||
}
|
||||
|
||||
// ConfigFromJob creates and returns a new DaemonConfig object
|
||||
// by parsing the contents of a job's environment.
|
||||
func ConfigFromJob(job *engine.Job) *DaemonConfig {
|
||||
var config DaemonConfig
|
||||
config.Pidfile = job.Getenv("Pidfile")
|
||||
config.Root = job.Getenv("Root")
|
||||
config.AutoRestart = job.GetenvBool("AutoRestart")
|
||||
config.EnableCors = job.GetenvBool("EnableCors")
|
||||
if dns := job.Getenv("Dns"); dns != "" {
|
||||
config.Dns = []string{dns}
|
||||
}
|
||||
config.EnableIptables = job.GetenvBool("EnableIptables")
|
||||
if br := job.Getenv("BridgeIface"); br != "" {
|
||||
config.BridgeIface = br
|
||||
} else {
|
||||
config.BridgeIface = DefaultNetworkBridge
|
||||
}
|
||||
config.DefaultIp = net.ParseIP(job.Getenv("DefaultIp"))
|
||||
config.InterContainerCommunication = job.GetenvBool("InterContainerCommunication")
|
||||
config.GraphDriver = job.Getenv("GraphDriver")
|
||||
return &config
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCompareConfig(t *testing.T) {
|
||||
volumes1 := make(map[string]struct{})
|
||||
volumes1["/test1"] = struct{}{}
|
||||
config1 := Config{
|
||||
Dns: []string{"1.1.1.1", "2.2.2.2"},
|
||||
PortSpecs: []string{"1111:1111", "2222:2222"},
|
||||
Env: []string{"VAR1=1", "VAR2=2"},
|
||||
VolumesFrom: "11111111",
|
||||
Volumes: volumes1,
|
||||
}
|
||||
config2 := Config{
|
||||
Dns: []string{"0.0.0.0", "2.2.2.2"},
|
||||
PortSpecs: []string{"1111:1111", "2222:2222"},
|
||||
Env: []string{"VAR1=1", "VAR2=2"},
|
||||
VolumesFrom: "11111111",
|
||||
Volumes: volumes1,
|
||||
}
|
||||
config3 := Config{
|
||||
Dns: []string{"1.1.1.1", "2.2.2.2"},
|
||||
PortSpecs: []string{"0000:0000", "2222:2222"},
|
||||
Env: []string{"VAR1=1", "VAR2=2"},
|
||||
VolumesFrom: "11111111",
|
||||
Volumes: volumes1,
|
||||
}
|
||||
config4 := Config{
|
||||
Dns: []string{"1.1.1.1", "2.2.2.2"},
|
||||
PortSpecs: []string{"0000:0000", "2222:2222"},
|
||||
Env: []string{"VAR1=1", "VAR2=2"},
|
||||
VolumesFrom: "22222222",
|
||||
Volumes: volumes1,
|
||||
}
|
||||
volumes2 := make(map[string]struct{})
|
||||
volumes2["/test2"] = struct{}{}
|
||||
config5 := Config{
|
||||
Dns: []string{"1.1.1.1", "2.2.2.2"},
|
||||
PortSpecs: []string{"0000:0000", "2222:2222"},
|
||||
Env: []string{"VAR1=1", "VAR2=2"},
|
||||
VolumesFrom: "11111111",
|
||||
Volumes: volumes2,
|
||||
}
|
||||
if CompareConfig(&config1, &config2) {
|
||||
t.Fatalf("CompareConfig should return false, Dns are different")
|
||||
}
|
||||
if CompareConfig(&config1, &config3) {
|
||||
t.Fatalf("CompareConfig should return false, PortSpecs are different")
|
||||
}
|
||||
if CompareConfig(&config1, &config4) {
|
||||
t.Fatalf("CompareConfig should return false, VolumesFrom are different")
|
||||
}
|
||||
if CompareConfig(&config1, &config5) {
|
||||
t.Fatalf("CompareConfig should return false, Volumes are different")
|
||||
}
|
||||
if !CompareConfig(&config1, &config1) {
|
||||
t.Fatalf("CompareConfig should return true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeConfig(t *testing.T) {
|
||||
volumesImage := make(map[string]struct{})
|
||||
volumesImage["/test1"] = struct{}{}
|
||||
volumesImage["/test2"] = struct{}{}
|
||||
configImage := &Config{
|
||||
Dns: []string{"1.1.1.1", "2.2.2.2"},
|
||||
PortSpecs: []string{"1111:1111", "2222:2222"},
|
||||
Env: []string{"VAR1=1", "VAR2=2"},
|
||||
VolumesFrom: "1111",
|
||||
Volumes: volumesImage,
|
||||
}
|
||||
|
||||
volumesUser := make(map[string]struct{})
|
||||
volumesUser["/test3"] = struct{}{}
|
||||
configUser := &Config{
|
||||
Dns: []string{"3.3.3.3"},
|
||||
PortSpecs: []string{"3333:2222", "3333:3333"},
|
||||
Env: []string{"VAR2=3", "VAR3=3"},
|
||||
Volumes: volumesUser,
|
||||
}
|
||||
|
||||
if err := MergeConfig(configUser, configImage); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(configUser.Dns) != 3 {
|
||||
t.Fatalf("Expected 3 dns, 1.1.1.1, 2.2.2.2 and 3.3.3.3, found %d", len(configUser.Dns))
|
||||
}
|
||||
for _, dns := range configUser.Dns {
|
||||
if dns != "1.1.1.1" && dns != "2.2.2.2" && dns != "3.3.3.3" {
|
||||
t.Fatalf("Expected 1.1.1.1 or 2.2.2.2 or 3.3.3.3, found %s", dns)
|
||||
}
|
||||
}
|
||||
|
||||
if len(configUser.ExposedPorts) != 3 {
|
||||
t.Fatalf("Expected 3 ExposedPorts, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
|
||||
}
|
||||
for portSpecs := range configUser.ExposedPorts {
|
||||
if portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
|
||||
t.Fatalf("Expected 1111 or 2222 or 3333, found %s", portSpecs)
|
||||
}
|
||||
}
|
||||
if len(configUser.Env) != 3 {
|
||||
t.Fatalf("Expected 3 env var, VAR1=1, VAR2=3 and VAR3=3, found %d", len(configUser.Env))
|
||||
}
|
||||
for _, env := range configUser.Env {
|
||||
if env != "VAR1=1" && env != "VAR2=3" && env != "VAR3=3" {
|
||||
t.Fatalf("Expected VAR1=1 or VAR2=3 or VAR3=3, found %s", env)
|
||||
}
|
||||
}
|
||||
|
||||
if len(configUser.Volumes) != 3 {
|
||||
t.Fatalf("Expected 3 volumes, /test1, /test2 and /test3, found %d", len(configUser.Volumes))
|
||||
}
|
||||
for v := range configUser.Volumes {
|
||||
if v != "/test1" && v != "/test2" && v != "/test3" {
|
||||
t.Fatalf("Expected /test1 or /test2 or /test3, found %s", v)
|
||||
}
|
||||
}
|
||||
|
||||
if configUser.VolumesFrom != "1111" {
|
||||
t.Fatalf("Expected VolumesFrom to be 1111, found %s", configUser.VolumesFrom)
|
||||
}
|
||||
|
||||
ports, _, err := parsePortSpecs([]string{"0000"})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
configImage2 := &Config{
|
||||
ExposedPorts: ports,
|
||||
}
|
||||
|
||||
if err := MergeConfig(configUser, configImage2); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(configUser.ExposedPorts) != 4 {
|
||||
t.Fatalf("Expected 4 ExposedPorts, 0000, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
|
||||
}
|
||||
for portSpecs := range configUser.ExposedPorts {
|
||||
if portSpecs.Port() != "0000" && portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
|
||||
t.Fatalf("Expected 0000 or 1111 or 2222 or 3333, found %s", portSpecs)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1407
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,28 @@
|
||||
package daemon
|
||||
package docker
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/nat"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseLxcConfOpt(t *testing.T) {
|
||||
opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
|
||||
|
||||
for _, o := range opts {
|
||||
k, v, err := parseLxcOpt(o)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
if k != "lxc.utsname" {
|
||||
t.Fail()
|
||||
}
|
||||
if v != "docker" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNetworkOptsPrivateOnly(t *testing.T) {
|
||||
ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::80"})
|
||||
ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::80"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -48,7 +64,7 @@ func TestParseNetworkOptsPrivateOnly(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseNetworkOptsPublic(t *testing.T) {
|
||||
ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100:8080:80"})
|
||||
ports, bindings, err := parsePortSpecs([]string{"192.168.1.100:8080:80"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -90,7 +106,7 @@ func TestParseNetworkOptsPublic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseNetworkOptsUdp(t *testing.T) {
|
||||
ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::6000/udp"})
|
||||
ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::6000/udp"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -132,14 +148,14 @@ func TestParseNetworkOptsUdp(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetFullName(t *testing.T) {
|
||||
name, err := GetFullContainerName("testing")
|
||||
name, err := getFullName("testing")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if name != "/testing" {
|
||||
t.Fatalf("Expected /testing got %s", name)
|
||||
}
|
||||
if _, err := GetFullContainerName(""); err == nil {
|
||||
if _, err := getFullName(""); err == nil {
|
||||
t.Fatal("Error should not be nil")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# [PackageDev] target_format: plist, ext: tmLanguage
|
||||
---
|
||||
name: Dockerfile
|
||||
scopeName: source.dockerfile
|
||||
uuid: a39d8795-59d2-49af-aa00-fe74ee29576e
|
||||
|
||||
patterns:
|
||||
# Keywords
|
||||
- name: keyword.control.dockerfile
|
||||
match: ^\s*(FROM|MAINTAINER|RUN|CMD|EXPOSE|ENV|ADD)\s
|
||||
- name: keyword.operator.dockerfile
|
||||
match: ^\s*(ENTRYPOINT|VOLUME|USER|WORKDIR)\s
|
||||
# String
|
||||
- name: string.quoted.double.dockerfile
|
||||
begin: "\""
|
||||
end: "\""
|
||||
patterns:
|
||||
- name: constant.character.escaped.dockerfile
|
||||
match: \\.
|
||||
# Comment
|
||||
- name: comment.block.dockerfile
|
||||
match: ^\s*#.*$
|
||||
...
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Dockerfile</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*(FROM|MAINTAINER|RUN|CMD|EXPOSE|ENV|ADD)\s</string>
|
||||
<key>name</key>
|
||||
<string>keyword.control.dockerfile</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*(ENTRYPOINT|VOLUME|USER|WORKDIR)\s</string>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.dockerfile</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>"</string>
|
||||
<key>end</key>
|
||||
<string>"</string>
|
||||
<key>name</key>
|
||||
<string>string.quoted.double.dockerfile</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\.</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escaped.dockerfile</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*#.*$</string>
|
||||
<key>name</key>
|
||||
<string>comment.block.dockerfile</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>scopeName</key>
|
||||
<string>source.dockerfile</string>
|
||||
<key>uuid</key>
|
||||
<string>a39d8795-59d2-49af-aa00-fe74ee29576e</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,9 @@
|
||||
# Dockerfile.tmLanguage
|
||||
|
||||
Pretty basic Dockerfile.tmLanguage for Sublime Text syntax highlighting.
|
||||
|
||||
PR's with syntax updates, suggestions etc. are all very much appreciated!
|
||||
|
||||
I'll get to making this installable via Package Control soon!
|
||||
|
||||
enjoy.
|
||||
@@ -1,153 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# bits of this were adapted from lxc-checkconfig
|
||||
# see also https://github.com/lxc/lxc/blob/lxc-1.0.2/src/lxc/lxc-checkconfig.in
|
||||
|
||||
possibleConfigs=(
|
||||
'/proc/config.gz'
|
||||
"/boot/config-$(uname -r)"
|
||||
"/usr/src/linux-$(uname -r)/.config"
|
||||
'/usr/src/linux/.config'
|
||||
)
|
||||
: ${CONFIG:="${possibleConfigs[0]}"}
|
||||
|
||||
if ! command -v zgrep &> /dev/null; then
|
||||
zgrep() {
|
||||
zcat "$2" | grep "$1"
|
||||
}
|
||||
fi
|
||||
|
||||
is_set() {
|
||||
zgrep "CONFIG_$1=[y|m]" "$CONFIG" > /dev/null
|
||||
}
|
||||
|
||||
# see http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
||||
declare -A colors=(
|
||||
[black]=30
|
||||
[red]=31
|
||||
[green]=32
|
||||
[yellow]=33
|
||||
[blue]=34
|
||||
[magenta]=35
|
||||
[cyan]=36
|
||||
[white]=37
|
||||
)
|
||||
color() {
|
||||
color=()
|
||||
if [ "$1" = 'bold' ]; then
|
||||
color+=( '1' )
|
||||
shift
|
||||
fi
|
||||
if [ $# -gt 0 ] && [ "${colors[$1]}" ]; then
|
||||
color+=( "${colors[$1]}" )
|
||||
fi
|
||||
local IFS=';'
|
||||
echo -en '\033['"${color[*]}"m
|
||||
}
|
||||
wrap_color() {
|
||||
text="$1"
|
||||
shift
|
||||
color "$@"
|
||||
echo -n "$text"
|
||||
color reset
|
||||
echo
|
||||
}
|
||||
|
||||
wrap_good() {
|
||||
echo "$(wrap_color "$1" white): $(wrap_color "$2" green)"
|
||||
}
|
||||
wrap_bad() {
|
||||
echo "$(wrap_color "$1" bold): $(wrap_color "$2" bold red)"
|
||||
}
|
||||
wrap_warning() {
|
||||
wrap_color >&2 "$*" red
|
||||
}
|
||||
|
||||
check_flag() {
|
||||
if is_set "$1"; then
|
||||
wrap_good "CONFIG_$1" 'enabled'
|
||||
else
|
||||
wrap_bad "CONFIG_$1" 'missing'
|
||||
fi
|
||||
}
|
||||
|
||||
check_flags() {
|
||||
for flag in "$@"; do
|
||||
echo "- $(check_flag "$flag")"
|
||||
done
|
||||
}
|
||||
|
||||
if [ ! -e "$CONFIG" ]; then
|
||||
wrap_warning "warning: $CONFIG does not exist, searching other paths for kernel config..."
|
||||
for tryConfig in "${possibleConfigs[@]}"; do
|
||||
if [ -e "$tryConfig" ]; then
|
||||
CONFIG="$tryConfig"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ ! -e "$CONFIG" ]; then
|
||||
wrap_warning "error: cannot find kernel config"
|
||||
wrap_warning " try running this script again, specifying the kernel config:"
|
||||
wrap_warning " CONFIG=/path/to/kernel/.config $0"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
wrap_color "info: reading kernel config from $CONFIG ..." white
|
||||
echo
|
||||
|
||||
echo 'Generally Necessary:'
|
||||
|
||||
echo -n '- '
|
||||
cgroupSubsystemDir="$(awk '/[, ](cpu|cpuacct|cpuset|devices|freezer|memory)[, ]/ && $3 == "cgroup" { print $2 }' /proc/mounts | head -n1)"
|
||||
cgroupDir="$(dirname "$cgroupSubsystemDir")"
|
||||
if [ -d "$cgroupDir/cpu" -o -d "$cgroupDir/cpuacct" -o -d "$cgroupDir/cpuset" -o -d "$cgroupDir/devices" -o -d "$cgroupDir/freezer" -o -d "$cgroupDir/memory" ]; then
|
||||
echo "$(wrap_good 'cgroup hierarchy' 'properly mounted') [$cgroupDir]"
|
||||
else
|
||||
if [ "$cgroupSubsystemDir" ]; then
|
||||
echo "$(wrap_bad 'cgroup hierarchy' 'single mountpoint!') [$cgroupSubsystemDir]"
|
||||
else
|
||||
echo "$(wrap_bad 'cgroup hierarchy' 'nonexistent??')"
|
||||
fi
|
||||
echo " $(wrap_color '(see https://github.com/tianon/cgroupfs-mount)' yellow)"
|
||||
fi
|
||||
|
||||
flags=(
|
||||
NAMESPACES {NET,PID,IPC,UTS}_NS
|
||||
DEVPTS_MULTIPLE_INSTANCES
|
||||
CGROUPS CGROUP_CPUACCT CGROUP_DEVICE CGROUP_FREEZER CGROUP_SCHED
|
||||
MACVLAN VETH BRIDGE
|
||||
NF_NAT_IPV4 IP_NF_TARGET_MASQUERADE
|
||||
NETFILTER_XT_MATCH_{ADDRTYPE,CONNTRACK}
|
||||
NF_NAT NF_NAT_NEEDED
|
||||
)
|
||||
check_flags "${flags[@]}"
|
||||
echo
|
||||
|
||||
echo 'Optional Features:'
|
||||
flags=(
|
||||
MEMCG_SWAP
|
||||
RESOURCE_COUNTERS
|
||||
)
|
||||
check_flags "${flags[@]}"
|
||||
|
||||
echo '- Storage Drivers:'
|
||||
{
|
||||
echo '- "'$(wrap_color 'aufs' blue)'":'
|
||||
check_flags AUFS_FS | sed 's/^/ /'
|
||||
if ! is_set AUFS_FS && grep -q aufs /proc/filesystems; then
|
||||
echo " $(wrap_color '(note that some kernels include AUFS patches but not the AUFS_FS flag)' bold black)"
|
||||
fi
|
||||
|
||||
echo '- "'$(wrap_color 'btrfs' blue)'":'
|
||||
check_flags BTRFS_FS | sed 's/^/ /'
|
||||
|
||||
echo '- "'$(wrap_color 'devicemapper' blue)'":'
|
||||
check_flags BLK_DEV_DM DM_THIN_PROVISIONING EXT4_FS | sed 's/^/ /'
|
||||
} | sed 's/^/ /'
|
||||
echo
|
||||
|
||||
#echo 'Potential Future Features:'
|
||||
#check_flags USER_NS
|
||||
#echo
|
||||
+129
-276
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# This script provides supports completion of:
|
||||
# - commands and their options
|
||||
# - container ids and names
|
||||
# - container ids
|
||||
# - image repos and tags
|
||||
# - filepaths
|
||||
#
|
||||
@@ -21,88 +21,60 @@
|
||||
# If the docker daemon is using a unix socket for communication your user
|
||||
# must have access to the socket for the completions to function correctly
|
||||
|
||||
__docker_q() {
|
||||
docker 2>/dev/null "$@"
|
||||
}
|
||||
|
||||
__docker_containers_all()
|
||||
{
|
||||
local containers="$( __docker_q ps -a -q )"
|
||||
local names="$( __docker_q inspect --format '{{.Name}}' $containers | sed 's,^/,,' )"
|
||||
COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) )
|
||||
local containers
|
||||
containers="$( docker ps -a -q )"
|
||||
COMPREPLY=( $( compgen -W "$containers" -- "$cur" ) )
|
||||
}
|
||||
|
||||
__docker_containers_running()
|
||||
{
|
||||
local containers="$( __docker_q ps -q )"
|
||||
local names="$( __docker_q inspect --format '{{.Name}}' $containers | sed 's,^/,,' )"
|
||||
COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) )
|
||||
local containers
|
||||
containers="$( docker ps -q )"
|
||||
COMPREPLY=( $( compgen -W "$containers" -- "$cur" ) )
|
||||
}
|
||||
|
||||
__docker_containers_stopped()
|
||||
{
|
||||
local containers="$( { __docker_q ps -a -q; __docker_q ps -q; } | sort | uniq -u )"
|
||||
local names="$( __docker_q inspect --format '{{.Name}}' $containers | sed 's,^/,,' )"
|
||||
COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) )
|
||||
local containers
|
||||
containers="$( comm -13 <(docker ps -q | sort -u) <(docker ps -a -q | sort -u) )"
|
||||
COMPREPLY=( $( compgen -W "$containers" -- "$cur" ) )
|
||||
}
|
||||
|
||||
__docker_image_repos()
|
||||
{
|
||||
local repos="$( __docker_q images | awk 'NR>1{print $1}' | grep -v '^<none>$' )"
|
||||
local repos
|
||||
repos="$( docker images | awk 'NR>1{print $1}' )"
|
||||
COMPREPLY=( $( compgen -W "$repos" -- "$cur" ) )
|
||||
}
|
||||
|
||||
__docker_images()
|
||||
{
|
||||
local images
|
||||
images="$( docker images | awk 'NR>1{print $1":"$2}' )"
|
||||
COMPREPLY=( $( compgen -W "$images" -- "$cur" ) )
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
__docker_image_repos_and_tags()
|
||||
{
|
||||
local repos="$( __docker_q images | awk 'NR>1{print $1}' | grep -v '^<none>$' )"
|
||||
local images="$( __docker_q images | awk 'NR>1{print $1":"$2}' | grep -v '^<none>:' )"
|
||||
local repos images
|
||||
repos="$( docker images | awk 'NR>1{print $1}' )"
|
||||
images="$( docker images | awk 'NR>1{print $1":"$2}' )"
|
||||
COMPREPLY=( $( compgen -W "$repos $images" -- "$cur" ) )
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
__docker_image_repos_and_tags_and_ids()
|
||||
{
|
||||
local repos="$( __docker_q images | awk 'NR>1{print $1}' | grep -v '^<none>$' )"
|
||||
local images="$( __docker_q images | awk 'NR>1{print $1":"$2}' | grep -v '^<none>:' )"
|
||||
local ids="$( __docker_q images -a -q )"
|
||||
COMPREPLY=( $( compgen -W "$repos $images $ids" -- "$cur" ) )
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
__docker_containers_and_images()
|
||||
{
|
||||
local containers="$( __docker_q ps -a -q )"
|
||||
local names="$( __docker_q inspect --format '{{.Name}}' $containers | sed 's,^/,,' )"
|
||||
local repos="$( __docker_q images | awk 'NR>1{print $1}' | grep -v '^<none>$' )"
|
||||
local images="$( __docker_q images | awk 'NR>1{print $1":"$2}' | grep -v '^<none>:' )"
|
||||
local ids="$( __docker_q images -a -q )"
|
||||
COMPREPLY=( $( compgen -W "$containers $names $repos $images $ids" -- "$cur" ) )
|
||||
local containers images
|
||||
containers="$( docker ps -a -q )"
|
||||
images="$( docker images | awk 'NR>1{print $1":"$2}' )"
|
||||
COMPREPLY=( $( compgen -W "$images $containers" -- "$cur" ) )
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
__docker_pos_first_nonflag()
|
||||
{
|
||||
local argument_flags=$1
|
||||
|
||||
local counter=$cpos
|
||||
while [ $counter -le $cword ]; do
|
||||
if [ -n "$argument_flags" ] && eval "case '${words[$counter]}' in $argument_flags) true ;; *) false ;; esac"; then
|
||||
(( counter++ ))
|
||||
else
|
||||
case "${words[$counter]}" in
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
(( counter++ ))
|
||||
done
|
||||
|
||||
echo $counter
|
||||
}
|
||||
|
||||
_docker_docker()
|
||||
{
|
||||
case "$prev" in
|
||||
@@ -125,24 +97,15 @@ _docker_docker()
|
||||
|
||||
_docker_attach()
|
||||
{
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--no-stdin --sig-proxy" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter="$(__docker_pos_first_nonflag)"
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_containers_running
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_running
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_build()
|
||||
{
|
||||
case "$prev" in
|
||||
-t|--tag)
|
||||
__docker_image_repos_and_tags
|
||||
-t)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
@@ -151,13 +114,10 @@ _docker_build()
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-t --tag -q --quiet --no-cache --rm" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "-no-cache -t -q -rm" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter="$(__docker_pos_first_nonflag '-t|--tag')"
|
||||
if [ $cword -eq $counter ]; then
|
||||
_filedir
|
||||
fi
|
||||
_filedir
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -165,7 +125,7 @@ _docker_build()
|
||||
_docker_commit()
|
||||
{
|
||||
case "$prev" in
|
||||
-m|--message|-a|--author|--run)
|
||||
-author|-m|-run)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
@@ -174,20 +134,26 @@ _docker_commit()
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-m --message -a --author --run" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "-author -m -run" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '-m|--message|-a|--author|--run')
|
||||
local counter=$cpos
|
||||
while [ $counter -le $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
-author|-m|-run)
|
||||
(( counter++ ))
|
||||
;;
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
(( counter++ ))
|
||||
done
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ $counter -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
return
|
||||
fi
|
||||
(( counter++ ))
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_image_repos_and_tags
|
||||
return
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
@@ -195,32 +161,16 @@ _docker_commit()
|
||||
|
||||
_docker_cp()
|
||||
{
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
case "$cur" in
|
||||
*:)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
__docker_containers_all
|
||||
COMPREPLY=( $( compgen -W "${COMPREPLY[*]}" -S ':' ) )
|
||||
compopt -o nospace
|
||||
return
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
(( counter++ ))
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
else
|
||||
_filedir
|
||||
return
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_diff()
|
||||
{
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
fi
|
||||
}
|
||||
@@ -228,7 +178,7 @@ _docker_diff()
|
||||
_docker_events()
|
||||
{
|
||||
case "$prev" in
|
||||
--since)
|
||||
-since)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
@@ -237,7 +187,7 @@ _docker_events()
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--since" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "-since" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
@@ -246,44 +196,45 @@ _docker_events()
|
||||
|
||||
_docker_export()
|
||||
{
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_help()
|
||||
{
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ $cpos -eq $cword ]; then
|
||||
COMPREPLY=( $( compgen -W "$commands" -- "$cur" ) )
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_history()
|
||||
{
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-q --quiet --no-trunc" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_image_repos_and_tags
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_images()
|
||||
{
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-q --quiet -a --all --no-trunc -v --viz -t --tree" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "-a -notrunc -q -viz" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
local counter=$cpos
|
||||
while [ $counter -le $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
(( counter++ ))
|
||||
done
|
||||
|
||||
if [ $counter -eq $cword ]; then
|
||||
__docker_image_repos
|
||||
fi
|
||||
;;
|
||||
@@ -292,16 +243,7 @@ _docker_images()
|
||||
|
||||
_docker_import()
|
||||
{
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
return
|
||||
fi
|
||||
(( counter++ ))
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_image_repos_and_tags
|
||||
return
|
||||
fi
|
||||
return
|
||||
}
|
||||
|
||||
_docker_info()
|
||||
@@ -309,24 +251,16 @@ _docker_info()
|
||||
return
|
||||
}
|
||||
|
||||
_docker_insert()
|
||||
{
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_image_repos_and_tags
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_inspect()
|
||||
{
|
||||
case "$prev" in
|
||||
-f|--format)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-f --format" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_containers_and_images
|
||||
;;
|
||||
esac
|
||||
__docker_containers_and_images
|
||||
}
|
||||
|
||||
_docker_kill()
|
||||
@@ -334,15 +268,10 @@ _docker_kill()
|
||||
__docker_containers_running
|
||||
}
|
||||
|
||||
_docker_load()
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
_docker_login()
|
||||
{
|
||||
case "$prev" in
|
||||
-u|--username|-p|--password|-e|--email)
|
||||
-e|-p|-u)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
@@ -351,7 +280,7 @@ _docker_login()
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-u --username -p --password -e --email" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "-e -p -u" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
@@ -360,23 +289,14 @@ _docker_login()
|
||||
|
||||
_docker_logs()
|
||||
{
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-f --follow" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_containers_all
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_port()
|
||||
{
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
fi
|
||||
}
|
||||
@@ -384,10 +304,7 @@ _docker_port()
|
||||
_docker_ps()
|
||||
{
|
||||
case "$prev" in
|
||||
--since|--before)
|
||||
__docker_containers_all
|
||||
;;
|
||||
-n)
|
||||
-beforeId|-n|-sinceId)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
@@ -396,7 +313,7 @@ _docker_ps()
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-q --quiet -s --size -a --all --no-trunc -l --latest --since --before -n" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "-a -beforeId -l -n -notrunc -q -s -sinceId" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
@@ -406,7 +323,7 @@ _docker_ps()
|
||||
_docker_pull()
|
||||
{
|
||||
case "$prev" in
|
||||
-t|--tag)
|
||||
-t)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
@@ -415,29 +332,22 @@ _docker_pull()
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-t --tag" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "-t" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '-t|--tag')
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_image_repos_and_tags
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_push()
|
||||
{
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_image_repos_and_tags
|
||||
fi
|
||||
__docker_image_repos
|
||||
}
|
||||
|
||||
_docker_restart()
|
||||
{
|
||||
case "$prev" in
|
||||
-t|--time)
|
||||
-t)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
@@ -446,7 +356,7 @@ _docker_restart()
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-t --time" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "-t" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_containers_all
|
||||
@@ -458,48 +368,29 @@ _docker_rm()
|
||||
{
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-f --force -l --link -v --volumes" -- "$cur" ) )
|
||||
return
|
||||
COMPREPLY=( $( compgen -W "-v" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local force=
|
||||
for arg in "${COMP_WORDS[@]}"; do
|
||||
case "$arg" in
|
||||
-f|--force)
|
||||
__docker_containers_all
|
||||
return
|
||||
;;
|
||||
esac
|
||||
done
|
||||
__docker_containers_stopped
|
||||
return
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_rmi()
|
||||
{
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
__docker_image_repos_and_tags
|
||||
}
|
||||
|
||||
_docker_run()
|
||||
{
|
||||
case "$prev" in
|
||||
--cidfile)
|
||||
-cidfile)
|
||||
_filedir
|
||||
;;
|
||||
--volumes-from)
|
||||
-volumes-from)
|
||||
__docker_containers_all
|
||||
;;
|
||||
-v|--volume)
|
||||
# TODO something magical with colons and _filedir ?
|
||||
return
|
||||
;;
|
||||
-e|--env)
|
||||
COMPREPLY=( $( compgen -e -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
--entrypoint|-h|--hostname|-m|--memory|-u|--user|-w|--workdir|-c|--cpu-shares|-n|--name|-a|--attach|--link|-p|--publish|--expose|--dns|--lxc-conf)
|
||||
-a|-c|-dns|-e|-entrypoint|-h|-lxc-conf|-m|-p|-u|-v|-w)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
@@ -508,61 +399,45 @@ _docker_run()
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--rm -d --detach -n --networking --privileged -P --publish-all -i --interactive -t --tty --cidfile --entrypoint -h --hostname -m --memory -u --user -w --workdir -c --cpu-shares --sig-proxy --name -a --attach -v --volume --link -e --env -p --publish --expose --dns --volumes-from --lxc-conf" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "-a -c -cidfile -d -dns -e -entrypoint -h -i -lxc-conf -m -n -p -privileged -t -u -v -volumes-from -w" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--cidfile|--volumes-from|-v|--volume|-e|--env|--entrypoint|-h|--hostname|-m|--memory|-u|--user|-w|--workdir|-c|--cpu-shares|-n|--name|-a|--attach|--link|-p|--publish|--expose|--dns|--lxc-conf')
|
||||
local counter=$cpos
|
||||
while [ $counter -le $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
-a|-c|-cidfile|-dns|-e|-entrypoint|-h|-lxc-conf|-m|-p|-u|-v|-volumes-from|-w)
|
||||
(( counter++ ))
|
||||
;;
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
(( counter++ ))
|
||||
done
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
if [ $counter -eq $cword ]; then
|
||||
__docker_image_repos_and_tags
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_save()
|
||||
{
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_search()
|
||||
{
|
||||
case "$prev" in
|
||||
-s|--stars)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--no-trunc --automated -s --stars" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $( compgen -W "-notrunc" "-stars" "-trusted" -- "$cur" ) )
|
||||
}
|
||||
|
||||
_docker_start()
|
||||
{
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-a --attach -i --interactive" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_containers_stopped
|
||||
;;
|
||||
esac
|
||||
__docker_containers_stopped
|
||||
}
|
||||
|
||||
_docker_stop()
|
||||
{
|
||||
case "$prev" in
|
||||
-t|--time)
|
||||
-t)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
@@ -571,7 +446,7 @@ _docker_stop()
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-t --time" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "-t" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_containers_running
|
||||
@@ -581,31 +456,12 @@ _docker_stop()
|
||||
|
||||
_docker_tag()
|
||||
{
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-f --force" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_image_repos_and_tags
|
||||
return
|
||||
fi
|
||||
(( counter++ ))
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_image_repos_and_tags
|
||||
return
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $( compgen -W "-f" -- "$cur" ) )
|
||||
}
|
||||
|
||||
_docker_top()
|
||||
{
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_running
|
||||
fi
|
||||
}
|
||||
@@ -622,6 +478,7 @@ _docker_wait()
|
||||
|
||||
_docker()
|
||||
{
|
||||
local cur prev words cword command="docker" counter=1 word cpos
|
||||
local commands="
|
||||
attach
|
||||
build
|
||||
@@ -637,7 +494,6 @@ _docker()
|
||||
insert
|
||||
inspect
|
||||
kill
|
||||
load
|
||||
login
|
||||
logs
|
||||
port
|
||||
@@ -648,7 +504,6 @@ _docker()
|
||||
rm
|
||||
rmi
|
||||
run
|
||||
save
|
||||
search
|
||||
start
|
||||
stop
|
||||
@@ -659,20 +514,18 @@ _docker()
|
||||
"
|
||||
|
||||
COMPREPLY=()
|
||||
local cur prev words cword
|
||||
_get_comp_words_by_ref -n : cur prev words cword
|
||||
|
||||
local command='docker'
|
||||
local counter=1
|
||||
while [ $counter -lt $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
word="${words[$counter]}"
|
||||
case "$word" in
|
||||
-H)
|
||||
(( counter++ ))
|
||||
;;
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
command="${words[$counter]}"
|
||||
command="$word"
|
||||
cpos=$counter
|
||||
(( cpos++ ))
|
||||
break
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
# docker.fish - docker completions for fish shell
|
||||
#
|
||||
# This file is generated by gen_docker_fish_completions.py from:
|
||||
# https://github.com/barnybug/docker-fish-completion
|
||||
#
|
||||
# To install the completions:
|
||||
# mkdir -p ~/.config/fish/completions
|
||||
# cp docker.fish ~/.config/fish/completions
|
||||
#
|
||||
# Completion supported:
|
||||
# - parameters
|
||||
# - commands
|
||||
# - containers
|
||||
# - images
|
||||
# - repositories
|
||||
|
||||
function __fish_docker_no_subcommand --description 'Test if docker has yet to be given the subcommand'
|
||||
for i in (commandline -opc)
|
||||
if contains -- $i attach build commit cp diff events export history images import info insert inspect kill load login logs port ps pull push restart rm rmi run save search start stop tag top version wait
|
||||
return 1
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function __fish_print_docker_containers --description 'Print a list of docker containers' -a select
|
||||
switch $select
|
||||
case running
|
||||
docker ps -a --no-trunc | command awk 'NR>1' | command awk 'BEGIN {FS=" +"}; $5 ~ "^Up" {print $1 "\n" $(NF-1)}' | tr ',' '\n'
|
||||
case stopped
|
||||
docker ps -a --no-trunc | command awk 'NR>1' | command awk 'BEGIN {FS=" +"}; $5 ~ "^Exit" {print $1 "\n" $(NF-1)}' | tr ',' '\n'
|
||||
case all
|
||||
docker ps -a --no-trunc | command awk 'NR>1' | command awk 'BEGIN {FS=" +"}; {print $1 "\n" $(NF-1)}' | tr ',' '\n'
|
||||
end
|
||||
end
|
||||
|
||||
function __fish_print_docker_images --description 'Print a list of docker images'
|
||||
docker images | command awk 'NR>1' | command grep -v '<none>' | command awk '{print $1":"$2}'
|
||||
end
|
||||
|
||||
function __fish_print_docker_repositories --description 'Print a list of docker repositories'
|
||||
docker images | command awk 'NR>1' | command grep -v '<none>' | command awk '{print $1}' | command sort | command uniq
|
||||
end
|
||||
|
||||
# common options
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s D -l debug -d 'Enable debug mode'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s G -l group -d "Group to assign the unix socket specified by -H when running in daemon mode; use '' (the empty string) to disable setting of a group"
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s H -l host -d 'tcp://host:port, unix://path/to/socket, fd://* or fd://socketfd to use in daemon mode. Multiple sockets can be specified'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l api-enable-cors -d 'Enable CORS headers in the remote API'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s b -l bridge -d "Attach containers to a pre-existing network bridge; use 'none' to disable container networking"
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l bip -d "Use this CIDR notation address for the network bridge's IP, not compatible with -b"
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s d -l daemon -d 'Enable daemon mode'
|
||||
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' -s e -l exec-driver -d 'Force the docker runtime to use a specific exec driver'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s g -l graph -d 'Path to use as the root of the docker runtime'
|
||||
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' -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 'Disable enabling of net.ipv4.ip_forward'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l iptables -d "Disable docker's addition of iptables rules"
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l mtu -d 'Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if no default route is available'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s p -l pidfile -d 'Path to use for daemon PID file'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s r -l restart -d 'Restart previously running containers'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s s -l storage-driver -d 'Force the docker runtime to use a specific storage driver'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s v -l version -d 'Print version information and quit'
|
||||
|
||||
# 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 no-stdin -d 'Do not attach stdin'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -l sig-proxy -d 'Proxify all received signal to the process (even in non-tty mode)'
|
||||
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' -l no-cache -d 'Do not use cache when building 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'
|
||||
|
||||
# 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' -s m -l message -d 'Commit message'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -l run -d 'Config automatically applied when the image is run. (ex: -run=\'{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}\')'
|
||||
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'
|
||||
|
||||
# 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' -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' -l since -d 'Show previously created events and then stream.'
|
||||
|
||||
# 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' -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 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"
|
||||
|
||||
# images
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a images -d 'List images'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s a -l all -d 'Show all images (by default filter out the intermediate image layers)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -l no-trunc -d "Don't truncate output"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s q -l quiet -d 'Only show numeric IDs'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s t -l tree -d 'Output graph in tree format'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s v -l viz -d 'Output graph in graphviz format'
|
||||
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'
|
||||
|
||||
# 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 -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' -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' -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'
|
||||
|
||||
# login
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a login -d 'Register or Login to the docker registry server'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s e -l email -d 'Email'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s p -l password -d 'Password'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s u -l username -d 'Username'
|
||||
|
||||
# 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' -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 which is NAT-ed to PRIVATE_PORT'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from port' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# ps
|
||||
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 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' -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 the docker registry server'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from pull' -s t -l tag -d 'Download tagged image in repository'
|
||||
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 the docker registry server'
|
||||
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"
|
||||
|
||||
# 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' -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=10'
|
||||
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 removal of running container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s l -l link -d 'Remove the specified link and not the underlying container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s v -l volumes -d 'Remove the volumes associated to the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -a '(__fish_print_docker_containers stopped)' -d "Container"
|
||||
|
||||
# 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'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rmi' -a '(__fish_print_docker_images)' -d "Image"
|
||||
|
||||
# run
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a run -d 'Run a command in a new container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s P -l publish-all -d 'Publish all exposed ports to the host interfaces'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s a -l attach -d 'Attach to stdin, stdout or stderr.'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s c -l cpu-shares -d 'CPU shares (relative weight)'
|
||||
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' -s d -l detach -d 'Detached mode: Run container in the background, print new container id'
|
||||
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' -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 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' -s h -l hostname -d 'Container host name'
|
||||
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 (name:alias)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l lxc-conf -d 'Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s m -l memory -d 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s n -l networking -d 'Enable networking for this container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s p -l publish -d "Publish a container's port to the host (format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort) (use 'docker port' to see the actual mapping)"
|
||||
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 rm -d 'Automatically remove the container when it exits (incompatible with -d)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l sig-proxy -d 'Proxify all received signal to the process (even in non-tty mode)'
|
||||
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)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l volumes-from -d 'Mount volumes from the specified container(s)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s w -l workdir -d 'Working directory inside the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -a '(__fish_print_docker_images)' -d "Image"
|
||||
|
||||
# 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' -a '(__fish_print_docker_images)' -d "Image"
|
||||
|
||||
# search
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a search -d 'Search for an image in the docker index'
|
||||
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 xxx stars'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from search' -l automated -d 'Only show automated builds'
|
||||
|
||||
# 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/stderr and forward all signals to the process"
|
||||
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"
|
||||
|
||||
# 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' -s t -l time -d 'Number of seconds to wait for the container to stop before killing it.'
|
||||
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' -a '(__fish_print_docker_images)' -d "Image"
|
||||
|
||||
# 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' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# version
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a version -d 'Show the docker version information'
|
||||
|
||||
# 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' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
|
||||
Regular → Executable
+10
-5
@@ -1,6 +1,6 @@
|
||||
#compdef docker
|
||||
#
|
||||
# zsh completion for docker (http://docker.com)
|
||||
# zsh completion for docker (http://docker.io)
|
||||
#
|
||||
# version: 0.2.2
|
||||
# author: Felix Riedel
|
||||
@@ -139,6 +139,11 @@ __docker_subcommand () {
|
||||
(history)
|
||||
_arguments ':images:__docker_images'
|
||||
;;
|
||||
(insert)
|
||||
_arguments '1:containers:__docker_containers' \
|
||||
'2:URL:(http:// file://)' \
|
||||
'3:file:_files'
|
||||
;;
|
||||
(kill)
|
||||
_arguments '*:containers:__docker_runningcontainers'
|
||||
;;
|
||||
@@ -169,7 +174,7 @@ __docker_subcommand () {
|
||||
(ps)
|
||||
_arguments '-a[Show all containers. Only running containers are shown by default]' \
|
||||
'-h[Show help]' \
|
||||
'--before-id=-[Show only container created before Id, include non-running one]:containers:__docker_containers' \
|
||||
'-beforeId=-[Show only container created before Id, include non-running one]:containers:__docker_containers' \
|
||||
'-n=-[Show n last created containers, include non-running one]:n:(1 5 10 25 50)'
|
||||
;;
|
||||
(tag)
|
||||
@@ -184,9 +189,9 @@ __docker_subcommand () {
|
||||
'-a=-[Attach to stdin, stdout or stderr]:toggle:(true false)' \
|
||||
'-c=-[CPU shares (relative weight)]:CPU shares: ' \
|
||||
'-d[Detached mode: leave the container running in the background]' \
|
||||
'*--dns=[Set custom dns servers]:dns server: ' \
|
||||
'*-dns=[Set custom dns servers]:dns server: ' \
|
||||
'*-e=[Set environment variables]:environment variable: ' \
|
||||
'--entrypoint=-[Overwrite the default entrypoint of the image]:entry point: ' \
|
||||
'-entrypoint=-[Overwrite the default entrypoint of the image]:entry point: ' \
|
||||
'-h=-[Container host name]:hostname:_hosts' \
|
||||
'-i[Keep stdin open even if not attached]' \
|
||||
'-m=-[Memory limit (in bytes)]:limit: ' \
|
||||
@@ -194,7 +199,7 @@ __docker_subcommand () {
|
||||
'-t=-[Allocate a pseudo-tty]:toggle:(true false)' \
|
||||
'-u=-[Username or UID]:user:_users' \
|
||||
'*-v=-[Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)]:volume: '\
|
||||
'--volumes-from=-[Mount volumes from the specified container]:volume: ' \
|
||||
'-volumes-from=-[Mount volumes from the specified container]:volume: ' \
|
||||
'(-):images:__docker_images' \
|
||||
'(-):command: _command_names -e' \
|
||||
'*::arguments: _normal'
|
||||
|
||||
@@ -8,4 +8,4 @@ Examples
|
||||
========
|
||||
|
||||
* Data container: ./data/Dockerfile creates a data image sharing /data volume
|
||||
* Iceweasel: ./iceweasel/Dockerfile shows a way to dockerize a common multimedia application
|
||||
* Firefox: ./firefox/Dockerfile shows a way to dockerize a common multimedia application
|
||||
@@ -6,33 +6,33 @@
|
||||
# /data volume is owned by sysadmin.
|
||||
# USAGE:
|
||||
# # Download data Dockerfile
|
||||
# wget http://raw.githubusercontent.com/dotcloud/docker/master/contrib/desktop-integration/data/Dockerfile
|
||||
# wget http://raw.github.com/dotcloud/docker/master/contrib/desktop-integration/data/Dockerfile
|
||||
#
|
||||
# # Build data image
|
||||
# docker build -t data .
|
||||
# docker build -t data -rm .
|
||||
#
|
||||
# # Create a data container. (eg: iceweasel-data)
|
||||
# docker run --name iceweasel-data data true
|
||||
# # Create a data container. (eg: firefox-data)
|
||||
# docker run -name firefox-data data true
|
||||
#
|
||||
# # List data from it
|
||||
# docker run --volumes-from iceweasel-data busybox ls -al /data
|
||||
# docker run -volumes-from firefox-data busybox ls -al /data
|
||||
|
||||
docker-version 0.6.5
|
||||
|
||||
# Smallest base image, just to launch a container
|
||||
FROM busybox
|
||||
MAINTAINER Daniel Mizyrycki <daniel@docker.com>
|
||||
from busybox
|
||||
maintainer Daniel Mizyrycki <daniel@docker.com>
|
||||
|
||||
# Create a regular user
|
||||
RUN echo 'sysadmin:x:1000:1000::/data:/bin/sh' >> /etc/passwd
|
||||
RUN echo 'sysadmin:x:1000:' >> /etc/group
|
||||
run echo 'sysadmin:x:1000:1000::/data:/bin/sh' >> /etc/passwd
|
||||
run echo 'sysadmin:x:1000:' >> /etc/group
|
||||
|
||||
# Create directory for that user
|
||||
RUN mkdir /data
|
||||
RUN chown sysadmin.sysadmin /data
|
||||
run mkdir /data
|
||||
run chown sysadmin.sysadmin /data
|
||||
|
||||
# Add content to /data. This will keep sysadmin ownership
|
||||
RUN touch /data/init_volume
|
||||
run touch /data/init_volume
|
||||
|
||||
# Create /data volume
|
||||
VOLUME /data
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# VERSION: 0.7
|
||||
# DESCRIPTION: Create firefox container with its dependencies
|
||||
# AUTHOR: Daniel Mizyrycki <daniel@dotcloud.com>
|
||||
# COMMENTS:
|
||||
# This file describes how to build a Firefox container with all
|
||||
# dependencies installed. It uses native X11 unix socket and alsa
|
||||
# sound devices. Tested on Debian 7.2
|
||||
# USAGE:
|
||||
# # Download Firefox Dockerfile
|
||||
# wget http://raw.github.com/dotcloud/docker/master/contrib/desktop-integration/firefox/Dockerfile
|
||||
#
|
||||
# # Build firefox image
|
||||
# docker build -t firefox -rm .
|
||||
#
|
||||
# # Run stateful data-on-host firefox. For ephemeral, remove -v /data/firefox:/data
|
||||
# docker run -v /data/firefox:/data -v /tmp/.X11-unix:/tmp/.X11-unix \
|
||||
# -v /dev/snd:/dev/snd -lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \
|
||||
# -e DISPLAY=unix$DISPLAY firefox
|
||||
#
|
||||
# # To run stateful dockerized data containers
|
||||
# docker run -volumes-from firefox-data -v /tmp/.X11-unix:/tmp/.X11-unix \
|
||||
# -v /dev/snd:/dev/snd -lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \
|
||||
# -e DISPLAY=unix$DISPLAY firefox
|
||||
|
||||
docker-version 0.6.5
|
||||
|
||||
# Base docker image
|
||||
from tianon/debian:wheezy
|
||||
maintainer Daniel Mizyrycki <daniel@docker.com>
|
||||
|
||||
# Install firefox dependencies
|
||||
run echo "deb http://ftp.debian.org/debian/ wheezy main contrib" > /etc/apt/sources.list
|
||||
run apt-get update
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y libXrender1 libasound2 \
|
||||
libdbus-glib-1-2 libgtk2.0-0 libpango1.0-0 libxt6 wget bzip2 sudo
|
||||
|
||||
# Install Firefox
|
||||
run mkdir /application
|
||||
run cd /application; wget -O - \
|
||||
http://ftp.mozilla.org/pub/mozilla.org/firefox/releases/25.0/linux-x86_64/en-US/firefox-25.0.tar.bz2 | tar jx
|
||||
|
||||
# create sysadmin account
|
||||
run useradd -m -d /data -p saIVpsc0EVTwA sysadmin
|
||||
run sed -Ei 's/sudo:x:27:/sudo:x:27:sysadmin/' /etc/group
|
||||
run sed -Ei 's/(\%sudo\s+ALL=\(ALL\:ALL\) )ALL/\1 NOPASSWD:ALL/' /etc/sudoers
|
||||
|
||||
# Autorun firefox. -no-remote is necessary to create a new container, as firefox
|
||||
# appears to communicate with itself through X11.
|
||||
cmd ["/bin/sh", "-c", "/usr/bin/sudo -u sysadmin -H -E /application/firefox/firefox -no-remote"]
|
||||
@@ -1,41 +0,0 @@
|
||||
# VERSION: 0.7
|
||||
# DESCRIPTION: Create iceweasel container with its dependencies
|
||||
# AUTHOR: Daniel Mizyrycki <daniel@dotcloud.com>
|
||||
# COMMENTS:
|
||||
# This file describes how to build a Iceweasel container with all
|
||||
# dependencies installed. It uses native X11 unix socket and alsa
|
||||
# sound devices. Tested on Debian 7.2
|
||||
# USAGE:
|
||||
# # Download Iceweasel Dockerfile
|
||||
# wget http://raw.githubusercontent.com/dotcloud/docker/master/contrib/desktop-integration/iceweasel/Dockerfile
|
||||
#
|
||||
# # Build iceweasel image
|
||||
# docker build -t iceweasel .
|
||||
#
|
||||
# # Run stateful data-on-host iceweasel. For ephemeral, remove -v /data/iceweasel:/data
|
||||
# docker run -v /data/iceweasel:/data -v /tmp/.X11-unix:/tmp/.X11-unix \
|
||||
# -v /dev/snd:/dev/snd --lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \
|
||||
# -e DISPLAY=unix$DISPLAY iceweasel
|
||||
#
|
||||
# # To run stateful dockerized data containers
|
||||
# docker run --volumes-from iceweasel-data -v /tmp/.X11-unix:/tmp/.X11-unix \
|
||||
# -v /dev/snd:/dev/snd --lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \
|
||||
# -e DISPLAY=unix$DISPLAY iceweasel
|
||||
|
||||
docker-version 0.6.5
|
||||
|
||||
# Base docker image
|
||||
FROM debian:wheezy
|
||||
MAINTAINER Daniel Mizyrycki <daniel@docker.com>
|
||||
|
||||
# Install Iceweasel and "sudo"
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq iceweasel sudo
|
||||
|
||||
# create sysadmin account
|
||||
RUN useradd -m -d /data -p saIVpsc0EVTwA sysadmin
|
||||
RUN sed -Ei 's/sudo:x:27:/sudo:x:27:sysadmin/' /etc/group
|
||||
RUN sed -Ei 's/(\%sudo\s+ALL=\(ALL\:ALL\) )ALL/\1 NOPASSWD:ALL/' /etc/sudoers
|
||||
|
||||
# Autorun iceweasel. -no-remote is necessary to create a new container, as
|
||||
# iceweasel appears to communicate with itself through X11.
|
||||
CMD ["/usr/bin/sudo", "-u", "sysadmin", "-H", "-E", "/usr/bin/iceweasel", "-no-remote"]
|
||||
@@ -3,7 +3,7 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/daemon/graphdriver/devmapper"
|
||||
"github.com/dotcloud/docker/graphdriver/devmapper"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#
|
||||
|
||||
FROM ubuntu:12.10
|
||||
MAINTAINER Guillaume J. Charmes <guillaume@docker.com>
|
||||
MAINTAINER Guillaume J. Charmes <guillaume@dotcloud.com>
|
||||
|
||||
RUN apt-get update && apt-get install -y wget git mercurial
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM busybox
|
||||
MAINTAINER Guillaume J. Charmes <guillaume@docker.com>
|
||||
MAINTAINER Guillaume J. Charmes <guillaume@dotcloud.com>
|
||||
ADD manager /usr/bin/
|
||||
ENTRYPOINT ["/usr/bin/manager"]
|
||||
|
||||
@@ -56,7 +56,7 @@ func main() {
|
||||
|
||||
// Check that the requested process manager is supported
|
||||
if _, exists := templates[*kind]; !exists {
|
||||
panic("Unknown script template")
|
||||
panic("Unkown script template")
|
||||
}
|
||||
|
||||
// Load the requested template
|
||||
@@ -70,7 +70,7 @@ func main() {
|
||||
bufErr := bytes.NewBuffer(nil)
|
||||
|
||||
// Instanciate the Docker CLI
|
||||
cli := docker.NewDockerCli(nil, bufOut, bufErr, "unix", "/var/run/docker.sock", false, nil)
|
||||
cli := docker.NewDockerCli(nil, bufOut, bufErr, "unix", "/var/run/docker.sock")
|
||||
// Retrieve the container info
|
||||
if err := cli.CmdInspect(flag.Arg(0)); err != nil {
|
||||
// As of docker v0.6.3, CmdInspect always returns nil
|
||||
|
||||
@@ -11,9 +11,6 @@ DOCKER_OPTS=${DOCKER_OPTS:-}
|
||||
start() {
|
||||
checkpath -f -m 0644 -o root:docker "$DOCKER_LOGFILE"
|
||||
|
||||
ulimit -n 1048576
|
||||
ulimit -u 1048576
|
||||
|
||||
ebegin "Starting docker daemon"
|
||||
start-stop-daemon --start --background \
|
||||
--exec "$DOCKER_BINARY" \
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[Unit]
|
||||
Description=Docker Application Container Engine
|
||||
Documentation=http://docs.docker.com
|
||||
After=network.target
|
||||
Description=Docker Application Container Engine
|
||||
Documentation=http://docs.docker.io
|
||||
Requires=network.target
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStartPre=/bin/mount --make-rprivate /
|
||||
ExecStart=/usr/bin/docker -d
|
||||
Restart=on-failure
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=1048576
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
[Unit]
|
||||
Description=Docker Application Container Engine
|
||||
Documentation=http://docs.docker.com
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/docker -d -H fd://
|
||||
Restart=on-failure
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=1048576
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,8 +0,0 @@
|
||||
[Unit]
|
||||
Description=Docker Socket for the API
|
||||
|
||||
[Socket]
|
||||
ListenStream=/var/run/docker.sock
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -1,138 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: docker
|
||||
# Required-Start: $syslog $remote_fs
|
||||
# Required-Stop: $syslog $remote_fs
|
||||
# Should-Start: cgroupfs-mount cgroup-lite
|
||||
# Should-Stop: cgroupfs-mount cgroup-lite
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Create lightweight, portable, self-sufficient containers.
|
||||
# Description:
|
||||
# Docker is an open-source project to easily create lightweight, portable,
|
||||
# self-sufficient containers from any application. The same container that a
|
||||
# developer builds and tests on a laptop can run at scale, in production, on
|
||||
# VMs, bare metal, OpenStack clusters, public clouds and more.
|
||||
### END INIT INFO
|
||||
|
||||
export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
||||
|
||||
BASE=$(basename $0)
|
||||
|
||||
# modify these in /etc/default/$BASE (/etc/default/docker)
|
||||
DOCKER=/usr/bin/$BASE
|
||||
# This is the pid file managed by docker itself
|
||||
DOCKER_PIDFILE=/var/run/$BASE.pid
|
||||
# This is the pid file created/managed by start-stop-daemon
|
||||
DOCKER_SSD_PIDFILE=/var/run/$BASE-ssd.pid
|
||||
DOCKER_LOGFILE=/var/log/$BASE.log
|
||||
DOCKER_OPTS=
|
||||
DOCKER_DESC="Docker"
|
||||
|
||||
# Get lsb functions
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
if [ -f /etc/default/$BASE ]; then
|
||||
. /etc/default/$BASE
|
||||
fi
|
||||
|
||||
# see also init_is_upstart in /lib/lsb/init-functions (which isn't available in Ubuntu 12.04, or we'd use it)
|
||||
if [ -x /sbin/initctl ] && /sbin/initctl version 2>/dev/null | grep -q upstart; then
|
||||
log_failure_msg "$DOCKER_DESC is managed via upstart, try using service $BASE $1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check docker is present
|
||||
if [ ! -x $DOCKER ]; then
|
||||
log_failure_msg "$DOCKER not present or not executable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
fail_unless_root() {
|
||||
if [ "$(id -u)" != '0' ]; then
|
||||
log_failure_msg "$DOCKER_DESC must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
cgroupfs_mount() {
|
||||
# see also https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount
|
||||
if grep -v '^#' /etc/fstab | grep -q cgroup \
|
||||
|| [ ! -e /proc/cgroups ] \
|
||||
|| [ ! -d /sys/fs/cgroup ]; then
|
||||
return
|
||||
fi
|
||||
if ! mountpoint -q /sys/fs/cgroup; then
|
||||
mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
|
||||
fi
|
||||
(
|
||||
cd /sys/fs/cgroup
|
||||
for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
|
||||
mkdir -p $sys
|
||||
if ! mountpoint -q $sys; then
|
||||
if ! mount -n -t cgroup -o $sys cgroup $sys; then
|
||||
rmdir $sys || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
)
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
fail_unless_root
|
||||
|
||||
cgroupfs_mount
|
||||
|
||||
touch "$DOCKER_LOGFILE"
|
||||
chgrp docker "$DOCKER_LOGFILE"
|
||||
|
||||
ulimit -n 1048576
|
||||
ulimit -u 1048576
|
||||
|
||||
log_begin_msg "Starting $DOCKER_DESC: $BASE"
|
||||
start-stop-daemon --start --background \
|
||||
--no-close \
|
||||
--exec "$DOCKER" \
|
||||
--pidfile "$DOCKER_SSD_PIDFILE" \
|
||||
--make-pidfile \
|
||||
-- \
|
||||
-d -p "$DOCKER_PIDFILE" \
|
||||
$DOCKER_OPTS \
|
||||
>> "$DOCKER_LOGFILE" 2>&1
|
||||
log_end_msg $?
|
||||
;;
|
||||
|
||||
stop)
|
||||
fail_unless_root
|
||||
log_begin_msg "Stopping $DOCKER_DESC: $BASE"
|
||||
start-stop-daemon --stop --pidfile "$DOCKER_SSD_PIDFILE"
|
||||
log_end_msg $?
|
||||
;;
|
||||
|
||||
restart)
|
||||
fail_unless_root
|
||||
docker_pid=`cat "$DOCKER_SSD_PIDFILE" 2>/dev/null`
|
||||
[ -n "$docker_pid" ] \
|
||||
&& ps -p $docker_pid > /dev/null 2>&1 \
|
||||
&& $0 stop
|
||||
$0 start
|
||||
;;
|
||||
|
||||
force-reload)
|
||||
fail_unless_root
|
||||
$0 restart
|
||||
;;
|
||||
|
||||
status)
|
||||
status_of_proc -p "$DOCKER_SSD_PIDFILE" "$DOCKER" docker
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -1,13 +0,0 @@
|
||||
# Docker Upstart and SysVinit configuration file
|
||||
|
||||
# Customize location of Docker binary (especially for development testing).
|
||||
#DOCKER="/usr/local/bin/docker"
|
||||
|
||||
# Use DOCKER_OPTS to modify the daemon startup options.
|
||||
#DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4"
|
||||
|
||||
# If you need Docker to use an HTTP proxy, it can also be specified here.
|
||||
#export http_proxy="http://127.0.0.1:3128/"
|
||||
|
||||
# This is also a handy place to tweak where Docker's temporary files go.
|
||||
#export TMPDIR="/mnt/bigdrive/docker-tmp"
|
||||
@@ -1,130 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# /etc/rc.d/init.d/docker
|
||||
#
|
||||
# Daemon for docker.com
|
||||
#
|
||||
# chkconfig: 2345 95 95
|
||||
# description: Daemon for docker.com
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: docker
|
||||
# Required-Start: $network cgconfig
|
||||
# Required-Stop:
|
||||
# Should-Start:
|
||||
# Should-Stop:
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: start and stop docker
|
||||
# Description: Daemon for docker.com
|
||||
### END INIT INFO
|
||||
|
||||
# Source function library.
|
||||
. /etc/rc.d/init.d/functions
|
||||
|
||||
prog="docker"
|
||||
exec="/usr/bin/$prog"
|
||||
pidfile="/var/run/$prog.pid"
|
||||
lockfile="/var/lock/subsys/$prog"
|
||||
logfile="/var/log/$prog"
|
||||
|
||||
[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
|
||||
|
||||
prestart() {
|
||||
service cgconfig status > /dev/null
|
||||
|
||||
if [[ $? != 0 ]]; then
|
||||
service cgconfig start
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
start() {
|
||||
[ -x $exec ] || exit 5
|
||||
|
||||
if ! [ -f $pidfile ]; then
|
||||
prestart
|
||||
printf "Starting $prog:\t"
|
||||
echo "\n$(date)\n" >> $logfile
|
||||
$exec -d $other_args &>> $logfile &
|
||||
pid=$!
|
||||
touch $lockfile
|
||||
# wait up to 10 seconds for the pidfile to exist. see
|
||||
# https://github.com/dotcloud/docker/issues/5359
|
||||
tries=0
|
||||
while [ ! -f $pidfile -a $tries -lt 10 ]; do
|
||||
sleep 1
|
||||
tries=$((tries + 1))
|
||||
done
|
||||
success
|
||||
echo
|
||||
else
|
||||
failure
|
||||
echo
|
||||
printf "$pidfile still exists...\n"
|
||||
exit 7
|
||||
fi
|
||||
}
|
||||
|
||||
stop() {
|
||||
echo -n $"Stopping $prog: "
|
||||
killproc -p $pidfile $prog
|
||||
retval=$?
|
||||
echo
|
||||
[ $retval -eq 0 ] && rm -f $lockfile
|
||||
return $retval
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
reload() {
|
||||
restart
|
||||
}
|
||||
|
||||
force_reload() {
|
||||
restart
|
||||
}
|
||||
|
||||
rh_status() {
|
||||
status -p $pidfile $prog
|
||||
}
|
||||
|
||||
rh_status_q() {
|
||||
rh_status >/dev/null 2>&1
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
rh_status_q && exit 0
|
||||
$1
|
||||
;;
|
||||
stop)
|
||||
rh_status_q || exit 0
|
||||
$1
|
||||
;;
|
||||
restart)
|
||||
$1
|
||||
;;
|
||||
reload)
|
||||
rh_status_q || exit 7
|
||||
$1
|
||||
;;
|
||||
force-reload)
|
||||
force_reload
|
||||
;;
|
||||
status)
|
||||
rh_status
|
||||
;;
|
||||
condrestart|try-restart)
|
||||
rh_status_q || exit 0
|
||||
restart
|
||||
;;
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
|
||||
exit 2
|
||||
esac
|
||||
|
||||
exit $?
|
||||
@@ -1,7 +0,0 @@
|
||||
# /etc/sysconfig/docker
|
||||
#
|
||||
# Other arguments to pass to the docker daemon process
|
||||
# These will be parsed by the sysv initscript and appended
|
||||
# to the arguments list passed to docker -d
|
||||
|
||||
other_args=""
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
#!/bin/sh
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: docker
|
||||
# Required-Start: $syslog $remote_fs
|
||||
# Required-Stop: $syslog $remote_fs
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Create lightweight, portable, self-sufficient containers.
|
||||
# Description:
|
||||
# Docker is an open-source project to easily create lightweight, portable,
|
||||
# self-sufficient containers from any application. The same container that a
|
||||
# developer builds and tests on a laptop can run at scale, in production, on
|
||||
# VMs, bare metal, OpenStack clusters, public clouds and more.
|
||||
### END INIT INFO
|
||||
|
||||
BASE=$(basename $0)
|
||||
|
||||
DOCKER=/usr/bin/$BASE
|
||||
DOCKER_PIDFILE=/var/run/$BASE.pid
|
||||
DOCKER_OPTS=
|
||||
|
||||
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
||||
|
||||
# Get lsb functions
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
if [ -f /etc/default/$BASE ]; then
|
||||
. /etc/default/$BASE
|
||||
fi
|
||||
|
||||
# see also init_is_upstart in /lib/lsb/init-functions (which isn't available in Ubuntu 12.04, or we'd use it)
|
||||
if [ -x /sbin/initctl ] && /sbin/initctl version 2>/dev/null | /bin/grep -q upstart; then
|
||||
log_failure_msg "Docker is managed via upstart, try using service $BASE $1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check docker is present
|
||||
if [ ! -x $DOCKER ]; then
|
||||
log_failure_msg "$DOCKER not present or not executable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
fail_unless_root() {
|
||||
if [ "$(id -u)" != '0' ]; then
|
||||
log_failure_msg "Docker must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
fail_unless_root
|
||||
log_begin_msg "Starting Docker: $BASE"
|
||||
mount | grep cgroup >/dev/null || mount -t cgroup none /sys/fs/cgroup 2>/dev/null
|
||||
start-stop-daemon --start --background \
|
||||
--exec "$DOCKER" \
|
||||
--pidfile "$DOCKER_PIDFILE" \
|
||||
-- -d -p "$DOCKER_PIDFILE" \
|
||||
$DOCKER_OPTS
|
||||
log_end_msg $?
|
||||
;;
|
||||
|
||||
stop)
|
||||
fail_unless_root
|
||||
log_begin_msg "Stopping Docker: $BASE"
|
||||
start-stop-daemon --stop \
|
||||
--pidfile "$DOCKER_PIDFILE"
|
||||
log_end_msg $?
|
||||
;;
|
||||
|
||||
restart)
|
||||
fail_unless_root
|
||||
docker_pid=`cat "$DOCKER_PIDFILE" 2>/dev/null`
|
||||
[ -n "$docker_pid" ] \
|
||||
&& ps -p $docker_pid > /dev/null 2>&1 \
|
||||
&& $0 stop
|
||||
$0 start
|
||||
;;
|
||||
|
||||
force-reload)
|
||||
fail_unless_root
|
||||
$0 restart
|
||||
;;
|
||||
|
||||
status)
|
||||
status_of_proc -p "$DOCKER_PIDFILE" "$DOCKER" docker
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -1,41 +1,15 @@
|
||||
description "Docker daemon"
|
||||
|
||||
start on (local-filesystems and net-device-up IFACE!=lo)
|
||||
start on filesystem and started lxc-net
|
||||
stop on runlevel [!2345]
|
||||
limit nofile 524288 1048576
|
||||
limit nproc 524288 1048576
|
||||
|
||||
respawn
|
||||
|
||||
pre-start script
|
||||
# see also https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount
|
||||
if grep -v '^#' /etc/fstab | grep -q cgroup \
|
||||
|| [ ! -e /proc/cgroups ] \
|
||||
|| [ ! -d /sys/fs/cgroup ]; then
|
||||
exit 0
|
||||
fi
|
||||
if ! mountpoint -q /sys/fs/cgroup; then
|
||||
mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
|
||||
fi
|
||||
(
|
||||
cd /sys/fs/cgroup
|
||||
for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
|
||||
mkdir -p $sys
|
||||
if ! mountpoint -q $sys; then
|
||||
if ! mount -n -t cgroup -o $sys cgroup $sys; then
|
||||
rmdir $sys || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
)
|
||||
end script
|
||||
|
||||
script
|
||||
# modify these in /etc/default/$UPSTART_JOB (/etc/default/docker)
|
||||
DOCKER=/usr/bin/$UPSTART_JOB
|
||||
DOCKER_OPTS=
|
||||
if [ -f /etc/default/$UPSTART_JOB ]; then
|
||||
. /etc/default/$UPSTART_JOB
|
||||
fi
|
||||
exec "$DOCKER" -d $DOCKER_OPTS
|
||||
"$DOCKER" -d $DOCKER_OPTS
|
||||
end script
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
[ $(id -u) -eq 0 ] || {
|
||||
printf >&2 '%s requires root\n' "$0"
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
printf >&2 '%s: [-r release] [-m mirror] [-s]\n' "$0"
|
||||
exit 1
|
||||
}
|
||||
|
||||
tmp() {
|
||||
TMP=$(mktemp -d ${TMPDIR:-/var/tmp}/alpine-docker-XXXXXXXXXX)
|
||||
ROOTFS=$(mktemp -d ${TMPDIR:-/var/tmp}/alpine-docker-rootfs-XXXXXXXXXX)
|
||||
trap "rm -rf $TMP $ROOTFS" EXIT TERM INT
|
||||
}
|
||||
|
||||
apkv() {
|
||||
curl -s $REPO/$ARCH/APKINDEX.tar.gz | tar -Oxz |
|
||||
grep '^P:apk-tools-static$' -A1 | tail -n1 | cut -d: -f2
|
||||
}
|
||||
|
||||
getapk() {
|
||||
curl -s $REPO/$ARCH/apk-tools-static-$(apkv).apk |
|
||||
tar -xz -C $TMP sbin/apk.static
|
||||
}
|
||||
|
||||
mkbase() {
|
||||
$TMP/sbin/apk.static --repository $REPO --update-cache --allow-untrusted \
|
||||
--root $ROOTFS --initdb add alpine-base
|
||||
}
|
||||
|
||||
conf() {
|
||||
printf '%s\n' $REPO > $ROOTFS/etc/apk/repositories
|
||||
}
|
||||
|
||||
pack() {
|
||||
local id
|
||||
id=$(tar --numeric-owner -C $ROOTFS -c . | docker import - alpine:$REL)
|
||||
|
||||
docker tag $id alpine:latest
|
||||
docker run -i -t alpine printf 'alpine:%s with id=%s created!\n' $REL $id
|
||||
}
|
||||
|
||||
save() {
|
||||
[ $SAVE -eq 1 ] || return
|
||||
|
||||
tar --numeric-owner -C $ROOTFS -c . | xz > rootfs.tar.xz
|
||||
}
|
||||
|
||||
while getopts "hr:m:s" opt; do
|
||||
case $opt in
|
||||
r)
|
||||
REL=$OPTARG
|
||||
;;
|
||||
m)
|
||||
MIRROR=$OPTARG
|
||||
;;
|
||||
s)
|
||||
SAVE=1
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
REL=${REL:-edge}
|
||||
MIRROR=${MIRROR:-http://nl.alpinelinux.org/alpine}
|
||||
SAVE=${SAVE:-0}
|
||||
REPO=$MIRROR/$REL/main
|
||||
ARCH=$(uname -m)
|
||||
|
||||
tmp
|
||||
getapk
|
||||
mkbase
|
||||
conf
|
||||
pack
|
||||
save
|
||||
@@ -1,92 +0,0 @@
|
||||
#
|
||||
# /etc/pacman.conf
|
||||
#
|
||||
# See the pacman.conf(5) manpage for option and repository directives
|
||||
|
||||
#
|
||||
# GENERAL OPTIONS
|
||||
#
|
||||
[options]
|
||||
# The following paths are commented out with their default values listed.
|
||||
# If you wish to use different paths, uncomment and update the paths.
|
||||
#RootDir = /
|
||||
#DBPath = /var/lib/pacman/
|
||||
#CacheDir = /var/cache/pacman/pkg/
|
||||
#LogFile = /var/log/pacman.log
|
||||
#GPGDir = /etc/pacman.d/gnupg/
|
||||
HoldPkg = pacman glibc
|
||||
#XferCommand = /usr/bin/curl -C - -f %u > %o
|
||||
#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
|
||||
#CleanMethod = KeepInstalled
|
||||
#UseDelta = 0.7
|
||||
Architecture = auto
|
||||
|
||||
# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
|
||||
#IgnorePkg =
|
||||
#IgnoreGroup =
|
||||
|
||||
#NoUpgrade =
|
||||
#NoExtract =
|
||||
|
||||
# Misc options
|
||||
#UseSyslog
|
||||
#Color
|
||||
#TotalDownload
|
||||
# We cannot check disk space from within a chroot environment
|
||||
#CheckSpace
|
||||
#VerbosePkgLists
|
||||
|
||||
# By default, pacman accepts packages signed by keys that its local keyring
|
||||
# trusts (see pacman-key and its man page), as well as unsigned packages.
|
||||
SigLevel = Required DatabaseOptional
|
||||
LocalFileSigLevel = Optional
|
||||
#RemoteFileSigLevel = Required
|
||||
|
||||
# NOTE: You must run `pacman-key --init` before first using pacman; the local
|
||||
# keyring can then be populated with the keys of all official Arch Linux
|
||||
# packagers with `pacman-key --populate archlinux`.
|
||||
|
||||
#
|
||||
# REPOSITORIES
|
||||
# - can be defined here or included from another file
|
||||
# - pacman will search repositories in the order defined here
|
||||
# - local/custom mirrors can be added here or in separate files
|
||||
# - repositories listed first will take precedence when packages
|
||||
# have identical names, regardless of version number
|
||||
# - URLs will have $repo replaced by the name of the current repo
|
||||
# - URLs will have $arch replaced by the name of the architecture
|
||||
#
|
||||
# Repository entries are of the format:
|
||||
# [repo-name]
|
||||
# Server = ServerName
|
||||
# Include = IncludePath
|
||||
#
|
||||
# The header [repo-name] is crucial - it must be present and
|
||||
# uncommented to enable the repo.
|
||||
#
|
||||
|
||||
# The testing repositories are disabled by default. To enable, uncomment the
|
||||
# repo name header and Include lines. You can add preferred servers immediately
|
||||
# after the header, and they will be used before the default mirrors.
|
||||
|
||||
#[testing]
|
||||
#Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[core]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[extra]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
#[community-testing]
|
||||
#Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[community]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
# An example of a custom package repository. See the pacman manpage for
|
||||
# tips on creating your own repositories.
|
||||
#[custom]
|
||||
#SigLevel = Optional TrustAll
|
||||
#Server = file:///home/custompkgs
|
||||
|
||||
+46
-25
@@ -1,45 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/bin/bash
|
||||
# Generate a minimal filesystem for archlinux and load it into the local
|
||||
# docker as "archlinux"
|
||||
# requires root
|
||||
set -e
|
||||
|
||||
hash pacstrap &>/dev/null || {
|
||||
PACSTRAP=$(which pacstrap)
|
||||
[ "$PACSTRAP" ] || {
|
||||
echo "Could not find pacstrap. Run pacman -S arch-install-scripts"
|
||||
exit 1
|
||||
}
|
||||
EXPECT=$(which expect)
|
||||
[ "$EXPECT" ] || {
|
||||
echo "Could not find expect. Run pacman -S expect"
|
||||
exit 1
|
||||
}
|
||||
|
||||
ROOTFS=$(mktemp -d ${TMPDIR:-/var/tmp}/rootfs-archlinux-XXXXXXXXXX)
|
||||
chmod 755 $ROOTFS
|
||||
ROOTFS=~/rootfs-arch-$$-$RANDOM
|
||||
mkdir $ROOTFS
|
||||
|
||||
# packages to ignore for space savings
|
||||
#packages to ignore for space savings
|
||||
PKGIGNORE=linux,jfsutils,lvm2,cryptsetup,groff,man-db,man-pages,mdadm,pciutils,pcmciautils,reiserfsprogs,s-nail,xfsprogs
|
||||
|
||||
pacstrap -C ./mkimage-arch-pacman.conf -c -d -G -i $ROOTFS base haveged --ignore $PKGIGNORE
|
||||
|
||||
expect <<EOF
|
||||
set timeout 60
|
||||
set send_slow {1 1}
|
||||
spawn pacstrap -c -d -G -i $ROOTFS base haveged --ignore $PKGIGNORE
|
||||
expect {
|
||||
"Install anyway?" { send n\r; exp_continue }
|
||||
"(default=all)" { send \r; exp_continue }
|
||||
"Proceed with installation?" { send "\r"; exp_continue }
|
||||
"skip the above package" {send "y\r"; exp_continue }
|
||||
"checking" { exp_continue }
|
||||
"loading" { exp_continue }
|
||||
"installing" { exp_continue }
|
||||
}
|
||||
EOF
|
||||
|
||||
arch-chroot $ROOTFS /bin/sh -c "haveged -w 1024; pacman-key --init; pkill haveged; pacman -Rs --noconfirm haveged; pacman-key --populate archlinux"
|
||||
arch-chroot $ROOTFS /bin/sh -c "ln -s /usr/share/zoneinfo/UTC /etc/localtime"
|
||||
echo 'en_US.UTF-8 UTF-8' > $ROOTFS/etc/locale.gen
|
||||
cat > $ROOTFS/etc/locale.gen <<DELIM
|
||||
en_US.UTF-8 UTF-8
|
||||
en_US ISO-8859-1
|
||||
DELIM
|
||||
arch-chroot $ROOTFS locale-gen
|
||||
arch-chroot $ROOTFS /bin/sh -c 'echo "Server = https://mirrors.kernel.org/archlinux/\$repo/os/\$arch" > /etc/pacman.d/mirrorlist'
|
||||
arch-chroot $ROOTFS /bin/sh -c 'echo "Server = http://mirrors.kernel.org/archlinux/\$repo/os/\$arch" > /etc/pacman.d/mirrorlist'
|
||||
|
||||
# udev doesn't work in containers, rebuild /dev
|
||||
DEV=$ROOTFS/dev
|
||||
rm -rf $DEV
|
||||
mkdir -p $DEV
|
||||
mknod -m 666 $DEV/null c 1 3
|
||||
mknod -m 666 $DEV/zero c 1 5
|
||||
mknod -m 666 $DEV/random c 1 8
|
||||
mknod -m 666 $DEV/urandom c 1 9
|
||||
mkdir -m 755 $DEV/pts
|
||||
mkdir -m 1777 $DEV/shm
|
||||
mknod -m 666 $DEV/tty c 5 0
|
||||
mknod -m 600 $DEV/console c 5 1
|
||||
mknod -m 666 $DEV/tty0 c 4 0
|
||||
mknod -m 666 $DEV/full c 1 7
|
||||
mknod -m 600 $DEV/initctl p
|
||||
mknod -m 666 $DEV/ptmx c 5 2
|
||||
ln -sf /proc/self/fd $DEV/fd
|
||||
DEV=${ROOTFS}/dev
|
||||
mv ${DEV} ${DEV}.old
|
||||
mkdir -p ${DEV}
|
||||
mknod -m 666 ${DEV}/null c 1 3
|
||||
mknod -m 666 ${DEV}/zero c 1 5
|
||||
mknod -m 666 ${DEV}/random c 1 8
|
||||
mknod -m 666 ${DEV}/urandom c 1 9
|
||||
mkdir -m 755 ${DEV}/pts
|
||||
mkdir -m 1777 ${DEV}/shm
|
||||
mknod -m 666 ${DEV}/tty c 5 0
|
||||
mknod -m 600 ${DEV}/console c 5 1
|
||||
mknod -m 666 ${DEV}/tty0 c 4 0
|
||||
mknod -m 666 ${DEV}/full c 1 7
|
||||
mknod -m 600 ${DEV}/initctl p
|
||||
mknod -m 666 ${DEV}/ptmx c 5 2
|
||||
|
||||
tar --numeric-owner -C $ROOTFS -c . | docker import - archlinux
|
||||
docker run -i -t archlinux echo Success.
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/bin/bash
|
||||
# Generate a very minimal filesystem based on busybox-static,
|
||||
# and load it into the local docker under the name "busybox".
|
||||
|
||||
echo >&2
|
||||
echo >&2 'warning: this script is deprecated - see mkimage.sh and mkimage/busybox-static'
|
||||
echo >&2
|
||||
|
||||
BUSYBOX=$(which busybox)
|
||||
[ "$BUSYBOX" ] || {
|
||||
echo "Sorry, I could not locate busybox."
|
||||
@@ -14,7 +10,7 @@ BUSYBOX=$(which busybox)
|
||||
}
|
||||
|
||||
set -e
|
||||
ROOTFS=${TMPDIR:-/var/tmp}/rootfs-busybox-$$-$RANDOM
|
||||
ROOTFS=/tmp/rootfs-busybox-$$-$RANDOM
|
||||
mkdir $ROOTFS
|
||||
cd $ROOTFS
|
||||
|
||||
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# Create a CentOS base image for Docker
|
||||
# From unclejack https://github.com/dotcloud/docker/issues/290
|
||||
set -e
|
||||
|
||||
MIRROR_URL="http://centos.netnitco.net/6.4/os/x86_64/"
|
||||
MIRROR_URL_UPDATES="http://centos.netnitco.net/6.4/updates/x86_64/"
|
||||
|
||||
yum install -y febootstrap xz
|
||||
|
||||
febootstrap -i bash -i coreutils -i tar -i bzip2 -i gzip -i vim-minimal -i wget -i patch -i diffutils -i iproute -i yum centos centos64 $MIRROR_URL -u $MIRROR_URL_UPDATES
|
||||
touch centos64/etc/resolv.conf
|
||||
touch centos64/sbin/init
|
||||
|
||||
tar --numeric-owner -Jcpf centos-64.tar.xz -C centos64 .
|
||||
@@ -1,75 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Generate a minimal filesystem for CRUX/Linux and load it into the local
|
||||
# docker as "cruxlinux"
|
||||
# requires root and the crux iso (http://crux.nu)
|
||||
|
||||
set -e
|
||||
|
||||
die () {
|
||||
echo >&2 "$@"
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ "$#" -eq 1 ] || die "1 argument(s) required, $# provided. Usage: ./mkimage-crux.sh /path/to/iso"
|
||||
|
||||
ISO=${1}
|
||||
|
||||
ROOTFS=$(mktemp -d ${TMPDIR:-/var/tmp}/rootfs-crux-XXXXXXXXXX)
|
||||
CRUX=$(mktemp -d ${TMPDIR:-/var/tmp}/crux-XXXXXXXXXX)
|
||||
TMP=$(mktemp -d ${TMPDIR:-/var/tmp}/XXXXXXXXXX)
|
||||
|
||||
VERSION=$(basename --suffix=.iso $ISO | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')
|
||||
|
||||
# Mount the ISO
|
||||
mount -o ro,loop $ISO $CRUX
|
||||
|
||||
# Extract pkgutils
|
||||
tar -C $TMP -xf $CRUX/tools/pkgutils#*.pkg.tar.gz
|
||||
|
||||
# Put pkgadd in the $PATH
|
||||
export PATH="$TMP/usr/bin:$PATH"
|
||||
|
||||
# Install core packages
|
||||
mkdir -p $ROOTFS/var/lib/pkg
|
||||
touch $ROOTFS/var/lib/pkg/db
|
||||
for pkg in $CRUX/crux/core/*; do
|
||||
pkgadd -r $ROOTFS $pkg
|
||||
done
|
||||
|
||||
# Remove agetty and inittab config
|
||||
if (grep agetty ${ROOTFS}/etc/inittab 2>&1 > /dev/null); then
|
||||
echo "Removing agetty from /etc/inittab ..."
|
||||
chroot ${ROOTFS} sed -i -e "/agetty/d" /etc/inittab
|
||||
chroot ${ROOTFS} sed -i -e "/shutdown/d" /etc/inittab
|
||||
chroot ${ROOTFS} sed -i -e "/^$/N;/^\n$/d" /etc/inittab
|
||||
fi
|
||||
|
||||
# Remove kernel source
|
||||
rm -rf $ROOTFS/usr/src/*
|
||||
|
||||
# udev doesn't work in containers, rebuild /dev
|
||||
DEV=$ROOTFS/dev
|
||||
rm -rf $DEV
|
||||
mkdir -p $DEV
|
||||
mknod -m 666 $DEV/null c 1 3
|
||||
mknod -m 666 $DEV/zero c 1 5
|
||||
mknod -m 666 $DEV/random c 1 8
|
||||
mknod -m 666 $DEV/urandom c 1 9
|
||||
mkdir -m 755 $DEV/pts
|
||||
mkdir -m 1777 $DEV/shm
|
||||
mknod -m 666 $DEV/tty c 5 0
|
||||
mknod -m 600 $DEV/console c 5 1
|
||||
mknod -m 666 $DEV/tty0 c 4 0
|
||||
mknod -m 666 $DEV/full c 1 7
|
||||
mknod -m 600 $DEV/initctl p
|
||||
mknod -m 666 $DEV/ptmx c 5 2
|
||||
|
||||
IMAGE_ID=$(tar --numeric-owner -C $ROOTFS -c . | docker import - crux:$VERSION)
|
||||
docker tag $IMAGE_ID crux:latest
|
||||
docker run -i -t crux echo Success.
|
||||
|
||||
# Cleanup
|
||||
umount $CRUX
|
||||
rm -rf $ROOTFS
|
||||
rm -rf $CRUX
|
||||
rm -rf $TMP
|
||||
@@ -1,10 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo >&2
|
||||
echo >&2 'warning: this script is deprecated - see mkimage.sh and mkimage/debootstrap'
|
||||
echo >&2
|
||||
|
||||
variant='minbase'
|
||||
include='iproute,iputils-ping'
|
||||
arch='amd64' # intentionally undocumented for now
|
||||
@@ -47,9 +43,7 @@ usage() {
|
||||
debianStable=wheezy
|
||||
debianUnstable=sid
|
||||
# this should match the name found at http://releases.ubuntu.com/
|
||||
ubuntuLatestLTS=trusty
|
||||
# this should match the name found at http://releases.tanglu.org/
|
||||
tangluLatest=aequorea
|
||||
ubuntuLatestLTS=precise
|
||||
|
||||
while getopts v:i:a:p:dst name; do
|
||||
case "$name" in
|
||||
@@ -118,16 +112,11 @@ fi
|
||||
# will be filled in later, if [ -z "$skipDetection" ]
|
||||
lsbDist=''
|
||||
|
||||
target="${TMPDIR:-/var/tmp}/docker-rootfs-debootstrap-$suite-$$-$RANDOM"
|
||||
target="/tmp/docker-rootfs-debootstrap-$suite-$$-$RANDOM"
|
||||
|
||||
cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
|
||||
returnTo="$(pwd -P)"
|
||||
|
||||
if [ "$suite" = 'lucid' ]; then
|
||||
# lucid fails and doesn't include gpgv in minbase; "apt-get update" fails
|
||||
include+=',gpgv'
|
||||
fi
|
||||
|
||||
set -x
|
||||
|
||||
# bootstrap
|
||||
@@ -149,34 +138,18 @@ if [ -z "$strictDebootstrap" ]; then
|
||||
# shrink the image, since apt makes us fat (wheezy: ~157.5MB vs ~120MB)
|
||||
sudo chroot . apt-get clean
|
||||
|
||||
if strings usr/bin/dpkg | grep -q unsafe-io; then
|
||||
# while we're at it, apt is unnecessarily slow inside containers
|
||||
# this forces dpkg not to call sync() after package extraction and speeds up install
|
||||
# the benefit is huge on spinning disks, and the penalty is nonexistent on SSD or decent server virtualization
|
||||
echo 'force-unsafe-io' | sudo tee etc/dpkg/dpkg.cfg.d/02apt-speedup > /dev/null
|
||||
# we have this wrapped up in an "if" because the "force-unsafe-io"
|
||||
# option was added in dpkg 1.15.8.6
|
||||
# (see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=584254#82),
|
||||
# and ubuntu lucid/10.04 only has 1.15.5.6
|
||||
fi
|
||||
|
||||
# we want to effectively run "apt-get clean" after every install to keep images small (see output of "apt-get clean -s" for context)
|
||||
{
|
||||
aptGetClean='"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true";'
|
||||
echo "DPkg::Post-Invoke { ${aptGetClean} };"
|
||||
echo "APT::Update::Post-Invoke { ${aptGetClean} };"
|
||||
echo 'Dir::Cache::pkgcache ""; Dir::Cache::srcpkgcache "";'
|
||||
} | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null
|
||||
|
||||
# and remove the translations, too
|
||||
echo 'Acquire::Languages "none";' | sudo tee etc/apt/apt.conf.d/no-languages > /dev/null
|
||||
# while we're at it, apt is unnecessarily slow inside containers
|
||||
# this forces dpkg not to call sync() after package extraction and speeds up install
|
||||
# the benefit is huge on spinning disks, and the penalty is nonexistent on SSD or decent server virtualization
|
||||
echo 'force-unsafe-io' | sudo tee etc/dpkg/dpkg.cfg.d/02apt-speedup > /dev/null
|
||||
# we want to effectively run "apt-get clean" after every install to keep images small
|
||||
echo 'DPkg::Post-Invoke {"/bin/rm -f /var/cache/apt/archives/*.deb || true";};' | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null
|
||||
|
||||
# helpful undo lines for each the above tweaks (for lack of a better home to keep track of them):
|
||||
# rm /usr/sbin/policy-rc.d
|
||||
# rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl
|
||||
# rm /etc/dpkg/dpkg.cfg.d/02apt-speedup
|
||||
# rm /etc/apt/apt.conf.d/no-cache
|
||||
# rm /etc/apt/apt.conf.d/no-languages
|
||||
|
||||
if [ -z "$skipDetection" ]; then
|
||||
# see also rudimentary platform detection in hack/install.sh
|
||||
@@ -207,23 +180,8 @@ if [ -z "$strictDebootstrap" ]; then
|
||||
s/ $suite-updates main/ ${suite}-security main/
|
||||
" etc/apt/sources.list
|
||||
;;
|
||||
Tanglu)
|
||||
# add the updates repository
|
||||
if [ "$suite" = "$tangluLatest" ]; then
|
||||
# ${suite}-updates only applies to stable Tanglu versions
|
||||
sudo sed -i "p; s/ $suite main$/ ${suite}-updates main/" etc/apt/sources.list
|
||||
fi
|
||||
;;
|
||||
SteamOS)
|
||||
# add contrib and non-free
|
||||
sudo sed -i "s/ $suite main$/ $suite main contrib non-free/" etc/apt/sources.list
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# make sure our packages lists are as up to date as we can get them
|
||||
sudo chroot . apt-get update
|
||||
sudo chroot . apt-get dist-upgrade -y
|
||||
fi
|
||||
|
||||
if [ "$justTar" ]; then
|
||||
@@ -266,28 +224,6 @@ else
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
Tanglu)
|
||||
if [ "$suite" = "$tangluLatest" ]; then
|
||||
# tag latest
|
||||
$docker tag $repo:$suite $repo:latest
|
||||
fi
|
||||
if [ -r etc/lsb-release ]; then
|
||||
lsbRelease="$(. etc/lsb-release && echo "$DISTRIB_RELEASE")"
|
||||
if [ "$lsbRelease" ]; then
|
||||
# tag specific Tanglu version number, if available (1.0, 2.0, etc.)
|
||||
$docker tag $repo:$suite $repo:$lsbRelease
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
SteamOS)
|
||||
if [ -r etc/lsb-release ]; then
|
||||
lsbRelease="$(. etc/lsb-release && echo "$DISTRIB_RELEASE")"
|
||||
if [ "$lsbRelease" ]; then
|
||||
# tag specific SteamOS version number, if available (1.0, 2.0, etc.)
|
||||
$docker tag $repo:$suite $repo:$lsbRelease
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Create a base CentOS Docker image.
|
||||
|
||||
# This script is useful on systems with rinse available (e.g.,
|
||||
# building a CentOS image on Debian). See contrib/mkimage-yum.sh for
|
||||
# a way to build CentOS images on systems with yum installed.
|
||||
|
||||
set -e
|
||||
|
||||
echo >&2
|
||||
echo >&2 'warning: this script is deprecated - see mkimage.sh and mkimage/rinse'
|
||||
echo >&2
|
||||
|
||||
repo="$1"
|
||||
distro="$2"
|
||||
mirror="$3"
|
||||
|
||||
if [ ! "$repo" ] || [ ! "$distro" ]; then
|
||||
self="$(basename $0)"
|
||||
echo >&2 "usage: $self repo distro [mirror]"
|
||||
echo >&2
|
||||
echo >&2 " ie: $self username/centos centos-5"
|
||||
echo >&2 " $self username/centos centos-6"
|
||||
echo >&2
|
||||
echo >&2 " ie: $self username/slc slc-5"
|
||||
echo >&2 " $self username/slc slc-6"
|
||||
echo >&2
|
||||
echo >&2 " ie: $self username/centos centos-5 http://vault.centos.org/5.8/os/x86_64/CentOS/"
|
||||
echo >&2 " $self username/centos centos-6 http://vault.centos.org/6.3/os/x86_64/Packages/"
|
||||
echo >&2
|
||||
echo >&2 'See /etc/rinse for supported values of "distro" and for examples of'
|
||||
echo >&2 ' expected values of "mirror".'
|
||||
echo >&2
|
||||
echo >&2 'This script is tested to work with the original upstream version of rinse,'
|
||||
echo >&2 ' found at http://www.steve.org.uk/Software/rinse/ and also in Debian at'
|
||||
echo >&2 ' http://packages.debian.org/wheezy/rinse -- as always, YMMV.'
|
||||
echo >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
target="${TMPDIR:-/var/tmp}/docker-rootfs-rinse-$distro-$$-$RANDOM"
|
||||
|
||||
cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
|
||||
returnTo="$(pwd -P)"
|
||||
|
||||
rinseArgs=( --arch amd64 --distribution "$distro" --directory "$target" )
|
||||
if [ "$mirror" ]; then
|
||||
rinseArgs+=( --mirror "$mirror" )
|
||||
fi
|
||||
|
||||
set -x
|
||||
|
||||
mkdir -p "$target"
|
||||
|
||||
sudo rinse "${rinseArgs[@]}"
|
||||
|
||||
cd "$target"
|
||||
|
||||
# rinse fails a little at setting up /dev, so we'll just wipe it out and create our own
|
||||
sudo rm -rf dev
|
||||
sudo mkdir -m 755 dev
|
||||
(
|
||||
cd dev
|
||||
sudo ln -sf /proc/self/fd ./
|
||||
sudo mkdir -m 755 pts
|
||||
sudo mkdir -m 1777 shm
|
||||
sudo mknod -m 600 console c 5 1
|
||||
sudo mknod -m 600 initctl p
|
||||
sudo mknod -m 666 full c 1 7
|
||||
sudo mknod -m 666 null c 1 3
|
||||
sudo mknod -m 666 ptmx c 5 2
|
||||
sudo mknod -m 666 random c 1 8
|
||||
sudo mknod -m 666 tty c 5 0
|
||||
sudo mknod -m 666 tty0 c 4 0
|
||||
sudo mknod -m 666 urandom c 1 9
|
||||
sudo mknod -m 666 zero c 1 5
|
||||
)
|
||||
|
||||
# effectively: febootstrap-minimize --keep-zoneinfo --keep-rpmdb --keep-services "$target"
|
||||
# locales
|
||||
sudo rm -rf usr/{{lib,share}/locale,{lib,lib64}/gconv,bin/localedef,sbin/build-locale-archive}
|
||||
# docs
|
||||
sudo rm -rf usr/share/{man,doc,info,gnome/help}
|
||||
# cracklib
|
||||
sudo rm -rf usr/share/cracklib
|
||||
# i18n
|
||||
sudo rm -rf usr/share/i18n
|
||||
# yum cache
|
||||
sudo rm -rf var/cache/yum
|
||||
sudo mkdir -p --mode=0755 var/cache/yum
|
||||
# sln
|
||||
sudo rm -rf sbin/sln
|
||||
# ldconfig
|
||||
#sudo rm -rf sbin/ldconfig
|
||||
sudo rm -rf etc/ld.so.cache var/cache/ldconfig
|
||||
sudo mkdir -p --mode=0755 var/cache/ldconfig
|
||||
|
||||
# allow networking init scripts inside the container to work without extra steps
|
||||
echo 'NETWORKING=yes' | sudo tee etc/sysconfig/network > /dev/null
|
||||
|
||||
# to restore locales later:
|
||||
# yum reinstall glibc-common
|
||||
|
||||
version=
|
||||
if [ -r etc/redhat-release ]; then
|
||||
version="$(sed -E 's/^[^0-9.]*([0-9.]+).*$/\1/' etc/redhat-release)"
|
||||
elif [ -r etc/SuSE-release ]; then
|
||||
version="$(awk '/^VERSION/ { print $3 }' etc/SuSE-release)"
|
||||
fi
|
||||
|
||||
if [ -z "$version" ]; then
|
||||
echo >&2 "warning: cannot autodetect OS version, using $distro as tag"
|
||||
sleep 20
|
||||
version="$distro"
|
||||
fi
|
||||
|
||||
sudo tar --numeric-owner -c . | docker import - $repo:$version
|
||||
|
||||
docker run -i -t $repo:$version echo success
|
||||
|
||||
cd "$returnTo"
|
||||
sudo rm -rf "$target"
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/bin/bash
|
||||
# Generate a very minimal filesystem based on busybox-static,
|
||||
# and load it into the local docker under the name "docker-ut".
|
||||
|
||||
@@ -15,7 +15,7 @@ SOCAT=$(which socat)
|
||||
|
||||
shopt -s extglob
|
||||
set -ex
|
||||
ROOTFS=`mktemp -d ${TMPDIR:-/var/tmp}/rootfs-busybox.XXXXXXXXXX`
|
||||
ROOTFS=`mktemp -d /tmp/rootfs-busybox.XXXXXXXXXX`
|
||||
trap "rm -rf $ROOTFS" INT QUIT TERM
|
||||
cd $ROOTFS
|
||||
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Create a base CentOS Docker image.
|
||||
#
|
||||
# This script is useful on systems with yum installed (e.g., building
|
||||
# a CentOS image on CentOS). See contrib/mkimage-rinse.sh for a way
|
||||
# to build CentOS images on other systems.
|
||||
|
||||
usage() {
|
||||
cat <<EOOPTS
|
||||
$(basename $0) [OPTIONS] <name>
|
||||
OPTIONS:
|
||||
-y <yumconf> The path to the yum config to install packages from. The
|
||||
default is /etc/yum.conf.
|
||||
EOOPTS
|
||||
exit 1
|
||||
}
|
||||
|
||||
# option defaults
|
||||
yum_config=/etc/yum.conf
|
||||
while getopts ":y:h" opt; do
|
||||
case $opt in
|
||||
y)
|
||||
yum_config=$OPTARG
|
||||
;;
|
||||
h)
|
||||
usage
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND - 1))
|
||||
name=$1
|
||||
|
||||
if [[ -z $name ]]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
#--------------------
|
||||
|
||||
target=$(mktemp -d --tmpdir $(basename $0).XXXXXX)
|
||||
|
||||
set -x
|
||||
|
||||
mkdir -m 755 "$target"/dev
|
||||
mknod -m 600 "$target"/dev/console c 5 1
|
||||
mknod -m 600 "$target"/dev/initctl p
|
||||
mknod -m 666 "$target"/dev/full c 1 7
|
||||
mknod -m 666 "$target"/dev/null c 1 3
|
||||
mknod -m 666 "$target"/dev/ptmx c 5 2
|
||||
mknod -m 666 "$target"/dev/random c 1 8
|
||||
mknod -m 666 "$target"/dev/tty c 5 0
|
||||
mknod -m 666 "$target"/dev/tty0 c 4 0
|
||||
mknod -m 666 "$target"/dev/urandom c 1 9
|
||||
mknod -m 666 "$target"/dev/zero c 1 5
|
||||
|
||||
yum -c "$yum_config" --installroot="$target" --setopt=tsflags=nodocs \
|
||||
--setopt=group_package_types=mandatory -y groupinstall Core
|
||||
yum -c "$yum_config" --installroot="$target" -y clean all
|
||||
|
||||
cat > "$target"/etc/sysconfig/network <<EOF
|
||||
NETWORKING=yes
|
||||
HOSTNAME=localhost.localdomain
|
||||
EOF
|
||||
|
||||
# effectively: febootstrap-minimize --keep-zoneinfo --keep-rpmdb
|
||||
# --keep-services "$target". Stolen from mkimage-rinse.sh
|
||||
# locales
|
||||
rm -rf "$target"/usr/{{lib,share}/locale,{lib,lib64}/gconv,bin/localedef,sbin/build-locale-archive}
|
||||
# docs
|
||||
rm -rf "$target"/usr/share/{man,doc,info,gnome/help}
|
||||
# cracklib
|
||||
rm -rf "$target"/usr/share/cracklib
|
||||
# i18n
|
||||
rm -rf "$target"/usr/share/i18n
|
||||
# sln
|
||||
rm -rf "$target"/sbin/sln
|
||||
# ldconfig
|
||||
rm -rf "$target"/etc/ld.so.cache
|
||||
rm -rf "$target"/var/cache/ldconfig/*
|
||||
|
||||
version=
|
||||
if [ -r "$target"/etc/redhat-release ]; then
|
||||
version="$(sed 's/^[^0-9\]*\([0-9.]\+\).*$/\1/' "$target"/etc/redhat-release)"
|
||||
fi
|
||||
|
||||
if [ -z "$version" ]; then
|
||||
echo >&2 "warning: cannot autodetect OS version, using '$name' as tag"
|
||||
version=$name
|
||||
fi
|
||||
|
||||
tar --numeric-owner -c -C "$target" . | docker import - $name:$version
|
||||
docker run -i -t $name:$version echo success
|
||||
|
||||
rm -rf "$target"
|
||||
@@ -1,107 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
mkimg="$(basename "$0")"
|
||||
|
||||
usage() {
|
||||
echo >&2 "usage: $mkimg [-d dir] [-t tag] script [script-args]"
|
||||
echo >&2 " ie: $mkimg -t someuser/debian debootstrap --variant=minbase jessie"
|
||||
echo >&2 " $mkimg -t someuser/ubuntu debootstrap --include=ubuntu-minimal --components main,universe trusty"
|
||||
echo >&2 " $mkimg -t someuser/busybox busybox-static"
|
||||
echo >&2 " $mkimg -t someuser/centos:5 rinse --distribution centos-5"
|
||||
echo >&2 " $mkimg -t someuser/mageia:4 mageia-urpmi --version=4"
|
||||
echo >&2 " $mkimg -t someuser/mageia:4 mageia-urpmi --version=4 --mirror=http://somemirror/"
|
||||
exit 1
|
||||
}
|
||||
|
||||
scriptDir="$(dirname "$(readlink -f "$BASH_SOURCE")")/mkimage"
|
||||
|
||||
optTemp=$(getopt --options '+d:t:h' --longoptions 'dir:,tag:,help' --name "$mkimg" -- "$@")
|
||||
eval set -- "$optTemp"
|
||||
unset optTemp
|
||||
|
||||
dir=
|
||||
tag=
|
||||
while true; do
|
||||
case "$1" in
|
||||
-d|--dir) dir="$2" ; shift 2 ;;
|
||||
-t|--tag) tag="$2" ; shift 2 ;;
|
||||
-h|--help) usage ;;
|
||||
--) shift ; break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
script="$1"
|
||||
[ "$script" ] || usage
|
||||
shift
|
||||
|
||||
if [ ! -x "$scriptDir/$script" ]; then
|
||||
echo >&2 "error: $script does not exist or is not executable"
|
||||
echo >&2 " see $scriptDir for possible scripts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# don't mistake common scripts like .febootstrap-minimize as image-creators
|
||||
if [[ "$script" == .* ]]; then
|
||||
echo >&2 "error: $script is a script helper, not a script"
|
||||
echo >&2 " see $scriptDir for possible scripts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
delDir=
|
||||
if [ -z "$dir" ]; then
|
||||
dir="$(mktemp -d ${TMPDIR:-/var/tmp}/docker-mkimage.XXXXXXXXXX)"
|
||||
delDir=1
|
||||
fi
|
||||
|
||||
rootfsDir="$dir/rootfs"
|
||||
( set -x; mkdir -p "$rootfsDir" )
|
||||
|
||||
# pass all remaining arguments to $script
|
||||
"$scriptDir/$script" "$rootfsDir" "$@"
|
||||
|
||||
# Docker mounts tmpfs at /dev and procfs at /proc so we can remove them
|
||||
rm -rf "$rootfsDir/dev" "$rootfsDir/proc"
|
||||
mkdir -p "$rootfsDir/dev" "$rootfsDir/proc"
|
||||
|
||||
# make sure /etc/resolv.conf has something useful in it
|
||||
mkdir -p "$rootfsDir/etc"
|
||||
cat > "$rootfsDir/etc/resolv.conf" <<'EOF'
|
||||
nameserver 8.8.8.8
|
||||
nameserver 8.8.4.4
|
||||
EOF
|
||||
|
||||
tarFile="$dir/rootfs.tar.xz"
|
||||
touch "$tarFile"
|
||||
|
||||
(
|
||||
set -x
|
||||
tar --numeric-owner -caf "$tarFile" -C "$rootfsDir" --transform='s,^./,,' .
|
||||
)
|
||||
|
||||
echo >&2 "+ cat > '$dir/Dockerfile'"
|
||||
cat > "$dir/Dockerfile" <<'EOF'
|
||||
FROM scratch
|
||||
ADD rootfs.tar.xz /
|
||||
EOF
|
||||
|
||||
# if our generated image has a decent shell, let's set a default command
|
||||
for shell in /bin/bash /usr/bin/fish /usr/bin/zsh /bin/sh; do
|
||||
if [ -x "$rootfsDir/$shell" ]; then
|
||||
( set -x; echo 'CMD ["'"$shell"'"]' >> "$dir/Dockerfile" )
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
( set -x; rm -rf "$rootfsDir" )
|
||||
|
||||
if [ "$tag" ]; then
|
||||
( set -x; docker build -t "$tag" "$dir" )
|
||||
elif [ "$delDir" ]; then
|
||||
# if we didn't specify a tag and we're going to delete our dir, let's just build an untagged image so that we did _something_
|
||||
( set -x; docker build "$dir" )
|
||||
fi
|
||||
|
||||
if [ "$delDir" ]; then
|
||||
( set -x; rm -rf "$dir" )
|
||||
fi
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
rootfsDir="$1"
|
||||
shift
|
||||
|
||||
(
|
||||
cd "$rootfsDir"
|
||||
|
||||
# effectively: febootstrap-minimize --keep-zoneinfo --keep-rpmdb --keep-services "$target"
|
||||
# locales
|
||||
rm -rf usr/{{lib,share}/locale,{lib,lib64}/gconv,bin/localedef,sbin/build-locale-archive}
|
||||
# docs
|
||||
rm -rf usr/share/{man,doc,info,gnome/help}
|
||||
# cracklib
|
||||
rm -rf usr/share/cracklib
|
||||
# i18n
|
||||
rm -rf usr/share/i18n
|
||||
# yum cache
|
||||
rm -rf var/cache/yum
|
||||
mkdir -p --mode=0755 var/cache/yum
|
||||
# sln
|
||||
rm -rf sbin/sln
|
||||
# ldconfig
|
||||
#rm -rf sbin/ldconfig
|
||||
rm -rf etc/ld.so.cache var/cache/ldconfig
|
||||
mkdir -p --mode=0755 var/cache/ldconfig
|
||||
)
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
rootfsDir="$1"
|
||||
shift
|
||||
|
||||
busybox="$(which busybox 2>/dev/null || true)"
|
||||
if [ -z "$busybox" ]; then
|
||||
echo >&2 'error: busybox: not found'
|
||||
echo >&2 ' install it with your distribution "busybox-static" package'
|
||||
exit 1
|
||||
fi
|
||||
if ! ldd "$busybox" 2>&1 | grep -q 'not a dynamic executable'; then
|
||||
echo >&2 "error: '$busybox' appears to be a dynamic executable"
|
||||
echo >&2 ' you should install your distribution "busybox-static" package instead'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$rootfsDir/bin"
|
||||
rm -f "$rootfsDir/bin/busybox" # just in case
|
||||
cp "$busybox" "$rootfsDir/bin/busybox"
|
||||
|
||||
(
|
||||
cd "$rootfsDir"
|
||||
|
||||
IFS=$'\n'
|
||||
modules=( $(bin/busybox --list-modules) )
|
||||
unset IFS
|
||||
|
||||
for module in "${modules[@]}"; do
|
||||
mkdir -p "$(dirname "$module")"
|
||||
ln -sf /bin/busybox "$module"
|
||||
done
|
||||
)
|
||||
@@ -1,176 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
rootfsDir="$1"
|
||||
shift
|
||||
|
||||
# we have to do a little fancy footwork to make sure "rootfsDir" becomes the second non-option argument to debootstrap
|
||||
|
||||
before=()
|
||||
while [ $# -gt 0 ] && [[ "$1" == -* ]]; do
|
||||
before+=( "$1" )
|
||||
shift
|
||||
done
|
||||
|
||||
suite="$1"
|
||||
shift
|
||||
|
||||
(
|
||||
set -x
|
||||
debootstrap "${before[@]}" "$suite" "$rootfsDir" "$@"
|
||||
)
|
||||
|
||||
# now for some Docker-specific tweaks
|
||||
|
||||
# prevent init scripts from running during install/update
|
||||
echo >&2 "+ echo exit 101 > '$rootfsDir/usr/sbin/policy-rc.d'"
|
||||
cat > "$rootfsDir/usr/sbin/policy-rc.d" <<'EOF'
|
||||
#!/bin/sh
|
||||
|
||||
# For most Docker users, "apt-get install" only happens during "docker build",
|
||||
# where starting services doesn't work and often fails in humorous ways. This
|
||||
# prevents those failures by stopping the services from attempting to start.
|
||||
|
||||
exit 101
|
||||
EOF
|
||||
chmod +x "$rootfsDir/usr/sbin/policy-rc.d"
|
||||
|
||||
# prevent upstart scripts from running during install/update
|
||||
(
|
||||
set -x
|
||||
chroot "$rootfsDir" dpkg-divert --local --rename --add /sbin/initctl
|
||||
cp -a "$rootfsDir/usr/sbin/policy-rc.d" "$rootfsDir/sbin/initctl"
|
||||
sed -i 's/^exit.*/exit 0/' "$rootfsDir/sbin/initctl"
|
||||
)
|
||||
|
||||
# shrink a little, since apt makes us cache-fat (wheezy: ~157.5MB vs ~120MB)
|
||||
( set -x; chroot "$rootfsDir" apt-get clean )
|
||||
|
||||
# Ubuntu 10.04 sucks... :)
|
||||
if strings "$rootfsDir/usr/bin/dpkg" | grep -q unsafe-io; then
|
||||
# force dpkg not to call sync() after package extraction (speeding up installs)
|
||||
echo >&2 "+ echo force-unsafe-io > '$rootfsDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup'"
|
||||
cat > "$rootfsDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup" <<-'EOF'
|
||||
# For most Docker users, package installs happen during "docker build", which
|
||||
# doesn't survive power loss and gets restarted clean afterwards anyhow, so
|
||||
# this minor tweak gives us a nice speedup (much nicer on spinning disks,
|
||||
# obviously).
|
||||
|
||||
force-unsafe-io
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ -d "$rootfsDir/etc/apt/apt.conf.d" ]; then
|
||||
# _keep_ us lean by effectively running "apt-get clean" after every install
|
||||
aptGetClean='"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true";'
|
||||
echo >&2 "+ cat > '$rootfsDir/etc/apt/apt.conf.d/docker-clean'"
|
||||
cat > "$rootfsDir/etc/apt/apt.conf.d/docker-clean" <<-EOF
|
||||
# Since for most Docker users, package installs happen in "docker build" steps,
|
||||
# they essentially become individual layers due to the way Docker handles
|
||||
# layering, especially using CoW filesystems. What this means for us is that
|
||||
# the caches that APT keeps end up just wasting space in those layers, making
|
||||
# our layers unnecessarily large (especially since we'll normally never use
|
||||
# these caches again and will instead just "docker build" again and make a brand
|
||||
# new image).
|
||||
|
||||
# Ideally, these would just be invoking "apt-get clean", but in our testing,
|
||||
# that ended up being cyclic and we got stuck on APT's lock, so we get this fun
|
||||
# creation that's essentially just "apt-get clean".
|
||||
DPkg::Post-Invoke { ${aptGetClean} };
|
||||
APT::Update::Post-Invoke { ${aptGetClean} };
|
||||
|
||||
Dir::Cache::pkgcache "";
|
||||
Dir::Cache::srcpkgcache "";
|
||||
|
||||
# Note that we do realize this isn't the ideal way to do this, and are always
|
||||
# open to better suggestions (https://github.com/dotcloud/docker/issues).
|
||||
EOF
|
||||
|
||||
# remove apt-cache translations for fast "apt-get update"
|
||||
echo >&2 "+ echo Acquire::Languages 'none' > '$rootfsDir/etc/apt/apt.conf.d/docker-no-languages'"
|
||||
cat > "$rootfsDir/etc/apt/apt.conf.d/docker-no-languages" <<-'EOF'
|
||||
# In Docker, we don't often need the "Translations" files, so we're just wasting
|
||||
# time and space by downloading them, and this inhibits that. For users that do
|
||||
# need them, it's a simple matter to delete this file and "apt-get update". :)
|
||||
|
||||
Acquire::Languages "none";
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ -z "$DONT_TOUCH_SOURCES_LIST" ]; then
|
||||
# tweak sources.list, where appropriate
|
||||
lsbDist=
|
||||
if [ -z "$lsbDist" -a -r "$rootfsDir/etc/os-release" ]; then
|
||||
lsbDist="$(. "$rootfsDir/etc/os-release" && echo "$ID")"
|
||||
fi
|
||||
if [ -z "$lsbDist" -a -r "$rootfsDir/etc/lsb-release" ]; then
|
||||
lsbDist="$(. "$rootfsDir/etc/lsb-release" && echo "$DISTRIB_ID")"
|
||||
fi
|
||||
if [ -z "$lsbDist" -a -r "$rootfsDir/etc/debian_version" ]; then
|
||||
lsbDist='Debian'
|
||||
fi
|
||||
# normalize to lowercase for easier matching
|
||||
lsbDist="$(echo "$lsbDist" | tr '[:upper:]' '[:lower:]')"
|
||||
case "$lsbDist" in
|
||||
debian)
|
||||
# updates and security!
|
||||
if [ "$suite" != 'sid' -a "$suite" != 'unstable' ]; then
|
||||
(
|
||||
set -x
|
||||
sed -i "
|
||||
p;
|
||||
s/ $suite / ${suite}-updates /
|
||||
" "$rootfsDir/etc/apt/sources.list"
|
||||
echo "deb http://security.debian.org $suite/updates main" >> "$rootfsDir/etc/apt/sources.list"
|
||||
# LTS
|
||||
if [ "$suite" = 'squeeze' ]; then
|
||||
head -1 "$rootfsDir/etc/apt/sources.list" \
|
||||
| sed "s/ $suite / ${suite}-lts /" \
|
||||
>> "$rootfsDir/etc/apt/sources.list"
|
||||
fi
|
||||
)
|
||||
fi
|
||||
;;
|
||||
ubuntu)
|
||||
# add the updates and security repositories
|
||||
(
|
||||
set -x
|
||||
sed -i "
|
||||
p;
|
||||
s/ $suite / ${suite}-updates /; p;
|
||||
s/ $suite-updates / ${suite}-security /
|
||||
" "$rootfsDir/etc/apt/sources.list"
|
||||
)
|
||||
;;
|
||||
tanglu)
|
||||
# add the updates repository
|
||||
if [ "$suite" != 'devel' ]; then
|
||||
(
|
||||
set -x
|
||||
sed -i "
|
||||
p;
|
||||
s/ $suite / ${suite}-updates /
|
||||
" "$rootfsDir/etc/apt/sources.list"
|
||||
)
|
||||
fi
|
||||
;;
|
||||
steamos)
|
||||
# add contrib and non-free if "main" is the only component
|
||||
(
|
||||
set -x
|
||||
sed -i "s/ $suite main$/ $suite main contrib non-free/" "$rootfsDir/etc/apt/sources.list"
|
||||
)
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
(
|
||||
set -x
|
||||
|
||||
# make sure we're fully up-to-date
|
||||
chroot "$rootfsDir" bash -c 'apt-get update && apt-get dist-upgrade -y'
|
||||
|
||||
# delete all the apt list files since they're big and get stale quickly
|
||||
rm -rf "$rootfsDir/var/lib/apt/lists"/*
|
||||
# this forces "apt-get update" in dependent images, which is also good
|
||||
)
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Needs to be run from Mageia 4 or greater for kernel support for docker.
|
||||
#
|
||||
# Mageia 4 does not have docker available in official repos, so please
|
||||
# install and run the docker binary manually.
|
||||
#
|
||||
# Tested working versions are for Mageia 2 onwards (inc. cauldron).
|
||||
#
|
||||
set -e
|
||||
|
||||
rootfsDir="$1"
|
||||
shift
|
||||
|
||||
optTemp=$(getopt --options '+v:,m:' --longoptions 'version:,mirror:' --name mageia-urpmi -- "$@")
|
||||
eval set -- "$optTemp"
|
||||
unset optTemp
|
||||
|
||||
installversion=
|
||||
mirror=
|
||||
while true; do
|
||||
case "$1" in
|
||||
-v|--version) installversion="$2" ; shift 2 ;;
|
||||
-m|--mirror) mirror="$2" ; shift 2 ;;
|
||||
--) shift ; break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z $installversion ]; then
|
||||
# Attempt to match host version
|
||||
if [ -r /etc/mageia-release ]; then
|
||||
installversion="$(sed 's/^[^0-9\]*\([0-9.]\+\).*$/\1/' /etc/mageia-release)"
|
||||
else
|
||||
echo "Error: no version supplied and unable to detect host mageia version"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z $mirror ]; then
|
||||
# No mirror provided, default to mirrorlist
|
||||
mirror="--mirrorlist https://mirrors.mageia.org/api/mageia.$installversion.x86_64.list"
|
||||
fi
|
||||
|
||||
(
|
||||
set -x
|
||||
urpmi.addmedia --distrib \
|
||||
$mirror \
|
||||
--urpmi-root "$rootfsDir"
|
||||
urpmi basesystem-minimal urpmi \
|
||||
--auto \
|
||||
--no-suggests \
|
||||
--urpmi-root "$rootfsDir" \
|
||||
--root "$rootfsDir"
|
||||
)
|
||||
|
||||
"$(dirname "$BASH_SOURCE")/.febootstrap-minimize" "$rootfsDir"
|
||||
|
||||
if [ -d "$rootfsDir/etc/sysconfig" ]; then
|
||||
# allow networking init scripts inside the container to work without extra steps
|
||||
echo 'NETWORKING=yes' > "$rootfsDir/etc/sysconfig/network"
|
||||
fi
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
rootfsDir="$1"
|
||||
shift
|
||||
|
||||
# specifying --arch below is safe because "$@" can override it and the "latest" one wins :)
|
||||
|
||||
(
|
||||
set -x
|
||||
rinse --directory "$rootfsDir" --arch amd64 "$@"
|
||||
)
|
||||
|
||||
"$(dirname "$BASH_SOURCE")/.febootstrap-minimize" "$rootfsDir"
|
||||
|
||||
if [ -d "$rootfsDir/etc/sysconfig" ]; then
|
||||
# allow networking init scripts inside the container to work without extra steps
|
||||
echo 'NETWORKING=yes' > "$rootfsDir/etc/sysconfig/network"
|
||||
fi
|
||||
|
||||
# make sure we're fully up-to-date, too
|
||||
(
|
||||
set -x
|
||||
chroot "$rootfsDir" yum update -y
|
||||
)
|
||||
@@ -1,77 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# A simple helper script to help people build seccomp profiles for
|
||||
# Docker/LXC. The goal is mostly to reduce the attack surface to the
|
||||
# kernel, by restricting access to rarely used, recently added or not used
|
||||
# syscalls.
|
||||
#
|
||||
# This script processes one or more files which contain the list of system
|
||||
# calls to be allowed. See mkseccomp.sample for more information how you
|
||||
# can configure the list of syscalls. When run, this script produces output
|
||||
# which, when stored in a file, can be passed to docker as follows:
|
||||
#
|
||||
# docker run --lxc-conf="lxc.seccomp=$file" <rest of arguments>
|
||||
#
|
||||
# The included sample file shows how to cut about a quarter of all syscalls,
|
||||
# which affecting most applications.
|
||||
#
|
||||
# For specific situations it is possible to reduce the list further. By
|
||||
# reducing the list to just those syscalls required by a certain application
|
||||
# you can make it difficult for unknown/unexpected code to run.
|
||||
#
|
||||
# Run this script as follows:
|
||||
#
|
||||
# ./mkseccomp.pl < mkseccomp.sample >syscalls.list
|
||||
# or
|
||||
# ./mkseccomp.pl mkseccomp.sample >syscalls.list
|
||||
#
|
||||
# Multiple files can be specified, in which case the lists of syscalls are
|
||||
# combined.
|
||||
#
|
||||
# By Martijn van Oosterhout <kleptog@svana.org> Nov 2013
|
||||
|
||||
# How it works:
|
||||
#
|
||||
# This program basically spawns two processes to form a chain like:
|
||||
#
|
||||
# <process data section to prefix __NR_> | cpp | <add header and filter unknown syscalls>
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
if( -t ) {
|
||||
print STDERR "Helper script to make seccomp filters for Docker/LXC.\n";
|
||||
print STDERR "Usage: mkseccomp.pl < [files...]\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $pid = open(my $in, "-|") // die "Couldn't fork1 ($!)\n";
|
||||
|
||||
if($pid == 0) { # Child
|
||||
$pid = open(my $out, "|-") // die "Couldn't fork2 ($!)\n";
|
||||
|
||||
if($pid == 0) { # Child, which execs cpp
|
||||
exec "cpp" or die "Couldn't exec cpp ($!)\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Process the DATA section and output to cpp
|
||||
print $out "#include <sys/syscall.h>\n";
|
||||
while(<>) {
|
||||
if(/^\w/) {
|
||||
print $out "__NR_$_";
|
||||
}
|
||||
}
|
||||
close $out;
|
||||
exit 0;
|
||||
|
||||
}
|
||||
|
||||
# Print header and then process output from cpp.
|
||||
print "1\n";
|
||||
print "whitelist\n";
|
||||
|
||||
while(<$in>) {
|
||||
print if( /^[0-9]/ );
|
||||
}
|
||||
|
||||
@@ -1,444 +0,0 @@
|
||||
/* This sample file is an example for mkseccomp.pl to produce a seccomp file
|
||||
* which restricts syscalls that are only useful for an admin but allows the
|
||||
* vast majority of normal userspace programs to run normally.
|
||||
*
|
||||
* The format of this file is one line per syscall. This is then processed
|
||||
* and passed to 'cpp' to convert the names to numbers using whatever is
|
||||
* correct for your platform. As such C-style comments are permitted. Note
|
||||
* this also means that C preprocessor macros are also allowed. So it is
|
||||
* possible to create groups surrounded by #ifdef/#endif and control their
|
||||
* inclusion via #define (not #include).
|
||||
*
|
||||
* Syscalls that don't exist on your architecture are silently filtered out.
|
||||
* Syscalls marked with (*) are required for a container to spawn a bash
|
||||
* shell successfully (not necessarily full featured). Listing the same
|
||||
* syscall multiple times is no problem.
|
||||
*
|
||||
* If you want to make a list specifically for one application the easiest
|
||||
* way is to run the application under strace, like so:
|
||||
*
|
||||
* $ strace -f -q -c -o strace.out application args...
|
||||
*
|
||||
* Once you have a reasonable sample of the execution of the program, exit
|
||||
* it. The file strace.out will have a summary of the syscalls used. Copy
|
||||
* that list into this file, comment out everything else except the starred
|
||||
* syscalls (which you need for the container to start) and you're done.
|
||||
*
|
||||
* To get the list of syscalls from the strace output this works well for
|
||||
* me
|
||||
*
|
||||
* $ cut -c52 < strace.out
|
||||
*
|
||||
* This sample list was compiled as a combination of all the syscalls
|
||||
* available on i386 and amd64 on Ubuntu Precise, as such it may not contain
|
||||
* everything and not everything may be relevent for your system. This
|
||||
* shouldn't be a problem.
|
||||
*/
|
||||
|
||||
// Filesystem/File descriptor related
|
||||
access // (*)
|
||||
chdir // (*)
|
||||
chmod
|
||||
chown
|
||||
chown32
|
||||
close // (*)
|
||||
creat
|
||||
dup // (*)
|
||||
dup2 // (*)
|
||||
dup3
|
||||
epoll_create
|
||||
epoll_create1
|
||||
epoll_ctl
|
||||
epoll_ctl_old
|
||||
epoll_pwait
|
||||
epoll_wait
|
||||
epoll_wait_old
|
||||
eventfd
|
||||
eventfd2
|
||||
faccessat // (*)
|
||||
fadvise64
|
||||
fadvise64_64
|
||||
fallocate
|
||||
fanotify_init
|
||||
fanotify_mark
|
||||
ioctl // (*)
|
||||
fchdir
|
||||
fchmod
|
||||
fchmodat
|
||||
fchown
|
||||
fchown32
|
||||
fchownat
|
||||
fcntl // (*)
|
||||
fcntl64
|
||||
fdatasync
|
||||
fgetxattr
|
||||
flistxattr
|
||||
flock
|
||||
fremovexattr
|
||||
fsetxattr
|
||||
fstat // (*)
|
||||
fstat64
|
||||
fstatat64
|
||||
fstatfs
|
||||
fstatfs64
|
||||
fsync
|
||||
ftruncate
|
||||
ftruncate64
|
||||
getcwd // (*)
|
||||
getdents // (*)
|
||||
getdents64
|
||||
getxattr
|
||||
inotify_add_watch
|
||||
inotify_init
|
||||
inotify_init1
|
||||
inotify_rm_watch
|
||||
io_cancel
|
||||
io_destroy
|
||||
io_getevents
|
||||
io_setup
|
||||
io_submit
|
||||
lchown
|
||||
lchown32
|
||||
lgetxattr
|
||||
link
|
||||
linkat
|
||||
listxattr
|
||||
llistxattr
|
||||
llseek
|
||||
_llseek
|
||||
lremovexattr
|
||||
lseek // (*)
|
||||
lsetxattr
|
||||
lstat
|
||||
lstat64
|
||||
mkdir
|
||||
mkdirat
|
||||
mknod
|
||||
mknodat
|
||||
newfstatat
|
||||
_newselect
|
||||
oldfstat
|
||||
oldlstat
|
||||
oldolduname
|
||||
oldstat
|
||||
olduname
|
||||
oldwait4
|
||||
open // (*)
|
||||
openat // (*)
|
||||
pipe // (*)
|
||||
pipe2
|
||||
poll
|
||||
ppoll
|
||||
pread64
|
||||
preadv
|
||||
futimesat
|
||||
pselect6
|
||||
pwrite64
|
||||
pwritev
|
||||
read // (*)
|
||||
readahead
|
||||
readdir
|
||||
readlink
|
||||
readlinkat
|
||||
readv
|
||||
removexattr
|
||||
rename
|
||||
renameat
|
||||
rmdir
|
||||
select
|
||||
sendfile
|
||||
sendfile64
|
||||
setxattr
|
||||
splice
|
||||
stat // (*)
|
||||
stat64
|
||||
statfs // (*)
|
||||
statfs64
|
||||
symlink
|
||||
symlinkat
|
||||
sync
|
||||
sync_file_range
|
||||
sync_file_range2
|
||||
syncfs
|
||||
tee
|
||||
truncate
|
||||
truncate64
|
||||
umask
|
||||
unlink
|
||||
unlinkat
|
||||
ustat
|
||||
utime
|
||||
utimensat
|
||||
utimes
|
||||
write // (*)
|
||||
writev
|
||||
|
||||
// Network related
|
||||
accept
|
||||
accept4
|
||||
bind // (*)
|
||||
connect // (*)
|
||||
getpeername
|
||||
getsockname // (*)
|
||||
getsockopt
|
||||
listen
|
||||
recv
|
||||
recvfrom // (*)
|
||||
recvmmsg
|
||||
recvmsg
|
||||
send
|
||||
sendmmsg
|
||||
sendmsg
|
||||
sendto // (*)
|
||||
setsockopt
|
||||
shutdown
|
||||
socket // (*)
|
||||
socketcall
|
||||
socketpair
|
||||
sethostname // (*)
|
||||
|
||||
// Signal related
|
||||
pause
|
||||
rt_sigaction // (*)
|
||||
rt_sigpending
|
||||
rt_sigprocmask // (*)
|
||||
rt_sigqueueinfo
|
||||
rt_sigreturn // (*)
|
||||
rt_sigsuspend
|
||||
rt_sigtimedwait
|
||||
rt_tgsigqueueinfo
|
||||
sigaction
|
||||
sigaltstack // (*)
|
||||
signal
|
||||
signalfd
|
||||
signalfd4
|
||||
sigpending
|
||||
sigprocmask
|
||||
sigreturn
|
||||
sigsuspend
|
||||
|
||||
// Other needed POSIX
|
||||
alarm
|
||||
brk // (*)
|
||||
clock_adjtime
|
||||
clock_getres
|
||||
clock_gettime
|
||||
clock_nanosleep
|
||||
//clock_settime
|
||||
gettimeofday
|
||||
nanosleep
|
||||
nice
|
||||
sysinfo
|
||||
syslog
|
||||
time
|
||||
timer_create
|
||||
timer_delete
|
||||
timerfd_create
|
||||
timerfd_gettime
|
||||
timerfd_settime
|
||||
timer_getoverrun
|
||||
timer_gettime
|
||||
timer_settime
|
||||
times
|
||||
uname // (*)
|
||||
|
||||
// Memory control
|
||||
madvise
|
||||
mbind
|
||||
mincore
|
||||
mlock
|
||||
mlockall
|
||||
mmap // (*)
|
||||
mmap2
|
||||
mprotect // (*)
|
||||
mremap
|
||||
msync
|
||||
munlock
|
||||
munlockall
|
||||
munmap // (*)
|
||||
remap_file_pages
|
||||
set_mempolicy
|
||||
vmsplice
|
||||
|
||||
// Process control
|
||||
capget
|
||||
capset // (*)
|
||||
clone // (*)
|
||||
execve // (*)
|
||||
exit // (*)
|
||||
exit_group // (*)
|
||||
fork
|
||||
getcpu
|
||||
getpgid
|
||||
getpgrp // (*)
|
||||
getpid // (*)
|
||||
getppid // (*)
|
||||
getpriority
|
||||
getresgid
|
||||
getresgid32
|
||||
getresuid
|
||||
getresuid32
|
||||
getrlimit // (*)
|
||||
getrusage
|
||||
getsid
|
||||
getuid // (*)
|
||||
getuid32
|
||||
getegid // (*)
|
||||
getegid32
|
||||
geteuid // (*)
|
||||
geteuid32
|
||||
getgid // (*)
|
||||
getgid32
|
||||
getgroups
|
||||
getgroups32
|
||||
getitimer
|
||||
get_mempolicy
|
||||
kill
|
||||
//personality
|
||||
prctl
|
||||
prlimit64
|
||||
sched_getaffinity
|
||||
sched_getparam
|
||||
sched_get_priority_max
|
||||
sched_get_priority_min
|
||||
sched_getscheduler
|
||||
sched_rr_get_interval
|
||||
//sched_setaffinity
|
||||
//sched_setparam
|
||||
//sched_setscheduler
|
||||
sched_yield
|
||||
setfsgid
|
||||
setfsgid32
|
||||
setfsuid
|
||||
setfsuid32
|
||||
setgid
|
||||
setgid32
|
||||
setgroups
|
||||
setgroups32
|
||||
setitimer
|
||||
setpgid // (*)
|
||||
setpriority
|
||||
setregid
|
||||
setregid32
|
||||
setresgid
|
||||
setresgid32
|
||||
setresuid
|
||||
setresuid32
|
||||
setreuid
|
||||
setreuid32
|
||||
setrlimit
|
||||
setsid
|
||||
setuid
|
||||
setuid32
|
||||
ugetrlimit
|
||||
vfork
|
||||
wait4 // (*)
|
||||
waitid
|
||||
waitpid
|
||||
|
||||
// IPC
|
||||
ipc
|
||||
mq_getsetattr
|
||||
mq_notify
|
||||
mq_open
|
||||
mq_timedreceive
|
||||
mq_timedsend
|
||||
mq_unlink
|
||||
msgctl
|
||||
msgget
|
||||
msgrcv
|
||||
msgsnd
|
||||
semctl
|
||||
semget
|
||||
semop
|
||||
semtimedop
|
||||
shmat
|
||||
shmctl
|
||||
shmdt
|
||||
shmget
|
||||
|
||||
// Linux specific, mostly needed for thread-related stuff
|
||||
arch_prctl // (*)
|
||||
get_robust_list
|
||||
get_thread_area
|
||||
gettid
|
||||
futex // (*)
|
||||
restart_syscall // (*)
|
||||
set_robust_list // (*)
|
||||
set_thread_area
|
||||
set_tid_address // (*)
|
||||
tgkill
|
||||
tkill
|
||||
|
||||
// Admin syscalls, these are blocked
|
||||
//acct
|
||||
//adjtimex
|
||||
//bdflush
|
||||
//chroot
|
||||
//create_module
|
||||
//delete_module
|
||||
//get_kernel_syms // Obsolete
|
||||
//idle // Obsolete
|
||||
//init_module
|
||||
//ioperm
|
||||
//iopl
|
||||
//ioprio_get
|
||||
//ioprio_set
|
||||
//kexec_load
|
||||
//lookup_dcookie // oprofile only?
|
||||
//migrate_pages // NUMA
|
||||
//modify_ldt
|
||||
//mount
|
||||
//move_pages // NUMA
|
||||
//name_to_handle_at // NFS server
|
||||
//nfsservctl // NFS server
|
||||
//open_by_handle_at // NFS server
|
||||
//perf_event_open
|
||||
//pivot_root
|
||||
//process_vm_readv // For debugger
|
||||
//process_vm_writev // For debugger
|
||||
//ptrace // For debugger
|
||||
//query_module
|
||||
//quotactl
|
||||
//reboot
|
||||
//setdomainname
|
||||
//setns
|
||||
//settimeofday
|
||||
//sgetmask // Obsolete
|
||||
//ssetmask // Obsolete
|
||||
//stime
|
||||
//swapoff
|
||||
//swapon
|
||||
//_sysctl
|
||||
//sysfs
|
||||
//sys_setaltroot
|
||||
//umount
|
||||
//umount2
|
||||
//unshare
|
||||
//uselib
|
||||
//vhangup
|
||||
//vm86
|
||||
//vm86old
|
||||
|
||||
// Kernel key management
|
||||
//add_key
|
||||
//keyctl
|
||||
//request_key
|
||||
|
||||
// Unimplemented
|
||||
//afs_syscall
|
||||
//break
|
||||
//ftime
|
||||
//getpmsg
|
||||
//gtty
|
||||
//lock
|
||||
//madvise1
|
||||
//mpx
|
||||
//prof
|
||||
//profil
|
||||
//putpmsg
|
||||
//security
|
||||
//stty
|
||||
//tuxcall
|
||||
//ulimit
|
||||
//vserver
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Auto sign all commits to allow them to be used by the Docker project.
|
||||
# see https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md#sign-your-work
|
||||
#
|
||||
GH_USER=$(git config --get github.user)
|
||||
SOB=$(git var GIT_AUTHOR_IDENT | sed -n "s/^\(.*>\).*$/Docker-DCO-1.1-Signed-off-by: \1 \(github: $GH_USER\)/p")
|
||||
grep -qs "^$SOB" "$1" || {
|
||||
echo
|
||||
echo "$SOB"
|
||||
} >> "$1"
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Comments</string>
|
||||
<key>scope</key>
|
||||
<string>source.dockerfile</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>shellVariables</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>TM_COMMENT_START</string>
|
||||
<key>value</key>
|
||||
<string># </string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>uuid</key>
|
||||
<string>2B215AC0-A7F3-4090-9FF6-F4842BD56CA7</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,93 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Dockerfile</string>
|
||||
<key>fileTypes</key>
|
||||
<array>
|
||||
<string>Dockerfile</string>
|
||||
</array>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*(ONBUILD\s+)?(FROM|MAINTAINER|RUN|EXPOSE|ENV|ADD|VOLUME|USER|WORKDIR|COPY)\s</string>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.control.dockerfile</string>
|
||||
</dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.other.special-method.dockerfile</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*(ONBUILD\s+)?(CMD|ENTRYPOINT)\s</string>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.dockerfile</string>
|
||||
</dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.other.special-method.dockerfile</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>"</string>
|
||||
<key>end</key>
|
||||
<string>"</string>
|
||||
<key>name</key>
|
||||
<string>string.quoted.double.dockerfile</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\.</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escaped.dockerfile</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>'</string>
|
||||
<key>end</key>
|
||||
<string>'</string>
|
||||
<key>name</key>
|
||||
<string>string.quoted.single.dockerfile</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\.</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escaped.dockerfile</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*#.*$</string>
|
||||
<key>name</key>
|
||||
<string>comment.block.dockerfile</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>scopeName</key>
|
||||
<string>source.dockerfile</string>
|
||||
<key>uuid</key>
|
||||
<string>a39d8795-59d2-49af-aa00-fe74ee29576e</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>contactEmailRot13</key>
|
||||
<string>germ@andz.com.ar</string>
|
||||
<key>contactName</key>
|
||||
<string>GermanDZ</string>
|
||||
<key>description</key>
|
||||
<string>Helpers for Docker.</string>
|
||||
<key>name</key>
|
||||
<string>Docker</string>
|
||||
<key>uuid</key>
|
||||
<string>8B9DDBAF-E65C-4E12-FFA7-467D4AA535B1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,16 +0,0 @@
|
||||
# Docker.tmbundle
|
||||
|
||||
Dockerfile syntaxt highlighting for TextMate and Sublime Text.
|
||||
|
||||
## Install
|
||||
|
||||
### Sublime Text
|
||||
|
||||
Available for Sublime Text under [package control](https://sublime.wbond.net/packages/Dockerfile%20Syntax%20Highlighting).
|
||||
Search for *Dockerfile Syntax Highlighting*
|
||||
|
||||
### TextMate 2
|
||||
|
||||
Copy the directory `Docker.tmbundle` (showed as a Package in OSX) to `~/Library/Application Support/TextMate/Managed/Bundles`
|
||||
|
||||
enjoy.
|
||||
@@ -1,3 +0,0 @@
|
||||
# hide docker's loopback devices from udisks, and thus from user desktops
|
||||
SUBSYSTEM=="block", ENV{DM_NAME}=="docker-*", ENV{UDISKS_PRESENTATION_HIDE}="1", ENV{UDISKS_IGNORE}="1"
|
||||
SUBSYSTEM=="block", DEVPATH=="/devices/virtual/block/loop*", ATTR{loop/backing_file}=="/var/lib/docker/*", ENV{UDISKS_PRESENTATION_HIDE}="1", ENV{UDISKS_IGNORE}="1"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user