Merge remote-tracking branch 'origin/master' into bump_v1.2.0

Conflicts:
	VERSION
	daemon/container.go
	daemon/daemon.go

Signed-off-by: Victor Vieux <vieux@docker.com>
This commit is contained in:
Victor Vieux 2014-08-20 20:19:03 +00:00
Родитель 1df87acb33 b1d20c483a
Коммит 380129d0d7
539 изменённых файлов: 17959 добавлений и 11280 удалений

Просмотреть файл

@ -1,4 +1,9 @@
# Generate AUTHORS: git log --format='%aN <%aE>' | sort -uf
# Generate AUTHORS: hack/generate-authors.sh
# Tip for finding duplicates (besides scanning the output of AUTHORS for name
# duplicates that aren't also email duplicates): scan the output of:
# git log --format='%aE - %aN' | sort -uf
<charles.hooper@dotcloud.com> <chooper@plumata.com>
<daniel.mizyrycki@dotcloud.com> <daniel@dotcloud.com>
<daniel.mizyrycki@dotcloud.com> <mzdaniel@glidelink.net>
@ -47,8 +52,9 @@ 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>
<michael@docker.com> <michael@crosbymichael.com>
<michael@docker.com> <crosby.michael@gmail.com>
<github@developersupport.net> <github@metaliveblog.com>
<brandon@ifup.org> <brandon@ifup.co>
<dano@spotify.com> <daniel.norberg@gmail.com>
<danny@codeaholics.org> <Danny.Yates@mailonline.co.uk>
@ -78,3 +84,16 @@ Sridhar Ratnakumar <sridharr@activestate.com> <github@srid.name>
Liang-Chi Hsieh <viirya@gmail.com>
Aleksa Sarai <cyphar@cyphar.com>
Will Weaver <monkey@buildingbananas.com>
Timothy Hobbs <timothyhobbs@seznam.cz>
Nathan LeClaire <nathan.leclaire@docker.com> <nathan.leclaire@gmail.com>
Nathan LeClaire <nathan.leclaire@docker.com> <nathanleclaire@gmail.com>
<github@hollensbe.org> <erik+github@hollensbe.org>
<github@albersweb.de> <albers@users.noreply.github.com>
<lsm5@fedoraproject.org> <lsm5@redhat.com>
<marc@marc-abramowitz.com> <msabramo@gmail.com>
Matthew Heon <mheon@redhat.com> <mheon@mheonlaptop.redhat.com>
<bernat@luffy.cx> <vincent@bernat.im>
<p@pwaller.net> <peter@scraperwiki.com>
<andrew.weiss@outlook.com> <andrew.weiss@microsoft.com>
Francisco Carriedo <fcarriedo@gmail.com>
<julienbordellier@gmail.com> <git@julienbordellier.com>

Просмотреть файл

@ -3,10 +3,20 @@
language: go
go: 1.2
go:
# This should match the version in the Dockerfile.
- 1.3.1
# Test against older versions too, just for a little extra retrocompat.
- 1.2
# Let us have pretty experimental Docker-based Travis workers.
# (These spin up much faster than the VM-based ones.)
sudo: false
# Disable the normal go build.
install: true
install:
- export DOCKER_BUILDTAGS='exclude_graphdriver_btrfs exclude_graphdriver_devicemapper' # btrfs and devicemapper fail to compile thanks to a couple missing headers (which we can't install thanks to "sudo: false")
- export AUTO_GOPATH=1
before_script:
- env | sort
@ -14,5 +24,7 @@ before_script:
script:
- hack/make.sh validate-dco
- hack/make.sh validate-gofmt
- ./hack/make.sh dynbinary
- DOCKER_CLIENTONLY=1 ./hack/make.sh dynbinary
# vim:set sw=2 ts=2:

213
AUTHORS
Просмотреть файл

@ -1,5 +1,5 @@
# This file lists all individuals having contributed content to the repository.
# For how it is generated, see `.mailmap`.
# For how it is generated, see `hack/generate-authors.sh`.
Aanand Prasad <aanand.prasad@gmail.com>
Aaron Feng <aaron.feng@gmail.com>
@ -9,33 +9,43 @@ Adam Miller <admiller@redhat.com>
Adam Singer <financeCoding@gmail.com>
Aditya <aditya@netroy.in>
Adrian Mouat <adrian.mouat@gmail.com>
Adrien Folie <folie.adrien@gmail.com>
AJ Bowen <aj@gandi.net>
Al Tobey <al@ooyala.com>
alambike <alambike@gmail.com>
Albert Zhang <zhgwenming@gmail.com>
Aleksa Sarai <cyphar@cyphar.com>
Alex Gaynor <alex.gaynor@gmail.com>
Alex Warhawk <ax.warhawk@gmail.com>
Alexander Larsson <alexl@redhat.com>
Alexander Shopov <ash@kambanaria.org>
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>
Al Tobey <al@ooyala.com>
amangoel <amangoel@gmail.com>
AnandkumarPatel <anandkumarpatel@gmail.com>
Andre Dublin <81dublin@gmail.com>
Andrea Luzzardi <aluzzardi@gmail.com>
Andrea Turli <andrea.turli@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 France <andrew@avito.co.uk>
Andrew Macgregor <andrew.macgregor@agworld.com.au>
Andrew Munsell <andrew@wizardapps.net>
Andrews Medina <andrewsmedina@gmail.com>
Andrew Weiss <andrew.weiss@outlook.com>
Andrew Williams <williams.andrew@gmail.com>
Andrews Medina <andrewsmedina@gmail.com>
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 Rothfusz <github@developersupport.net>
Andy Smith <github@anarkystic.com>
Anthony Bishopric <git@anthonybishopric.com>
Anton Löfgren <anton.lofgren@gmail.com>
Anton Nikitin <anton.k.nikitin@gmail.com>
Antony Messerli <amesserl@rackspace.com>
apocas <petermdias@gmail.com>
@ -44,25 +54,34 @@ 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>
bdevloed <boris.de.vloed@gmail.com>
Ben Firshman <ben@firshman.co.uk>
Ben Sargent <ben@brokendigits.com>
Ben Toews <mastahyeti@gmail.com>
Ben Wiklund <ben@daisyowl.com>
Benjamin Atkin <ben@benatkin.com>
Benoit Chesneau <bchesneau@gmail.com>
Bernerd Schaefer <bj.schaefer@gmail.com>
Bhiraj Butala <abhiraj.butala@gmail.com>
bin liu <liubin0329@users.noreply.github.com>
Bouke Haarsma <bouke@webatoom.nl>
Boyd Hemphill <boyd@feedmagnet.com>
Brandon Liu <bdon@bdon.org>
Brandon Philips <brandon@ifup.org>
Brandon Rhodes <brandon@rhodesmill.org>
Brett Kochendorfer <brett.kochendorfer@gmail.com>
Brian (bex) Exelbierd <bexelbie@redhat.com>
Brian Dorsey <brian@dorseys.org>
Brian Flad <bflad417@gmail.com>
Brian Goff <cpuguy83@gmail.com>
Brian McCallister <brianm@skife.org>
Brian Olsen <brian@maven-group.org>
Brian Shumate <brian@couchbase.com>
Brice Jaglin <bjaglin@teads.tv>
Briehan Lombaard <briehan.lombaard@gmail.com>
Bruno Bigras <bigras.bruno@gmail.com>
Bruno Renié <brutasse@gmail.com>
Bryan Bess <squarejaw@bsbess.com>
Bryan Matsuo <bryan.matsuo@gmail.com>
Bryan Murphy <bmurphy1976@gmail.com>
Caleb Spare <cespare@gmail.com>
@ -73,19 +92,35 @@ Charles Hooper <charles.hooper@dotcloud.com>
Charles Lindsay <chaz@chazomatic.us>
Charles Merriam <charles.merriam@gmail.com>
Charlie Lewis <charliel@lab41.org>
Chewey <prosto-chewey@users.noreply.github.com>
Chia-liang Kao <clkao@clkao.org>
Chris Alfonso <calfonso@redhat.com>
Chris Snow <chsnow123@gmail.com>
Chris St. Pierre <chris.a.st.pierre@gmail.com>
chrismckinnel <chris.mckinnel@tangentlabs.co.uk>
Christian Berendt <berendt@b1-systems.de>
ChristoperBiscardi <biscarch@sketcht.com>
Christophe Troestler <christophe.Troestler@umons.ac.be>
Christopher Currie <codemonkey+github@gmail.com>
Christopher Rigor <crigor@gmail.com>
Christophe Troestler <christophe.Troestler@umons.ac.be>
Ciro S. Costa <ciro.costa@usp.br>
Clayton Coleman <ccoleman@redhat.com>
Colin Dunklau <colin.dunklau@gmail.com>
Colin Rice <colin@daedrum.net>
Colin Walters <walters@verbum.org>
Cory Forsyth <cory.forsyth@gmail.com>
cpuguy83 <cpuguy83@gmail.com>
cressie176 <github@stephen-cresswell.net>
Cruceru Calin-Cristian <crucerucalincristian@gmail.com>
Daan van Berkel <daan.v.berkel.1980@gmail.com>
Dafydd Crosby <dtcrsby@gmail.com>
Dan Buch <d.buch@modcloth.com>
Dan Hirsch <thequux@upstandinghackers.com>
Dan Keder <dan.keder@gmail.com>
Dan McPherson <dmcphers@redhat.com>
Dan Stine <sw@stinemail.com>
Dan Walsh <dwalsh@redhat.com>
Dan Williams <me@deedubs.com>
Daniel Exner <dex@dragonslave.de>
Daniel Garcia <daniel@danielgarcia.info>
Daniel Gasienica <daniel@gasienica.ch>
@ -95,22 +130,21 @@ Daniel Nordberg <dnordberg@gmail.com>
Daniel Robinson <gottagetmac@gmail.com>
Daniel Von Fange <daniel@leancoder.com>
Daniel YC Lin <dlin.tw@gmail.com>
Dan Keder <dan.keder@gmail.com>
Dan McPherson <dmcphers@redhat.com>
Daniel, Dao Quang Minh <dqminh89@gmail.com>
Danny Berger <dpb587@gmail.com>
Danny Yates <danny@codeaholics.org>
Dan Stine <sw@stinemail.com>
Dan Walsh <dwalsh@redhat.com>
Dan Williams <me@deedubs.com>
Darren Coxall <darren@darrencoxall.com>
Darren Shepherd <darren.s.shepherd@gmail.com>
David Anderson <dave@natulte.net>
David Calavera <david.calavera@gmail.com>
David Corking <dmc-source@dcorking.com>
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>
Derek <crq@kernel.org>
Deric Crago <deric.crago@gmail.com>
Dinesh Subhraveti <dineshs@altiscale.com>
Djibril Koné <kone.djibril@gmail.com>
dkumor <daniel@dkumor.com>
@ -118,8 +152,10 @@ 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>
Doug Davis <dug@us.ibm.com>
doug tangren <d.tangren@gmail.com>
Dr Nic Williams <drnicwilliams@gmail.com>
Dražen Lučanin <kermit666@gmail.com>
Dustin Sallings <dustin@spy.net>
Edmund Wagner <edmund-wagner@web.de>
Eiichi Tsukata <devel@etsukata.com>
@ -130,13 +166,17 @@ 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>
Eric Windisch <eric@windisch.us>
Eric Windisch <ewindisch@docker.com>
Erik Hollensbe <github@hollensbe.org>
Erik Inge Bolsø <knan@redpill-linpro.com>
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>
evanderkoogh <info@erronis.nl>
Eystein Måløy Stenberg <eystein.maloy.stenberg@cfengine.com>
ezbercih <cem.ezberci@gmail.com>
Fabio Falci <fabiofalci@gmail.com>
@ -147,12 +187,16 @@ Fareed Dudhia <fareeddudhia@googlemail.com>
Felix Rabe <felix@rabe.io>
Fernando <fermayo@gmail.com>
Flavio Castelli <fcastelli@suse.com>
FLGMwt <ryan.stelly@live.com>
Francisco Carriedo <fcarriedo@gmail.com>
Francisco Souza <f@souza.cc>
Frank Macreery <frank@macreery.com>
Fred Lifton <fred.lifton@docker.com>
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
Frederik Loeffert <frederik@zitrusmedia.de>
Freek Kalter <freek@kalteronline.org>
Gabe Rosenhouse <gabe@missionst.com>
Gabor Nagy <mail@aigeruth.hu>
Gabriel Monroy <gabriel@opdemand.com>
Galen Sampson <galen.sampson@gmail.com>
Gareth Rushgrove <gareth@morethanseven.net>
@ -160,75 +204,106 @@ 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>
Giuseppe Mazzotta <gdm85@users.noreply.github.com>
Gleb Fotengauer-Malinovskiy <glebfm@altlinux.org>
Glyn Normington <gnormington@gopivotal.com>
Goffert van Gool <goffert@phusion.nl>
Graydon Hoare <graydon@pobox.com>
Greg Thornton <xdissent@me.com>
grunny <mwgrunny@gmail.com>
Guilherme Salgado <gsalgado@gmail.com>
Guillaume J. Charmes <guillaume.charmes@docker.com>
Gurjeet Singh <gurjeet@singh.im>
Guruprasad <lgp171188@gmail.com>
Harald Albers <github@albersweb.de>
Harley Laue <losinggeneration@gmail.com>
Hector Castro <hectcastro@gmail.com>
Henning Sprang <henning.sprang@gmail.com>
Hobofan <goisser94@gmail.com>
Hollie Teal <hollie.teal@docker.com>
Hollie Teal <hollietealok@users.noreply.github.com>
hollietealok <hollie@docker.com>
Hunter Blanks <hunter@twilio.com>
hyeongkyu.lee <hyeongkyu.lee@navercorp.com>
Ian Babrou <ibobrik@gmail.com>
Ian Bull <irbull@gmail.com>
Ian Main <imain@redhat.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>
Ivan Fraixedes <ifcdev@gmail.com>
Jack Danger Canty <jackdanger@squareup.com>
jakedt <jake@devtable.com>
Jake Moshenko <jake@devtable.com>
jakedt <jake@devtable.com>
James Allen <jamesallen0108@gmail.com>
James Carr <james.r.carr@gmail.com>
James DeFelice <james.defelice@ishisystems.com>
James Harrison Fisher <jameshfisher@gmail.com>
James Kyle <james@jameskyle.org>
James Mills <prologic@shortcircuit.net.au>
James Turnbull <james@lovedthanlost.net>
Jan Pazdziora <jpazdziora@redhat.com>
Jan Toebes <jan@toebes.info>
Jaroslaw Zabiello <hipertracker@gmail.com>
jaseg <jaseg@jaseg.net>
Jason Giedymin <jasong@apache.org>
Jason Hall <imjasonh@gmail.com>
Jason Livesay <ithkuil@gmail.com>
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>
Jeff Welch <whatthejeff@gmail.com>
Jeffrey Bolle <jeffreybolle@gmail.com>
Jeremy Grosser <jeremy@synack.me>
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
Jesse Dubay <jesse@thefortytwo.net>
Jezeniel Zapanta <jpzapanta22@gmail.com>
Jilles Oldenbeuving <ojilles@gmail.com>
Jim Alateras <jima@comware.com.au>
Jim Perrin <jperrin@centos.org>
Jimmy Cuadra <jimmy@jimmycuadra.com>
Jiří Župka <jzupka@redhat.com>
Joe Beda <joe.github@bedafamily.com>
Joel Handwell <joelhandwell@gmail.com>
Joe Shaw <joe@joeshaw.org>
Joe Van Dyk <joe@tanga.com>
Joel Handwell <joelhandwell@gmail.com>
Joffrey F <joffrey@docker.com>
Johan Euphrosine <proppy@google.com>
Johannes 'fish' Ziemke <github@freigeist.org>
Johan Rydberg <johan.rydberg@gmail.com>
Johannes 'fish' Ziemke <github@freigeist.org>
John Costa <john.costa@gmail.com>
John Feminella <jxf@jxf.me>
John Gardiner Myers <jgmyers@proofpoint.com>
John OBrien III <jobrieniii@yahoo.com>
John Warwick <jwarwick@gmail.com>
Jon Wedaman <jweede@gmail.com>
Jonas Pfenniger <jonas@pfenniger.name>
Jonathan Boulle <jonathanboulle@gmail.com>
Jonathan Camp <jonathan@irondojo.com>
Jonathan McCrohan <jmccrohan@gmail.com>
Jonathan Mueller <j.mueller@apoveda.ch>
Jonathan Pares <jonathanpa@users.noreply.github.com>
Jonathan Rudenberg <jonathan@titanous.com>
Jon Wedaman <jweede@gmail.com>
Joost Cassee <joost@cassee.net>
Jordan Arentsen <blissdev@gmail.com>
Jordan Sissel <jls@semicomplete.com>
Joseph Anthony Pasquale Holsten <joseph@josephholsten.com>
Joseph Hager <ajhager@gmail.com>
Josh <jokajak@gmail.com>
Josh Hawn <josh.hawn@docker.com>
Josh Poimboeuf <jpoimboe@redhat.com>
JP <jpellerin@leapfrogonline.com>
Julien Barbier <write0@gmail.com>
Julien Bordellier <julienbordellier@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>
@ -236,36 +311,49 @@ Kawsar Saiyeed <kawsar.saiyeed@projiris.com>
Keli Hu <dev@keli.hu>
Ken Cochrane <kencochrane@gmail.com>
Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com>
Kevin "qwazerty" Houdebert <kevin.houdebert@gmail.com>
Kevin Clark <kevin.clark@gmail.com>
Kevin J. Lynagh <kevin@keminglabs.com>
Kevin Menard <kevin@nirvdrum.com>
Kevin Wallace <kevin@pentabarf.net>
Keyvan Fatehi <keyvanfatehi@gmail.com>
kim0 <email.ahmedkamal@googlemail.com>
kies <lleelm@gmail.com>
Kim BKC Carlbacker <kim.carlbacker@gmail.com>
kim0 <email.ahmedkamal@googlemail.com>
Kimbro Staken <kstaken@kstaken.com>
Kiran Gangadharan <kiran.daredevil@gmail.com>
knappe <tyler.knappe@gmail.com>
Kohei Tsuruta <coheyxyz@gmail.com>
Konstantin Pelykh <kpelykh@zettaset.com>
Kyle Conroy <kyle.j.conroy@gmail.com>
kyu <leehk1227@gmail.com>
Lachlan Coote <lcoote@vmware.com>
lalyos <lalyos@yahoo.com>
Lance Chen <cyen0312@gmail.com>
Lars R. Damerow <lars@pixar.com>
Laurie Voss <github@seldo.com>
leeplay <hyeongkyu.lee@navercorp.com>
Len Weincier <len@cloudafrica.net>
Levi Gross <levi@levigross.com>
Lewis Peckover <lew+github@lew.io>
Liang-Chi Hsieh <viirya@gmail.com>
Lokesh Mandvekar <lsm5@redhat.com>
Lokesh Mandvekar <lsm5@fedoraproject.org>
Louis Opter <kalessin@kalessin.fr>
lukaspustina <lukas.pustina@centerdevice.com>
lukemarsden <luke@digital-crocus.com>
Mahesh Tiyyagura <tmahesh@gmail.com>
Manfred Zabarauskas <manfredas@zabarauskas.com>
Manuel Meurer <manuel@krautcomputing.com>
Manuel Woelker <github@manuel.woelker.org>
Marc Abramowitz <marc@marc-abramowitz.com>
Marc Kuo <kuomarc2@gmail.com>
Marc Tamsky <mtamsky@gmail.com>
Marco Hennings <marco.hennings@freiheit.com>
Marcus Farkas <toothlessgear@finitebox.com>
Marcus Ramberg <marcus@nordaaker.com>
marcuslinke <marcus.linke@gmx.de>
Marek Goldmann <marek.goldmann@gmail.com>
Marius Voila <marius.voila@gmail.com>
Mark Allen <mrallen1@yahoo.com>
Mark McGranaghan <mmcgrana@gmail.com>
Marko Mikulicic <mmikulicic@gmail.com>
@ -278,30 +366,40 @@ Mathieu Le Marec - Pasquet <kiorky@cryptelium.net>
Matt Apperson <me@mattapperson.com>
Matt Bachmann <bachmann.matt@gmail.com>
Matt Haggard <haggardii@gmail.com>
Matthew Heon <mheon@redhat.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>
mattyw <mattyw@me.com>
Max Shytikov <mshytikov@gmail.com>
Maxim Treskin <zerthurd@gmail.com>
Maxime Petazzoni <max@signalfuse.com>
meejah <meejah@meejah.ca>
Michael Brown <michael@netdirect.ca>
Michael Crosby <michael@crosbymichael.com>
Michael Crosby <michael@docker.com>
Michael Gorsuch <gorsuch@github.com>
Michael Neale <michael.neale@gmail.com>
Michael Prokop <github@michael-prokop.at>
Michael Stapelberg <michael+gh@stapelberg.de>
Michaël Pailloncy <mpapo.dev@gmail.com>
Michiel@unhosted <michiel@unhosted.org>
Miguel Angel Fernández <elmendalerenda@gmail.com>
Mike Chelen <michael.chelen@gmail.com>
Mike Gaffney <mike@uberu.com>
Mike MacCana <mike.maccana@gmail.com>
Mike Naberezny <mike@naberezny.com>
Mike Snitzer <snitzer@redhat.com>
Mikhail Sobolev <mss@mawhrin.net>
Mohit Soni <mosoni@ebay.com>
Morgante Pell <morgante.pell@morgante.net>
Morten Siebuhr <sbhr@sbhr.dk>
Mrunal Patel <mrunalp@gmail.com>
Nan Monnand Deng <monnand@gmail.com>
Naoki Orii <norii@cs.cmu.edu>
Nate Jones <nate@endot.org>
Nathan Kleyn <nathan@nathankleyn.com>
Nathan LeClaire <nathan.leclaire@docker.com>
Nelson Chen <crazysim@gmail.com>
Niall O'Higgins <niallo@unworkable.org>
Nick Payne <nick@kurai.co.uk>
@ -309,15 +407,20 @@ 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>
NikolaMandic <mn080202@gmail.com>
noducks <onemannoducks@gmail.com>
Nolan Darilek <nolan@thewordnerd.info>
O.S. Tezer <ostezer@gmail.com>
OddBloke <daniel@daniel-watkins.co.uk>
odk- <github@odkurzacz.org>
Oguz Bilgic <fisyonet@gmail.com>
Ole Reifschneider <mail@ole-reifschneider.de>
O.S. Tezer <ostezer@gmail.com>
Olivier Gambier <dmp42@users.noreply.github.com>
pandrew <letters@paulnotcom.se>
Pascal Borreli <pascal@borreli.com>
Patrick Hemmer <patrick.hemmer@gmail.com>
pattichen <craftsbear@gmail.com>
Paul <paul9869@gmail.com>
Paul Annesley <paul@annesley.cc>
Paul Bowsher <pbowsher@globalpersonals.co.uk>
Paul Hammond <paul@paulhammond.org>
@ -325,49 +428,71 @@ Paul Jimenez <pj@place.org>
Paul Lietar <paul@lietar.net>
Paul Morie <pmorie@gmail.com>
Paul Nasrat <pnasrat@gmail.com>
Paul <paul9869@gmail.com>
Paul Weaver <pauweave@cisco.com>
Peter Bourgon <peter@bourgon.org>
Peter Braden <peterbraden@peterbraden.co.uk>
Peter Waller <peter@scraperwiki.com>
Phillip Alexander <git@phillipalexander.io>
Peter Waller <p@pwaller.net>
Phil <underscorephil@gmail.com>
Phil Spitler <pspitler@gmail.com>
Phillip Alexander <git@phillipalexander.io>
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>
r0n22 <cameron.regan@gmail.com>
Rafal Jeczalik <rjeczalik@gmail.com>
Rajat Pandit <rp@rajatpandit.com>
Rajdeep Dua <dua_rajdeep@yahoo.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>
Richard Harvey <richard@squarecows.com>
Richo Healey <richo@psych0tik.net>
Rick Bradley <rick@users.noreply.github.com>
Rick van de Loo <rickvandeloo@gmail.com>
Robert Bachmann <rb@robertbachmann.at>
Robert Obryk <robryk@gmail.com>
Roberto G. Hashioka <roberto.hashioka@docker.com>
Robin Speekenbrink <robin@kingsquare.nl>
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 Huß <roland@jolokia.org>
Roland Moriz <rmoriz@users.noreply.github.com>
Ron Smits <ron.smits@gmail.com>
Rovanion Luckey <rovanion.luckey@gmail.com>
Rudolph Gottesheim <r.gottesheim@loot.at>
Ryan Anderson <anderson.ryanc@gmail.com>
Ryan Aslett <github@mixologic.com>
Ryan Fowler <rwfowler@gmail.com>
Ryan O'Donnell <odonnellryanc@gmail.com>
Ryan Seto <ryanseto@yak.net>
Ryan Thomas <rthomas@atlassian.com>
s-ko <aleks@s-ko.net>
Sam Alba <sam.alba@gmail.com>
Sam Bailey <cyprix@cyprix.com.au>
Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
Sam Reis <sreis@atlassian.com>
Sam Rijs <srijs@airpost.net>
Samuel Andaya <samuel@andaya.net>
satoru <satorulogic@gmail.com>
Satoshi Amemiya <satoshi_amemiya@voyagegroup.com>
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>
Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn <thaJeztah@users.noreply.github.com>
Senthil Kumar Selvaraj <senthil.thecoder@gmail.com>
SeongJae Park <sj38.park@gmail.com>
Shane Canon <scanon@lbl.gov>
shaunol <shaunol@gmail.com>
Shawn Landden <shawn@churchofgit.com>
Shawn Siefkas <shawn.siefkas@meredith.com>
Shih-Yuan Lee <fourdollars@gmail.com>
@ -378,14 +503,19 @@ Sjoerd Langkemper <sjoerd-github@linuxonly.nl>
Solomon Hykes <solomon@docker.com>
Song Gao <song@gao.io>
Soulou <leo@unbekandt.eu>
soulshake <amy@gandi.net>
Sridatta Thatipamala <sthatipamala@gmail.com>
Sridhar Ratnakumar <sridharr@activestate.com>
Steeve Morin <steeve.morin@gmail.com>
Stefan Praszalowicz <stefan@greplin.com>
Stephen Crosby <stevecrozz@gmail.com>
Steven Burgess <steven.a.burgess@hotmail.com>
sudosurootdev <sudosurootdev@gmail.com>
Sven Dowideit <SvenDowideit@home.org.au>
Sven Dowideit <svendowideit@home.org.au>
Sylvain Bellemare <sylvain.bellemare@ezeep.com>
Sébastien <sebastien@yoozio.com>
Sébastien Luttringer <seblu@seblu.net>
Sébastien Stormacq <sebsto@users.noreply.github.com>
tang0th <tang0th@gmx.com>
Tatsuki Sugiura <sugi@nemui.org>
Tehmasp Chaudhri <tehmasp@gmail.com>
@ -400,19 +530,24 @@ 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 Ruffles <timruffles@googlemail.com>
Tim Terhorst <mynamewastaken+git@gmail.com>
Timothy Hobbs <timothyhobbs@seznam.cz>
tjmehta <tj@init.me>
Tobias Bieniek <Tobias.Bieniek@gmx.de>
Tobias Gesellchen <tobias@gesellix.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>
Tom Maaswinkel <tom.maaswinkel@12wiki.eu>
Tommaso Visconti <tommaso.visconti@gmail.com>
Tony Daws <tony@daws.ca>
tpng <benny.tpng@gmail.com>
Travis Cline <travis.cline@gmail.com>
Trent Ogren <tedwardo2@gmail.com>
Tyler Brock <tyler.brock@gmail.com>
Tzu-Jung Lee <roylee17@gmail.com>
Ulysse Carion <ulyssecarion@gmail.com>
@ -434,21 +569,29 @@ Vivek Agarwal <me@vivek.im>
Vladimir Bulyga <xx@ccxx.cc>
Vladimir Kirillov <proger@wilab.org.ua>
Vladimir Rutsky <altsysrq@gmail.com>
waitingkuo <waitingkuo0527@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>
William Delanoue <william.delanoue@gmail.com>
William Henry <whenry@redhat.com>
William Riancho <wr.wllm@gmail.com>
William Thurston <thurstw@amazon.com>
wyc <wayne@neverfear.org>
Xiuming Chen <cc@cxm.cc>
Yang Bai <hamo.by@gmail.com>
Yasunori Mahata <nori@mahata.net>
Yurii Rashkovskii <yrashk@gmail.com>
Zac Dover <zdover@redhat.com>
Zain Memon <zain@inzain.net>
Zaiste! <oh@zaiste.net>
Zane DeGraffenried <zane.deg@gmail.com>
Zilin Du <zilin.du@gmail.com>
zimbatm <zimbatm@zimbatm.com>
Zoltan Tombol <zoltan.tombol@gmail.com>
zqh <zqhxuyuan@gmail.com>
Álvaro Lázaro <alvaro.lazaro.g@gmail.com>

Просмотреть файл

@ -313,7 +313,7 @@
- Add newlines to the JSON stream functions.
#### Runtime
* Do not ping the registry from the CLI. All requests to registres flow through the daemon.
* Do not ping the registry from the CLI. All requests to registries 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.
@ -905,7 +905,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
+ Add domainname support
+ Implement image filtering with path.Match
* Remove unnecesasry warnings
* Remove unnecessary warnings
* Remove os/user dependency
* Only mount the hostname file when the config exists
* Handle signals within the `docker login` command
@ -928,7 +928,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
+ Hack: Vendor all dependencies
* Remote API: Bump to v1.5
* Packaging: Break down hack/make.sh into small scripts, one per 'bundle': test, binary, ubuntu etc.
* Documentation: General improvments
* Documentation: General improvements
## 0.6.1 (2013-08-23)
@ -1198,7 +1198,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
* Prevent rm of running containers
* Use go1.1 cookiejar
- Fix issue detaching from running TTY container
- Forbid parralel push/pull for a single image/repo. Fixes #311
- Forbid parallel push/pull for a single image/repo. Fixes #311
- Fix race condition within Run command when attaching.
#### Client
@ -1314,7 +1314,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
+ Add caching to docker builder
+ Add support for docker builder with native API as top level command
+ Implement ENV within docker builder
- Check the command existance prior create and add Unit tests for the case
- Check the command existence prior create and add Unit tests for the case
* use any whitespaces instead of tabs
#### Runtime
@ -1353,13 +1353,13 @@ With the ongoing changes to the networking and execution subsystems of docker te
#### Runtime
- Fix the command existance check
- Fix the command existence check
- strings.Split may return an empty string on no match
- Fix an index out of range crash if cgroup memory is not
#### Documentation
* Various improvments
* Various improvements
+ New example: sharing data between 2 couchdb databases
#### Other
@ -1389,7 +1389,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
## 0.2.0 (2013-04-23)
- Runtime: ghost containers can be killed and waited for
* Documentation: update install intructions
* Documentation: update install instructions
- Packaging: fix Vagrantfile
- Development: automate releasing binaries and ubuntu packages
+ Add a changelog

Просмотреть файл

@ -4,14 +4,52 @@ 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.
## Topics
* [Security Reports](#security-reports)
* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
* [Reporting Issues](#reporting-issues)
* [Build Environment](#build-environment)
* [Contribution Guidelines](#contribution-guidelines)
* [Community Guidelines](#docker-community-guidelines)
## Security Reports
Please **DO NOT** file an issue for security related issues. Please send your
reports to [security@docker.com](mailto:security@docker.com) instead.
## Design and Cleanup Proposals
When considering a design proposal, we are looking for:
* A description of the problem this design proposal solves
* An issue -- not a pull request -- that describes what you will take action on
* Please prefix your issue with `Proposal:` in the title
* Please review [the existing Proposals](https://github.com/dotcloud/docker/issues?direction=asc&labels=Proposal&page=1&sort=created&state=open)
before reporting a new issue. You can always pair with someone if you both
have the same idea.
When considering a cleanup task, we are looking for:
* A description of the refactors made
* Please note any logic changes if necessary
* A pull request with the code
* Please prefix your PR's title with `Cleanup:` so we can quickly address it.
* Your pull request must remain up to date with master, so rebase as necessary.
## 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.
When reporting [issues](https://github.com/docker/docker/issues) on
GitHub please include your host OS (Ubuntu 12.04, Fedora 19, etc).
Please include:
* The output of `uname -a`.
* The output of `docker version`.
* The output of `docker -D info`.
Please also 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
@ -34,7 +72,7 @@ received feedback on what to improve.
We're trying very hard to keep Docker lean and focused. We don't want it
to do everything for everybody. This means that we might decide against
incorporating a new feature. However, there might be a way to implement
that feature *on top of* docker.
that feature *on top of* Docker.
### Discuss your design on the mailing list
@ -48,7 +86,7 @@ else is working on the same thing.
### Create issues...
Any significant improvement should be documented as [a GitHub
issue](https://github.com/dotcloud/docker/issues) before anybody
issue](https://github.com/docker/docker/issues) before anybody
starts working on it.
### ...but check for existing issues first!
@ -60,12 +98,12 @@ help prioritize the most common problems and requests.
### Conventions
Fork the repo and make changes on your fork in a feature branch:
Fork the repository and make changes on your fork in a feature branch:
- If it's a bugfix branch, name it XXX-something where XXX is the number of the
issue
- If it's a bug fix branch, name it XXXX-something where XXXX is the number of the
issue.
- If it's a feature branch, create an enhancement issue to announce your
intentions, and name it XXX-something where XXX is the number of the issue.
intentions, and name it XXXX-something where XXXX is the number of the issue.
Submit unit tests for your changes. Go has a great test framework built in; use
it! Take a look at existing tests for inspiration. Run the full test suite on
@ -73,18 +111,16 @@ 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
information on building the docs and how docs get released.
well as a clean documentation build. See `docs/README.md` for more
information on building the docs and how they 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.
committing your changes. Most editors have plug-ins that do this automatically.
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.
@ -95,26 +131,33 @@ sure to post a comment after pushing. The new commits will show up in the pull
request automatically, but the reviewers will not be notified unless you
comment.
Pull requests must be cleanly rebased ontop of master without multiple branches
mixed into the PR.
**Git tip**: If your PR no longer merges cleanly, use `rebase master` in your
feature branch to update your pull request rather than `merge master`.
Before the pull request is merged, make sure that you squash your commits into
logical units of work using `git rebase -i` and `git push -f`. After every
commit the test suite should be passing. Include documentation changes in the
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.
Commits that fix or close an issue should include a reference like
`Closes #XXXX` or `Fixes #XXXX`, which will automatically close the
issue when merged.
Please do not add yourself to the AUTHORS file, as it is regenerated
Please do not add yourself to the `AUTHORS` file, as it is regenerated
regularly from the Git history.
### Merge approval
Docker maintainers use LGTM (looks good to me) in comments on the code review
Docker maintainers use LGTM (Looks Good To Me) in comments on the code review
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.
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/`.
For more details see [MAINTAINERS.md](hack/MAINTAINERS.md)
@ -137,7 +180,6 @@ 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:
@ -165,20 +207,18 @@ By making a contribution to this project, I certify that:
this project or the open source license(s) involved.
```
then you just add a line to every git commit message:
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)
Signed-off-by: Joe Smith <joe.smith@email.com>
using your real name (sorry, no pseudonyms or anonymous contributions.)
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:
If you set your `user.name` and `user.email` git configs, you can sign your
commit automatically with `git commit -s`.
```
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``
Note that the old-style `Docker-DCO-1.1-Signed-off-by: ...` format is still
accepted, so there is no need to update outstanding pull requests to the new
format right away, but please do adjust your processes for future contributions.
#### Small patch exception
@ -194,11 +234,83 @@ If you have any questions, please refer to the FAQ in the [docs](http://docs.doc
### 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
* Step 1: Learn the component inside out
* Step 2: Make yourself useful by contributing code, bug fixes, support etc.
* Step 3: Volunteer on the IRC channel (#docker at 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!
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!
### IRC Meetings
There are two monthly meetings taking place on #docker-dev IRC to accomodate all timezones.
Anybody can ask for a topic to be discussed prior to the meeting.
If you feel the conversation is going off-topic, feel free to point it out.
For the exact dates and times, have a look at [the irc-minutes repo](https://github.com/docker/irc-minutes).
They also contain all the notes from previous meetings.
## Docker Community Guidelines
We want to keep the Docker community awesome, growing and collaborative. We
need your help to keep it that way. To help with this we've come up with some
general guidelines for the community as a whole:
* Be nice: Be courteous, respectful and polite to fellow community members: no
regional, racial, gender, or other abuse will be tolerated. We like nice people
way better than mean ones!
* Encourage diversity and participation: Make everyone in our community
feel welcome, regardless of their background and the extent of their
contributions, and do everything possible to encourage participation in
our community.
* Keep it legal: Basically, don't get us in trouble. Share only content that
you own, do not share private or sensitive information, and don't break the
law.
* Stay on topic: Make sure that you are posting to the correct channel
and avoid off-topic discussions. Remember when you update an issue or
respond to an email you are potentially sending to a large number of
people. Please consider this before you update. Also remember that
nobody likes spam.
### Guideline Violations — 3 Strikes Method
The point of this section is not to find opportunities to punish people, but we
do need a fair way to deal with people who are making our community suck.
1. First occurrence: We'll give you a friendly, but public reminder that the
behavior is inappropriate according to our guidelines.
2. Second occurrence: We will send you a private message with a warning that
any additional violations will result in removal from the community.
3. Third occurrence: Depending on the violation, we may need to delete or ban
your account.
**Notes:**
* Obvious spammers are banned on first occurrence. If we don't do this, we'll
have spam all over the place.
* Violations are forgiven after 6 months of good behavior, and we won't
hold a grudge.
* People who commit minor infractions will get some education,
rather than hammering them in the 3 strikes process.
* The rules apply equally to everyone in the community, no matter how
much you've contributed.
* Extreme violations of a threatening, abusive, destructive or illegal nature
will be addressed immediately and are not subject to 3 strikes or
forgiveness.
* Contact james@docker.com to report abuse or appeal violations. In the case of
appeals, we know that mistakes happen, and we'll work with you to come up with
a fair solution if there has been a misunderstanding.

Просмотреть файл

@ -6,7 +6,7 @@
# 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/docker/docker --privileged -i -t docker bash
#
# # Run the test suite:
# docker run --privileged docker hack/make.sh test
@ -28,8 +28,7 @@ FROM ubuntu:14.04
MAINTAINER Tianon Gravi <admwiggin@gmail.com> (@tianon)
# Packaged dependencies
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq \
apt-utils \
RUN apt-get update && apt-get install -y \
aufs-tools \
automake \
btrfs-tools \
@ -43,7 +42,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq \
libsqlite3-dev \
lxc=1.0* \
mercurial \
pandoc \
parallel \
reprepro \
ruby1.9.1 \
ruby1.9.1-dev \
@ -60,9 +59,10 @@ RUN cd /usr/local/lvm2 && ./configure --enable-static_link && make device-mapper
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
# Install Go
RUN curl -s https://go.googlecode.com/files/go1.2.1.src.tar.gz | tar -v -C /usr/local -xz
RUN curl -sSL https://golang.org/dl/go1.3.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
ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
ENV PATH /go/bin:$PATH
RUN cd /usr/local/go/src && ./make.bash --no-clean 2>&1
# Compile Go for cross compilation
@ -80,6 +80,12 @@ RUN go get code.google.com/p/go.tools/cmd/cover
# TODO replace FPM with some very minimal debhelper stuff
RUN gem install --no-rdoc --no-ri fpm --version 1.0.2
# Install man page generator
RUN mkdir -p /go/src/github.com/cpuguy83 \
&& git clone -b v1 https://github.com/cpuguy83/go-md2man.git /go/src/github.com/cpuguy83/go-md2man \
&& cd /go/src/github.com/cpuguy83/go-md2man \
&& go get -v ./...
# 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
@ -94,11 +100,11 @@ RUN groupadd -r docker
RUN useradd --create-home --gid docker unprivilegeduser
VOLUME /var/lib/docker
WORKDIR /go/src/github.com/dotcloud/docker
WORKDIR /go/src/github.com/docker/docker
ENV DOCKER_BUILDTAGS apparmor selinux
# Wrap all commands in the "docker-in-docker" script to allow nested containers
ENTRYPOINT ["hack/dind"]
# Upload docker source
ADD . /go/src/github.com/dotcloud/docker
COPY . /go/src/github.com/docker/docker

24
FIXME
Просмотреть файл

@ -1,24 +0,0 @@
## FIXME
This file is a loose collection of things to improve in the codebase, for the internal
use of the maintainers.
They are not big enough to be in the roadmap, not user-facing enough to be github issues,
and not important enough to be discussed in the mailing list.
They are just like FIXME comments in the source code, except we're not sure where in the source
to put them - so we put them here :)
* 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
* use size header for progress bar in pull
* Clean up context upload in build!!!
* Parallel pull
* 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
* Clean up the ProgressReader api, it's a PITA to use

Просмотреть файл

@ -1,7 +1,8 @@
.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
# (default to no bind mount if DOCKER_HOST is set)
BINDDIR := $(if $(DOCKER_HOST),,bundles)
# to allow `make DOCSPORT=9000 docs`
DOCSPORT := 8000
@ -9,7 +10,7 @@ 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_MOUNT := $(if $(BINDDIR),-v "$(CURDIR)/$(BINDDIR):/go/src/github.com/docker/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`
@ -33,7 +34,7 @@ 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
$(DOCKER_RUN_DOCS) -e BUILD_ROOT "$(DOCKER_DOCS_IMAGE)" ./release.sh
test: build
$(DOCKER_RUN_DOCKER) hack/make.sh binary cross test-unit test-integration test-integration-cli

Просмотреть файл

@ -131,9 +131,8 @@ 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 apt-get update && apt-get install -y python python-pip curl
RUN curl -sSL https://github.com/shykes/helloflask/archive/master.tar.gz | tar -xzv
RUN cd helloflask-master && pip install -r requirements.txt
```
@ -178,6 +177,9 @@ Under the hood, Docker is built on the following components:
Contributing to Docker
======================
[![GoDoc](https://godoc.org/github.com/docker/docker?status.png)](https://godoc.org/github.com/docker/docker)
[![Travis](https://travis-ci.org/docker/docker.svg?branch=master)](https://travis-ci.org/docker/docker)
Want to hack on Docker? Awesome! There are instructions to get you
started [here](CONTRIBUTING.md).

Просмотреть файл

@ -1 +1 @@
1.1.2
1.1.2-dev

Просмотреть файл

@ -1,5 +1,5 @@
This directory contains code pertaining to the Docker API:
- Used by the docker client when comunicating with the docker deamon
- Used by the docker client when communicating with the docker daemon
- Used by third party tools wishing to interface with the docker deamon
- Used by third party tools wishing to interface with the docker daemon

Просмотреть файл

@ -10,11 +10,24 @@ import (
"strings"
"text/template"
flag "github.com/dotcloud/docker/pkg/mflag"
"github.com/dotcloud/docker/pkg/term"
"github.com/dotcloud/docker/registry"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/term"
"github.com/docker/docker/registry"
)
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
}
var funcMap = template.FuncMap{
"json": func(v interface{}) string {
a, _ := json.Marshal(v)
@ -34,7 +47,8 @@ func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) {
return method.Interface().(func(...string) error), true
}
func (cli *DockerCli) ParseCommands(args ...string) error {
// Cmd executes the specified command
func (cli *DockerCli) Cmd(args ...string) error {
if len(args) > 0 {
method, exists := cli.getMethod(args[0])
if !exists {
@ -97,16 +111,3 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsC
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
}

Просмотреть файл

@ -22,19 +22,21 @@ import (
"text/template"
"time"
"github.com/dotcloud/docker/api"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/dockerversion"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/opts"
"github.com/dotcloud/docker/pkg/signal"
"github.com/dotcloud/docker/pkg/term"
"github.com/dotcloud/docker/pkg/units"
"github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"github.com/dotcloud/docker/utils/filters"
"github.com/docker/docker/api"
"github.com/docker/docker/archive"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/engine"
"github.com/docker/docker/nat"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/pkg/parsers/filters"
"github.com/docker/docker/pkg/signal"
"github.com/docker/docker/pkg/term"
"github.com/docker/docker/pkg/units"
"github.com/docker/docker/registry"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/utils"
)
const (
@ -67,7 +69,8 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
{"inspect", "Return low-level information on a container"},
{"kill", "Kill a running container"},
{"load", "Load an image from a tar archive"},
{"login", "Register or log in to the Docker registry server"},
{"login", "Register or log in to a Docker registry server"},
{"logout", "Log out from a Docker registry server"},
{"logs", "Fetch the logs of a container"},
{"port", "Lookup the public-facing port that is NAT-ed to PRIVATE_PORT"},
{"pause", "Pause all processes within a container"},
@ -161,28 +164,32 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
if _, err = os.Stat(filename); os.IsNotExist(err) {
return fmt.Errorf("no Dockerfile found in %s", cmd.Arg(0))
}
if err = utils.ValidateContextDirectory(root); err != nil {
var excludes []string
ignore, err := ioutil.ReadFile(path.Join(root, ".dockerignore"))
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("Error reading .dockerignore: '%s'", err)
}
for _, pattern := range strings.Split(string(ignore), "\n") {
ok, err := filepath.Match(pattern, "Dockerfile")
if err != nil {
return fmt.Errorf("Bad .dockerignore pattern: '%s', error: %s", pattern, err)
}
if ok {
return fmt.Errorf("Dockerfile was excluded by .dockerignore pattern '%s'", pattern)
}
excludes = append(excludes, pattern)
}
if err = utils.ValidateContextDirectory(root, excludes); err != nil {
return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err)
}
options := &archive.TarOptions{
Compression: archive.Uncompressed,
}
if ignore, err := ioutil.ReadFile(path.Join(root, ".dockerignore")); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("Error reading .dockerignore: '%s'", err)
} else if err == nil {
for _, pattern := range strings.Split(string(ignore), "\n") {
ok, err := filepath.Match(pattern, "Dockerfile")
if err != nil {
utils.Errorf("Bad .dockerignore pattern: '%s', error: %s", pattern, err)
continue
}
if ok {
return fmt.Errorf("Dockerfile was excluded by .dockerignore pattern '%s'", pattern)
}
options.Excludes = append(options.Excludes, pattern)
}
Excludes: excludes,
}
context, err = archive.TarWithOptions(root, options)
if err != nil {
return err
}
}
var body io.Reader
// Setup an upload progress bar
@ -196,7 +203,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
//Check if the given image name can be resolved
if *tag != "" {
repository, _ := utils.ParseRepositoryTag(*tag)
repository, _ := parsers.ParseRepositoryTag(*tag)
if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
return err
}
@ -349,6 +356,32 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
return nil
}
// log out from a Docker registry
func (cli *DockerCli) CmdLogout(args ...string) error {
cmd := cli.Subcmd("logout", "[SERVER]", "Log out from a Docker registry, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.")
if err := cmd.Parse(args); err != nil {
return nil
}
serverAddress := registry.IndexServerAddress()
if len(cmd.Args()) > 0 {
serverAddress = cmd.Arg(0)
}
cli.LoadConfigFile()
if _, ok := cli.configFile.Configs[serverAddress]; !ok {
fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress)
} else {
fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress)
delete(cli.configFile.Configs, serverAddress)
if err := registry.SaveConfig(cli.configFile); err != nil {
return fmt.Errorf("Failed to save docker config: %v", err)
}
}
return nil
}
// 'docker wait': block until a container stops
func (cli *DockerCli) CmdWait(args ...string) error {
cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.")
@ -391,6 +424,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
if dockerversion.GITCOMMIT != "" {
fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT)
}
fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH)
body, _, err := readBody(cli.call("GET", "/version", nil, false))
if err != nil {
@ -400,11 +434,11 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
out := engine.NewOutput()
remoteVersion, err := out.AddEnv()
if err != nil {
utils.Errorf("Error reading remote version: %s\n", err)
log.Errorf("Error reading remote version: %s", err)
return err
}
if _, err := out.Write(body); err != nil {
utils.Errorf("Error reading remote version: %s\n", err)
log.Errorf("Error reading remote version: %s", err)
return err
}
out.Close()
@ -440,7 +474,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
}
if _, err := out.Write(body); err != nil {
utils.Errorf("Error reading remote info: %s\n", err)
log.Errorf("Error reading remote info: %s", err)
return err
}
out.Close()
@ -457,6 +491,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
}
fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver"))
fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
fmt.Fprintf(cli.out, "Operating System: %s\n", remoteInfo.Get("OperatingSystem"))
if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" {
fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug"))
@ -471,9 +506,6 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
if initPath := remoteInfo.Get("InitPath"); initPath != "" {
fmt.Fprintf(cli.out, "Init Path: %s\n", initPath)
}
if len(remoteInfo.GetList("Sockets")) != 0 {
fmt.Fprintf(cli.out, "Sockets: %v\n", remoteInfo.GetList("Sockets"))
}
}
if len(remoteInfo.GetList("IndexServerAddress")) != 0 {
@ -551,7 +583,7 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
}
func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
sigc := make(chan os.Signal, 1)
sigc := make(chan os.Signal, 128)
signal.CatchAll(sigc)
go func() {
for s := range sigc {
@ -566,10 +598,10 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
}
}
if sig == "" {
utils.Errorf("Unsupported signal: %d. Discarding.", s)
log.Errorf("Unsupported signal: %d. Discarding.", s)
}
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
utils.Debugf("Error sending signal: %s", err)
log.Debugf("Error sending signal: %s", err)
}
}
}()
@ -659,7 +691,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
if *openStdin || *attach {
if tty && cli.isTerminal {
if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
utils.Errorf("Error monitoring TTY size: %s\n", err)
log.Errorf("Error monitoring TTY size: %s", err)
}
}
return <-cErr
@ -982,7 +1014,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
cmd := cli.Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container")
link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container")
force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of running container")
force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)")
if err := cmd.Parse(args); err != nil {
return nil
@ -991,6 +1023,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
cmd.Usage()
return nil
}
val := url.Values{}
if *v {
val.Set("v", "1")
@ -998,6 +1031,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
if *link {
val.Set("link", "1")
}
if *force {
val.Set("force", "1")
}
@ -1051,16 +1085,19 @@ func (cli *DockerCli) CmdImport(args ...string) error {
return nil
}
var src, repository, tag string
var (
v = url.Values{}
src = cmd.Arg(0)
repository = cmd.Arg(1)
)
v.Set("fromSrc", src)
v.Set("repo", repository)
if cmd.NArg() == 3 {
fmt.Fprintf(cli.err, "[DEPRECATED] The format 'URL|- [REPOSITORY [TAG]]' as been deprecated. Please use URL|- [REPOSITORY[:TAG]]\n")
src, repository, tag = cmd.Arg(0), cmd.Arg(1), cmd.Arg(2)
} else {
src = cmd.Arg(0)
repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
v.Set("tag", cmd.Arg(2))
}
v := url.Values{}
if repository != "" {
//Check if the given image name can be resolved
@ -1069,10 +1106,6 @@ func (cli *DockerCli) CmdImport(args ...string) error {
}
}
v.Set("repo", repository)
v.Set("tag", tag)
v.Set("fromSrc", src)
var in io.Reader
if src == "-" {
@ -1096,7 +1129,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
cli.LoadConfigFile()
remote, tag := utils.ParseRepositoryTag(name)
remote, tag := parsers.ParseRepositoryTag(name)
// Resolve the Repository name from fqn to hostname + name
hostname, _, err := registry.ResolveRepositoryName(remote)
@ -1158,12 +1191,18 @@ func (cli *DockerCli) CmdPull(args ...string) error {
cmd.Usage()
return nil
}
var (
v = url.Values{}
remote = cmd.Arg(0)
)
v.Set("fromImage", remote)
remote, parsedTag := utils.ParseRepositoryTag(cmd.Arg(0))
if *tag == "" {
*tag = parsedTag
v.Set("tag", *tag)
}
remote, _ = parsers.ParseRepositoryTag(remote)
// Resolve the Repository name from fqn to hostname + name
hostname, _, err := registry.ResolveRepositoryName(remote)
if err != nil {
@ -1174,9 +1213,6 @@ func (cli *DockerCli) CmdPull(args ...string) error {
// Resolve the Auth config relevant for this server
authConfig := cli.configFile.ResolveAuthConfig(hostname)
v := url.Values{}
v.Set("fromImage", remote)
v.Set("tag", *tag)
pull := func(authConfig registry.AuthConfig) error {
buf, err := json.Marshal(authConfig)
@ -1216,7 +1252,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
flViz := cmd.Bool([]string{"#v", "#viz", "#-viz"}, false, "Output graph in graphviz format")
flTree := cmd.Bool([]string{"#t", "#tree", "#-tree"}, false, "Output graph in tree format")
var flFilter opts.ListOpts
flFilter := opts.NewListOpts(nil)
cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'dangling=true')")
if err := cmd.Parse(args); err != nil {
@ -1349,7 +1385,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
for _, out := range outs.Data {
for _, repotag := range out.GetList("RepoTags") {
repo, tag := utils.ParseRepositoryTag(repotag)
repo, tag := parsers.ParseRepositoryTag(repotag)
outID := out.Get("Id")
if !*noTrunc {
outID = utils.TruncateID(outID)
@ -1449,6 +1485,9 @@ func (cli *DockerCli) CmdPs(args ...string) error {
before := cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name, include non-running ones.")
last := cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.")
flFilter := opts.NewListOpts(nil)
cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited=<int> - containers with exit code of <int>")
if err := cmd.Parse(args); err != nil {
return nil
}
@ -1472,6 +1511,24 @@ func (cli *DockerCli) CmdPs(args ...string) error {
v.Set("size", "1")
}
// Consolidate all filter flags, and sanity check them.
// They'll get processed in the daemon/server.
psFilterArgs := filters.Args{}
for _, f := range flFilter.GetAll() {
var err error
psFilterArgs, err = filters.ParseFlag(f, psFilterArgs)
if err != nil {
return err
}
}
if len(psFilterArgs) > 0 {
filterJson, err := filters.ToParam(psFilterArgs)
if err != nil {
return err
}
v.Set("filters", filterJson)
}
body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false))
if err != nil {
return err
@ -1511,6 +1568,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
outCommand = out.Get("Command")
ports = engine.NewTable("", 0)
)
outCommand = strconv.Quote(outCommand)
if !*noTrunc {
outCommand = utils.Trunc(outCommand, 20)
}
@ -1549,7 +1607,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
var (
name = cmd.Arg(0)
repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1))
)
if name == "" || len(cmd.Args()) > 2 {
@ -1614,7 +1672,7 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
loc = time.FixedZone(time.Now().Zone())
)
var setTime = func(key, value string) {
format := "2006-01-02 15:04:05 -0700 MST"
format := time.RFC3339Nano
if len(value) < len(format) {
format = format[:len(value)]
}
@ -1736,7 +1794,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
var (
cmd = cli.Subcmd("attach", "[OPTIONS] CONTAINER", "Attach to a running container")
noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN")
proxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signals to the process (even in non-TTY mode). SIGCHLD is not proxied.")
proxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process (even in non-TTY mode). SIGCHLD, SIGKILL, and SIGSTOP are not proxied.")
)
if err := cmd.Parse(args); err != nil {
@ -1770,7 +1828,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
if tty && cli.isTerminal {
if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
utils.Debugf("Error monitoring TTY size: %s", err)
log.Debugf("Error monitoring TTY size: %s", err)
}
}
@ -1862,7 +1920,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
type ports []int
func (cli *DockerCli) CmdTag(args ...string) error {
cmd := cli.Subcmd("tag", "[OPTIONS] IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository")
cmd := cli.Subcmd("tag", "[OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository")
force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
if err := cmd.Parse(args); err != nil {
return nil
@ -1873,7 +1931,7 @@ func (cli *DockerCli) CmdTag(args ...string) error {
}
var (
repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1))
v = url.Values{}
)
@ -1894,6 +1952,41 @@ func (cli *DockerCli) CmdTag(args ...string) error {
return nil
}
func (cli *DockerCli) pullImage(image string) error {
v := url.Values{}
repos, tag := parsers.ParseRepositoryTag(image)
// pull only the image tagged 'latest' if no tag was specified
if tag == "" {
tag = "latest"
}
v.Set("fromImage", repos)
v.Set("tag", tag)
// Resolve the Repository name from fqn to hostname + name
hostname, _, err := registry.ResolveRepositoryName(repos)
if err != nil {
return err
}
// Load the auth config file, to be able to pull the image
cli.LoadConfigFile()
// Resolve the Auth config relevant for this server
authConfig := cli.configFile.ResolveAuthConfig(hostname)
buf, err := json.Marshal(authConfig)
if err != nil {
return err
}
registryAuthHeader := []string{
base64.URLEncoding.EncodeToString(buf),
}
if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil {
return err
}
return nil
}
func (cli *DockerCli) CmdRun(args ...string) error {
// FIXME: just use runconfig.Parse already
config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
@ -1955,37 +2048,10 @@ func (cli *DockerCli) CmdRun(args ...string) error {
if statusCode == 404 {
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image)
v := url.Values{}
repos, tag := utils.ParseRepositoryTag(config.Image)
// pull only the image tagged 'latest' if no tag was specified
if tag == "" {
tag = "latest"
}
v.Set("fromImage", repos)
v.Set("tag", tag)
// Resolve the Repository name from fqn to hostname + name
hostname, _, err := registry.ResolveRepositoryName(repos)
if err != nil {
return err
}
// Load the auth config file, to be able to pull the image
cli.LoadConfigFile()
// Resolve the Auth config relevant for this server
authConfig := cli.configFile.ResolveAuthConfig(hostname)
buf, err := json.Marshal(authConfig)
if err != nil {
return err
}
registryAuthHeader := []string{
base64.URLEncoding.EncodeToString(buf),
}
if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil {
if err = cli.pullImage(config.Image); err != nil {
return err
}
// Retry
if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false); err != nil {
return err
}
@ -2033,9 +2099,9 @@ func (cli *DockerCli) CmdRun(args ...string) error {
// Block the return until the chan gets closed
defer func() {
utils.Debugf("End of CmdRun(), Waiting for hijack to finish.")
log.Debugf("End of CmdRun(), Waiting for hijack to finish.")
if _, ok := <-hijacked; ok {
utils.Errorf("Hijack did not finish (chan still open)")
log.Errorf("Hijack did not finish (chan still open)")
}
}()
@ -2081,7 +2147,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
}
case err := <-errCh:
if err != nil {
utils.Debugf("Error hijack: %s", err)
log.Debugf("Error hijack: %s", err)
return err
}
}
@ -2093,13 +2159,13 @@ func (cli *DockerCli) CmdRun(args ...string) error {
if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminal {
if err := cli.monitorTtySize(runResult.Get("Id")); err != nil {
utils.Errorf("Error monitoring TTY size: %s\n", err)
log.Errorf("Error monitoring TTY size: %s", err)
}
}
if errCh != nil {
if err := <-errCh; err != nil {
utils.Debugf("Error hijack: %s", err)
log.Debugf("Error hijack: %s", err)
return err
}
}

Просмотреть файл

@ -11,10 +11,11 @@ import (
"runtime"
"strings"
"github.com/dotcloud/docker/api"
"github.com/dotcloud/docker/dockerversion"
"github.com/dotcloud/docker/pkg/term"
"github.com/dotcloud/docker/utils"
"github.com/docker/docker/api"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/term"
"github.com/docker/docker/utils"
)
func (cli *DockerCli) dial() (net.Conn, error) {
@ -88,12 +89,12 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
}()
// When TTY is ON, use regular copy
if setRawTerminal {
if setRawTerminal && stdout != nil {
_, err = io.Copy(stdout, br)
} else {
_, err = utils.StdCopy(stdout, stderr, br)
}
utils.Debugf("[hijack] End of stdout")
log.Debugf("[hijack] End of stdout")
return err
})
}
@ -101,15 +102,15 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
sendStdin := utils.Go(func() error {
if in != nil {
io.Copy(rwc, in)
utils.Debugf("[hijack] End of stdin")
log.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)
log.Debugf("Couldn't send EOF: %s", err)
}
} else if unixc, ok := rwc.(*net.UnixConn); ok {
if err := unixc.CloseWrite(); err != nil {
utils.Debugf("Couldn't send EOF: %s\n", err)
log.Debugf("Couldn't send EOF: %s", err)
}
}
// Discard errors due to pipe interruption
@ -118,14 +119,14 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
if stdout != nil || stderr != nil {
if err := <-receiveStdout; err != nil {
utils.Debugf("Error receiveStdout: %s", err)
log.Debugf("Error receiveStdout: %s", err)
return err
}
}
if !cli.isTerminal {
if err := <-sendStdin; err != nil {
utils.Debugf("Error sendStdin: %s", err)
log.Debugf("Error sendStdin: %s", err)
return err
}
}

Просмотреть файл

@ -17,12 +17,13 @@ import (
"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"
"github.com/docker/docker/api"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/engine"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/term"
"github.com/docker/docker/registry"
"github.com/docker/docker/utils"
)
var (
@ -165,7 +166,7 @@ func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in
} else {
_, err = utils.StdCopy(stdout, stderr, resp.Body)
}
utils.Debugf("[stream] End of stdout")
log.Debugf("[stream] End of stdout")
return err
}
return nil
@ -180,7 +181,7 @@ func (cli *DockerCli) resizeTty(id string) {
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)
log.Debugf("Error resize: %s", err)
}
}
@ -237,7 +238,7 @@ func (cli *DockerCli) getTtySize() (int, int) {
}
ws, err := term.GetWinsize(cli.terminalFd)
if err != nil {
utils.Debugf("Error getting size: %s", err)
log.Debugf("Error getting size: %s", err)
if ws == nil {
return 0, 0
}

Просмотреть файл

@ -5,19 +5,20 @@ import (
"mime"
"strings"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/pkg/version"
"github.com/dotcloud/docker/utils"
"github.com/docker/docker/engine"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/pkg/version"
)
const (
APIVERSION version.Version = "1.13"
APIVERSION version.Version = "1.14"
DEFAULTHTTPHOST = "127.0.0.1"
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
)
func ValidateHost(val string) (string, error) {
host, err := utils.ParseHost(DEFAULTHTTPHOST, DEFAULTUNIXSOCKET, val)
host, err := parsers.ParseHost(DEFAULTHTTPHOST, DEFAULTUNIXSOCKET, val)
if err != nil {
return val, err
}
@ -42,7 +43,7 @@ func DisplayablePorts(ports *engine.Table) string {
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())
log.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
}
return err == nil && mimetype == expectedType
}

Просмотреть файл

@ -1,2 +1,3 @@
Victor Vieux <vieux@docker.com> (@vieux)
Johan Euphrosine <proppy@google.com> (@proppy)
# off the grid until september
# Johan Euphrosine <proppy@google.com> (@proppy)

Просмотреть файл

@ -11,7 +11,6 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/pprof"
@ -21,16 +20,18 @@ import (
"syscall"
"code.google.com/p/go.net/websocket"
"github.com/dotcloud/docker/api"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/pkg/listenbuffer"
"github.com/dotcloud/docker/pkg/systemd"
"github.com/dotcloud/docker/pkg/user"
"github.com/dotcloud/docker/pkg/version"
"github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/utils"
"github.com/docker/libcontainer/user"
"github.com/gorilla/mux"
"github.com/docker/docker/api"
"github.com/docker/docker/engine"
"github.com/docker/docker/pkg/listenbuffer"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/pkg/systemd"
"github.com/docker/docker/pkg/version"
"github.com/docker/docker/registry"
"github.com/docker/docker/utils"
)
var (
@ -87,7 +88,7 @@ func httpError(w http.ResponseWriter, err error) {
}
if err != nil {
utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
log.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
http.Error(w, err.Error(), statusCode)
}
}
@ -237,10 +238,10 @@ func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseW
outsLegacy := engine.NewTable("Created", 0)
for _, out := range outs.Data {
for _, repoTag := range out.GetList("RepoTags") {
parts := strings.Split(repoTag, ":")
repo, tag := parsers.ParseRepositoryTag(repoTag)
outLegacy := &engine.Env{}
outLegacy.Set("Repository", parts[0])
outLegacy.Set("Tag", parts[1])
outLegacy.Set("Repository", repo)
outLegacy.SetJson("Tag", tag)
outLegacy.Set("Id", out.Get("Id"))
outLegacy.SetInt64("Created", out.GetInt64("Created"))
outLegacy.SetInt64("Size", out.GetInt64("Size"))
@ -301,7 +302,7 @@ func getContainersChanges(eng *engine.Engine, version version.Version, w http.Re
if vars == nil {
return fmt.Errorf("Missing parameter")
}
var job = eng.Job("changes", vars["name"])
var job = eng.Job("container_changes", vars["name"])
streamJSON(job, w, false)
return job.Run()
@ -338,6 +339,7 @@ func getContainersJSON(eng *engine.Engine, version version.Version, w http.Respo
job.Setenv("since", r.Form.Get("since"))
job.Setenv("before", r.Form.Get("before"))
job.Setenv("limit", r.Form.Get("limit"))
job.Setenv("filters", r.Form.Get("filters"))
if version.GreaterThanOrEqualTo("1.5") {
streamJSON(job, w, false)
@ -437,7 +439,7 @@ func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWrit
stdoutBuffer = bytes.NewBuffer(nil)
)
if err := config.Decode(r.Body); err != nil {
utils.Errorf("%s", err)
log.Errorf("%s", err)
}
if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
@ -468,6 +470,7 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon
var (
image = r.Form.Get("fromImage")
repo = r.Form.Get("repo")
tag = r.Form.Get("tag")
job *engine.Job
)
@ -482,18 +485,24 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon
}
}
if image != "" { //pull
if tag == "" {
image, tag = parsers.ParseRepositoryTag(image)
}
metaHeaders := map[string][]string{}
for k, v := range r.Header {
if strings.HasPrefix(k, "X-Meta-") {
metaHeaders[k] = v
}
}
job = eng.Job("pull", r.Form.Get("fromImage"), tag)
job = eng.Job("pull", image, tag)
job.SetenvBool("parallel", version.GreaterThan("1.3"))
job.SetenvJson("metaHeaders", metaHeaders)
job.SetenvJson("authConfig", authConfig)
} else { //import
job = eng.Job("import", r.Form.Get("fromSrc"), r.Form.Get("repo"), tag)
if tag == "" {
repo, tag = parsers.ParseRepositoryTag(repo)
}
job = eng.Job("import", r.Form.Get("fromSrc"), repo, tag)
job.Stdin.Add(r.Body)
}
@ -670,10 +679,12 @@ func deleteContainers(eng *engine.Engine, version version.Version, w http.Respon
if vars == nil {
return fmt.Errorf("Missing parameter")
}
job := eng.Job("container_delete", vars["name"])
job := eng.Job("delete", vars["name"])
job.Setenv("forceRemove", r.Form.Get("force"))
job.Setenv("removeVolume", r.Form.Get("v"))
job.Setenv("removeLink", r.Form.Get("link"))
job.Setenv("forceRemove", r.Form.Get("force"))
if err := job.Run(); err != nil {
return err
}
@ -706,13 +717,16 @@ func postContainersStart(eng *engine.Engine, version version.Version, w http.Res
)
// allow a nil body for backwards compatibility
if r.Body != nil {
if api.MatchesContentType(r.Header.Get("Content-Type"), "application/json") {
if err := job.DecodeEnv(r.Body); err != nil {
return err
}
if r.Body != nil && r.ContentLength > 0 {
if !api.MatchesContentType(r.Header.Get("Content-Type"), "application/json") {
return fmt.Errorf("Content-Type of application/json is required")
}
if err := job.DecodeEnv(r.Body); err != nil {
return err
}
}
if err := job.Run(); err != nil {
if err.Error() == "Container already started" {
w.WriteHeader(http.StatusNotModified)
@ -864,7 +878,7 @@ func wsContainersAttach(eng *engine.Engine, version version.Version, w http.Resp
job.Stdout.Add(ws)
job.Stderr.Set(ws)
if err := job.Run(); err != nil {
utils.Errorf("Error attaching websocket: %s", err)
log.Errorf("Error attaching websocket: %s", err)
}
})
h.ServeHTTP(w, r)
@ -991,7 +1005,7 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp
job := eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
job.Stdout.Add(w)
if err := job.Run(); err != nil {
utils.Errorf("%s", err.Error())
log.Errorf("%s", err.Error())
if strings.Contains(err.Error(), "No such container") {
w.WriteHeader(http.StatusNotFound)
} else if strings.Contains(err.Error(), "no such file or directory") {
@ -1019,16 +1033,16 @@ func ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r
func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// log the request
utils.Debugf("Calling %s %s", localMethod, localRoute)
log.Debugf("Calling %s %s", localMethod, localRoute)
if logging {
log.Println(r.Method, r.RequestURI)
log.Infof("%s %s", r.Method, r.RequestURI)
}
if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) {
utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
log.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
}
}
version := version.Version(mux.Vars(r)["version"])
@ -1045,7 +1059,7 @@ func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, local
}
if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil {
utils.Errorf("Error making handler: %s", err)
log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err)
httpError(w, err)
}
}
@ -1134,7 +1148,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
for method, routes := range m {
for route, fct := range routes {
utils.Debugf("Registering %s, %s", method, route)
log.Debugf("Registering %s, %s", method, route)
// NOTE: scope issue, make sure the variables are local and won't be changed
localRoute := route
localFct := fct
@ -1181,7 +1195,7 @@ func ServeFd(addr string, handle http.Handler) error {
chErrors := make(chan error, len(ls))
// We don't want to start serving on these sockets until the
// "initserver" job has completed. Otherwise required handlers
// daemon is initialized and installed. Otherwise required handlers
// won't be ready.
<-activationLock
@ -1224,7 +1238,7 @@ func changeGroup(addr string, nameOrGid string) error {
return err
}
utils.Debugf("%s group found. gid: %d", nameOrGid, gid)
log.Debugf("%s group found. gid: %d", nameOrGid, gid)
return os.Chown(addr, 0, gid)
}
@ -1295,7 +1309,7 @@ func ListenAndServe(proto, addr string, job *engine.Job) error {
switch proto {
case "tcp":
if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") {
log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
log.Infof("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
}
case "unix":
socketGroup := job.Getenv("SocketGroup")
@ -1303,7 +1317,7 @@ func ListenAndServe(proto, addr string, job *engine.Job) error {
if err := changeGroup(addr, socketGroup); err != nil {
if socketGroup == "docker" {
// if the user hasn't explicitly specified the group ownership, don't fail on errors.
utils.Debugf("Warning: could not chgrp %s to docker: %s", addr, err.Error())
log.Debugf("Warning: could not chgrp %s to docker: %s", addr, err.Error())
} else {
return err
}
@ -1338,7 +1352,7 @@ func ServeApi(job *engine.Job) engine.Status {
return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
}
go func() {
log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job)
}()
}

Просмотреть файл

@ -7,11 +7,13 @@ import (
"io"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
"github.com/dotcloud/docker/api"
"github.com/dotcloud/docker/engine"
"github.com/docker/docker/api"
"github.com/docker/docker/engine"
"github.com/docker/docker/pkg/version"
)
func TestGetBoolParam(t *testing.T) {
@ -111,8 +113,105 @@ func TestGetInfo(t *testing.T) {
if v.GetInt("Containers") != 1 {
t.Fatalf("%#v\n", v)
}
if r.HeaderMap.Get("Content-Type") != "application/json" {
t.Fatalf("%#v\n", r)
assertContentType(r, "application/json", t)
}
func TestGetImagesJSON(t *testing.T) {
eng := engine.New()
var called bool
eng.Register("images", func(job *engine.Job) engine.Status {
called = true
v := createEnvFromGetImagesJSONStruct(sampleImage)
if _, err := v.WriteTo(job.Stdout); err != nil {
return job.Error(err)
}
return engine.StatusOK
})
r := serveRequest("GET", "/images/json", nil, eng, t)
if !called {
t.Fatal("handler was not called")
}
assertHttpNotError(r, t)
assertContentType(r, "application/json", t)
var observed getImagesJSONStruct
if err := json.Unmarshal(r.Body.Bytes(), &observed); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(observed, sampleImage) {
t.Errorf("Expected %#v but got %#v", sampleImage, observed)
}
}
func TestGetImagesJSONFilter(t *testing.T) {
eng := engine.New()
filter := "nothing"
eng.Register("images", func(job *engine.Job) engine.Status {
filter = job.Getenv("filter")
return engine.StatusOK
})
serveRequest("GET", "/images/json?filter=aaaa", nil, eng, t)
if filter != "aaaa" {
t.Errorf("%#v", filter)
}
}
func TestGetImagesJSONFilters(t *testing.T) {
eng := engine.New()
filter := "nothing"
eng.Register("images", func(job *engine.Job) engine.Status {
filter = job.Getenv("filters")
return engine.StatusOK
})
serveRequest("GET", "/images/json?filters=nnnn", nil, eng, t)
if filter != "nnnn" {
t.Errorf("%#v", filter)
}
}
func TestGetImagesJSONAll(t *testing.T) {
eng := engine.New()
allFilter := "-1"
eng.Register("images", func(job *engine.Job) engine.Status {
allFilter = job.Getenv("all")
return engine.StatusOK
})
serveRequest("GET", "/images/json?all=1", nil, eng, t)
if allFilter != "1" {
t.Errorf("%#v", allFilter)
}
}
func TestGetImagesJSONLegacyFormat(t *testing.T) {
eng := engine.New()
var called bool
eng.Register("images", func(job *engine.Job) engine.Status {
called = true
outsLegacy := engine.NewTable("Created", 0)
outsLegacy.Add(createEnvFromGetImagesJSONStruct(sampleImage))
if _, err := outsLegacy.WriteListTo(job.Stdout); err != nil {
return job.Error(err)
}
return engine.StatusOK
})
r := serveRequestUsingVersion("GET", "/images/json", "1.6", nil, eng, t)
if !called {
t.Fatal("handler was not called")
}
assertHttpNotError(r, t)
assertContentType(r, "application/json", t)
images := engine.NewTable("Created", 0)
if _, err := images.ReadListFrom(r.Body.Bytes()); err != nil {
t.Fatal(err)
}
if images.Len() != 1 {
t.Fatalf("Expected 1 image, %d found", images.Len())
}
image := images.Data[0]
if image.Get("Tag") != "test-tag" {
t.Errorf("Expected tag 'test-tag', found '%s'", image.Get("Tag"))
}
if image.Get("Repository") != "test-name" {
t.Errorf("Expected repository 'test-name', found '%s'", image.Get("Repository"))
}
}
@ -123,12 +222,12 @@ func TestGetContainersByName(t *testing.T) {
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])
t.Errorf("name != '%s': %#v", name, job.Args[0])
}
if api.APIVERSION.LessThan("1.12") && !job.GetenvBool("dirty") {
t.Fatal("dirty env variable not set")
t.Errorf("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")
t.Errorf("dirty env variable set when it shouldn't")
}
v := &engine.Env{}
v.SetBool("dirty", true)
@ -141,9 +240,7 @@ func TestGetContainersByName(t *testing.T) {
if !called {
t.Fatal("handler was not called")
}
if r.HeaderMap.Get("Content-Type") != "application/json" {
t.Fatalf("%#v\n", r)
}
assertContentType(r, "application/json", t)
var stdoutJson interface{}
if err := json.Unmarshal(r.Body.Bytes(), &stdoutJson); err != nil {
t.Fatalf("%#v", err)
@ -178,21 +275,19 @@ func TestGetEvents(t *testing.T) {
if !called {
t.Fatal("handler was not called")
}
if r.HeaderMap.Get("Content-Type") != "application/json" {
t.Fatalf("%#v\n", r)
}
assertContentType(r, "application/json", t)
var stdout_json struct {
Since int
Until int
}
if err := json.Unmarshal(r.Body.Bytes(), &stdout_json); err != nil {
t.Fatalf("%#v", err)
t.Fatal(err)
}
if stdout_json.Since != 1 {
t.Fatalf("since != 1: %#v", stdout_json.Since)
t.Errorf("since != 1: %#v", stdout_json.Since)
}
if stdout_json.Until != 0 {
t.Fatalf("until != 0: %#v", stdout_json.Until)
t.Errorf("until != 0: %#v", stdout_json.Until)
}
}
@ -319,13 +414,77 @@ func TestGetImagesHistory(t *testing.T) {
}
}
func TestGetImagesByName(t *testing.T) {
eng := engine.New()
name := "image_name"
var called bool
eng.Register("image_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", "/images/"+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 TestDeleteContainers(t *testing.T) {
eng := engine.New()
name := "foo"
var called bool
eng.Register("delete", func(job *engine.Job) engine.Status {
called = true
if len(job.Args) == 0 {
t.Fatalf("Job arguments is empty")
}
if job.Args[0] != name {
t.Fatalf("name != '%s': %#v", name, job.Args[0])
}
return engine.StatusOK
})
r := serveRequest("DELETE", "/containers/"+name, nil, eng, t)
if !called {
t.Fatalf("handler was not called")
}
if r.Code != http.StatusNoContent {
t.Fatalf("Got status %d, expected %d", r.Code, http.StatusNoContent)
}
}
func serveRequest(method, target string, body io.Reader, eng *engine.Engine, t *testing.T) *httptest.ResponseRecorder {
return serveRequestUsingVersion(method, target, api.APIVERSION, body, eng, t)
}
func serveRequestUsingVersion(method, target string, version version.Version, 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 {
if err := ServeRequest(eng, version, r, req); err != nil {
t.Fatal(err)
}
return r
@ -351,3 +510,46 @@ func toJson(data interface{}, t *testing.T) io.Reader {
}
return &buf
}
func assertContentType(recorder *httptest.ResponseRecorder, content_type string, t *testing.T) {
if recorder.HeaderMap.Get("Content-Type") != content_type {
t.Fatalf("%#v\n", recorder)
}
}
// XXX: Duplicated from integration/utils_test.go, but maybe that's OK as that
// should die as soon as we converted all integration tests?
// assertHttpNotError expect the given response to not have an error.
// Otherwise the it causes the test to fail.
func assertHttpNotError(r *httptest.ResponseRecorder, t *testing.T) {
// Non-error http status are [200, 400)
if r.Code < http.StatusOK || r.Code >= http.StatusBadRequest {
t.Fatal(fmt.Errorf("Unexpected http error: %v", r.Code))
}
}
func createEnvFromGetImagesJSONStruct(data getImagesJSONStruct) *engine.Env {
v := &engine.Env{}
v.SetList("RepoTags", data.RepoTags)
v.Set("Id", data.Id)
v.SetInt64("Created", data.Created)
v.SetInt64("Size", data.Size)
v.SetInt64("VirtualSize", data.VirtualSize)
return v
}
type getImagesJSONStruct struct {
RepoTags []string
Id string
Created int64
Size int64
VirtualSize int64
}
var sampleImage getImagesJSONStruct = getImagesJSONStruct{
RepoTags: []string{"test-name:test-tag"},
Id: "ID",
Created: 999,
Size: 777,
VirtualSize: 666,
}

Просмотреть файл

@ -1 +1,2 @@
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
Cristian Staretu <cristian.staretu@gmail.com> (@unclejack)
Tibor Vass <teabee89@gmail.com> (@tiborvass)

Просмотреть файл

@ -16,9 +16,11 @@ import (
"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"
"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/utils"
)
type (
@ -61,7 +63,7 @@ func DetectCompression(source []byte) Compression {
Xz: {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00},
} {
if len(source) < len(m) {
utils.Debugf("Len too short")
log.Debugf("Len too short")
continue
}
if bytes.Compare(m, source[:len(m)]) == 0 {
@ -83,7 +85,7 @@ func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
if err != nil {
return nil, err
}
utils.Debugf("[tar autodetect] n: %v", bs)
log.Debugf("[tar autodetect] n: %v", bs)
compression := DetectCompression(bs)
@ -131,7 +133,7 @@ func (compression *Compression) Extension() string {
return ""
}
func addTarFile(path, name string, tw *tar.Writer) error {
func addTarFile(path, name string, tw *tar.Writer, twBuf *bufio.Writer) error {
fi, err := os.Lstat(path)
if err != nil {
return err
@ -177,15 +179,22 @@ func addTarFile(path, name string, tw *tar.Writer) error {
}
if hdr.Typeflag == tar.TypeReg {
if file, err := os.Open(path); err != nil {
file, err := os.Open(path)
if err != nil {
return err
} else {
_, err := io.Copy(tw, file)
if err != nil {
return err
}
file.Close()
}
twBuf.Reset(tw)
_, err = io.Copy(twBuf, file)
file.Close()
if err != nil {
return err
}
err = twBuf.Flush()
if err != nil {
return err
}
twBuf.Reset(nil)
}
return nil
@ -245,7 +254,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
}
case tar.TypeXGlobalHeader:
utils.Debugf("PAX Global Extended Headers found and ignored")
log.Debugf("PAX Global Extended Headers found and ignored")
return nil
default:
@ -328,10 +337,12 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
options.Includes = []string{"."}
}
twBuf := bufio.NewWriterSize(nil, twBufSize)
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)
log.Debugf("Tar: Can't stat file %s to tar: %s", srcPath, err)
return nil
}
@ -340,23 +351,21 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
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
}
skip, err := utils.Matches(relFilePath, options.Excludes)
if err != nil {
log.Debugf("Error matching %s", relFilePath, err)
return err
}
if err := addTarFile(filePath, relFilePath, tw); err != nil {
utils.Debugf("Can't add file %s to tar: %s\n", srcPath, err)
if skip {
if f.IsDir() {
return filepath.SkipDir
}
return nil
}
if err := addTarFile(filePath, relFilePath, tw, twBuf); err != nil {
log.Debugf("Can't add file %s to tar: %s", srcPath, err)
}
return nil
})
@ -364,13 +373,13 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
// Make sure to check the error on Close.
if err := tw.Close(); err != nil {
utils.Debugf("Can't close tar writer: %s\n", err)
log.Debugf("Can't close tar writer: %s", err)
}
if err := compressWriter.Close(); err != nil {
utils.Debugf("Can't close compress writer: %s\n", err)
log.Debugf("Can't close compress writer: %s", err)
}
if err := pipeWriter.Close(); err != nil {
utils.Debugf("Can't close pipe writer: %s\n", err)
log.Debugf("Can't close pipe writer: %s", err)
}
}()
@ -383,10 +392,18 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
// 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 {
if options == nil {
options = &TarOptions{}
}
if archive == nil {
return fmt.Errorf("Empty archive")
}
if options.Excludes == nil {
options.Excludes = []string{}
}
decompressedArchive, err := DecompressStream(archive)
if err != nil {
return err
@ -394,10 +411,12 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
defer decompressedArchive.Close()
tr := tar.NewReader(decompressedArchive)
trBuf := bufio.NewReaderSize(nil, trBufSize)
var dirs []*tar.Header
// Iterate through the files in the archive.
loop:
for {
hdr, err := tr.Next()
if err == io.EOF {
@ -411,6 +430,12 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
// Normalize name, for safety and for a simple is-root check
hdr.Name = filepath.Clean(hdr.Name)
for _, exclude := range options.Excludes {
if strings.HasPrefix(hdr.Name, exclude) {
continue loop
}
}
if !strings.HasSuffix(hdr.Name, "/") {
// Not the root directory, ensure that the parent directory exists
parent := filepath.Dir(hdr.Name)
@ -439,7 +464,8 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
}
}
}
if err := createTarFile(path, dest, hdr, tr, options == nil || !options.NoLchown); err != nil {
trBuf.Reset(tr)
if err := createTarFile(path, dest, hdr, trBuf, !options.NoLchown); err != nil {
return err
}
@ -465,7 +491,7 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
// 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)
log.Debugf("TarUntar(%s %s)", src, dst)
archive, err := TarWithOptions(src, &TarOptions{Compression: Uncompressed})
if err != nil {
return err
@ -502,11 +528,11 @@ func CopyWithTar(src, dst string) error {
return CopyFileWithTar(src, dst)
}
// Create dst, copy src's content into it
utils.Debugf("Creating dest directory: %s", dst)
log.Debugf("Creating dest directory: %s", dst)
if err := os.MkdirAll(dst, 0755); err != nil && !os.IsExist(err) {
return err
}
utils.Debugf("Calling TarUntar(%s, %s)", src, dst)
log.Debugf("Calling TarUntar(%s, %s)", src, dst)
return TarUntar(src, dst)
}
@ -517,7 +543,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) {
utils.Debugf("CopyFileWithTar(%s, %s)", src, dst)
log.Debugf("CopyFileWithTar(%s, %s)", src, dst)
srcSt, err := os.Stat(src)
if err != nil {
return err
@ -544,19 +570,19 @@ func CopyFileWithTar(src, dst string) (err error) {
}
defer srcF.Close()
tw := tar.NewWriter(w)
hdr, err := tar.FileInfoHeader(srcSt, "")
if err != nil {
return err
}
hdr.Name = filepath.Base(dst)
tw := tar.NewWriter(w)
defer tw.Close()
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() {

Просмотреть файл

@ -11,7 +11,7 @@ import (
"testing"
"time"
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
)
func TestCmdStreamLargeStderr(t *testing.T) {
@ -109,6 +109,9 @@ func TestTarUntar(t *testing.T) {
if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(path.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil {
t.Fatal(err)
}
for _, c := range []Compression{
Uncompressed,
@ -116,13 +119,14 @@ func TestTarUntar(t *testing.T) {
} {
changes, err := tarUntar(t, origin, &TarOptions{
Compression: c,
Excludes: []string{"3"},
})
if err != nil {
t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
}
if len(changes) != 0 {
if len(changes) != 1 || changes[0].Path != "/3" {
t.Fatalf("Unexpected differences after tarUntar: %v", changes)
}
}
@ -199,3 +203,42 @@ func TestUntarUstarGnuConflict(t *testing.T) {
t.Fatalf("%s not found in the archive", "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm")
}
}
func prepareUntarSourceDirectory(numberOfFiles int, targetPath string) (int, error) {
fileData := []byte("fooo")
for n := 0; n < numberOfFiles; n++ {
fileName := fmt.Sprintf("file-%d", n)
if err := ioutil.WriteFile(path.Join(targetPath, fileName), fileData, 0700); err != nil {
return 0, err
}
}
totalSize := numberOfFiles * len(fileData)
return totalSize, nil
}
func BenchmarkTarUntar(b *testing.B) {
origin, err := ioutil.TempDir("", "docker-test-untar-origin")
if err != nil {
b.Fatal(err)
}
tempDir, err := ioutil.TempDir("", "docker-test-untar-destination")
if err != nil {
b.Fatal(err)
}
target := path.Join(tempDir, "dest")
n, err := prepareUntarSourceDirectory(100, origin)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
b.SetBytes(int64(n))
defer os.RemoveAll(origin)
defer os.RemoveAll(tempDir)
for n := 0; n < b.N; n++ {
err := TarUntar(origin, target)
if err != nil {
b.Fatal(err)
}
os.RemoveAll(target)
}
}

Просмотреть файл

@ -1,6 +1,7 @@
package archive
import (
"bufio"
"bytes"
"fmt"
"io"
@ -10,9 +11,10 @@ import (
"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"
"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/system"
)
type ChangeType int
@ -343,6 +345,7 @@ func ExportChanges(dir string, changes []Change) (Archive, error) {
tw := tar.NewWriter(writer)
go func() {
twBuf := bufio.NewWriterSize(nil, twBufSize)
// 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
@ -361,19 +364,19 @@ func ExportChanges(dir string, changes []Change) (Archive, error) {
ChangeTime: timestamp,
}
if err := tw.WriteHeader(hdr); err != nil {
utils.Debugf("Can't write whiteout header: %s\n", err)
log.Debugf("Can't write whiteout header: %s", 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)
if err := addTarFile(path, change.Path[1:], tw, twBuf); err != nil {
log.Debugf("Can't add file %s to tar: %s", path, err)
}
}
}
// Make sure to check the error on Close.
if err := tw.Close(); err != nil {
utils.Debugf("Can't close layer: %s\n", err)
log.Debugf("Can't close layer: %s", err)
}
writer.Close()
}()

4
archive/common.go Normal file
Просмотреть файл

@ -0,0 +1,4 @@
package archive
const twBufSize = 32 * 1024
const trBufSize = 32 * 1024

Просмотреть файл

@ -1,6 +1,7 @@
package archive
import (
"bufio"
"fmt"
"io"
"io/ioutil"
@ -9,7 +10,7 @@ import (
"strings"
"syscall"
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
)
// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes.
@ -32,6 +33,7 @@ func ApplyLayer(dest string, layer ArchiveReader) error {
}
tr := tar.NewReader(layer)
trBuf := bufio.NewReaderSize(nil, trBufSize)
var dirs []*tar.Header
@ -108,7 +110,8 @@ func ApplyLayer(dest string, layer ArchiveReader) error {
}
}
srcData := io.Reader(tr)
trBuf.Reset(tr)
srcData := io.Reader(trBuf)
srcHdr := hdr
// Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so

Просмотреть файл

@ -2,7 +2,7 @@ package archive
import (
"bytes"
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
"io/ioutil"
)

Просмотреть файл

@ -3,14 +3,14 @@ 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"
"github.com/docker/docker/api"
apiserver "github.com/docker/docker/api/server"
"github.com/docker/docker/daemon/networkdriver/bridge"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/engine"
"github.com/docker/docker/events"
"github.com/docker/docker/pkg/parsers/kernel"
"github.com/docker/docker/registry"
)
func Register(eng *engine.Engine) error {
@ -20,6 +20,9 @@ func Register(eng *engine.Engine) error {
if err := remote(eng); err != nil {
return err
}
if err := events.New().Install(eng); err != nil {
return err
}
if err := eng.Register("version", dockerVersion); err != nil {
return err
}
@ -50,9 +53,6 @@ func remote(eng *engine.Engine) error {
// 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)
}
@ -65,7 +65,7 @@ func dockerVersion(job *engine.Job) engine.Status {
v.Set("GoVersion", runtime.Version())
v.Set("Os", runtime.GOOS)
v.Set("Arch", runtime.GOARCH)
if kernelVersion, err := utils.GetKernelVersion(); err == nil {
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
v.Set("KernelVersion", kernelVersion.String())
}
if _, err := v.WriteTo(job.Stdout); err != nil {

Просмотреть файл

@ -113,6 +113,23 @@ else
echo " $(wrap_color '(see https://github.com/tianon/cgroupfs-mount)' yellow)"
fi
if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = 'Y' ]; then
echo -n '- '
if command -v apparmor_parser &> /dev/null; then
echo "$(wrap_good 'apparmor' 'enabled and tools installed')"
else
echo "$(wrap_bad 'apparmor' 'enabled, but apparmor_parser missing')"
echo -n ' '
if command -v apt-get &> /dev/null; then
echo "$(wrap_color '(use "apt-get install apparmor" to fix this)')"
elif command -v yum &> /dev/null; then
echo "$(wrap_color '(your best bet is "yum install apparmor-parser")')"
else
echo "$(wrap_color '(look for an "apparmor" package for your distribution)')"
fi
fi
fi
flags=(
NAMESPACES {NET,PID,IPC,UTS}_NS
DEVPTS_MULTIPLE_INSTANCES

Просмотреть файл

@ -156,7 +156,7 @@ _docker_build()
*)
local counter="$(__docker_pos_first_nonflag '-t|--tag')"
if [ $cword -eq $counter ]; then
_filedir
_filedir -d
fi
;;
esac
@ -485,21 +485,52 @@ _docker_rmi()
_docker_run()
{
case "$prev" in
--cidfile)
-a|--attach)
COMPREPLY=( $( compgen -W 'stdin stdout stderr' -- "$cur" ) )
return
;;
--cidfile|--env-file)
_filedir
return
;;
--volumes-from)
__docker_containers_all
return
;;
-v|--volume)
# TODO something magical with colons and _filedir ?
case "$cur" in
*:*)
# TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
;;
'')
COMPREPLY=( $( compgen -W '/' -- "$cur" ) )
compopt -o nospace
;;
/*)
_filedir
compopt -o nospace
;;
esac
return
;;
-e|--env)
COMPREPLY=( $( compgen -e -- "$cur" ) )
compopt -o nospace
return
;;
--entrypoint|-h|--hostname|-m|--memory|-u|--user|-w|--workdir|-c|--cpu-shares|-n|--name|-a|--attach|--link|-p|--publish|--expose|--dns|--lxc-conf)
--link)
case "$cur" in
*:*)
;;
*)
__docker_containers_running
COMPREPLY=( $( compgen -W "${COMPREPLY[*]}" -S ':' ) )
compopt -o nospace
;;
esac
return
;;
--entrypoint|-h|--hostname|-m|--memory|-u|--user|-w|--workdir|-c|--cpu-shares|-n|--name|-p|--publish|--expose|--dns|--lxc-conf)
return
;;
*)

Просмотреть файл

@ -85,7 +85,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -l run -d 'Conf
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -a '(__fish_print_docker_containers all)' -d "Container"
# cp
complete -c docker -f -n '__fish_docker_no_subcommand' -a cp -d 'Copy files/folders from a container's filesystem to the host path'
complete -c docker -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"

Просмотреть файл

@ -1,58 +1,118 @@
#compdef docker
#compdef docker
#
# zsh completion for docker (http://docker.com)
#
# version: 0.2.2
# author: Felix Riedel
# license: BSD License
# version: 0.3.0
# github: https://github.com/felixr/docker-zsh-completion
#
# contributers:
# - Felix Riedel
# - Vincent Bernat
#
# license:
#
# Copyright (c) 2013, Felix Riedel
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the <organization> nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
__parse_docker_list() {
sed -e '/^ID/d' -e 's/[ ]\{2,\}/|/g' -e 's/ \([hdwm]\)\(inutes\|ays\|ours\|eeks\)/\1/' | awk ' BEGIN {FS="|"} { printf("%s:%7s, %s\n", $1, $4, $2)}'
awk '
NR == 1 {
idx=1;i=0;f[i]=0
header=$0
while ( match(header, / ([A-Z]+|[A-Z]+ [A-Z]+)/) ) {
idx += RSTART+1
f[++i]=idx
header = substr($0,idx)
}
f[++i]=999
}
NR > 1 '"$1"' {
for(j=0;j<i;j++) {
x[j] = substr($0, f[j], f[j+1]-f[j]-1)
gsub(/[ ]+$/, "", x[j])
}
printf("%s:%7s, %s\n", x[0], x[3], x[1])
if (x[6] != "") {
split(x[6], names, /,/)
for (name in names) printf("%s:%7s, %s\n", names[name], x[3], x[1])
}
}
'| sed -e 's/ \([hdwm]\)\(inutes\|ays\|ours\|eeks\)/\1/'
}
__docker_stoppedcontainers() {
local expl
declare -a stoppedcontainers
stoppedcontainers=(${(f)"$(docker ps -a | grep --color=never 'Exit' | __parse_docker_list )"})
_describe -t containers-stopped "Stopped Containers" stoppedcontainers
declare -a stoppedcontainers
stoppedcontainers=(${(f)"$(_call_program commands docker ps -a | __parse_docker_list '&& / Exit/')"})
_describe -t containers-stopped "Stopped Containers" stoppedcontainers "$@"
}
__docker_runningcontainers() {
local expl
declare -a containers
declare -a containers
containers=(${(f)"$(docker ps | __parse_docker_list)"})
_describe -t containers-active "Running Containers" containers
containers=(${(f)"$(_call_program commands docker ps | __parse_docker_list)"})
_describe -t containers-active "Running Containers" containers "$@"
}
__docker_containers () {
__docker_stoppedcontainers
__docker_runningcontainers
__docker_stoppedcontainers "$@"
__docker_runningcontainers "$@"
}
__docker_images () {
local expl
declare -a images
images=(${(f)"$(docker images | awk '(NR > 1){printf("%s\\:%s\n", $1,$2)}')"})
images=($images ${(f)"$(docker images | awk '(NR > 1){printf("%s:%-15s in %s\n", $3,$2,$1)}')"})
images=(${(f)"$(_call_program commands docker images | awk '(NR > 1 && $1 != "<none>"){printf("%s", $1);if ($2 != "<none>") printf("\\:%s", $2); printf("\n")}')"})
images=($images ${(f)"$(_call_program commands docker images | awk '(NR > 1){printf("%s:%-15s in %s\n", $3,$2,$1)}')"})
_describe -t docker-images "Images" images
}
__docker_tags() {
local expl
declare -a tags
tags=(${(f)"$(docker images | awk '(NR>1){print $2}'| sort | uniq)"})
tags=(${(f)"$(_call_program commands docker images | awk '(NR>1){print $2}'| sort | uniq)"})
_describe -t docker-tags "tags" tags
}
__docker_repositories_with_tags() {
if compset -P '*:'; then
__docker_tags
else
__docker_repositories -qS ":"
fi
}
__docker_search() {
# declare -a dockersearch
local cache_policy
zstyle -s ":completion:${curcontext}:" cache-policy cache_policy
if [[ -z "$cache_policy" ]]; then
zstyle ":completion:${curcontext}:" cache-policy __docker_caching_policy
zstyle ":completion:${curcontext}:" cache-policy __docker_caching_policy
fi
local searchterm cachename
@ -60,14 +120,14 @@ __docker_search() {
cachename=_docker-search-$searchterm
local expl
local -a result
local -a result
if ( [[ ${(P)+cachename} -eq 0 ]] || _cache_invalid ${cachename#_} ) \
&& ! _retrieve_cache ${cachename#_}; then
_message "Searching for ${searchterm}..."
result=(${(f)"$(docker search ${searchterm} | awk '(NR>2){print $1}')"})
result=(${(f)"$(_call_program commands docker search ${searchterm} | awk '(NR>2){print $1}')"})
_store_cache ${cachename#_} result
fi
_wanted dockersearch expl 'Available images' compadd -a result
fi
_wanted dockersearch expl 'Available images' compadd -a result
}
__docker_caching_policy()
@ -81,8 +141,8 @@ __docker_caching_policy()
__docker_repositories () {
local expl
declare -a repos
repos=(${(f)"$(docker images | sed -e '1d' -e 's/[ ].*//' | sort | uniq)"})
_describe -t docker-repos "Repositories" repos
repos=(${(f)"$(_call_program commands docker images | sed -e '1d' -e 's/[ ].*//' | sort | uniq)"})
_describe -t docker-repos "Repositories" repos "$@"
}
__docker_commands () {
@ -91,15 +151,15 @@ __docker_commands () {
zstyle -s ":completion:${curcontext}:" cache-policy cache_policy
if [[ -z "$cache_policy" ]]; then
zstyle ":completion:${curcontext}:" cache-policy __docker_caching_policy
zstyle ":completion:${curcontext}:" cache-policy __docker_caching_policy
fi
if ( [[ ${+_docker_subcommands} -eq 0 ]] || _cache_invalid docker_subcommands) \
&& ! _retrieve_cache docker_subcommands;
&& ! _retrieve_cache docker_subcommands;
then
_docker_subcommands=(${${(f)"$(_call_program commands
_docker_subcommands=(${${(f)"$(_call_program commands
docker 2>&1 | sed -e '1,6d' -e '/^[ ]*$/d' -e 's/[ ]*\([^ ]\+\)\s*\([^ ].*\)/\1:\2/' )"}})
_docker_subcommands=($_docker_subcommands 'help:Show help for a command')
_docker_subcommands=($_docker_subcommands 'help:Show help for a command')
_store_cache docker_subcommands _docker_subcommands
fi
_describe -t docker-commands "docker command" _docker_subcommands
@ -108,100 +168,206 @@ __docker_commands () {
__docker_subcommand () {
local -a _command_args
case "$words[1]" in
(attach|wait)
_arguments ':containers:__docker_runningcontainers'
(attach)
_arguments \
'--no-stdin[Do not attach stdin]' \
'--sig-proxy[Proxify all received signal]' \
':containers:__docker_runningcontainers'
;;
(build)
_arguments \
'-t=-:repository:__docker_repositories' \
'--no-cache[Do not use cache when building the image]' \
'-q[Suppress verbose build output]' \
'--rm[Remove intermediate containers after a successful build]' \
'-t=-:repository:__docker_repositories_with_tags' \
':path or URL:_directories'
;;
(commit)
_arguments \
'--author=-[Author]:author: ' \
'-m=-[Commit message]:message: ' \
'--run=-[Configuration automatically applied when the image is run]:configuration: ' \
':container:__docker_containers' \
':repository:__docker_repositories' \
':tag: '
':repository:__docker_repositories_with_tags'
;;
(diff|export|logs)
(cp)
_arguments \
':container:->container' \
':hostpath:_files'
case $state in
(container)
if compset -P '*:'; then
_files
else
__docker_containers -qS ":"
fi
;;
esac
;;
(diff|export)
_arguments '*:containers:__docker_containers'
;;
(history)
_arguments '*:images:__docker_images'
_arguments \
'--no-trunc[Do not truncate output]' \
'-q[Only show numeric IDs]' \
'*:images:__docker_images'
;;
(images)
_arguments \
'-a[Show all images]' \
'--no-trunc[Do not truncate output]' \
'-q[Only show numeric IDs]' \
'--tree[Output graph in tree format]' \
'--viz[Output graph in graphviz format]' \
':repository:__docker_repositories'
;;
(inspect)
_arguments '*:containers:__docker_containers'
_arguments \
'--format=-[Format the output using the given go template]:template: ' \
'*:containers:__docker_containers'
;;
(history)
_arguments ':images:__docker_images'
(import)
_arguments \
':URL:(- http:// file://)' \
':repository:__docker_repositories_with_tags'
;;
(info)
;;
(import)
_arguments \
':URL:(- http:// file://)' \
':repository:__docker_repositories_with_tags'
;;
(insert)
_arguments '1:containers:__docker_containers' \
'2:URL:(http:// file://)' \
'3:file:_files'
;;
(kill)
_arguments '*:containers:__docker_runningcontainers'
;;
(load)
;;
(login)
_arguments \
'-e=-[Email]:email: ' \
'-p=-[Password]:password: ' \
'-u=-[Username]:username: ' \
':server: '
;;
(logs)
_arguments \
'-f[Follow log output]' \
'*:containers:__docker_containers'
;;
(port)
_arguments '1:containers:__docker_runningcontainers'
_arguments \
'1:containers:__docker_runningcontainers' \
'2:port:_ports'
;;
(start)
_arguments '*:containers:__docker_stoppedcontainers'
_arguments \
'-a[Attach container'"'"'s stdout/stderr and forward all signals]' \
'-i[Attach container'"'"'s stding]' \
'*:containers:__docker_stoppedcontainers'
;;
(rm)
_arguments '-v[Remove the volumes associated to the container]' \
_arguments \
'--link[Remove the specified link and not the underlying container]' \
'-v[Remove the volumes associated to the container]' \
'*:containers:__docker_stoppedcontainers'
;;
(rmi)
_arguments '-v[Remove the volumes associated to the container]' \
_arguments \
'*:images:__docker_images'
;;
(top)
_arguments '1:containers:__docker_runningcontainers'
;;
(restart|stop)
_arguments '-t=-[Number of seconds to try to stop for before killing the container]:seconds to before killing:(1 5 10 30 60)' \
'*:containers:__docker_runningcontainers'
;;
(top)
_arguments ':containers:__docker_runningcontainers'
_arguments \
'1:containers:__docker_runningcontainers' \
'(-)*:: :->ps-arguments'
case $state in
(ps-arguments)
_ps
;;
esac
;;
(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' \
'-n=-[Show n last created containers, include non-running one]:n:(1 5 10 25 50)'
_arguments \
'-a[Show all containers]' \
'--before=-[Show only container created before...]:containers:__docker_containers' \
'-l[Show only the latest created container]' \
'-n=-[Show n last created containers, include non-running one]:n:(1 5 10 25 50)' \
'--no-trunc[Do not truncate output]' \
'-q[Only show numeric IDs]' \
'-s[Display sizes]' \
'--since=-[Show only containers created since...]:containers:__docker_containers'
;;
(tag)
_arguments \
'-f[force]'\
':image:__docker_images'\
':repository:__docker_repositories' \
':tag:__docker_tags'
':repository:__docker_repositories_with_tags'
;;
(run)
_arguments \
'-a=-[Attach to stdin, stdout or stderr]:toggle:(true false)' \
'-c=-[CPU shares (relative weight)]:CPU shares: ' \
'-P[Publish all exposed ports to the host]' \
'-a[Attach to stdin, stdout or stderr]' \
'-c=-[CPU shares (relative weight)]:CPU shares:(0 10 100 200 500 800 1000)' \
'--cidfile=-[Write the container ID to the file]:CID file:_files' \
'-d[Detached mode: leave the container running in the background]' \
'*--dns=[Set custom dns servers]:dns server: ' \
'*-e=[Set environment variables]:environment variable: ' \
'*--dns=-[Set custom dns servers]:dns server: ' \
'*-e=-[Set environment variables]:environment variable: ' \
'--entrypoint=-[Overwrite the default entrypoint of the image]:entry point: ' \
'*--expose=-[Expose a port from the container without publishing it]: ' \
'-h=-[Container host name]:hostname:_hosts' \
'-i[Keep stdin open even if not attached]' \
'--link=-[Add link to another container]:link:->link' \
'--lxc-conf=-[Add custom lxc options]:lxc options: ' \
'-m=-[Memory limit (in bytes)]:limit: ' \
'*-p=-[Expose a container''s port to the host]:port:_ports' \
'-t=-[Allocate a pseudo-tty]:toggle:(true false)' \
'--name=-[Container name]:name: ' \
'*-p=-[Expose a container'"'"'s port to the host]:port:_ports' \
'--privileged[Give extended privileges to this container]' \
'--rm[Remove intermediate containers when it exits]' \
'--sig-proxy[Proxify all received signal]' \
'-t[Allocate a pseudo-tty]' \
'-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: ' \
'-w=-[Working directory inside the container]:directory:_directories' \
'(-):images:__docker_images' \
'(-):command: _command_names -e' \
'*::arguments: _normal'
;;
case $state in
(link)
if compset -P '*:'; then
_wanted alias expl 'Alias' compadd -E ""
else
__docker_runningcontainers -qS ":"
fi
;;
esac
;;
(pull|search)
_arguments ':name:__docker_search'
;;
(push)
_arguments ':repository:__docker_repositories_with_tags'
;;
(save)
_arguments \
':images:__docker_images'
;;
(wait)
_arguments ':containers:__docker_runningcontainers'
;;
(help)
_arguments ':subcommand:__docker_commands'
;;
@ -212,24 +378,31 @@ __docker_subcommand () {
}
_docker () {
# Support for subservices, which allows for `compdef _docker docker-shell=_docker_containers`.
# Based on /usr/share/zsh/functions/Completion/Unix/_git without support for `ret`.
if [[ $service != docker ]]; then
_call_function - _$service
return
fi
local curcontext="$curcontext" state line
typeset -A opt_args
_arguments -C \
'-H=-[tcp://host:port to bind/connect to]:socket: ' \
'(-): :->command' \
'(-)*:: :->option-or-argument'
'(-)*:: :->option-or-argument'
if (( CURRENT == 1 )); then
fi
case $state in
case $state in
(command)
__docker_commands
;;
(option-or-argument)
curcontext=${curcontext%:*:*}:docker-$words[1]:
__docker_subcommand
__docker_subcommand
;;
esac
}

Просмотреть файл

@ -1,125 +0,0 @@
package main
import (
"fmt"
"io"
"log"
"net"
"os"
"os/exec"
"path"
"time"
)
var DOCKERPATH = path.Join(os.Getenv("DOCKERPATH"), "docker")
// WARNING: this crashTest will 1) crash your host, 2) remove all containers
func runDaemon() (*exec.Cmd, error) {
os.Remove("/var/run/docker.pid")
exec.Command("rm", "-rf", "/var/lib/docker/containers").Run()
cmd := exec.Command(DOCKERPATH, "-d")
outPipe, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
errPipe, err := cmd.StderrPipe()
if err != nil {
return nil, err
}
if err := cmd.Start(); err != nil {
return nil, err
}
go func() {
io.Copy(os.Stdout, outPipe)
}()
go func() {
io.Copy(os.Stderr, errPipe)
}()
return cmd, nil
}
func crashTest() error {
if err := exec.Command("/bin/bash", "-c", "while true; do true; done").Start(); err != nil {
return err
}
var endpoint string
if ep := os.Getenv("TEST_ENDPOINT"); ep == "" {
endpoint = "192.168.56.1:7979"
} else {
endpoint = ep
}
c := make(chan bool)
var conn io.Writer
go func() {
conn, _ = net.Dial("tcp", endpoint)
c <- false
}()
go func() {
time.Sleep(2 * time.Second)
c <- true
}()
<-c
restartCount := 0
totalTestCount := 1
for {
daemon, err := runDaemon()
if err != nil {
return err
}
restartCount++
// time.Sleep(5000 * time.Millisecond)
var stop bool
go func() error {
stop = false
for i := 0; i < 100 && !stop; {
func() error {
cmd := exec.Command(DOCKERPATH, "run", "ubuntu", "echo", fmt.Sprintf("%d", totalTestCount))
i++
totalTestCount++
outPipe, err := cmd.StdoutPipe()
if err != nil {
return err
}
inPipe, err := cmd.StdinPipe()
if err != nil {
return err
}
if err := cmd.Start(); err != nil {
return err
}
if conn != nil {
go io.Copy(conn, outPipe)
}
// Expecting error, do not check
inPipe.Write([]byte("hello world!!!!!\n"))
go inPipe.Write([]byte("hello world!!!!!\n"))
go inPipe.Write([]byte("hello world!!!!!\n"))
inPipe.Close()
if err := cmd.Wait(); err != nil {
return err
}
outPipe.Close()
return nil
}()
}
return nil
}()
time.Sleep(20 * time.Second)
stop = true
if err := daemon.Process.Kill(); err != nil {
return err
}
}
}
func main() {
if err := crashTest(); err != nil {
log.Println(err)
}
}

Просмотреть файл

@ -29,7 +29,7 @@ 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
RUN apt-get update && apt-get install -y iceweasel sudo
# create sysadmin account
RUN useradd -m -d /data -p saIVpsc0EVTwA sysadmin

Просмотреть файл

@ -3,7 +3,7 @@ package main
import (
"flag"
"fmt"
"github.com/dotcloud/docker/daemon/graphdriver/devmapper"
"github.com/docker/docker/daemon/graphdriver/devmapper"
"os"
"path"
"sort"

Просмотреть файл

@ -19,7 +19,7 @@ ENV GOROOT /goroot
ENV GOPATH /go
ENV PATH $GOROOT/bin:$PATH
RUN go get github.com/dotcloud/docker && cd /go/src/github.com/dotcloud/docker && git checkout v0.6.3
RUN go get github.com/docker/docker && cd /go/src/github.com/docker/docker && git checkout v0.6.3
ADD manager.go /manager/
RUN cd /manager && go build -o /usr/bin/manager

Просмотреть файл

@ -5,7 +5,7 @@ import (
"encoding/json"
"flag"
"fmt"
"github.com/dotcloud/docker"
"github.com/docker/docker"
"os"
"strings"
"text/template"

Просмотреть файл

@ -37,7 +37,7 @@ if [ ! -e "manager/$script" ]; then
exit 1
fi
# TODO https://github.com/dotcloud/docker/issues/734 (docker inspect formatting)
# TODO https://github.com/docker/docker/issues/734 (docker inspect formatting)
#if command -v docker > /dev/null 2>&1; then
# image="$(docker inspect -f '{{.Image}}' "$cid")"
# if [ "$image" ]; then

Просмотреть файл

@ -0,0 +1,2 @@
Lokesh Mandvekar <lsm5@fedoraproject.org> (@lsm5)
Brandon Philips <brandon.philips@coreos.com> (@philips)

Просмотреть файл

@ -1,13 +1,13 @@
[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.com
After=network.target
After=network.target docker.socket
Requires=docker.socket
[Service]
ExecStart=/usr/bin/docker -d
Restart=on-failure
ExecStart=/usr/bin/docker -d -H fd://
LimitNOFILE=1048576
LimitNPROC=1048576
[Install]
WantedBy=multi-user.target
Also=docker.socket

Просмотреть файл

@ -3,6 +3,9 @@ Description=Docker Socket for the API
[Socket]
ListenStream=/var/run/docker.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker
[Install]
WantedBy=sockets.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,4 +1,5 @@
#!/bin/sh
set -e
### BEGIN INIT INFO
# Provides: docker
@ -130,7 +131,7 @@ case "$1" in
;;
status)
status_of_proc -p "$DOCKER_SSD_PIDFILE" "$DOCKER" docker
status_of_proc -p "$DOCKER_SSD_PIDFILE" "$DOCKER" "$DOCKER_DESC"
;;
*)
@ -138,5 +139,3 @@ case "$1" in
exit 1
;;
esac
exit 0

Просмотреть файл

@ -50,7 +50,7 @@ start() {
pid=$!
touch $lockfile
# wait up to 10 seconds for the pidfile to exist. see
# https://github.com/dotcloud/docker/issues/5359
# https://github.com/docker/docker/issues/5359
tries=0
while [ ! -f $pidfile -a $tries -lt 10 ]; do
sleep 1

Просмотреть файл

@ -19,12 +19,12 @@ tmp() {
}
apkv() {
curl -s $REPO/$ARCH/APKINDEX.tar.gz | tar -Oxz |
curl -sSL $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 |
curl -sSL $REPO/$ARCH/apk-tools-static-$(apkv).apk |
tar -xz -C $TMP sbin/apk.static
}

Просмотреть файл

@ -5,8 +5,13 @@
set -e
hash pacstrap &>/dev/null || {
echo "Could not find pacstrap. Run pacman -S arch-install-scripts"
exit 1
echo "Could not find pacstrap. Run pacman -S arch-install-scripts"
exit 1
}
hash expect &>/dev/null || {
echo "Could not find expect. Run pacman -S expect"
exit 1
}
ROOTFS=$(mktemp -d ${TMPDIR:-/var/tmp}/rootfs-archlinux-XXXXXXXXXX)
@ -15,7 +20,21 @@ chmod 755 $ROOTFS
# 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 send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- \$arg
}
set timeout 60
spawn pacstrap -C ./mkimage-arch-pacman.conf -c -d -G -i $ROOTFS base haveged --ignore $PKGIGNORE
expect {
-exact "anyway? \[Y/n\] " { send -- "n\r"; exp_continue }
-exact "(default=all): " { send -- "\r"; exp_continue }
-exact "installation? \[Y/n\]" { send -- "y\r"; 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"

Просмотреть файл

@ -144,7 +144,7 @@ if [ -z "$strictDebootstrap" ]; then
# initctl (for some pesky upstart scripts)
sudo chroot . dpkg-divert --local --rename --add /sbin/initctl
sudo ln -sf /bin/true sbin/initctl
# see https://github.com/dotcloud/docker/issues/446#issuecomment-16953173
# see https://github.com/docker/docker/issues/446#issuecomment-16953173
# shrink the image, since apt makes us fat (wheezy: ~157.5MB vs ~120MB)
sudo chroot . apt-get clean

Просмотреть файл

@ -83,7 +83,7 @@ if [ -d "$rootfsDir/etc/apt/apt.conf.d" ]; then
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).
# open to better suggestions (https://github.com/docker/docker/issues).
EOF
# remove apt-cache translations for fast "apt-get update"
@ -95,6 +95,21 @@ if [ -d "$rootfsDir/etc/apt/apt.conf.d" ]; then
Acquire::Languages "none";
EOF
echo >&2 "+ echo Acquire::GzipIndexes 'true' > '$rootfsDir/etc/apt/apt.conf.d/docker-gzip-indexes'"
cat > "$rootfsDir/etc/apt/apt.conf.d/docker-gzip-indexes" <<-'EOF'
# Since Docker users using "RUN apt-get update && apt-get install -y ..." in
# their Dockerfiles don't go delete the lists files afterwards, we want them to
# be as small as possible on-disk, so we explicitly request "gz" versions and
# tell Apt to keep them gzipped on-disk.
# For comparison, an "apt-get update" layer without this on a pristine
# "debian:wheezy" base image was "29.88 MB", where with this it was only
# "8.273 MB".
Acquire::GzipIndexes "true";
Acquire::CompressionTypes::Order:: "gz";
EOF
fi
if [ -z "$DONT_TOUCH_SOURCES_LIST" ]; then
@ -123,9 +138,9 @@ if [ -z "$DONT_TOUCH_SOURCES_LIST" ]; then
" "$rootfsDir/etc/apt/sources.list"
echo "deb http://security.debian.org $suite/updates main" >> "$rootfsDir/etc/apt/sources.list"
# LTS
if [ "$suite" = 'squeeze' ]; then
if [ "$suite" = 'squeeze' -o "$suite" = 'oldstable' ]; then
head -1 "$rootfsDir/etc/apt/sources.list" \
| sed "s/ $suite / ${suite}-lts /" \
| sed "s/ $suite / squeeze-lts /" \
>> "$rootfsDir/etc/apt/sources.list"
fi
)
@ -173,4 +188,6 @@ fi
# 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
mkdir "$rootfsDir/var/lib/apt/lists/partial" # Lucid... "E: Lists directory /var/lib/apt/lists/partial is missing."
)

64
contrib/nuke-graph-directory.sh Executable file
Просмотреть файл

@ -0,0 +1,64 @@
#!/bin/sh
set -e
dir="$1"
if [ -z "$dir" ]; then
{
echo 'This script is for destroying old /var/lib/docker directories more safely than'
echo ' "rm -rf", which can cause data loss or other serious issues.'
echo
echo "usage: $0 directory"
echo " ie: $0 /var/lib/docker"
} >&2
exit 1
fi
if [ "$(id -u)" != 0 ]; then
echo >&2 "error: $0 must be run as root"
exit 1
fi
if [ ! -d "$dir" ]; then
echo >&2 "error: $dir is not a directory"
exit 1
fi
dir="$(readlink -f "$dir")"
echo
echo "Nuking $dir ..."
echo ' (if this is wrong, press Ctrl+C NOW!)'
echo
( set -x; sleep 10 )
echo
dir_in_dir() {
inner="$1"
outer="$2"
[ "${inner#$outer}" != "$inner" ]
}
# let's start by unmounting any submounts in $dir
# (like -v /home:... for example - DON'T DELETE MY HOME DIRECTORY BRU!)
for mount in $(awk '{ print $5 }' /proc/self/mountinfo); do
mount="$(readlink -f "$mount" || true)"
if dir_in_dir "$mount" "$dir"; then
( set -x; umount -f "$mount" )
fi
done
# now, let's go destroy individual btrfs subvolumes, if any exist
if command -v btrfs &> /dev/null; then
root="$(df "$dir" | awk 'NR>1 { print $NF }')"
for subvol in $(btrfs subvolume list -o "$root" 2>/dev/null | awk -F' path ' '{ print $2 }'); do
subvolDir="$root/$subvol"
if dir_in_dir "$subvolDir" "$dir"; then
( set -x; btrfs subvolume delete "$subvolDir" )
fi
done
fi
# finally, DESTROY ALL THINGS
( set -x; rm -rf "$dir" )

Просмотреть файл

@ -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"

6
daemon/MAINTAINERS Normal file
Просмотреть файл

@ -0,0 +1,6 @@
Solomon Hykes <solomon@docker.com> (@shykes)
Victor Vieux <vieux@docker.com> (@vieux)
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
Cristian Staretu <cristian.staretu@gmail.com> (@unclejack)
Tibor Vass <teabee89@gmail.com> (@tiborvass)
volumes.go: Brian Goff <cpuguy83@gmail.com> (@cpuguy83)

Просмотреть файл

@ -1,11 +1,124 @@
package daemon
import (
"encoding/json"
"fmt"
"io"
"os"
"time"
"github.com/dotcloud/docker/utils"
"github.com/docker/docker/engine"
"github.com/docker/docker/pkg/jsonlog"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/utils"
)
func (daemon *Daemon) ContainerAttach(job *engine.Job) engine.Status {
if len(job.Args) != 1 {
return job.Errorf("Usage: %s CONTAINER\n", job.Name)
}
var (
name = job.Args[0]
logs = job.GetenvBool("logs")
stream = job.GetenvBool("stream")
stdin = job.GetenvBool("stdin")
stdout = job.GetenvBool("stdout")
stderr = job.GetenvBool("stderr")
)
container := daemon.Get(name)
if container == nil {
return job.Errorf("No such container: %s", name)
}
//logs
if logs {
cLog, err := container.ReadLog("json")
if err != nil && os.IsNotExist(err) {
// Legacy logs
log.Debugf("Old logs format")
if stdout {
cLog, err := container.ReadLog("stdout")
if err != nil {
log.Errorf("Error reading logs (stdout): %s", err)
} else if _, err := io.Copy(job.Stdout, cLog); err != nil {
log.Errorf("Error streaming logs (stdout): %s", err)
}
}
if stderr {
cLog, err := container.ReadLog("stderr")
if err != nil {
log.Errorf("Error reading logs (stderr): %s", err)
} else if _, err := io.Copy(job.Stderr, cLog); err != nil {
log.Errorf("Error streaming logs (stderr): %s", err)
}
}
} else if err != nil {
log.Errorf("Error reading logs (json): %s", err)
} else {
dec := json.NewDecoder(cLog)
for {
l := &jsonlog.JSONLog{}
if err := dec.Decode(l); err == io.EOF {
break
} else if err != nil {
log.Errorf("Error streaming logs: %s", err)
break
}
if l.Stream == "stdout" && stdout {
fmt.Fprintf(job.Stdout, "%s", l.Log)
}
if l.Stream == "stderr" && stderr {
fmt.Fprintf(job.Stderr, "%s", l.Log)
}
}
}
}
//stream
if stream {
var (
cStdin io.ReadCloser
cStdout, cStderr io.Writer
cStdinCloser io.Closer
)
if stdin {
r, w := io.Pipe()
go func() {
defer w.Close()
defer log.Debugf("Closing buffered stdin pipe")
io.Copy(w, job.Stdin)
}()
cStdin = r
cStdinCloser = job.Stdin
}
if stdout {
cStdout = job.Stdout
}
if stderr {
cStderr = job.Stderr
}
<-daemon.Attach(container, cStdin, cStdinCloser, cStdout, cStderr)
// If we are in stdinonce mode, wait for the process to end
// otherwise, simply return
if container.Config.StdinOnce && !container.Config.Tty {
container.State.WaitStop(-1 * time.Second)
}
}
return engine.StatusOK
}
// FIXME: this should be private, and every outside subsystem
// should go through the "container_attach" job. But that would require
// that job to be properly documented, as well as the relationship betweem
// Attach and ContainerAttach.
//
// This method is in use by builder/builder.go.
func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
var (
cStdout, cStderr io.ReadCloser
@ -19,8 +132,8 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo
errors <- err
} else {
go func() {
utils.Debugf("attach: stdin: begin")
defer utils.Debugf("attach: stdin: end")
log.Debugf("attach: stdin: begin")
defer log.Debugf("attach: stdin: end")
// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
if container.Config.StdinOnce && !container.Config.Tty {
defer cStdin.Close()
@ -43,7 +156,7 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo
err = nil
}
if err != nil {
utils.Errorf("attach: stdin: %s", err)
log.Errorf("attach: stdin: %s", err)
}
errors <- err
}()
@ -56,8 +169,8 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo
} else {
cStdout = p
go func() {
utils.Debugf("attach: stdout: begin")
defer utils.Debugf("attach: stdout: end")
log.Debugf("attach: stdout: begin")
defer log.Debugf("attach: stdout: end")
// If we are in StdinOnce mode, then close stdin
if container.Config.StdinOnce && stdin != nil {
defer stdin.Close()
@ -70,7 +183,7 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo
err = nil
}
if err != nil {
utils.Errorf("attach: stdout: %s", err)
log.Errorf("attach: stdout: %s", err)
}
errors <- err
}()
@ -81,7 +194,7 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo
defer stdinCloser.Close()
}
if cStdout, err := container.StdoutPipe(); err != nil {
utils.Errorf("attach: stdout pipe: %s", err)
log.Errorf("attach: stdout pipe: %s", err)
} else {
io.Copy(&utils.NopWriter{}, cStdout)
}
@ -94,8 +207,8 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo
} else {
cStderr = p
go func() {
utils.Debugf("attach: stderr: begin")
defer utils.Debugf("attach: stderr: end")
log.Debugf("attach: stderr: begin")
defer log.Debugf("attach: stderr: end")
// If we are in StdinOnce mode, then close stdin
if container.Config.StdinOnce && stdin != nil {
defer stdin.Close()
@ -108,7 +221,7 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo
err = nil
}
if err != nil {
utils.Errorf("attach: stderr: %s", err)
log.Errorf("attach: stderr: %s", err)
}
errors <- err
}()
@ -120,7 +233,7 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo
}
if cStderr, err := container.StderrPipe(); err != nil {
utils.Errorf("attach: stdout pipe: %s", err)
log.Errorf("attach: stdout pipe: %s", err)
} else {
io.Copy(&utils.NopWriter{}, cStderr)
}
@ -140,14 +253,14 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo
// FIXME: how to clean up the stdin goroutine without the unwanted side effect
// of closing the passed stdin? Add an intermediary io.Pipe?
for i := 0; i < nJobs; i += 1 {
utils.Debugf("attach: waiting for job %d/%d", i+1, nJobs)
log.Debugf("attach: waiting for job %d/%d", i+1, nJobs)
if err := <-errors; err != nil {
utils.Errorf("attach: job %d returned error %s, aborting all jobs", i+1, err)
log.Errorf("attach: job %d returned error %s, aborting all jobs", i+1, err)
return err
}
utils.Debugf("attach: job %d completed successfully", i+1)
log.Debugf("attach: job %d completed successfully", i+1)
}
utils.Debugf("attach: all jobs completed successfully")
log.Debugf("attach: all jobs completed successfully")
return nil
})
}

Просмотреть файл

@ -1,4 +1,4 @@
package server
package daemon
import (
"crypto/sha256"
@ -10,6 +10,7 @@ import (
"io/ioutil"
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
"reflect"
@ -19,16 +20,99 @@ import (
"syscall"
"time"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/daemon"
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/pkg/symlink"
"github.com/dotcloud/docker/pkg/system"
"github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"github.com/docker/docker/archive"
"github.com/docker/docker/engine"
"github.com/docker/docker/nat"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/pkg/symlink"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/pkg/tarsum"
"github.com/docker/docker/registry"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/utils"
)
func (daemon *Daemon) CmdBuild(job *engine.Job) engine.Status {
if len(job.Args) != 0 {
return job.Errorf("Usage: %s\n", job.Name)
}
var (
remoteURL = job.Getenv("remote")
repoName = job.Getenv("t")
suppressOutput = job.GetenvBool("q")
noCache = job.GetenvBool("nocache")
rm = job.GetenvBool("rm")
forceRm = job.GetenvBool("forcerm")
authConfig = &registry.AuthConfig{}
configFile = &registry.ConfigFile{}
tag string
context io.ReadCloser
)
job.GetenvJson("authConfig", authConfig)
job.GetenvJson("configFile", configFile)
repoName, tag = parsers.ParseRepositoryTag(repoName)
if remoteURL == "" {
context = ioutil.NopCloser(job.Stdin)
} else if utils.IsGIT(remoteURL) {
if !strings.HasPrefix(remoteURL, "git://") {
remoteURL = "https://" + remoteURL
}
root, err := ioutil.TempDir("", "docker-build-git")
if err != nil {
return job.Error(err)
}
defer os.RemoveAll(root)
if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
return job.Errorf("Error trying to use git: %s (%s)", err, output)
}
c, err := archive.Tar(root, archive.Uncompressed)
if err != nil {
return job.Error(err)
}
context = c
} else if utils.IsURL(remoteURL) {
f, err := utils.Download(remoteURL)
if err != nil {
return job.Error(err)
}
defer f.Body.Close()
dockerFile, err := ioutil.ReadAll(f.Body)
if err != nil {
return job.Error(err)
}
c, err := archive.Generate("Dockerfile", string(dockerFile))
if err != nil {
return job.Error(err)
}
context = c
}
defer context.Close()
sf := utils.NewStreamFormatter(job.GetenvBool("json"))
b := NewBuildFile(daemon, daemon.eng,
&utils.StdoutFormater{
Writer: job.Stdout,
StreamFormatter: sf,
},
&utils.StderrFormater{
Writer: job.Stdout,
StreamFormatter: sf,
},
!suppressOutput, !noCache, rm, forceRm, job.Stdout, sf, authConfig, configFile)
id, err := b.Build(context)
if err != nil {
return job.Error(err)
}
if repoName != "" {
daemon.Repositories().Set(repoName, tag, id, false)
}
return engine.StatusOK
}
var (
ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty")
)
@ -40,15 +124,15 @@ type BuildFile interface {
}
type buildFile struct {
daemon *daemon.Daemon
srv *Server
daemon *Daemon
eng *engine.Engine
image string
maintainer string
config *runconfig.Config
contextPath string
context *utils.TarSum
context *tarsum.TarSum
verbose bool
utilizeCache bool
@ -67,6 +151,9 @@ type buildFile struct {
// Deprecated, original writer used for ImagePull. To be removed.
outOld io.Writer
sf *utils.StreamFormatter
// cmdSet indicates is CMD was set in current Dockerfile
cmdSet bool
}
func (b *buildFile) clearTmp(containers map[string]struct{}) {
@ -85,7 +172,7 @@ func (b *buildFile) CmdFrom(name string) error {
image, err := b.daemon.Repositories().LookupImage(name)
if err != nil {
if b.daemon.Graph().IsNotExist(err) {
remote, tag := utils.ParseRepositoryTag(name)
remote, tag := parsers.ParseRepositoryTag(name)
pullRegistryAuth := b.authConfig
if len(b.configFile.Configs) > 0 {
// The request came with a full auth config file, we prefer to use that
@ -96,7 +183,7 @@ func (b *buildFile) CmdFrom(name string) error {
resolvedAuth := b.configFile.ResolveAuthConfig(endpoint)
pullRegistryAuth = &resolvedAuth
}
job := b.srv.Eng.Job("pull", remote, tag)
job := b.eng.Job("pull", remote, tag)
job.SetenvBool("json", b.sf.Json())
job.SetenvBool("parallel", true)
job.SetenvJson("authConfig", pullRegistryAuth)
@ -118,7 +205,7 @@ func (b *buildFile) CmdFrom(name string) error {
b.config = image.Config
}
if b.config.Env == nil || len(b.config.Env) == 0 {
b.config.Env = append(b.config.Env, "HOME=/", "PATH="+daemon.DefaultPathEnv)
b.config.Env = append(b.config.Env, "PATH="+DefaultPathEnv)
}
// Process ONBUILD triggers if they exist
if nTriggers := len(b.config.OnBuild); nTriggers != 0 {
@ -167,20 +254,20 @@ func (b *buildFile) CmdMaintainer(name string) error {
// probeCache checks to see if image-caching is enabled (`b.utilizeCache`)
// and if so attempts to look up the current `b.image` and `b.config` pair
// in the current server `b.srv`. If an image is found, probeCache returns
// in the current server `b.daemon`. If an image is found, probeCache returns
// `(true, nil)`. If no image is found, it returns `(false, nil)`. If there
// is any error, it returns `(false, err)`.
func (b *buildFile) probeCache() (bool, error) {
if b.utilizeCache {
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
if cache, err := b.daemon.ImageGetCached(b.image, b.config); err != nil {
return false, err
} else if cache != nil {
fmt.Fprintf(b.outStream, " ---> Using cache\n")
utils.Debugf("[BUILDER] Use cached version")
log.Debugf("[BUILDER] Use cached version")
b.image = cache.ID
return true, nil
} else {
utils.Debugf("[BUILDER] Cache miss")
log.Debugf("[BUILDER] Cache miss")
}
}
return false, nil
@ -196,12 +283,13 @@ func (b *buildFile) CmdRun(args string) error {
}
cmd := b.config.Cmd
b.config.Cmd = nil
// set Cmd manually, this is special case only for Dockerfiles
b.config.Cmd = config.Cmd
runconfig.Merge(b.config, config)
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
utils.Debugf("Command to be executed: %v", b.config.Cmd)
log.Debugf("Command to be executed: %v", b.config.Cmd)
hit, err := b.probeCache()
if err != nil {
@ -291,7 +379,7 @@ func (b *buildFile) CmdEnv(args string) error {
func (b *buildFile) buildCmdFromJson(args string) []string {
var cmd []string
if err := json.Unmarshal([]byte(args), &cmd); err != nil {
utils.Debugf("Error unmarshalling: %s, setting to /bin/sh -c", err)
log.Debugf("Error unmarshalling: %s, setting to /bin/sh -c", err)
cmd = []string{"/bin/sh", "-c", args}
}
return cmd
@ -303,12 +391,17 @@ func (b *buildFile) CmdCmd(args string) error {
if err := b.commit("", b.config.Cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
return err
}
b.cmdSet = true
return nil
}
func (b *buildFile) CmdEntrypoint(args string) error {
entrypoint := b.buildCmdFromJson(args)
b.config.Entrypoint = entrypoint
// if there is no cmd in current Dockerfile - cleanup cmd
if !b.cmdSet {
b.config.Cmd = nil
}
if err := b.commit("", b.config.Cmd, fmt.Sprintf("ENTRYPOINT %v", entrypoint)); err != nil {
return err
}
@ -404,7 +497,7 @@ func (b *buildFile) checkPathForAddition(orig string) error {
return nil
}
func (b *buildFile) addContext(container *daemon.Container, orig, dest string, decompress bool) error {
func (b *buildFile) addContext(container *Container, orig, dest string, decompress bool) error {
var (
err error
destExists = true
@ -459,7 +552,7 @@ func (b *buildFile) addContext(container *daemon.Container, orig, dest string, d
if err := archive.UntarPath(origPath, tarDest); err == nil {
return nil
} else if err != io.EOF {
utils.Debugf("Couldn't untar %s to %s: %s", origPath, tarDest, err)
log.Debugf("Couldn't untar %s to %s: %s", origPath, tarDest, err)
}
}
@ -553,7 +646,7 @@ func (b *buildFile) runContextCommand(args string, allowRemote bool, allowDecomp
if err != nil {
return err
}
tarSum := &utils.TarSum{Reader: r, DisableCompression: true}
tarSum := &tarsum.TarSum{Reader: r, DisableCompression: true}
if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
return err
}
@ -656,7 +749,7 @@ func (b *buildFile) CmdAdd(args string) error {
return b.runContextCommand(args, true, true, "ADD")
}
func (b *buildFile) create() (*daemon.Container, error) {
func (b *buildFile) create() (*Container, error) {
if b.image == "" {
return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
}
@ -677,10 +770,15 @@ func (b *buildFile) create() (*daemon.Container, error) {
return c, nil
}
func (b *buildFile) run(c *daemon.Container) error {
func (b *buildFile) run(c *Container) error {
var errCh chan error
if b.verbose {
errCh = utils.Go(func() error {
// FIXME: call the 'attach' job so that daemon.Attach can be made private
//
// FIXME (LK4D4): Also, maybe makes sense to call "logs" job, it is like attach
// but without hijacking for stdin. Also, with attach there can be race
// condition because of some output already was printed before it.
return <-b.daemon.Attach(c, nil, nil, b.outStream, b.errStream)
})
}
@ -775,7 +873,7 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
return "", err
}
b.context = &utils.TarSum{Reader: decompressedStream, DisableCompression: true}
b.context = &tarsum.TarSum{Reader: decompressedStream, DisableCompression: true}
if err := archive.Untar(b.context, tmpdirPath, nil); err != nil {
return "", err
}
@ -889,10 +987,10 @@ func fixPermissions(destination string, uid, gid int) error {
})
}
func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, forceRm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *registry.AuthConfig, authConfigFile *registry.ConfigFile) BuildFile {
func NewBuildFile(d *Daemon, eng *engine.Engine, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, forceRm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *registry.AuthConfig, authConfigFile *registry.ConfigFile) BuildFile {
return &buildFile{
daemon: srv.daemon,
srv: srv,
daemon: d,
eng: eng,
config: &runconfig.Config{},
outStream: outStream,
errStream: errStream,

32
daemon/changes.go Normal file
Просмотреть файл

@ -0,0 +1,32 @@
package daemon
import (
"github.com/docker/docker/engine"
)
func (daemon *Daemon) ContainerChanges(job *engine.Job) engine.Status {
if n := len(job.Args); n != 1 {
return job.Errorf("Usage: %s CONTAINER", job.Name)
}
name := job.Args[0]
if container := daemon.Get(name); container != nil {
outs := engine.NewTable("", 0)
changes, err := container.Changes()
if err != nil {
return job.Error(err)
}
for _, change := range changes {
out := &engine.Env{}
if err := out.Import(change); err != nil {
return job.Error(err)
}
outs.Add(out)
}
if _, err := outs.WriteListTo(job.Stdout); err != nil {
return job.Error(err)
}
} else {
return job.Errorf("No such container: %s", name)
}
return engine.StatusOK
}

84
daemon/commit.go Normal file
Просмотреть файл

@ -0,0 +1,84 @@
package daemon
import (
"github.com/docker/docker/engine"
"github.com/docker/docker/image"
"github.com/docker/docker/runconfig"
)
func (daemon *Daemon) ContainerCommit(job *engine.Job) engine.Status {
if len(job.Args) != 1 {
return job.Errorf("Not enough arguments. Usage: %s CONTAINER\n", job.Name)
}
name := job.Args[0]
container := daemon.Get(name)
if container == nil {
return job.Errorf("No such container: %s", name)
}
var (
config = container.Config
newConfig runconfig.Config
)
if err := job.GetenvJson("config", &newConfig); err != nil {
return job.Error(err)
}
if err := runconfig.Merge(&newConfig, config); err != nil {
return job.Error(err)
}
img, err := daemon.Commit(container, job.Getenv("repo"), job.Getenv("tag"), job.Getenv("comment"), job.Getenv("author"), job.GetenvBool("pause"), &newConfig)
if err != nil {
return job.Error(err)
}
job.Printf("%s\n", img.ID)
return engine.StatusOK
}
// Commit creates a new filesystem image from the current state of a container.
// The image can optionally be tagged into a repository
func (daemon *Daemon) Commit(container *Container, repository, tag, comment, author string, pause bool, config *runconfig.Config) (*image.Image, error) {
if pause {
container.Pause()
defer container.Unpause()
}
if err := container.Mount(); err != nil {
return nil, err
}
defer container.Unmount()
rwTar, err := container.ExportRw()
if err != nil {
return nil, err
}
defer rwTar.Close()
// Create a new image from the container's base layers + a new layer from container changes
var (
containerID, containerImage string
containerConfig *runconfig.Config
)
if container != nil {
containerID = container.ID
containerImage = container.Image
containerConfig = container.Config
}
img, err := daemon.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
if err != nil {
return nil, err
}
// Register the image if needed
if repository != "" {
if err := daemon.repositories.Set(repository, tag, img.ID, true); err != nil {
return img, err
}
}
return img, nil
}

70
daemon/config.go Normal file
Просмотреть файл

@ -0,0 +1,70 @@
package daemon
import (
"net"
"github.com/docker/docker/daemon/networkdriver"
"github.com/docker/docker/opts"
flag "github.com/docker/docker/pkg/mflag"
)
const (
defaultNetworkMtu = 1500
DisableNetworkBridge = "none"
)
// Config define the configuration of a docker daemon
// These are the configuration settings that you pass
// to the docker daemon when you launch it with say: `docker -d -e lxc`
// FIXME: separate runtime configuration from http api configuration
type Config struct {
Pidfile string
Root string
AutoRestart bool
Dns []string
DnsSearch []string
EnableIptables bool
EnableIpForward bool
DefaultIp net.IP
BridgeIface string
BridgeIP string
InterContainerCommunication bool
GraphDriver string
GraphOptions []string
ExecDriver string
Mtu int
DisableNetwork bool
EnableSelinuxSupport bool
Context map[string][]string
}
// InstallFlags adds command-line options to the top-level flag parser for
// the current process.
// Subsequent calls to `flag.Parse` will populate config with values parsed
// from the command-line.
func (config *Config) InstallFlags() {
flag.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, "/var/run/docker.pid", "Path to use for daemon PID file")
flag.StringVar(&config.Root, []string{"g", "-graph"}, "/var/lib/docker", "Path to use as the root of the Docker runtime")
flag.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, "--restart on the daemon has been deprecated infavor of --restart policies on docker run")
flag.BoolVar(&config.EnableIptables, []string{"#iptables", "-iptables"}, true, "Enable Docker's addition of iptables rules")
flag.BoolVar(&config.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking")
flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")
flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Force the Docker runtime to use a specific storage driver")
flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, "native", "Force the Docker runtime to use a specific exec driver")
flag.BoolVar(&config.EnableSelinuxSupport, []string{"-selinux-enabled"}, false, "Enable selinux support. SELinux does not presently support the BTRFS storage driver")
flag.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, "Set the containers network MTU\nif no value is provided: default to the default route MTU or 1500 if no default route is available")
opts.IPVar(&config.DefaultIp, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container ports")
opts.ListVar(&config.GraphOptions, []string{"-storage-opt"}, "Set storage driver options")
// FIXME: why the inconsistency between "hosts" and "sockets"?
opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "Force Docker to use specific DNS servers")
opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "Force Docker to use specific DNS search domains")
}
func GetDefaultNetworkMtu() int {
if iface, err := networkdriver.GetDefaultRouteIface(); err == nil {
return iface.MTU
}
return defaultNetworkMtu
}

Просмотреть файл

@ -6,7 +6,6 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
@ -17,18 +16,21 @@ import (
"github.com/docker/libcontainer/devices"
"github.com/docker/libcontainer/label"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/daemon/graphdriver"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/image"
"github.com/dotcloud/docker/links"
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/pkg/networkfs/etchosts"
"github.com/dotcloud/docker/pkg/networkfs/resolvconf"
"github.com/dotcloud/docker/pkg/symlink"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"github.com/docker/docker/archive"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/engine"
"github.com/docker/docker/image"
"github.com/docker/docker/links"
"github.com/docker/docker/nat"
"github.com/docker/docker/pkg/broadcastwriter"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/networkfs/etchosts"
"github.com/docker/docker/pkg/networkfs/resolvconf"
"github.com/docker/docker/pkg/symlink"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/utils"
)
const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
@ -66,13 +68,14 @@ type Container struct {
ExecDriver string
command *execdriver.Command
stdout *utils.WriteBroadcaster
stderr *utils.WriteBroadcaster
stdout *broadcastwriter.BroadcastWriter
stderr *broadcastwriter.BroadcastWriter
stdin io.ReadCloser
stdinPipe io.WriteCloser
daemon *Daemon
MountLabel, ProcessLabel string
RestartCount int
Volumes map[string]string
// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
@ -81,6 +84,7 @@ type Container struct {
hostConfig *runconfig.HostConfig
activeLinks map[string]*links.Link
monitor *containerMonitor
}
func (container *Container) FromDisk() error {
@ -105,7 +109,7 @@ func (container *Container) FromDisk() error {
return container.readHostConfig()
}
func (container *Container) ToDisk() error {
func (container *Container) toDisk() error {
data, err := json.Marshal(container)
if err != nil {
return err
@ -124,6 +128,13 @@ func (container *Container) ToDisk() error {
return container.WriteHostConfig()
}
func (container *Container) ToDisk() error {
container.Lock()
err := container.toDisk()
container.Unlock()
return err
}
func (container *Container) readHostConfig() error {
container.hostConfig = &runconfig.HostConfig{}
// If the hostconfig file does not exist, do not read it.
@ -160,6 +171,13 @@ func (container *Container) WriteHostConfig() error {
return ioutil.WriteFile(pth, data, 0666)
}
func (container *Container) LogEvent(action string) {
d := container.daemon
if err := d.eng.Job("log", action, container.ID, d.Repositories().ImageName(container.Image)).Run(); err != nil {
log.Errorf("Error logging event %s for %s: %s", action, container.ID, err)
}
}
func (container *Container) getResourcePath(path string) (string, error) {
cleanPath := filepath.Join("/", path)
return symlink.FollowSymlinkInScope(filepath.Join(container.basefs, cleanPath), container.basefs)
@ -208,6 +226,20 @@ func populateCommand(c *Container, env []string) error {
return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode)
}
// Build lists of devices allowed and created within the container.
userSpecifiedDevices := make([]*devices.Device, len(c.hostConfig.Devices))
for i, deviceMapping := range c.hostConfig.Devices {
device, err := devices.GetDevice(deviceMapping.PathOnHost, deviceMapping.CgroupPermissions)
device.Path = deviceMapping.PathInContainer
if err != nil {
return fmt.Errorf("error gathering device information while adding custom device %s", err)
}
userSpecifiedDevices[i] = device
}
allowedDevices := append(devices.DefaultAllowedDevices, userSpecifiedDevices...)
autoCreatedDevices := append(devices.DefaultAutoCreatedDevices, userSpecifiedDevices...)
// TODO: this can be removed after lxc-conf is fully deprecated
mergeLxcConfIntoOptions(c.hostConfig, context)
@ -230,8 +262,10 @@ func populateCommand(c *Container, env []string) error {
User: c.Config.User,
Config: context,
Resources: resources,
AllowedDevices: devices.DefaultAllowedDevices,
AutoCreatedDevices: devices.DefaultAutoCreatedDevices,
AllowedDevices: allowedDevices,
AutoCreatedDevices: autoCreatedDevices,
CapAdd: c.hostConfig.CapAdd,
CapDrop: c.hostConfig.CapDrop,
}
c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
c.command.Env = env
@ -245,6 +279,7 @@ func (container *Container) Start() (err error) {
if container.State.IsRunning() {
return nil
}
// if we encounter and error during start we need to ensure that any other
// setup has been cleaned up properly
defer func() {
@ -280,9 +315,6 @@ func (container *Container) Start() (err error) {
if err := setupMountsForContainer(container); err != nil {
return err
}
if err := container.startLoggingToDisk(); err != nil {
return err
}
return container.waitForStart()
}
@ -463,40 +495,8 @@ func (container *Container) releaseNetwork() {
container.NetworkSettings = &NetworkSettings{}
}
func (container *Container) monitor(callback execdriver.StartCallback) error {
var (
err error
exitCode int
)
pipes := execdriver.NewPipes(container.stdin, container.stdout, container.stderr, container.Config.OpenStdin)
exitCode, err = container.daemon.Run(container, pipes, callback)
if err != nil {
utils.Errorf("Error running container: %s", err)
}
container.State.SetStopped(exitCode)
// Cleanup
container.cleanup()
// Re-create a brand new stdin pipe once the container exited
if container.Config.OpenStdin {
container.stdin, container.stdinPipe = io.Pipe()
}
if container.daemon != nil && container.daemon.srv != nil {
container.daemon.srv.LogEvent("die", container.ID, container.daemon.repositories.ImageName(container.Image))
}
if container.daemon != nil && container.daemon.srv != nil && container.daemon.srv.IsRunning() {
// FIXME: here is race condition between two RUN instructions in Dockerfile
// because they share same runconfig and change image. Must be fixed
// in server/buildfile.go
if err := container.ToDisk(); err != nil {
utils.Errorf("Error dumping container %s state to disk: %s\n", container.ID, err)
}
}
return err
}
// cleanup releases any network resources allocated to the container along with any rules
// around how containers are linked together. It also unmounts the container's root filesystem.
func (container *Container) cleanup() {
container.releaseNetwork()
@ -506,30 +506,14 @@ func (container *Container) cleanup() {
link.Disable()
}
}
if container.Config.OpenStdin {
if err := container.stdin.Close(); err != nil {
utils.Errorf("%s: Error close stdin: %s", container.ID, err)
}
}
if err := container.stdout.CloseWriters(); err != nil {
utils.Errorf("%s: Error close stdout: %s", container.ID, err)
}
if err := container.stderr.CloseWriters(); err != nil {
utils.Errorf("%s: Error close stderr: %s", container.ID, err)
}
if container.command != nil && container.command.Terminal != nil {
if err := container.command.Terminal.Close(); err != nil {
utils.Errorf("%s: Error closing terminal: %s", container.ID, err)
}
}
if err := container.Unmount(); err != nil {
log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
log.Errorf("%v: Failed to umount filesystem: %v", container.ID, err)
}
}
func (container *Container) KillSig(sig int) error {
utils.Debugf("Sending %d to %s", sig, container.ID)
log.Debugf("Sending %d to %s", sig, container.ID)
container.Lock()
defer container.Unlock()
@ -541,6 +525,18 @@ func (container *Container) KillSig(sig int) error {
if !container.State.IsRunning() {
return nil
}
// signal to the monitor that it should not restart the container
// after we send the kill signal
container.monitor.ExitOnNext()
// if the container is currently restarting we do not need to send the signal
// to the process. Telling the monitor that it should exit on it's next event
// loop is enough
if container.State.IsRestarting() {
return nil
}
return container.daemon.Kill(container, sig)
}
@ -578,7 +574,7 @@ func (container *Container) Kill() error {
if _, err := container.State.WaitStop(10 * time.Second); err != nil {
// Ensure that we don't kill ourselves
if pid := container.State.GetPid(); pid != 0 {
log.Printf("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", utils.TruncateID(container.ID))
log.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", utils.TruncateID(container.ID))
if err := syscall.Kill(pid, 9); err != nil {
return err
}
@ -596,7 +592,7 @@ func (container *Container) Stop(seconds int) error {
// 1. Send a SIGTERM
if err := container.KillSig(15); err != nil {
log.Print("Failed to send SIGTERM to the process, force killing")
log.Infof("Failed to send SIGTERM to the process, force killing")
if err := container.KillSig(9); err != nil {
return err
}
@ -604,7 +600,7 @@ func (container *Container) Stop(seconds int) error {
// 2. Wait for the process to exit on its own
if _, err := container.State.WaitStop(time.Duration(seconds) * time.Second); err != nil {
log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
log.Infof("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
// 3. If it doesn't, then send SIGKILL
if err := container.Kill(); err != nil {
container.State.WaitStop(-1 * time.Second)
@ -733,7 +729,7 @@ func (container *Container) GetSize() (int64, int64) {
)
if err := container.Mount(); err != nil {
utils.Errorf("Warning: failed to compute size of container rootfs %s: %s", container.ID, err)
log.Errorf("Warning: failed to compute size of container rootfs %s: %s", container.ID, err)
return sizeRw, sizeRootfs
}
defer container.Unmount()
@ -741,7 +737,7 @@ func (container *Container) GetSize() (int64, int64) {
if differ, ok := container.daemon.driver.(graphdriver.Differ); ok {
sizeRw, err = differ.DiffSize(container.ID)
if err != nil {
utils.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
log.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
// FIXME: GetSize should return an error. Not changing it now in case
// there is a side-effect.
sizeRw = -1
@ -838,7 +834,7 @@ func (container *Container) DisableLink(name string) {
if link, exists := container.activeLinks[name]; exists {
link.Disable()
} else {
utils.Debugf("Could not find active link for %s", name)
log.Debugf("Could not find active link for %s", name)
}
}
}
@ -853,18 +849,16 @@ func (container *Container) setupContainerDns() error {
daemon = container.daemon
)
if config.NetworkMode == "host" {
container.ResolvConfPath = "/etc/resolv.conf"
return nil
}
resolvConf, err := resolvconf.Get()
if err != nil {
return err
}
container.ResolvConfPath, err = container.getRootResourcePath("resolv.conf")
if err != nil {
return err
}
// If custom dns exists, then create a resolv.conf for the container
if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 {
if config.NetworkMode != "host" && (len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0) {
var (
dns = resolvconf.GetNameservers(resolvConf)
dnsSearch = resolvconf.GetSearchDomains(resolvConf)
@ -879,18 +873,9 @@ func (container *Container) setupContainerDns() error {
} else if len(daemon.config.DnsSearch) > 0 {
dnsSearch = daemon.config.DnsSearch
}
resolvConfPath, err := container.getRootResourcePath("resolv.conf")
if err != nil {
return err
}
container.ResolvConfPath = resolvConfPath
return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch)
} else {
container.ResolvConfPath = "/etc/resolv.conf"
}
return nil
return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644)
}
func (container *Container) initializeNetworking() error {
@ -950,15 +935,15 @@ func (container *Container) initializeNetworking() error {
// Make sure the config is compatible with the current kernel
func (container *Container) verifyDaemonSettings() {
if container.Config.Memory > 0 && !container.daemon.sysInfo.MemoryLimit {
log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
log.Infof("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
container.Config.Memory = 0
}
if container.Config.Memory > 0 && !container.daemon.sysInfo.SwapLimit {
log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
log.Infof("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.")
container.Config.MemorySwap = -1
}
if container.daemon.sysInfo.IPv4ForwardingDisabled {
log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
log.Infof("WARNING: IPv4 forwarding is disabled. Networking will not work")
}
}
@ -1019,9 +1004,12 @@ func (container *Container) setupLinkedContainers() ([]string, error) {
func (container *Container) createDaemonEnvironment(linkedEnv []string) []string {
// Setup environment
env := []string{
"HOME=/",
"PATH=" + DefaultPathEnv,
"HOSTNAME=" + container.Config.Hostname,
// Note: we don't set HOME here because it'll get autoset intelligently
// based on the value of USER inside dockerinit, but only if it isn't
// set already (ie, that can be overridden by setting HOME via -e or ENV
// in a Dockerfile).
}
if container.Config.Tty {
env = append(env, "TERM=xterm")
@ -1080,38 +1068,16 @@ func (container *Container) startLoggingToDisk() error {
}
func (container *Container) waitForStart() error {
callback := func(command *execdriver.Command) {
if command.Tty {
// The callback is called after the process Start()
// so we are in the parent process. In TTY mode, stdin/out/err is the PtySlace
// which we close here.
if c, ok := command.Stdout.(io.Closer); ok {
c.Close()
}
}
container.State.SetRunning(command.Pid())
if err := container.ToDisk(); err != nil {
utils.Debugf("%s", err)
}
}
container.monitor = newContainerMonitor(container, container.hostConfig.RestartPolicy)
// We use a callback here instead of a goroutine and an chan for
// syncronization purposes
cErr := utils.Go(func() error { return container.monitor(callback) })
waitStart := make(chan struct{})
go func() {
container.State.WaitRunning(-1 * time.Second)
close(waitStart)
}()
// Start should not return until the process is actually running
// block until we either receive an error from the initial start of the container's
// process or until the process is running in the container
select {
case <-waitStart:
case err := <-cErr:
case <-container.monitor.startSignal:
case err := <-utils.Go(container.monitor.Start):
return err
}
return nil
}

Просмотреть файл

@ -1,7 +1,7 @@
package daemon
import (
"github.com/dotcloud/docker/nat"
"github.com/docker/docker/nat"
"testing"
)
@ -89,6 +89,41 @@ func TestParseNetworkOptsPublic(t *testing.T) {
}
}
func TestParseNetworkOptsPublicNoPort(t *testing.T) {
ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100"})
if err == nil {
t.Logf("Expected error Invalid containerPort")
t.Fail()
}
if ports != nil {
t.Logf("Expected nil got %s", ports)
t.Fail()
}
if bindings != nil {
t.Logf("Expected nil got %s", bindings)
t.Fail()
}
}
func TestParseNetworkOptsNegativePorts(t *testing.T) {
ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100:-1:-1"})
if err == nil {
t.Fail()
}
t.Logf("%v", len(ports))
t.Logf("%v", bindings)
if len(ports) != 0 {
t.Logf("Expected nil got %s", len(ports))
t.Fail()
}
if len(bindings) != 0 {
t.Logf("Expected 0 got %s", len(bindings))
t.Fail()
}
}
func TestParseNetworkOptsUdp(t *testing.T) {
ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::6000/udp"})
if err != nil {

33
daemon/copy.go Normal file
Просмотреть файл

@ -0,0 +1,33 @@
package daemon
import (
"io"
"github.com/docker/docker/engine"
)
func (daemon *Daemon) ContainerCopy(job *engine.Job) engine.Status {
if len(job.Args) != 2 {
return job.Errorf("Usage: %s CONTAINER RESOURCE\n", job.Name)
}
var (
name = job.Args[0]
resource = job.Args[1]
)
if container := daemon.Get(name); container != nil {
data, err := container.Copy(resource)
if err != nil {
return job.Error(err)
}
defer data.Close()
if _, err := io.Copy(job.Stdout, data); err != nil {
return job.Error(err)
}
return engine.StatusOK
}
return job.Errorf("No such container: %s", name)
}

86
daemon/create.go Normal file
Просмотреть файл

@ -0,0 +1,86 @@
package daemon
import (
"github.com/docker/docker/engine"
"github.com/docker/docker/graph"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/runconfig"
)
func (daemon *Daemon) ContainerCreate(job *engine.Job) engine.Status {
var name string
if len(job.Args) == 1 {
name = job.Args[0]
} else if len(job.Args) > 1 {
return job.Errorf("Usage: %s", job.Name)
}
config := runconfig.ContainerConfigFromJob(job)
if config.Memory != 0 && config.Memory < 524288 {
return job.Errorf("Minimum memory limit allowed is 512k")
}
if config.Memory > 0 && !daemon.SystemConfig().MemoryLimit {
job.Errorf("Your kernel does not support memory limit capabilities. Limitation discarded.\n")
config.Memory = 0
}
if config.Memory > 0 && !daemon.SystemConfig().SwapLimit {
job.Errorf("Your kernel does not support swap limit capabilities. Limitation discarded.\n")
config.MemorySwap = -1
}
container, buildWarnings, err := daemon.Create(config, name)
if err != nil {
if daemon.Graph().IsNotExist(err) {
_, tag := parsers.ParseRepositoryTag(config.Image)
if tag == "" {
tag = graph.DEFAULTTAG
}
return job.Errorf("No such image: %s (tag: %s)", config.Image, tag)
}
return job.Error(err)
}
if !container.Config.NetworkDisabled && daemon.SystemConfig().IPv4ForwardingDisabled {
job.Errorf("IPv4 forwarding is disabled.\n")
}
container.LogEvent("create")
// FIXME: this is necessary because daemon.Create might return a nil container
// with a non-nil error. This should not happen! Once it's fixed we
// can remove this workaround.
if container != nil {
job.Printf("%s\n", container.ID)
}
for _, warning := range buildWarnings {
job.Errorf("%s\n", warning)
}
return engine.StatusOK
}
// Create creates a new container from the given configuration with a given name.
func (daemon *Daemon) Create(config *runconfig.Config, name string) (*Container, []string, error) {
var (
container *Container
warnings []string
)
img, err := daemon.repositories.LookupImage(config.Image)
if err != nil {
return nil, nil, err
}
if err := img.CheckDepth(); err != nil {
return nil, nil, err
}
if warnings, err = daemon.mergeAndVerifyConfig(config, img); err != nil {
return nil, nil, err
}
if container, err = daemon.newContainer(name, config, img); err != nil {
return nil, nil, err
}
if err := daemon.createRootfs(container, img); err != nil {
return nil, nil, err
}
if err := container.ToDisk(); err != nil {
return nil, nil, err
}
if err := daemon.Register(container); err != nil {
return nil, nil, err
}
return container, warnings, nil
}

Просмотреть файл

@ -4,42 +4,40 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
"regexp"
"runtime"
"strings"
"sync"
"time"
"github.com/docker/libcontainer/label"
"github.com/docker/libcontainer/selinux"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/daemon/execdriver/execdrivers"
"github.com/dotcloud/docker/daemon/execdriver/lxc"
"github.com/dotcloud/docker/daemon/graphdriver"
_ "github.com/dotcloud/docker/daemon/graphdriver/vfs"
_ "github.com/dotcloud/docker/daemon/networkdriver/bridge"
"github.com/dotcloud/docker/daemon/networkdriver/portallocator"
"github.com/dotcloud/docker/daemonconfig"
"github.com/dotcloud/docker/dockerversion"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/graph"
"github.com/dotcloud/docker/image"
"github.com/dotcloud/docker/pkg/graphdb"
"github.com/dotcloud/docker/pkg/namesgenerator"
"github.com/dotcloud/docker/pkg/networkfs/resolvconf"
"github.com/dotcloud/docker/pkg/sysinfo"
"github.com/dotcloud/docker/pkg/truncindex"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
)
// Set the max depth to the aufs default that most
// kernels are compiled with
// For more information see: http://sourceforge.net/p/aufs/aufs3-standalone/ci/aufs3.12/tree/config.mk
const MaxImageDepth = 127
"github.com/docker/docker/archive"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/daemon/execdriver/execdrivers"
"github.com/docker/docker/daemon/execdriver/lxc"
"github.com/docker/docker/daemon/graphdriver"
_ "github.com/docker/docker/daemon/graphdriver/vfs"
_ "github.com/docker/docker/daemon/networkdriver/bridge"
"github.com/docker/docker/daemon/networkdriver/portallocator"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/engine"
"github.com/docker/docker/graph"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/broadcastwriter"
"github.com/docker/docker/pkg/graphdb"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/namesgenerator"
"github.com/docker/docker/pkg/networkfs/resolvconf"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/pkg/parsers/kernel"
"github.com/docker/docker/pkg/sysinfo"
"github.com/docker/docker/pkg/truncindex"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/utils"
)
var (
DefaultDns = []string{"8.8.8.8", "8.8.4.4"}
@ -91,38 +89,65 @@ type Daemon struct {
idIndex *truncindex.TruncIndex
sysInfo *sysinfo.SysInfo
volumes *graph.Graph
srv Server
eng *engine.Engine
config *daemonconfig.Config
config *Config
containerGraph *graphdb.Database
driver graphdriver.Driver
execDriver execdriver.Driver
Sockets []string
}
// Install installs daemon capabilities to eng.
func (daemon *Daemon) Install(eng *engine.Engine) error {
return eng.Register("container_inspect", daemon.ContainerInspect)
}
// List returns an array of all containers registered in the daemon.
func (daemon *Daemon) List() []*Container {
return daemon.containers.List()
// FIXME: rename "delete" to "rm" for consistency with the CLI command
// FIXME: rename ContainerDestroy to ContainerRm for consistency with the CLI command
// FIXME: remove ImageDelete's dependency on Daemon, then move to graph/
for name, method := range map[string]engine.Handler{
"attach": daemon.ContainerAttach,
"build": daemon.CmdBuild,
"commit": daemon.ContainerCommit,
"container_changes": daemon.ContainerChanges,
"container_copy": daemon.ContainerCopy,
"container_inspect": daemon.ContainerInspect,
"containers": daemon.Containers,
"create": daemon.ContainerCreate,
"delete": daemon.ContainerDestroy,
"export": daemon.ContainerExport,
"info": daemon.CmdInfo,
"kill": daemon.ContainerKill,
"logs": daemon.ContainerLogs,
"pause": daemon.ContainerPause,
"resize": daemon.ContainerResize,
"restart": daemon.ContainerRestart,
"start": daemon.ContainerStart,
"stop": daemon.ContainerStop,
"top": daemon.ContainerTop,
"unpause": daemon.ContainerUnpause,
"wait": daemon.ContainerWait,
"image_delete": daemon.ImageDelete, // FIXME: see above
} {
if err := eng.Register(name, method); err != nil {
return err
}
}
if err := daemon.Repositories().Install(eng); err != nil {
return err
}
// FIXME: this hack is necessary for legacy integration tests to access
// the daemon object.
eng.Hack_SetGlobalVar("httpapi.daemon", daemon)
return nil
}
// Get looks for a container by the specified ID or name, and returns it.
// If the container is not found, or if an error occurs, nil is returned.
func (daemon *Daemon) Get(name string) *Container {
if id, err := daemon.idIndex.Get(name); err == nil {
return daemon.containers.Get(id)
}
if c, _ := daemon.GetByName(name); c != nil {
return c
}
id, err := daemon.idIndex.Get(name)
if err != nil {
return nil
}
return daemon.containers.Get(id)
return nil
}
// Exists returns a true if a container of the specified ID or name exists,
@ -142,20 +167,24 @@ func (daemon *Daemon) load(id string) (*Container, error) {
if err := container.FromDisk(); err != nil {
return nil, err
}
if container.ID != id {
return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
}
container.readHostConfig()
return container, nil
}
// Register makes a container object usable by the daemon as <container.ID>
// This is a wrapper for register
func (daemon *Daemon) Register(container *Container) error {
return daemon.register(container, true, nil)
return daemon.register(container, true)
}
// register makes a container object usable by the daemon as <container.ID>
func (daemon *Daemon) register(container *Container, updateSuffixarray bool, containersToStart *[]*Container) error {
func (daemon *Daemon) register(container *Container, updateSuffixarray bool) error {
if container.daemon != nil || daemon.Exists(container.ID) {
return fmt.Errorf("Container is already loaded")
}
@ -169,8 +198,8 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool, con
container.daemon = daemon
// Attach to stdout and stderr
container.stderr = utils.NewWriteBroadcaster()
container.stdout = utils.NewWriteBroadcaster()
container.stderr = broadcastwriter.New()
container.stdout = broadcastwriter.New()
// Attach to stdin
if container.Config.OpenStdin {
container.stdin, container.stdinPipe = io.Pipe()
@ -188,7 +217,7 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool, con
// if so, then we need to restart monitor and init a new lock
// If the container is supposed to be running, make sure of it
if container.State.IsRunning() {
utils.Debugf("killing old running container %s", container.ID)
log.Debugf("killing old running container %s", container.ID)
existingPid := container.State.Pid
container.State.SetStopped(0)
@ -205,36 +234,28 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool, con
var err error
cmd.Process, err = os.FindProcess(existingPid)
if err != nil {
utils.Debugf("cannot find existing process for %d", existingPid)
log.Debugf("cannot find existing process for %d", existingPid)
}
daemon.execDriver.Terminate(cmd)
}
if err := container.Unmount(); err != nil {
utils.Debugf("unmount error %s", err)
log.Debugf("unmount error %s", err)
}
if err := container.ToDisk(); err != nil {
utils.Debugf("saving stopped state to disk %s", err)
log.Debugf("saving stopped state to disk %s", err)
}
info := daemon.execDriver.Info(container.ID)
if !info.IsRunning() {
utils.Debugf("Container %s was supposed to be running but is not.", container.ID)
log.Debugf("Container %s was supposed to be running but is not.", container.ID)
utils.Debugf("Marking as stopped")
log.Debugf("Marking as stopped")
container.State.SetStopped(-127)
if err := container.ToDisk(); err != nil {
return err
}
if daemon.config.AutoRestart {
utils.Debugf("Marking as restarting")
if containersToStart != nil {
*containersToStart = append(*containersToStart, container)
}
}
}
}
return nil
@ -249,13 +270,13 @@ func (daemon *Daemon) ensureName(container *Container) error {
container.Name = name
if err := container.ToDisk(); err != nil {
utils.Debugf("Error saving container name %s", err)
log.Debugf("Error saving container name %s", err)
}
}
return nil
}
func (daemon *Daemon) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error {
func (daemon *Daemon) LogToDisk(src *broadcastwriter.BroadcastWriter, dst, stream string) error {
log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
if err != nil {
return err
@ -264,56 +285,15 @@ func (daemon *Daemon) LogToDisk(src *utils.WriteBroadcaster, dst, stream string)
return nil
}
// Destroy unregisters a container from the daemon and cleanly removes its contents from the filesystem.
func (daemon *Daemon) Destroy(container *Container) error {
if container == nil {
return fmt.Errorf("The given container is <nil>")
}
element := daemon.containers.Get(container.ID)
if element == nil {
return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
}
if err := container.Stop(3); err != nil {
return err
}
// Deregister the container before removing its directory, to avoid race conditions
daemon.idIndex.Delete(container.ID)
daemon.containers.Delete(container.ID)
if _, err := daemon.containerGraph.Purge(container.ID); err != nil {
utils.Debugf("Unable to remove container from link graph: %s", err)
}
if err := daemon.driver.Remove(container.ID); err != nil {
return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", daemon.driver, container.ID, err)
}
initID := fmt.Sprintf("%s-init", container.ID)
if err := daemon.driver.Remove(initID); err != nil {
return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", daemon.driver, initID, err)
}
if err := os.RemoveAll(container.root); err != nil {
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
}
selinux.FreeLxcContexts(container.ProcessLabel)
return nil
}
func (daemon *Daemon) restore() error {
var (
debug = (os.Getenv("DEBUG") != "" || os.Getenv("TEST") != "")
containers = make(map[string]*Container)
currentDriver = daemon.driver.String()
containersToStart = []*Container{}
debug = (os.Getenv("DEBUG") != "" || os.Getenv("TEST") != "")
containers = make(map[string]*Container)
currentDriver = daemon.driver.String()
)
if !debug {
fmt.Printf("Loading containers: ")
log.Infof("Loading containers: ")
}
dir, err := ioutil.ReadDir(daemon.repository)
if err != nil {
@ -327,29 +307,38 @@ func (daemon *Daemon) restore() error {
fmt.Print(".")
}
if err != nil {
utils.Errorf("Failed to load container %v: %v", id, err)
log.Errorf("Failed to load container %v: %v", id, err)
continue
}
// Ignore the container if it does not support the current driver being used by the graph
if container.Driver == "" && currentDriver == "aufs" || container.Driver == currentDriver {
utils.Debugf("Loaded container %v", container.ID)
if (container.Driver == "" && currentDriver == "aufs") || container.Driver == currentDriver {
log.Debugf("Loaded container %v", container.ID)
containers[container.ID] = container
} else {
utils.Debugf("Cannot load container %s because it was created with another graph driver.", container.ID)
log.Debugf("Cannot load container %s because it was created with another graph driver.", container.ID)
}
}
registeredContainers := []*Container{}
if entities := daemon.containerGraph.List("/", -1); entities != nil {
for _, p := range entities.Paths() {
if !debug {
fmt.Print(".")
}
e := entities[p]
if container, ok := containers[e.ID()]; ok {
if err := daemon.register(container, false, &containersToStart); err != nil {
utils.Debugf("Failed to register container %s: %s", container.ID, err)
if err := daemon.register(container, false); err != nil {
log.Debugf("Failed to register container %s: %s", container.ID, err)
}
registeredContainers = append(registeredContainers, container)
// delete from the map so that a new name is not automatically generated
delete(containers, e.ID())
}
}
@ -360,72 +349,40 @@ func (daemon *Daemon) restore() error {
// Try to set the default name for a container if it exists prior to links
container.Name, err = daemon.generateNewName(container.ID)
if err != nil {
utils.Debugf("Setting default id - %s", err)
log.Debugf("Setting default id - %s", err)
}
if err := daemon.register(container, false, &containersToStart); err != nil {
utils.Debugf("Failed to register container %s: %s", container.ID, err)
if err := daemon.register(container, false); err != nil {
log.Debugf("Failed to register container %s: %s", container.ID, err)
}
registeredContainers = append(registeredContainers, container)
}
for _, container := range containersToStart {
utils.Debugf("Starting container %d", container.ID)
if err := container.Start(); err != nil {
utils.Debugf("Failed to start container %s: %s", container.ID, err)
// check the restart policy on the containers and restart any container with
// the restart policy of "always"
if daemon.config.AutoRestart {
log.Debugf("Restarting containers...")
for _, container := range registeredContainers {
if container.hostConfig.RestartPolicy.Name == "always" ||
(container.hostConfig.RestartPolicy.Name == "on-failure" && container.State.ExitCode != 0) {
log.Debugf("Starting container %s", container.ID)
if err := container.Start(); err != nil {
log.Debugf("Failed to start container %s: %s", container.ID, err)
}
}
}
}
if !debug {
fmt.Printf(": done.\n")
log.Infof(": done.")
}
return nil
}
// Create creates a new container from the given configuration with a given name.
func (daemon *Daemon) Create(config *runconfig.Config, name string) (*Container, []string, error) {
var (
container *Container
warnings []string
)
img, err := daemon.repositories.LookupImage(config.Image)
if err != nil {
return nil, nil, err
}
if err := daemon.checkImageDepth(img); err != nil {
return nil, nil, err
}
if warnings, err = daemon.mergeAndVerifyConfig(config, img); err != nil {
return nil, nil, err
}
if container, err = daemon.newContainer(name, config, img); err != nil {
return nil, nil, err
}
if err := daemon.createRootfs(container, img); err != nil {
return nil, nil, err
}
if err := container.ToDisk(); err != nil {
return nil, nil, err
}
if err := daemon.Register(container); err != nil {
return nil, nil, err
}
return container, warnings, nil
}
func (daemon *Daemon) checkImageDepth(img *image.Image) error {
// We add 2 layers to the depth because the container's rw and
// init layer add to the restriction
depth, err := img.Depth()
if err != nil {
return err
}
if depth+2 >= MaxImageDepth {
return fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth)
}
return nil
}
func (daemon *Daemon) checkDeprecatedExpose(config *runconfig.Config) bool {
if config != nil {
if config.PortSpecs != nil {
@ -618,51 +575,6 @@ func (daemon *Daemon) createRootfs(container *Container, img *image.Image) error
return nil
}
// Commit creates a new filesystem image from the current state of a container.
// The image can optionally be tagged into a repository
func (daemon *Daemon) Commit(container *Container, repository, tag, comment, author string, pause bool, config *runconfig.Config) (*image.Image, error) {
if pause {
container.Pause()
defer container.Unpause()
}
if err := container.Mount(); err != nil {
return nil, err
}
defer container.Unmount()
rwTar, err := container.ExportRw()
if err != nil {
return nil, err
}
defer rwTar.Close()
// Create a new image from the container's base layers + a new layer from container changes
var (
containerID, containerImage string
containerConfig *runconfig.Config
)
if container != nil {
containerID = container.ID
containerImage = container.Image
containerConfig = container.Config
}
img, err := daemon.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
if err != nil {
return nil, err
}
// Register the image if needed
if repository != "" {
if err := daemon.repositories.Set(repository, tag, img.ID, true); err != nil {
return img, err
}
}
return img, nil
}
func GetFullContainerName(name string) (string, error) {
if name == "" {
return "", fmt.Errorf("Container name cannot be empty")
@ -723,7 +635,7 @@ func (daemon *Daemon) RegisterLink(parent, child *Container, alias string) error
func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.HostConfig) error {
if hostConfig != nil && hostConfig.Links != nil {
for _, l := range hostConfig.Links {
parts, err := utils.PartParser("name:alias", l)
parts, err := parsers.PartParser("name:alias", l)
if err != nil {
return err
}
@ -750,7 +662,7 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.
}
// FIXME: harmonize with NewGraph()
func NewDaemon(config *daemonconfig.Config, eng *engine.Engine) (*Daemon, error) {
func NewDaemon(config *Config, eng *engine.Engine) (*Daemon, error) {
daemon, err := NewDaemonFromDirectory(config, eng)
if err != nil {
return nil, err
@ -758,11 +670,71 @@ func NewDaemon(config *daemonconfig.Config, eng *engine.Engine) (*Daemon, error)
return daemon, nil
}
func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*Daemon, error) {
if !config.EnableSelinuxSupport {
selinux.SetDisabled()
func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error) {
// Apply configuration defaults
if config.Mtu == 0 {
// FIXME: GetDefaultNetwork Mtu doesn't need to be public anymore
config.Mtu = GetDefaultNetworkMtu()
}
// Check for mutually incompatible config options
if config.BridgeIface != "" && config.BridgeIP != "" {
return nil, fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one.")
}
if !config.EnableIptables && !config.InterContainerCommunication {
return nil, fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.")
}
// FIXME: DisableNetworkBidge doesn't need to be public anymore
config.DisableNetwork = config.BridgeIface == DisableNetworkBridge
// Claim the pidfile first, to avoid any and all unexpected race conditions.
// Some of the init doesn't need a pidfile lock - but let's not try to be smart.
if config.Pidfile != "" {
if err := utils.CreatePidFile(config.Pidfile); err != nil {
return nil, err
}
eng.OnShutdown(func() {
// Always release the pidfile last, just in case
utils.RemovePidFile(config.Pidfile)
})
}
// Check that the system is supported and we have sufficient privileges
// FIXME: return errors instead of calling Fatal
if runtime.GOOS != "linux" {
log.Fatalf("The Docker daemon is only supported on linux")
}
if os.Geteuid() != 0 {
log.Fatalf("The Docker daemon needs to be run as root")
}
if err := checkKernelAndArch(); err != nil {
log.Fatalf(err.Error())
}
// set up the TempDir to use a canonical path
tmp, err := utils.TempDir(config.Root)
if err != nil {
log.Fatalf("Unable to get the TempDir under %s: %s", config.Root, err)
}
realTmp, err := utils.ReadSymlinkedDirectory(tmp)
if err != nil {
log.Fatalf("Unable to get the full path to the TempDir (%s): %s", tmp, err)
}
os.Setenv("TMPDIR", realTmp)
if !config.EnableSelinuxSupport {
selinuxSetDisabled()
}
// get the canonical path to the Docker root directory
var realRoot string
if _, err := os.Stat(config.Root); err != nil && os.IsNotExist(err) {
realRoot = config.Root
} else {
realRoot, err = utils.ReadSymlinkedDirectory(config.Root)
if err != nil {
log.Fatalf("Unable to get the full path to root (%s): %s", config.Root, err)
}
}
config.Root = realRoot
// Create the root directory if it doesn't exists
if err := os.MkdirAll(config.Root, 0700); err != nil && !os.IsExist(err) {
return nil, err
@ -776,7 +748,12 @@ func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*D
if err != nil {
return nil, err
}
utils.Debugf("Using graph driver %s", driver)
log.Debugf("Using graph driver %s", driver)
// As Docker on btrfs and SELinux are incompatible at present, error on both being enabled
if config.EnableSelinuxSupport && driver.String() == "btrfs" {
return nil, fmt.Errorf("SELinux is not supported with the BTRFS graph driver!")
}
daemonRepo := path.Join(config.Root, "containers")
@ -789,7 +766,7 @@ func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*D
return nil, err
}
utils.Debugf("Creating images graph")
log.Debugf("Creating images graph")
g, err := graph.NewGraph(path.Join(config.Root, "graph"), driver)
if err != nil {
return nil, err
@ -801,12 +778,12 @@ func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*D
if err != nil {
return nil, err
}
utils.Debugf("Creating volumes graph")
log.Debugf("Creating volumes graph")
volumes, err := graph.NewGraph(path.Join(config.Root, "volumes"), volumesDriver)
if err != nil {
return nil, err
}
utils.Debugf("Creating repository list")
log.Debugf("Creating repository list")
repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), g)
if err != nil {
return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
@ -873,34 +850,52 @@ func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*D
sysInitPath: sysInitPath,
execDriver: ed,
eng: eng,
Sockets: config.Sockets,
}
if err := daemon.checkLocaldns(); err != nil {
return nil, err
}
if err := daemon.restore(); err != nil {
return nil, err
}
// Setup shutdown handlers
// FIXME: can these shutdown handlers be registered closer to their source?
eng.OnShutdown(func() {
// FIXME: if these cleanup steps can be called concurrently, register
// them as separate handlers to speed up total shutdown time
// FIXME: use engine logging instead of log.Errorf
if err := daemon.shutdown(); err != nil {
log.Errorf("daemon.shutdown(): %s", err)
}
if err := portallocator.ReleaseAll(); err != nil {
log.Errorf("portallocator.ReleaseAll(): %s", err)
}
if err := daemon.driver.Cleanup(); err != nil {
log.Errorf("daemon.driver.Cleanup(): %s", err.Error())
}
if err := daemon.containerGraph.Close(); err != nil {
log.Errorf("daemon.containerGraph.Close(): %s", err.Error())
}
})
return daemon, nil
}
func (daemon *Daemon) shutdown() error {
group := sync.WaitGroup{}
utils.Debugf("starting clean shutdown of all containers...")
log.Debugf("starting clean shutdown of all containers...")
for _, container := range daemon.List() {
c := container
if c.State.IsRunning() {
utils.Debugf("stopping %s", c.ID)
log.Debugf("stopping %s", c.ID)
group.Add(1)
go func() {
defer group.Done()
if err := c.KillSig(15); err != nil {
utils.Debugf("kill 15 error for %s - %s", c.ID, err)
log.Debugf("kill 15 error for %s - %s", c.ID, err)
}
c.State.WaitStop(-1 * time.Second)
utils.Debugf("container stopped %s", c.ID)
log.Debugf("container stopped %s", c.ID)
}()
}
}
@ -909,30 +904,6 @@ func (daemon *Daemon) shutdown() error {
return nil
}
func (daemon *Daemon) Close() error {
errorsStrings := []string{}
if err := daemon.shutdown(); err != nil {
utils.Errorf("daemon.shutdown(): %s", err)
errorsStrings = append(errorsStrings, err.Error())
}
if err := portallocator.ReleaseAll(); err != nil {
utils.Errorf("portallocator.ReleaseAll(): %s", err)
errorsStrings = append(errorsStrings, err.Error())
}
if err := daemon.driver.Cleanup(); err != nil {
utils.Errorf("daemon.driver.Cleanup(): %s", err.Error())
errorsStrings = append(errorsStrings, err.Error())
}
if err := daemon.containerGraph.Close(); err != nil {
utils.Errorf("daemon.containerGraph.Close(): %s", err.Error())
errorsStrings = append(errorsStrings, err.Error())
}
if len(errorsStrings) > 0 {
return fmt.Errorf("%s", strings.Join(errorsStrings, ", "))
}
return nil
}
func (daemon *Daemon) Mount(container *Container) error {
dir, err := daemon.driver.Get(container.ID, container.GetMountLabel())
if err != nil {
@ -1023,6 +994,8 @@ func (daemon *Daemon) Kill(c *Container, sig int) error {
// from the content root, including images, volumes and
// container filesystems.
// Again: this will remove your entire docker daemon!
// FIXME: this is deprecated, and only used in legacy
// tests. Please remove.
func (daemon *Daemon) Nuke() error {
var wg sync.WaitGroup
for _, container := range daemon.List() {
@ -1033,7 +1006,6 @@ func (daemon *Daemon) Nuke() error {
}(container)
}
wg.Wait()
daemon.Close()
return os.RemoveAll(daemon.config.Root)
}
@ -1050,7 +1022,7 @@ func (daemon *Daemon) Repositories() *graph.TagStore {
return daemon.repositories
}
func (daemon *Daemon) Config() *daemonconfig.Config {
func (daemon *Daemon) Config() *Config {
return daemon.config
}
@ -1078,18 +1050,70 @@ func (daemon *Daemon) ContainerGraph() *graphdb.Database {
return daemon.containerGraph
}
func (daemon *Daemon) SetServer(server Server) {
daemon.srv = server
}
func (daemon *Daemon) checkLocaldns() error {
resolvConf, err := resolvconf.Get()
if err != nil {
return err
}
if len(daemon.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
log.Printf("Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v\n", DefaultDns)
log.Infof("Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v", DefaultDns)
daemon.config.Dns = DefaultDns
}
return nil
}
func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*image.Image, error) {
// Retrieve all images
images, err := daemon.Graph().Map()
if err != nil {
return nil, err
}
// Store the tree in a map of map (map[parentId][childId])
imageMap := make(map[string]map[string]struct{})
for _, img := range images {
if _, exists := imageMap[img.Parent]; !exists {
imageMap[img.Parent] = make(map[string]struct{})
}
imageMap[img.Parent][img.ID] = struct{}{}
}
// Loop on the children of the given image and check the config
var match *image.Image
for elem := range imageMap[imgID] {
img, err := daemon.Graph().Get(elem)
if err != nil {
return nil, err
}
if runconfig.Compare(&img.ContainerConfig, config) {
if match == nil || match.Created.Before(img.Created) {
match = img
}
}
}
return match, nil
}
func checkKernelAndArch() error {
// Check for unsupported architectures
if runtime.GOARCH != "amd64" {
return fmt.Errorf("The Docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
}
// Check for unsupported kernel versions
// FIXME: it would be cleaner to not test for specific versions, but rather
// test for specific functionalities.
// Unfortunately we can't test for the feature "does not cause a kernel panic"
// without actually causing a kernel panic, so we need this workaround until
// the circumstances of pre-3.8 crashes are clearer.
// For details see http://github.com/docker/docker/issues/407
if k, err := kernel.GetKernelVersion(); err != nil {
log.Infof("WARNING: %s", err)
} else {
if kernel.CompareKernelVersion(k, &kernel.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 {
if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" {
log.Infof("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String())
}
}
}
return nil
}

Просмотреть файл

@ -3,17 +3,17 @@
package daemon
import (
"github.com/dotcloud/docker/daemon/graphdriver"
"github.com/dotcloud/docker/daemon/graphdriver/aufs"
"github.com/dotcloud/docker/graph"
"github.com/dotcloud/docker/utils"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/daemon/graphdriver/aufs"
"github.com/docker/docker/graph"
"github.com/docker/docker/pkg/log"
)
// Given the graphdriver ad, if it is aufs, then migrate it.
// If aufs driver is not built, this func is a noop.
func migrateIfAufs(driver graphdriver.Driver, root string) error {
if ad, ok := driver.(*aufs.Driver); ok {
utils.Debugf("Migrating existing containers")
log.Debugf("Migrating existing containers")
if err := ad.Migrate(root, graph.SetupInitLayer); err != nil {
return err
}

Просмотреть файл

@ -3,5 +3,5 @@
package daemon
import (
_ "github.com/dotcloud/docker/daemon/graphdriver/btrfs"
_ "github.com/docker/docker/daemon/graphdriver/btrfs"
)

Просмотреть файл

@ -3,5 +3,5 @@
package daemon
import (
_ "github.com/dotcloud/docker/daemon/graphdriver/devmapper"
_ "github.com/docker/docker/daemon/graphdriver/devmapper"
)

Просмотреть файл

@ -3,7 +3,7 @@
package daemon
import (
"github.com/dotcloud/docker/daemon/graphdriver"
"github.com/docker/docker/daemon/graphdriver"
)
func migrateIfAufs(driver graphdriver.Driver, root string) error {

174
daemon/delete.go Normal file
Просмотреть файл

@ -0,0 +1,174 @@
package daemon
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"github.com/docker/docker/engine"
"github.com/docker/docker/pkg/log"
)
// FIXME: rename to ContainerRemove for consistency with the CLI command.
func (daemon *Daemon) ContainerDestroy(job *engine.Job) engine.Status {
if len(job.Args) != 1 {
return job.Errorf("Not enough arguments. Usage: %s CONTAINER\n", job.Name)
}
name := job.Args[0]
removeVolume := job.GetenvBool("removeVolume")
removeLink := job.GetenvBool("removeLink")
forceRemove := job.GetenvBool("forceRemove")
container := daemon.Get(name)
if removeLink {
if container == nil {
return job.Errorf("No such link: %s", name)
}
name, err := GetFullContainerName(name)
if err != nil {
job.Error(err)
}
parent, n := path.Split(name)
if parent == "/" {
return job.Errorf("Conflict, cannot remove the default name of the container")
}
pe := daemon.ContainerGraph().Get(parent)
if pe == nil {
return job.Errorf("Cannot get parent %s for name %s", parent, name)
}
parentContainer := daemon.Get(pe.ID())
if parentContainer != nil {
parentContainer.DisableLink(n)
}
if err := daemon.ContainerGraph().Delete(name); err != nil {
return job.Error(err)
}
return engine.StatusOK
}
if container != nil {
if container.State.IsRunning() {
if forceRemove {
if err := container.Kill(); err != nil {
return job.Errorf("Could not kill running container, cannot remove - %v", err)
}
} else {
return job.Errorf("You cannot remove a running container. Stop the container before attempting removal or use -f")
}
}
if err := daemon.Destroy(container); err != nil {
return job.Errorf("Cannot destroy container %s: %s", name, err)
}
container.LogEvent("destroy")
if removeVolume {
var (
volumes = make(map[string]struct{})
binds = make(map[string]struct{})
usedVolumes = make(map[string]*Container)
)
// the volume id is always the base of the path
getVolumeId := func(p string) string {
return filepath.Base(strings.TrimSuffix(p, "/layer"))
}
// populate bind map so that they can be skipped and not removed
for _, bind := range container.HostConfig().Binds {
source := strings.Split(bind, ":")[0]
// TODO: refactor all volume stuff, all of it
// it is very important that we eval the link or comparing the keys to container.Volumes will not work
//
// eval symlink can fail, ref #5244 if we receive an is not exist error we can ignore it
p, err := filepath.EvalSymlinks(source)
if err != nil && !os.IsNotExist(err) {
return job.Error(err)
}
if p != "" {
source = p
}
binds[source] = struct{}{}
}
// Store all the deleted containers volumes
for _, volumeId := range container.Volumes {
// Skip the volumes mounted from external
// bind mounts here will will be evaluated for a symlink
if _, exists := binds[volumeId]; exists {
continue
}
volumeId = getVolumeId(volumeId)
volumes[volumeId] = struct{}{}
}
// Retrieve all volumes from all remaining containers
for _, container := range daemon.List() {
for _, containerVolumeId := range container.Volumes {
containerVolumeId = getVolumeId(containerVolumeId)
usedVolumes[containerVolumeId] = container
}
}
for volumeId := range volumes {
// If the requested volu
if c, exists := usedVolumes[volumeId]; exists {
log.Infof("The volume %s is used by the container %s. Impossible to remove it. Skipping.", volumeId, c.ID)
continue
}
if err := daemon.Volumes().Delete(volumeId); err != nil {
return job.Errorf("Error calling volumes.Delete(%q): %v", volumeId, err)
}
}
}
} else {
return job.Errorf("No such container: %s", name)
}
return engine.StatusOK
}
// Destroy unregisters a container from the daemon and cleanly removes its contents from the filesystem.
// FIXME: rename to Rm for consistency with the CLI command
func (daemon *Daemon) Destroy(container *Container) error {
if container == nil {
return fmt.Errorf("The given container is <nil>")
}
element := daemon.containers.Get(container.ID)
if element == nil {
return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
}
if err := container.Stop(3); err != nil {
return err
}
// Deregister the container before removing its directory, to avoid race conditions
daemon.idIndex.Delete(container.ID)
daemon.containers.Delete(container.ID)
if _, err := daemon.containerGraph.Purge(container.ID); err != nil {
log.Debugf("Unable to remove container from link graph: %s", err)
}
if err := daemon.driver.Remove(container.ID); err != nil {
return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", daemon.driver, container.ID, err)
}
initID := fmt.Sprintf("%s-init", container.ID)
if err := daemon.driver.Remove(initID); err != nil {
return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", daemon.driver, initID, err)
}
if err := os.RemoveAll(container.root); err != nil {
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
}
selinuxFreeLxcContexts(container.ProcessLabel)
return nil
}

Просмотреть файл

@ -20,47 +20,7 @@ var (
ErrDriverNotFound = errors.New("The requested docker init has not been found")
)
var dockerInitFcts map[string]InitFunc
type (
StartCallback func(*Command)
InitFunc func(i *InitArgs) error
)
func RegisterInitFunc(name string, fct InitFunc) error {
if dockerInitFcts == nil {
dockerInitFcts = make(map[string]InitFunc)
}
if _, ok := dockerInitFcts[name]; ok {
return ErrDriverAlreadyRegistered
}
dockerInitFcts[name] = fct
return nil
}
func GetInitFunc(name string) (InitFunc, error) {
fct, ok := dockerInitFcts[name]
if !ok {
return nil, ErrDriverNotFound
}
return fct, nil
}
// Args provided to the init function for a driver
type InitArgs struct {
User string
Gateway string
Ip string
WorkDir string
Privileged bool
Env []string
Args []string
Mtu int
Driver string
Console string
Pipe int
Root string
}
type StartCallback func(*Command)
// Driver specific information based on
// processes registered with the driver
@ -140,6 +100,8 @@ type Command struct {
Mounts []Mount `json:"mounts"`
AllowedDevices []*devices.Device `json:"allowed_devices"`
AutoCreatedDevices []*devices.Device `json:"autocreated_devices"`
CapAdd []string `json:"cap_add"`
CapDrop []string `json:"cap_drop"`
Terminal Terminal `json:"-"` // standard or tty terminal
Console string `json:"-"` // dev/console path

Просмотреть файл

@ -2,10 +2,10 @@ package execdrivers
import (
"fmt"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/daemon/execdriver/lxc"
"github.com/dotcloud/docker/daemon/execdriver/native"
"github.com/dotcloud/docker/pkg/sysinfo"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/daemon/execdriver/lxc"
"github.com/docker/docker/daemon/execdriver/native"
"github.com/docker/docker/pkg/sysinfo"
"path"
)
@ -15,7 +15,7 @@ func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdrive
// we want to give the lxc driver the full docker root because it needs
// to access and write config and template files in /var/lib/docker/containers/*
// to be backwards compatible
return lxc.NewDriver(root, sysInfo.AppArmor)
return lxc.NewDriver(root, initPath, sysInfo.AppArmor)
case "native":
return native.NewDriver(path.Join(root, "execdriver", "native"), initPath)
}

Просмотреть файл

@ -3,69 +3,47 @@ package lxc
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
"time"
"github.com/kr/pty"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/term"
"github.com/docker/docker/utils"
"github.com/docker/libcontainer/cgroups"
"github.com/docker/libcontainer/label"
"github.com/docker/libcontainer/mount/nodes"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/utils"
)
const DriverName = "lxc"
func init() {
execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
runtime.LockOSThread()
if err := setupEnv(args); err != nil {
return err
}
if err := setupHostname(args); err != nil {
return err
}
if err := setupNetworking(args); err != nil {
return err
}
if err := finalizeNamespace(args); err != nil {
return err
}
path, err := exec.LookPath(args.Args[0])
if err != nil {
log.Printf("Unable to locate %v", args.Args[0])
os.Exit(127)
}
if err := syscall.Exec(path, args.Args, os.Environ()); err != nil {
return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
}
panic("Unreachable")
})
}
type driver struct {
root string // root path for the driver to use
initPath string
apparmor bool
sharedRoot bool
}
func NewDriver(root string, apparmor bool) (*driver, error) {
func NewDriver(root, initPath string, apparmor bool) (*driver, error) {
// setup unconfined symlink
if err := linkLxcStart(root); err != nil {
return nil, err
}
return &driver{
apparmor: apparmor,
root: root,
initPath: initPath,
sharedRoot: rootIsShared(),
}, nil
}
@ -76,9 +54,25 @@ func (d *driver) Name() string {
}
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
if err := execdriver.SetTerminal(c, pipes); err != nil {
return -1, err
var (
term execdriver.Terminal
err error
)
if c.Tty {
term, err = NewTtyConsole(c, pipes)
} else {
term, err = execdriver.NewStdConsole(c, pipes)
}
c.Terminal = term
c.Mounts = append(c.Mounts, execdriver.Mount{
Source: d.initPath,
Destination: c.InitPath,
Writable: false,
Private: true,
})
if err := d.generateEnvConfig(c); err != nil {
return -1, err
}
@ -92,8 +86,6 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
"-f", configPath,
"--",
c.InitPath,
"-driver",
DriverName,
}
if c.Network.Interface != nil {
@ -122,6 +114,14 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
params = append(params, "-w", c.WorkingDir)
}
if len(c.CapAdd) > 0 {
params = append(params, fmt.Sprintf("-cap-add=%s", strings.Join(c.CapAdd, ":")))
}
if len(c.CapDrop) > 0 {
params = append(params, fmt.Sprintf("-cap-drop=%s", strings.Join(c.CapDrop, ":")))
}
params = append(params, "--", c.Entrypoint)
params = append(params, c.Arguments...)
@ -320,7 +320,7 @@ func (i *info) IsRunning() bool {
output, err := i.driver.getInfo(i.ID)
if err != nil {
utils.Errorf("Error getting info for lxc container %s: %s (%s)", i.ID, err, output)
log.Errorf("Error getting info for lxc container %s: %s (%s)", i.ID, err, output)
return false
}
if strings.Contains(string(output), "RUNNING") {
@ -447,7 +447,83 @@ func (d *driver) generateEnvConfig(c *execdriver.Command) error {
return err
}
p := path.Join(d.root, "containers", c.ID, "config.env")
c.Mounts = append(c.Mounts, execdriver.Mount{p, "/.dockerenv", false, true})
c.Mounts = append(c.Mounts, execdriver.Mount{
Source: p,
Destination: "/.dockerenv",
Writable: false,
Private: true,
})
return ioutil.WriteFile(p, data, 0600)
}
type TtyConsole struct {
MasterPty *os.File
SlavePty *os.File
}
func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyConsole, error) {
// lxc is special in that we cannot create the master outside of the container without
// opening the slave because we have nothing to provide to the cmd. We have to open both then do
// the crazy setup on command right now instead of passing the console path to lxc and telling it
// to open up that console. we save a couple of openfiles in the native driver because we can do
// this.
ptyMaster, ptySlave, err := pty.Open()
if err != nil {
return nil, err
}
tty := &TtyConsole{
MasterPty: ptyMaster,
SlavePty: ptySlave,
}
if err := tty.AttachPipes(&command.Cmd, pipes); err != nil {
tty.Close()
return nil, err
}
command.Console = tty.SlavePty.Name()
return tty, nil
}
func (t *TtyConsole) Master() *os.File {
return t.MasterPty
}
func (t *TtyConsole) Resize(h, w int) error {
return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
}
func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
command.Stdout = t.SlavePty
command.Stderr = t.SlavePty
go func() {
if wb, ok := pipes.Stdout.(interface {
CloseWriters() error
}); ok {
defer wb.CloseWriters()
}
io.Copy(pipes.Stdout, t.MasterPty)
}()
if pipes.Stdin != nil {
command.Stdin = t.SlavePty
command.SysProcAttr.Setctty = true
go func() {
io.Copy(t.MasterPty, pipes.Stdin)
pipes.Stdin.Close()
}()
}
return nil
}
func (t *TtyConsole) Close() error {
t.SlavePty.Close()
return t.MasterPty.Close()
}

Просмотреть файл

@ -2,19 +2,116 @@ package lxc
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"os/exec"
"runtime"
"strings"
"syscall"
"github.com/docker/docker/reexec"
"github.com/docker/libcontainer/netlink"
"github.com/dotcloud/docker/daemon/execdriver"
)
// Args provided to the init function for a driver
type InitArgs struct {
User string
Gateway string
Ip string
WorkDir string
Privileged bool
Env []string
Args []string
Mtu int
Console string
Pipe int
Root string
CapAdd string
CapDrop string
}
func init() {
// like always lxc requires a hack to get this to work
reexec.Register("/.dockerinit", dockerInititalizer)
}
func dockerInititalizer() {
initializer()
}
// initializer is the lxc driver's init function that is run inside the namespace to setup
// additional configurations
func initializer() {
runtime.LockOSThread()
args := getArgs()
if err := setupNamespace(args); err != nil {
log.Fatal(err)
}
}
func setupNamespace(args *InitArgs) error {
if err := setupEnv(args); err != nil {
return err
}
if err := setupHostname(args); err != nil {
return err
}
if err := setupNetworking(args); err != nil {
return err
}
if err := finalizeNamespace(args); err != nil {
return err
}
path, err := exec.LookPath(args.Args[0])
if err != nil {
log.Printf("Unable to locate %v", args.Args[0])
os.Exit(127)
}
if err := syscall.Exec(path, args.Args, os.Environ()); err != nil {
return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
}
return nil
}
func getArgs() *InitArgs {
var (
// Get cmdline arguments
user = flag.String("u", "", "username or uid")
gateway = flag.String("g", "", "gateway address")
ip = flag.String("i", "", "ip address")
workDir = flag.String("w", "", "workdir")
privileged = flag.Bool("privileged", false, "privileged mode")
mtu = flag.Int("mtu", 1500, "interface mtu")
capAdd = flag.String("cap-add", "", "capabilities to add")
capDrop = flag.String("cap-drop", "", "capabilities to drop")
)
flag.Parse()
return &InitArgs{
User: *user,
Gateway: *gateway,
Ip: *ip,
WorkDir: *workDir,
Privileged: *privileged,
Args: flag.Args(),
Mtu: *mtu,
CapAdd: *capAdd,
CapDrop: *capDrop,
}
}
// Clear environment pollution introduced by lxc-start
func setupEnv(args *execdriver.InitArgs) error {
func setupEnv(args *InitArgs) error {
// Get env
var env []string
content, err := ioutil.ReadFile(".dockerenv")
@ -41,7 +138,7 @@ func setupEnv(args *execdriver.InitArgs) error {
return nil
}
func setupHostname(args *execdriver.InitArgs) error {
func setupHostname(args *InitArgs) error {
hostname := getEnv(args, "HOSTNAME")
if hostname == "" {
return nil
@ -50,7 +147,7 @@ func setupHostname(args *execdriver.InitArgs) error {
}
// Setup networking
func setupNetworking(args *execdriver.InitArgs) error {
func setupNetworking(args *InitArgs) error {
if args.Ip != "" {
// eth0
iface, err := net.InterfaceByName("eth0")
@ -95,7 +192,7 @@ func setupNetworking(args *execdriver.InitArgs) error {
}
// Setup working directory
func setupWorkingDirectory(args *execdriver.InitArgs) error {
func setupWorkingDirectory(args *InitArgs) error {
if args.WorkDir == "" {
return nil
}
@ -105,7 +202,7 @@ func setupWorkingDirectory(args *execdriver.InitArgs) error {
return nil
}
func getEnv(args *execdriver.InitArgs, key string) string {
func getEnv(args *InitArgs, key string) string {
for _, kv := range args.Env {
parts := strings.SplitN(kv, "=", 2)
if parts[0] == key && len(parts) == 2 {

Просмотреть файл

@ -1,24 +1,23 @@
// +build amd64
package lxc
import (
"fmt"
"strings"
"syscall"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/daemon/execdriver/native/template"
"github.com/docker/libcontainer/namespaces"
"github.com/docker/libcontainer/security/capabilities"
"github.com/docker/libcontainer/system"
"github.com/docker/libcontainer/utils"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/daemon/execdriver/native/template"
"github.com/dotcloud/docker/pkg/system"
)
func setHostname(hostname string) error {
return syscall.Sethostname([]byte(hostname))
}
func finalizeNamespace(args *execdriver.InitArgs) error {
func finalizeNamespace(args *InitArgs) error {
if err := utils.CloseExecFrom(3); err != nil {
return err
}
@ -48,8 +47,25 @@ func finalizeNamespace(args *execdriver.InitArgs) error {
return fmt.Errorf("clear keep caps %s", err)
}
var (
adds []string
drops []string
)
if args.CapAdd != "" {
adds = strings.Split(args.CapAdd, ":")
}
if args.CapDrop != "" {
drops = strings.Split(args.CapDrop, ":")
}
caps, err := execdriver.TweakCapabilities(container.Capabilities, adds, drops)
if err != nil {
return err
}
// drop all other capabilities
if err := capabilities.DropCapabilities(container.Capabilities); err != nil {
if err := capabilities.DropCapabilities(caps); err != nil {
return fmt.Errorf("drop capabilities %s", err)
}
}

Просмотреть файл

@ -1,8 +1,8 @@
// +build !linux !amd64
// +build !linux
package lxc
import "github.com/dotcloud/docker/daemon/execdriver"
import "github.com/docker/docker/daemon/execdriver"
func setHostname(hostname string) error {
panic("Not supported on darwin")

Просмотреть файл

@ -4,8 +4,8 @@ import (
"strings"
"text/template"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/libcontainer/label"
"github.com/dotcloud/docker/daemon/execdriver"
)
const LxcTemplate = `
@ -75,9 +75,9 @@ lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs {{formatMountL
{{range $value := .Mounts}}
{{if $value.Writable}}
lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none bind,rw 0 0
lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none rbind,rw 0 0
{{else}}
lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none bind,ro 0 0
lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none rbind,ro 0 0
{{end}}
{{end}}

Просмотреть файл

@ -1,3 +1,5 @@
// +build linux
package lxc
import (
@ -11,8 +13,8 @@ import (
"testing"
"time"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/libcontainer/devices"
"github.com/dotcloud/docker/daemon/execdriver"
)
func TestLXCConfig(t *testing.T) {
@ -35,7 +37,7 @@ func TestLXCConfig(t *testing.T) {
cpu = cpuMin + rand.Intn(cpuMax-cpuMin)
)
driver, err := NewDriver(root, false)
driver, err := NewDriver(root, "", false)
if err != nil {
t.Fatal(err)
}
@ -71,7 +73,7 @@ func TestCustomLxcConfig(t *testing.T) {
os.MkdirAll(path.Join(root, "containers", "1"), 0777)
driver, err := NewDriver(root, false)
driver, err := NewDriver(root, "", false)
if err != nil {
t.Fatal(err)
}

Просмотреть файл

@ -7,8 +7,8 @@ import (
"strconv"
"strings"
"github.com/docker/docker/pkg/units"
"github.com/docker/libcontainer"
"github.com/dotcloud/docker/pkg/units"
)
type Action func(*libcontainer.Config, interface{}, string) error

Просмотреть файл

@ -3,8 +3,8 @@ package configuration
import (
"testing"
"github.com/docker/docker/daemon/execdriver/native/template"
"github.com/docker/libcontainer/security/capabilities"
"github.com/dotcloud/docker/daemon/execdriver/native/template"
)
// Checks whether the expected capability is specified in the capabilities.

Просмотреть файл

@ -1,3 +1,5 @@
// +build linux,cgo
package native
import (
@ -6,14 +8,14 @@ import (
"os/exec"
"path/filepath"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/daemon/execdriver/native/configuration"
"github.com/docker/docker/daemon/execdriver/native/template"
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/apparmor"
"github.com/docker/libcontainer/devices"
"github.com/docker/libcontainer/mount"
"github.com/docker/libcontainer/security/capabilities"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/daemon/execdriver/native/configuration"
"github.com/dotcloud/docker/daemon/execdriver/native/template"
)
// createContainer populates and configures the container type with the
@ -42,6 +44,10 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e
if err := d.setPrivileged(container); err != nil {
return nil, err
}
} else {
if err := d.setCapabilities(container, c); err != nil {
return nil, err
}
}
if err := d.setupCgroups(container, c); err != nil {
@ -136,6 +142,11 @@ func (d *driver) setPrivileged(container *libcontainer.Config) (err error) {
return nil
}
func (d *driver) setCapabilities(container *libcontainer.Config, c *execdriver.Command) (err error) {
container.Capabilities, err = execdriver.TweakCapabilities(container.Capabilities, c.CapAdd, c.CapDrop)
return err
}
func (d *driver) setupCgroups(container *libcontainer.Config, c *execdriver.Command) error {
if c.Resources != nil {
container.Cgroups.CpuShares = c.Resources.CpuShares

Просмотреть файл

@ -1,8 +1,11 @@
// +build linux,cgo
package native
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
@ -11,13 +14,15 @@ import (
"sync"
"syscall"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/pkg/term"
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/apparmor"
"github.com/docker/libcontainer/cgroups/fs"
"github.com/docker/libcontainer/cgroups/systemd"
consolepkg "github.com/docker/libcontainer/console"
"github.com/docker/libcontainer/namespaces"
"github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/pkg/system"
"github.com/docker/libcontainer/system"
)
const (
@ -25,34 +30,6 @@ const (
Version = "0.2"
)
func init() {
execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
var container *libcontainer.Config
f, err := os.Open(filepath.Join(args.Root, "container.json"))
if err != nil {
return err
}
if err := json.NewDecoder(f).Decode(&container); err != nil {
f.Close()
return err
}
f.Close()
rootfs, err := os.Getwd()
if err != nil {
return err
}
syncPipe, err := namespaces.NewSyncPipeFromFd(0, uintptr(args.Pipe))
if err != nil {
return err
}
if err := namespaces.Init(container, rootfs, args.Console, syncPipe, args.Args); err != nil {
return err
}
return nil
})
}
type activeContainer struct {
container *libcontainer.Config
cmd *exec.Cmd
@ -88,6 +65,19 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
if err != nil {
return -1, err
}
var term execdriver.Terminal
if c.Tty {
term, err = NewTtyConsole(c, pipes)
} else {
term, err = execdriver.NewStdConsole(c, pipes)
}
if err != nil {
return -1, err
}
c.Terminal = term
d.Lock()
d.activeContainers[c.ID] = &activeContainer{
container: container,
@ -99,6 +89,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
dataPath = filepath.Join(d.root, c.ID)
args = append([]string{c.Entrypoint}, c.Arguments...)
)
if err := d.createContainerRoot(c.ID); err != nil {
return -1, err
}
@ -108,16 +99,10 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
return -1, err
}
term := getTerminal(c, pipes)
return namespaces.Exec(container, term, c.Rootfs, dataPath, args, func(container *libcontainer.Config, console, rootfs, dataPath, init string, child *os.File, args []string) *exec.Cmd {
// we need to join the rootfs because namespaces will setup the rootfs and chroot
initPath := filepath.Join(c.Rootfs, c.InitPath)
return namespaces.Exec(container, c.Stdin, c.Stdout, c.Stderr, c.Console, c.Rootfs, dataPath, args, func(container *libcontainer.Config, console, rootfs, dataPath, init string, child *os.File, args []string) *exec.Cmd {
c.Path = d.initPath
c.Args = append([]string{
initPath,
"-driver", DriverName,
DriverName,
"-console", console,
"-pipe", "3",
"-root", filepath.Join(d.root, c.ID),
@ -125,8 +110,9 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
}, args...)
// set this to nil so that when we set the clone flags anything else is reset
c.SysProcAttr = nil
system.SetCloneFlags(&c.Cmd, uintptr(namespaces.GetNamespaceFlags(container.Namespaces)))
c.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: uintptr(namespaces.GetNamespaceFlags(container.Namespaces)),
}
c.ExtraFiles = []*os.File{child}
c.Env = container.Env
@ -194,11 +180,13 @@ func (d *driver) Terminate(p *execdriver.Command) error {
if err != nil {
return err
}
if state.InitStartTime == currentStartTime {
err = syscall.Kill(p.Process.Pid, 9)
syscall.Wait4(p.Process.Pid, nil, 0, nil)
}
d.removeContainerRoot(p.ID)
return err
}
@ -260,17 +248,60 @@ func getEnv(key string, env []string) string {
return ""
}
func getTerminal(c *execdriver.Command, pipes *execdriver.Pipes) namespaces.Terminal {
var term namespaces.Terminal
if c.Tty {
term = &dockerTtyTerm{
pipes: pipes,
}
} else {
term = &dockerStdTerm{
pipes: pipes,
}
}
c.Terminal = term
return term
type TtyConsole struct {
MasterPty *os.File
}
func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyConsole, error) {
ptyMaster, console, err := consolepkg.CreateMasterAndConsole()
if err != nil {
return nil, err
}
tty := &TtyConsole{
MasterPty: ptyMaster,
}
if err := tty.AttachPipes(&command.Cmd, pipes); err != nil {
tty.Close()
return nil, err
}
command.Console = console
return tty, nil
}
func (t *TtyConsole) Master() *os.File {
return t.MasterPty
}
func (t *TtyConsole) Resize(h, w int) error {
return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
}
func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
go func() {
if wb, ok := pipes.Stdout.(interface {
CloseWriters() error
}); ok {
defer wb.CloseWriters()
}
io.Copy(pipes.Stdout, t.MasterPty)
}()
if pipes.Stdin != nil {
go func() {
io.Copy(t.MasterPty, pipes.Stdin)
pipes.Stdin.Close()
}()
}
return nil
}
func (t *TtyConsole) Close() error {
return t.MasterPty.Close()
}

Просмотреть файл

@ -0,0 +1,13 @@
// +build !linux
package native
import (
"fmt"
"github.com/docker/docker/daemon/execdriver"
)
func NewDriver(root, initPath string) (execdriver.Driver, error) {
return nil, fmt.Errorf("native driver not supported on non-linux")
}

Просмотреть файл

@ -0,0 +1,13 @@
// +build linux,!cgo
package native
import (
"fmt"
"github.com/docker/docker/daemon/execdriver"
)
func NewDriver(root, initPath string) (execdriver.Driver, error) {
return nil, fmt.Errorf("native driver not supported on non-linux")
}

Просмотреть файл

@ -1,3 +1,5 @@
// +build linux,cgo
package native
import (

Просмотреть файл

@ -0,0 +1,66 @@
// +build linux
package native
import (
"encoding/json"
"flag"
"fmt"
"os"
"path/filepath"
"runtime"
"github.com/docker/docker/reexec"
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/namespaces"
"github.com/docker/libcontainer/syncpipe"
)
func init() {
reexec.Register(DriverName, initializer)
}
func initializer() {
runtime.LockOSThread()
var (
pipe = flag.Int("pipe", 0, "sync pipe fd")
console = flag.String("console", "", "console (pty slave) path")
root = flag.String("root", ".", "root path for configuration files")
)
flag.Parse()
var container *libcontainer.Config
f, err := os.Open(filepath.Join(*root, "container.json"))
if err != nil {
writeError(err)
}
if err := json.NewDecoder(f).Decode(&container); err != nil {
f.Close()
writeError(err)
}
f.Close()
rootfs, err := os.Getwd()
if err != nil {
writeError(err)
}
syncPipe, err := syncpipe.NewSyncPipeFromFd(0, uintptr(*pipe))
if err != nil {
writeError(err)
}
if err := namespaces.Init(container, rootfs, *console, syncPipe, flag.Args()); err != nil {
writeError(err)
}
panic("Unreachable")
}
func writeError(err error) {
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}

Просмотреть файл

@ -12,6 +12,7 @@ func New() *libcontainer.Config {
Capabilities: []string{
"CHOWN",
"DAC_OVERRIDE",
"FSETID",
"FOWNER",
"MKNOD",
"NET_RAW",
@ -22,6 +23,7 @@ func New() *libcontainer.Config {
"NET_BIND_SERVICE",
"SYS_CHROOT",
"KILL",
"AUDIT_WRITE",
},
Namespaces: map[string]bool{
"NEWNS": true,

Просмотреть файл

@ -1,42 +0,0 @@
/*
These types are wrappers around the libcontainer Terminal interface so that
we can resuse the docker implementations where possible.
*/
package native
import (
"github.com/dotcloud/docker/daemon/execdriver"
"io"
"os"
"os/exec"
)
type dockerStdTerm struct {
execdriver.StdConsole
pipes *execdriver.Pipes
}
func (d *dockerStdTerm) Attach(cmd *exec.Cmd) error {
return d.AttachPipes(cmd, d.pipes)
}
func (d *dockerStdTerm) SetMaster(master *os.File) {
// do nothing
}
type dockerTtyTerm struct {
execdriver.TtyConsole
pipes *execdriver.Pipes
}
func (t *dockerTtyTerm) Attach(cmd *exec.Cmd) error {
go io.Copy(t.pipes.Stdout, t.MasterPty)
if t.pipes.Stdin != nil {
go io.Copy(t.MasterPty, t.pipes.Stdin)
}
return nil
}
func (t *dockerTtyTerm) SetMaster(master *os.File) {
t.MasterPty = master
}

Просмотреть файл

@ -1,90 +1,10 @@
package execdriver
import (
"github.com/dotcloud/docker/pkg/term"
"github.com/kr/pty"
"io"
"os"
"os/exec"
)
func SetTerminal(command *Command, pipes *Pipes) error {
var (
term Terminal
err error
)
if command.Tty {
term, err = NewTtyConsole(command, pipes)
} else {
term, err = NewStdConsole(command, pipes)
}
if err != nil {
return err
}
command.Terminal = term
return nil
}
type TtyConsole struct {
MasterPty *os.File
SlavePty *os.File
}
func NewTtyConsole(command *Command, pipes *Pipes) (*TtyConsole, error) {
ptyMaster, ptySlave, err := pty.Open()
if err != nil {
return nil, err
}
tty := &TtyConsole{
MasterPty: ptyMaster,
SlavePty: ptySlave,
}
if err := tty.AttachPipes(&command.Cmd, pipes); err != nil {
tty.Close()
return nil, err
}
command.Console = tty.SlavePty.Name()
return tty, nil
}
func (t *TtyConsole) Master() *os.File {
return t.MasterPty
}
func (t *TtyConsole) Resize(h, w int) error {
return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
}
func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
command.Stdout = t.SlavePty
command.Stderr = t.SlavePty
go func() {
if wb, ok := pipes.Stdout.(interface {
CloseWriters() error
}); ok {
defer wb.CloseWriters()
}
io.Copy(pipes.Stdout, t.MasterPty)
}()
if pipes.Stdin != nil {
command.Stdin = t.SlavePty
command.SysProcAttr.Setctty = true
go func() {
defer pipes.Stdin.Close()
io.Copy(t.MasterPty, pipes.Stdin)
}()
}
return nil
}
func (t *TtyConsole) Close() error {
t.SlavePty.Close()
return t.MasterPty.Close()
}
type StdConsole struct {
}

Просмотреть файл

@ -0,0 +1,63 @@
package execdriver
import (
"fmt"
"strings"
"github.com/docker/docker/utils"
"github.com/docker/libcontainer/security/capabilities"
)
func TweakCapabilities(basics, adds, drops []string) ([]string, error) {
var (
newCaps []string
allCaps = capabilities.GetAllCapabilities()
)
// look for invalid cap in the drop list
for _, cap := range drops {
if strings.ToLower(cap) == "all" {
continue
}
if !utils.StringsContainsNoCase(allCaps, cap) {
return nil, fmt.Errorf("Unknown capability drop: %q", cap)
}
}
// handle --cap-add=all
if utils.StringsContainsNoCase(adds, "all") {
basics = capabilities.GetAllCapabilities()
}
if !utils.StringsContainsNoCase(drops, "all") {
for _, cap := range basics {
// skip `all` aready handled above
if strings.ToLower(cap) == "all" {
continue
}
// if we don't drop `all`, add back all the non-dropped caps
if !utils.StringsContainsNoCase(drops, cap) {
newCaps = append(newCaps, strings.ToUpper(cap))
}
}
}
for _, cap := range adds {
// skip `all` aready handled above
if strings.ToLower(cap) == "all" {
continue
}
if !utils.StringsContainsNoCase(allCaps, cap) {
return nil, fmt.Errorf("Unknown capability to add: %q", cap)
}
// add cap if not already in the list
if !utils.StringsContainsNoCase(newCaps, cap) {
newCaps = append(newCaps, strings.ToUpper(cap))
}
}
return newCaps, nil
}

30
daemon/export.go Normal file
Просмотреть файл

@ -0,0 +1,30 @@
package daemon
import (
"io"
"github.com/docker/docker/engine"
)
func (daemon *Daemon) ContainerExport(job *engine.Job) engine.Status {
if len(job.Args) != 1 {
return job.Errorf("Usage: %s container_id", job.Name)
}
name := job.Args[0]
if container := daemon.Get(name); container != nil {
data, err := container.Export()
if err != nil {
return job.Errorf("%s: %s", name, err)
}
defer data.Close()
// Stream the entire contents of the container (basically a volatile snapshot)
if _, err := io.Copy(job.Stdout, data); err != nil {
return job.Errorf("%s: %s", name, err)
}
// FIXME: factor job-specific LogEvent to engine.Job.Run()
container.LogEvent("export")
return engine.StatusOK
}
return job.Errorf("No such container: %s", name)
}

Просмотреть файл

@ -30,11 +30,12 @@ import (
"sync"
"syscall"
"github.com/docker/docker/archive"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/pkg/log"
mountpk "github.com/docker/docker/pkg/mount"
"github.com/docker/docker/utils"
"github.com/docker/libcontainer/label"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/daemon/graphdriver"
mountpk "github.com/dotcloud/docker/pkg/mount"
"github.com/dotcloud/docker/utils"
)
var (
@ -209,7 +210,7 @@ func (a *Driver) Remove(id string) error {
defer a.Unlock()
if a.active[id] != 0 {
utils.Errorf("Warning: removing active id %s\n", id)
log.Errorf("Warning: removing active id %s", id)
}
// Make sure the dir is umounted first
@ -378,7 +379,7 @@ func (a *Driver) Cleanup() error {
for _, id := range ids {
if err := a.unmount(id); err != nil {
utils.Errorf("Unmounting %s: %s", utils.TruncateID(id), err)
log.Errorf("Unmounting %s: %s", utils.TruncateID(id), err)
}
}

Просмотреть файл

@ -4,8 +4,8 @@ import (
"crypto/sha256"
"encoding/hex"
"fmt"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/daemon/graphdriver"
"github.com/docker/docker/archive"
"github.com/docker/docker/daemon/graphdriver"
"io/ioutil"
"os"
"path"

Просмотреть файл

@ -1,14 +1,15 @@
package aufs
import (
"github.com/dotcloud/docker/utils"
"os/exec"
"syscall"
"github.com/docker/docker/pkg/log"
)
func Unmount(target string) error {
if err := exec.Command("auplink", target, "flush").Run(); err != nil {
utils.Errorf("[warning]: couldn't run auplink before unmount: %s", err)
log.Errorf("[warning]: couldn't run auplink before unmount: %s", err)
}
if err := syscall.Unmount(target, 0); err != nil {
return err

Просмотреть файл

@ -1,5 +1,3 @@
// +build amd64
package aufs
import "syscall"

Просмотреть файл

@ -1,4 +1,4 @@
// +build !linux !amd64
// +build !linux
package aufs

Просмотреть файл

@ -1,4 +1,4 @@
// +build linux,amd64
// +build linux
package btrfs
@ -16,8 +16,8 @@ import (
"syscall"
"unsafe"
"github.com/dotcloud/docker/daemon/graphdriver"
"github.com/dotcloud/docker/pkg/mount"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/pkg/mount"
)
func init() {

Просмотреть файл

@ -1,7 +1,7 @@
package btrfs
import (
"github.com/dotcloud/docker/daemon/graphdriver/graphtest"
"github.com/docker/docker/daemon/graphdriver/graphtest"
"testing"
)

Просмотреть файл

@ -1,3 +1,3 @@
// +build !linux !amd64
// +build !linux !cgo
package btrfs

Просмотреть файл

@ -7,7 +7,7 @@ module (dm-thinp) to implement CoW snapshots. For each devicemapper
graph location (typically `/var/lib/docker/devicemapper`, $graph below)
a thin pool is created based on two block devices, one for data and
one for metadata. By default these block devices are created
automatically by using loopback mounts of automatically creates sparse
automatically by using loopback mounts of automatically created sparse
files.
The default loopback files used are `$graph/devicemapper/data` and
@ -15,15 +15,15 @@ The default loopback files used are `$graph/devicemapper/data` and
from docker entities to the corresponding devicemapper volumes is
stored in the `$graph/devicemapper/json` file (encoded as Json).
In order to support multiple devicemapper graphs on a system the thin
In order to support multiple devicemapper graphs on a system, the thin
pool will be named something like: `docker-0:33-19478248-pool`, where
the `0:30` part is the minor/major device nr and `19478248` is the
the `0:33` part is the minor/major device nr and `19478248` is the
inode number of the $graph directory.
On the thin pool docker automatically creates a base thin device,
On the thin pool, docker automatically creates a base thin device,
called something like `docker-0:33-19478248-base` of a fixed
size. This is automatically formated on creation and contains just an
empty filesystem. This device is the base of all docker images and
size. This is automatically formatted with an empty filesystem on
creation. This device is the base of all docker images and
containers. All base images are snapshots of this device and those
images are then in turn used as snapshots for other images and
eventually containers.
@ -31,8 +31,8 @@ eventually containers.
### options
The devicemapper backend supports some options that you can specify
when starting the docker daemon using the --storage-opt flags.
This uses the `dm` prefix and would be used somthing like `docker -d --storage-opt dm.foo=bar`.
when starting the docker daemon using the `--storage-opt` flags.
This uses the `dm` prefix and would be used something like `docker -d --storage-opt dm.foo=bar`.
Here is the list of supported options:
@ -43,7 +43,11 @@ Here is the list of supported options:
10G. Note, thin devices are inherently "sparse", so a 10G device
which is mostly empty doesn't use 10 GB of space on the
pool. However, the filesystem will use more space for the empty
case the larger the device is.
case the larger the device is. **Warning**: This value affects the
system-wide "base" empty filesystem that may already be
initialized and inherited by pulled images. Typically, a change
to this value will require additional steps to take effect: 1)
stop `docker -d`, 2) `rm -rf /var/lib/docker`, 3) start `docker -d`.
Example use:
@ -126,6 +130,15 @@ Here is the list of supported options:
``docker -d --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1``
* `dm.blocksize`
Specifies a custom blocksize to use for the thin pool. The default
blocksize is 64K.
Example use:
``docker -d --storage-opt dm.blocksize=512K``
* `dm.blkdiscard`
Enables or disables the use of blkdiscard when removing

Просмотреть файл

@ -1,4 +1,4 @@
// +build linux,amd64
// +build linux
package devmapper
@ -7,7 +7,7 @@ import (
"os"
"syscall"
"github.com/dotcloud/docker/utils"
"github.com/docker/docker/pkg/log"
)
func stringToLoopName(src string) [LoNameSize]uint8 {
@ -39,20 +39,20 @@ func openNextAvailableLoopback(index int, sparseFile *os.File) (loopFile *os.Fil
fi, err := os.Stat(target)
if err != nil {
if os.IsNotExist(err) {
utils.Errorf("There are no more loopback devices available.")
log.Errorf("There are no more loopback devices available.")
}
return nil, ErrAttachLoopbackDevice
}
if fi.Mode()&os.ModeDevice != os.ModeDevice {
utils.Errorf("Loopback device %s is not a block device.", target)
log.Errorf("Loopback device %s is not a block device.", target)
continue
}
// OpenFile adds O_CLOEXEC
loopFile, err = os.OpenFile(target, os.O_RDWR, 0644)
if err != nil {
utils.Errorf("Error openning loopback device: %s", err)
log.Errorf("Error openning loopback device: %s", err)
return nil, ErrAttachLoopbackDevice
}
@ -62,7 +62,7 @@ func openNextAvailableLoopback(index int, sparseFile *os.File) (loopFile *os.Fil
// If the error is EBUSY, then try the next loopback
if err != syscall.EBUSY {
utils.Errorf("Cannot set up loopback device %s: %s", target, err)
log.Errorf("Cannot set up loopback device %s: %s", target, err)
return nil, ErrAttachLoopbackDevice
}
@ -75,7 +75,7 @@ func openNextAvailableLoopback(index int, sparseFile *os.File) (loopFile *os.Fil
// This can't happen, but let's be sure
if loopFile == nil {
utils.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name())
log.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name())
return nil, ErrAttachLoopbackDevice
}
@ -91,13 +91,13 @@ func attachLoopDevice(sparseName string) (loop *os.File, err error) {
// loopback from index 0.
startIndex, err := getNextFreeLoopbackIndex()
if err != nil {
utils.Debugf("Error retrieving the next available loopback: %s", err)
log.Debugf("Error retrieving the next available loopback: %s", err)
}
// OpenFile adds O_CLOEXEC
sparseFile, err := os.OpenFile(sparseName, os.O_RDWR, 0644)
if err != nil {
utils.Errorf("Error openning sparse file %s: %s", sparseName, err)
log.Errorf("Error openning sparse file %s: %s", sparseName, err)
return nil, ErrAttachLoopbackDevice
}
defer sparseFile.Close()
@ -115,11 +115,11 @@ func attachLoopDevice(sparseName string) (loop *os.File, err error) {
}
if err := ioctlLoopSetStatus64(loopFile.Fd(), loopInfo); err != nil {
utils.Errorf("Cannot set up loopback device info: %s", err)
log.Errorf("Cannot set up loopback device info: %s", err)
// If the call failed, then free the loopback device
if err := ioctlLoopClrFd(loopFile.Fd()); err != nil {
utils.Errorf("Error while cleaning up the loopback device")
log.Errorf("Error while cleaning up the loopback device")
}
loopFile.Close()
return nil, ErrAttachLoopbackDevice

Просмотреть файл

@ -1,4 +1,4 @@
// +build linux,amd64
// +build linux
package devmapper
@ -18,16 +18,18 @@ import (
"syscall"
"time"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/pkg/log"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/pkg/units"
"github.com/docker/libcontainer/label"
"github.com/dotcloud/docker/daemon/graphdriver"
"github.com/dotcloud/docker/pkg/units"
"github.com/dotcloud/docker/utils"
)
var (
DefaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024
DefaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024
DefaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024
DefaultThinpBlockSize uint32 = 128 // 64K = 128 512b sectors
)
type DevInfo struct {
@ -78,6 +80,7 @@ type DeviceSet struct {
dataDevice string
metadataDevice string
doBlkDiscard bool
thinpBlockSize uint32
}
type DiskUsage struct {
@ -171,7 +174,7 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
if !os.IsNotExist(err) {
return "", err
}
utils.Debugf("Creating loopback file %s for device-manage use", filename)
log.Debugf("Creating loopback file %s for device-manage use", filename)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
return "", err
@ -249,7 +252,7 @@ func (devices *DeviceSet) lookupDevice(hash string) (*DevInfo, error) {
}
func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*DevInfo, error) {
utils.Debugf("registerDevice(%v, %v)", id, hash)
log.Debugf("registerDevice(%v, %v)", id, hash)
info := &DevInfo{
Hash: hash,
DeviceId: id,
@ -275,7 +278,7 @@ func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*Dev
}
func (devices *DeviceSet) activateDeviceIfNeeded(info *DevInfo) error {
utils.Debugf("activateDeviceIfNeeded(%v)", info.Hash)
log.Debugf("activateDeviceIfNeeded(%v)", info.Hash)
if devinfo, _ := getInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
return nil
@ -382,13 +385,13 @@ func (devices *DeviceSet) setupBaseImage() error {
}
if oldInfo != nil && !oldInfo.Initialized {
utils.Debugf("Removing uninitialized base image")
log.Debugf("Removing uninitialized base image")
if err := devices.deleteDevice(oldInfo); err != nil {
return err
}
}
utils.Debugf("Initializing base device-manager snapshot")
log.Debugf("Initializing base device-manager snapshot")
id := devices.nextDeviceId
@ -400,14 +403,14 @@ func (devices *DeviceSet) setupBaseImage() error {
// Ids are 24bit, so wrap around
devices.nextDeviceId = (id + 1) & 0xffffff
utils.Debugf("Registering base device (id %v) with FS size %v", id, devices.baseFsSize)
log.Debugf("Registering base device (id %v) with FS size %v", id, devices.baseFsSize)
info, err := devices.registerDevice(id, "", devices.baseFsSize)
if err != nil {
_ = deleteDevice(devices.getPoolDevName(), id)
return err
}
utils.Debugf("Creating filesystem on base device-manager snapshot")
log.Debugf("Creating filesystem on base device-manager snapshot")
if err = devices.activateDeviceIfNeeded(info); err != nil {
return err
@ -445,7 +448,7 @@ func (devices *DeviceSet) log(level int, file string, line int, dmError int, mes
return // Ignore _LOG_DEBUG
}
utils.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
log.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
}
func major(device uint64) uint64 {
@ -510,7 +513,7 @@ func (devices *DeviceSet) ResizePool(size int64) error {
}
// Reload with the new block sizes
if err := reloadPool(devices.getPoolName(), dataloopback, metadataloopback); err != nil {
if err := reloadPool(devices.getPoolName(), dataloopback, metadataloopback, devices.thinpBlockSize); err != nil {
return fmt.Errorf("Unable to reload pool: %s", err)
}
@ -549,13 +552,13 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
// - The target of this device is at major <maj> and minor <min>
// - If <inode> is defined, use that file inside the device as a loopback image. Otherwise use the device itself.
devices.devicePrefix = fmt.Sprintf("docker-%d:%d-%d", major(sysSt.Dev), minor(sysSt.Dev), sysSt.Ino)
utils.Debugf("Generated prefix: %s", devices.devicePrefix)
log.Debugf("Generated prefix: %s", devices.devicePrefix)
// Check for the existence of the device <prefix>-pool
utils.Debugf("Checking for existence of the pool '%s'", devices.getPoolName())
log.Debugf("Checking for existence of the pool '%s'", devices.getPoolName())
info, err := getInfo(devices.getPoolName())
if info == nil {
utils.Debugf("Error device getInfo: %s", err)
log.Debugf("Error device getInfo: %s", err)
return err
}
@ -571,7 +574,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
// If the pool doesn't exist, create it
if info.Exists == 0 {
utils.Debugf("Pool doesn't exist. Creating it.")
log.Debugf("Pool doesn't exist. Creating it.")
var (
dataFile *os.File
@ -593,7 +596,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
data, err := devices.ensureImage("data", devices.dataLoopbackSize)
if err != nil {
utils.Debugf("Error device ensureImage (data): %s\n", err)
log.Debugf("Error device ensureImage (data): %s", err)
return err
}
@ -624,7 +627,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
metadata, err := devices.ensureImage("metadata", devices.metaDataLoopbackSize)
if err != nil {
utils.Debugf("Error device ensureImage (metadata): %s\n", err)
log.Debugf("Error device ensureImage (metadata): %s", err)
return err
}
@ -640,7 +643,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
}
defer metadataFile.Close()
if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil {
if err := createPool(devices.getPoolName(), dataFile, metadataFile, devices.thinpBlockSize); err != nil {
return err
}
}
@ -656,7 +659,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
// Setup the base image
if doInit {
if err := devices.setupBaseImage(); err != nil {
utils.Debugf("Error device setupBaseImage: %s\n", err)
log.Debugf("Error device setupBaseImage: %s", err)
return err
}
}
@ -683,7 +686,7 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
deviceId := devices.nextDeviceId
if err := createSnapDevice(devices.getPoolDevName(), &deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
utils.Debugf("Error creating snap device: %s\n", err)
log.Debugf("Error creating snap device: %s", err)
return err
}
@ -692,7 +695,7 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
if _, err := devices.registerDevice(deviceId, hash, baseInfo.Size); err != nil {
deleteDevice(devices.getPoolDevName(), deviceId)
utils.Debugf("Error registering device: %s\n", err)
log.Debugf("Error registering device: %s", err)
return err
}
return nil
@ -705,7 +708,7 @@ func (devices *DeviceSet) deleteDevice(info *DevInfo) error {
// manually
if err := devices.activateDeviceIfNeeded(info); err == nil {
if err := BlockDeviceDiscard(info.DevName()); err != nil {
utils.Debugf("Error discarding block on device: %s (ignoring)\n", err)
log.Debugf("Error discarding block on device: %s (ignoring)", err)
}
}
}
@ -713,13 +716,13 @@ func (devices *DeviceSet) deleteDevice(info *DevInfo) error {
devinfo, _ := getInfo(info.Name())
if devinfo != nil && devinfo.Exists != 0 {
if err := devices.removeDeviceAndWait(info.Name()); err != nil {
utils.Debugf("Error removing device: %s\n", err)
log.Debugf("Error removing device: %s", err)
return err
}
}
if err := deleteDevice(devices.getPoolDevName(), info.DeviceId); err != nil {
utils.Debugf("Error deleting device: %s\n", err)
log.Debugf("Error deleting device: %s", err)
return err
}
@ -732,7 +735,7 @@ func (devices *DeviceSet) deleteDevice(info *DevInfo) error {
devices.devicesLock.Lock()
devices.Devices[info.Hash] = info
devices.devicesLock.Unlock()
utils.Debugf("Error removing meta data: %s\n", err)
log.Debugf("Error removing meta data: %s", err)
return err
}
@ -755,8 +758,8 @@ func (devices *DeviceSet) DeleteDevice(hash string) error {
}
func (devices *DeviceSet) deactivatePool() error {
utils.Debugf("[devmapper] deactivatePool()")
defer utils.Debugf("[devmapper] deactivatePool END")
log.Debugf("[devmapper] deactivatePool()")
defer log.Debugf("[devmapper] deactivatePool END")
devname := devices.getPoolDevName()
devinfo, err := getInfo(devname)
if err != nil {
@ -770,13 +773,13 @@ func (devices *DeviceSet) deactivatePool() error {
}
func (devices *DeviceSet) deactivateDevice(info *DevInfo) error {
utils.Debugf("[devmapper] deactivateDevice(%s)", info.Hash)
defer utils.Debugf("[devmapper] deactivateDevice END")
log.Debugf("[devmapper] deactivateDevice(%s)", info.Hash)
defer log.Debugf("[devmapper] deactivateDevice END")
// Wait for the unmount to be effective,
// by watching the value of Info.OpenCount for the device
if err := devices.waitClose(info); err != nil {
utils.Errorf("Warning: error waiting for device %s to close: %s\n", info.Hash, err)
log.Errorf("Warning: error waiting for device %s to close: %s", info.Hash, err)
}
devinfo, err := getInfo(info.Name())
@ -826,8 +829,8 @@ func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
// a) the device registered at <device_set_prefix>-<hash> is removed,
// or b) the 10 second timeout expires.
func (devices *DeviceSet) waitRemove(devname string) error {
utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, devname)
defer utils.Debugf("[deviceset %s] waitRemove(%s) END", devices.devicePrefix, devname)
log.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, devname)
defer log.Debugf("[deviceset %s] waitRemove(%s) END", devices.devicePrefix, devname)
i := 0
for ; i < 1000; i += 1 {
devinfo, err := getInfo(devname)
@ -837,7 +840,7 @@ func (devices *DeviceSet) waitRemove(devname string) error {
return nil
}
if i%100 == 0 {
utils.Debugf("Waiting for removal of %s: exists=%d", devname, devinfo.Exists)
log.Debugf("Waiting for removal of %s: exists=%d", devname, devinfo.Exists)
}
if devinfo.Exists == 0 {
break
@ -864,7 +867,7 @@ func (devices *DeviceSet) waitClose(info *DevInfo) error {
return err
}
if i%100 == 0 {
utils.Debugf("Waiting for unmount of %s: opencount=%d", info.Hash, devinfo.OpenCount)
log.Debugf("Waiting for unmount of %s: opencount=%d", info.Hash, devinfo.OpenCount)
}
if devinfo.OpenCount == 0 {
break
@ -881,9 +884,9 @@ func (devices *DeviceSet) waitClose(info *DevInfo) error {
func (devices *DeviceSet) Shutdown() error {
utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix)
utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root)
defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
log.Debugf("[deviceset %s] shutdown()", devices.devicePrefix)
log.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root)
defer log.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
var devs []*DevInfo
@ -900,12 +903,12 @@ func (devices *DeviceSet) Shutdown() error {
// container. This means it'll go away from the global scope directly,
// and the device will be released when that container dies.
if err := syscall.Unmount(info.mountPath, syscall.MNT_DETACH); err != nil {
utils.Debugf("Shutdown unmounting %s, error: %s\n", info.mountPath, err)
log.Debugf("Shutdown unmounting %s, error: %s", info.mountPath, err)
}
devices.Lock()
if err := devices.deactivateDevice(info); err != nil {
utils.Debugf("Shutdown deactivate %s , error: %s\n", info.Hash, err)
log.Debugf("Shutdown deactivate %s , error: %s", info.Hash, err)
}
devices.Unlock()
}
@ -917,7 +920,7 @@ func (devices *DeviceSet) Shutdown() error {
info.lock.Lock()
devices.Lock()
if err := devices.deactivateDevice(info); err != nil {
utils.Debugf("Shutdown deactivate base , error: %s\n", err)
log.Debugf("Shutdown deactivate base , error: %s", err)
}
devices.Unlock()
info.lock.Unlock()
@ -925,7 +928,7 @@ func (devices *DeviceSet) Shutdown() error {
devices.Lock()
if err := devices.deactivatePool(); err != nil {
utils.Debugf("Shutdown deactivate pool , error: %s\n", err)
log.Debugf("Shutdown deactivate pool , error: %s", err)
}
devices.Unlock()
@ -989,8 +992,8 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
}
func (devices *DeviceSet) UnmountDevice(hash string) error {
utils.Debugf("[devmapper] UnmountDevice(hash=%s)", hash)
defer utils.Debugf("[devmapper] UnmountDevice END")
log.Debugf("[devmapper] UnmountDevice(hash=%s)", hash)
defer log.Debugf("[devmapper] UnmountDevice END")
info, err := devices.lookupDevice(hash)
if err != nil {
@ -1012,11 +1015,11 @@ func (devices *DeviceSet) UnmountDevice(hash string) error {
return nil
}
utils.Debugf("[devmapper] Unmount(%s)", info.mountPath)
log.Debugf("[devmapper] Unmount(%s)", info.mountPath)
if err := syscall.Unmount(info.mountPath, 0); err != nil {
return err
}
utils.Debugf("[devmapper] Unmount done")
log.Debugf("[devmapper] Unmount done")
if err := devices.deactivateDevice(info); err != nil {
return err
@ -1159,30 +1162,31 @@ func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error
baseFsSize: DefaultBaseFsSize,
filesystem: "ext4",
doBlkDiscard: true,
thinpBlockSize: DefaultThinpBlockSize,
}
foundBlkDiscard := false
for _, option := range options {
key, val, err := utils.ParseKeyValueOpt(option)
key, val, err := parsers.ParseKeyValueOpt(option)
if err != nil {
return nil, err
}
key = strings.ToLower(key)
switch key {
case "dm.basesize":
size, err := units.FromHumanSize(val)
size, err := units.RAMInBytes(val)
if err != nil {
return nil, err
}
devices.baseFsSize = uint64(size)
case "dm.loopdatasize":
size, err := units.FromHumanSize(val)
size, err := units.RAMInBytes(val)
if err != nil {
return nil, err
}
devices.dataLoopbackSize = size
case "dm.loopmetadatasize":
size, err := units.FromHumanSize(val)
size, err := units.RAMInBytes(val)
if err != nil {
return nil, err
}
@ -1206,6 +1210,13 @@ func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error
if err != nil {
return nil, err
}
case "dm.blocksize":
size, err := units.RAMInBytes(val)
if err != nil {
return nil, err
}
// convert to 512b sectors
devices.thinpBlockSize = uint32(size) >> 9
default:
return nil, fmt.Errorf("Unknown option %s\n", key)
}

Просмотреть файл

@ -1,4 +1,4 @@
// +build linux,amd64
// +build linux
package devmapper
@ -9,7 +9,7 @@ import (
"runtime"
"syscall"
"github.com/dotcloud/docker/utils"
"github.com/docker/docker/pkg/log"
)
type DevmapperLogger interface {
@ -198,7 +198,7 @@ func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64,
func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) {
loopInfo, err := ioctlLoopGetStatus64(file.Fd())
if err != nil {
utils.Errorf("Error get loopback backing file: %s\n", err)
log.Errorf("Error get loopback backing file: %s", err)
return 0, 0, ErrGetLoopbackBackingFile
}
return loopInfo.loDevice, loopInfo.loInode, nil
@ -206,7 +206,7 @@ func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) {
func LoopbackSetCapacity(file *os.File) error {
if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil {
utils.Errorf("Error loopbackSetCapacity: %s", err)
log.Errorf("Error loopbackSetCapacity: %s", err)
return ErrLoopbackSetCapacity
}
return nil
@ -246,7 +246,7 @@ func FindLoopDeviceFor(file *os.File) *os.File {
func UdevWait(cookie uint) error {
if res := DmUdevWait(cookie); res != 1 {
utils.Debugf("Failed to wait on udev cookie %d", cookie)
log.Debugf("Failed to wait on udev cookie %d", cookie)
return ErrUdevWait
}
return nil
@ -265,7 +265,7 @@ func logInit(logger DevmapperLogger) {
func SetDevDir(dir string) error {
if res := DmSetDevDir(dir); res != 1 {
utils.Debugf("Error dm_set_dev_dir")
log.Debugf("Error dm_set_dev_dir")
return ErrSetDevDir
}
return nil
@ -286,7 +286,7 @@ func RemoveDevice(name string) error {
return ErrCreateRemoveTask
}
if err := task.SetName(name); err != nil {
utils.Debugf("Can't set task name %s", name)
log.Debugf("Can't set task name %s", name)
return err
}
if err := task.Run(); err != nil {
@ -298,7 +298,7 @@ func RemoveDevice(name string) error {
func GetBlockDeviceSize(file *os.File) (uint64, error) {
size, err := ioctlBlkGetSize64(file.Fd())
if err != nil {
utils.Errorf("Error getblockdevicesize: %s", err)
log.Errorf("Error getblockdevicesize: %s", err)
return 0, ErrGetBlockSize
}
return uint64(size), nil
@ -328,7 +328,7 @@ func BlockDeviceDiscard(path string) error {
}
// This is the programmatic example of "dmsetup create"
func createPool(poolName string, dataFile, metadataFile *os.File) error {
func createPool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
task, err := createTask(DeviceCreate, poolName)
if task == nil {
return err
@ -339,7 +339,7 @@ func createPool(poolName string, dataFile, metadataFile *os.File) error {
return fmt.Errorf("Can't get data size %s", err)
}
params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768 1 skip_block_zeroing"
params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
return fmt.Errorf("Can't add target %s", err)
}
@ -358,7 +358,7 @@ func createPool(poolName string, dataFile, metadataFile *os.File) error {
return nil
}
func reloadPool(poolName string, dataFile, metadataFile *os.File) error {
func reloadPool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
task, err := createTask(DeviceReload, poolName)
if task == nil {
return err
@ -369,7 +369,7 @@ func reloadPool(poolName string, dataFile, metadataFile *os.File) error {
return fmt.Errorf("Can't get data size %s", err)
}
params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768"
params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
return fmt.Errorf("Can't add target %s", err)
}
@ -417,21 +417,21 @@ func getDriverVersion() (string, error) {
func getStatus(name string) (uint64, uint64, string, string, error) {
task, err := createTask(DeviceStatus, name)
if task == nil {
utils.Debugf("getStatus: Error createTask: %s", err)
log.Debugf("getStatus: Error createTask: %s", err)
return 0, 0, "", "", err
}
if err := task.Run(); err != nil {
utils.Debugf("getStatus: Error Run: %s", err)
log.Debugf("getStatus: Error Run: %s", err)
return 0, 0, "", "", err
}
devinfo, err := task.GetInfo()
if err != nil {
utils.Debugf("getStatus: Error GetInfo: %s", err)
log.Debugf("getStatus: Error GetInfo: %s", err)
return 0, 0, "", "", err
}
if devinfo.Exists == 0 {
utils.Debugf("getStatus: Non existing device %s", name)
log.Debugf("getStatus: Non existing device %s", name)
return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
}
@ -491,7 +491,7 @@ func resumeDevice(name string) error {
}
func createDevice(poolName string, deviceId *int) error {
utils.Debugf("[devmapper] createDevice(poolName=%v, deviceId=%v)", poolName, *deviceId)
log.Debugf("[devmapper] createDevice(poolName=%v, deviceId=%v)", poolName, *deviceId)
for {
task, err := createTask(DeviceTargetMsg, poolName)
@ -542,8 +542,8 @@ func deleteDevice(poolName string, deviceId int) error {
}
func removeDevice(name string) error {
utils.Debugf("[devmapper] removeDevice START")
defer utils.Debugf("[devmapper] removeDevice END")
log.Debugf("[devmapper] removeDevice START")
defer log.Debugf("[devmapper] removeDevice END")
task, err := createTask(DeviceRemove, name)
if task == nil {
return err

Просмотреть файл

@ -1,4 +1,4 @@
// +build linux,amd64
// +build linux
package devmapper

Просмотреть файл

@ -1,9 +1,9 @@
// +build linux,amd64
// +build linux
package devmapper
import (
"github.com/dotcloud/docker/daemon/graphdriver/graphtest"
"github.com/docker/docker/daemon/graphdriver/graphtest"
"testing"
)

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше