mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-19 20:26:54 +00:00
Compare commits
151 Commits
v0.7.0-rc1
..
v0.6.4
| Author | SHA1 | Date | |
|---|---|---|---|
| e8da76605f | |||
| f16e60665b | |||
| 3ba9893786 | |||
| 6ee2964a4f | |||
| 49c62879b8 | |||
| 34745ee0f4 | |||
| d21bd5b964 | |||
| 82220ea2ad | |||
| 655b16c712 | |||
| eb2d0d4bf5 | |||
| cc851dbb3f | |||
| e40fd1e3e1 | |||
| dab84f7e8b | |||
| 7df8ec2093 | |||
| a2ffa637ce | |||
| ed6ca109bf | |||
| 6e48b73955 | |||
| 664acd2971 | |||
| 3acfc60028 | |||
| 0e64a4d7e7 | |||
| 104a86936a | |||
| df697b4318 | |||
| 27fc01271e | |||
| 97ab118fbb | |||
| fc659b68e4 | |||
| 99f6b43f4f | |||
| 2b92aa71f9 | |||
| f854529ae8 | |||
| e32debcf5f | |||
| 0e08e1d695 | |||
| 65b62307c3 | |||
| 03e5d94b12 | |||
| 0ca533ca35 | |||
| d94a027da6 | |||
| 7250f94874 | |||
| 4196c704f0 | |||
| adb48ef319 | |||
| ee7d9b0bde | |||
| 2f4526d5c4 | |||
| 261285c2c2 | |||
| cbc49d7d76 | |||
| 4dc90405e3 | |||
| b7abe9dab5 | |||
| cd455ca6fa | |||
| ca56b35e53 | |||
| ace059e409 | |||
| 9da2b3590e | |||
| e99ab1df97 | |||
| fde157425c | |||
| a0e5657cb6 | |||
| f4a98b4598 | |||
| 53a07d5475 | |||
| 93b258413f | |||
| c0bfb75e5f | |||
| 620bc5b9e1 | |||
| 6d6097cddf | |||
| 42277d0329 | |||
| d62df6b2b5 | |||
| 97779ff7cf | |||
| 9afa48a92f | |||
| ad723bbfe7 | |||
| f15cd71bb8 | |||
| 94bf5b0011 | |||
| d542318d63 | |||
| 6e3068700f | |||
| fac190959f | |||
| 1dcdc3deb7 | |||
| ee256ce6bf | |||
| 5fe699f813 | |||
| 7561e2570c | |||
| 640ca68e4f | |||
| f9b9dfc82d | |||
| c978f0fa5c | |||
| d91bf33df3 | |||
| 310a174260 | |||
| 2eb404ab14 | |||
| 60e426938e | |||
| 9041d6a47b | |||
| 8abd328f53 | |||
| ae780fb5ea | |||
| 152037f011 | |||
| 0367032bd1 | |||
| edca9f7e9c | |||
| e19cc1e843 | |||
| feabce3bcc | |||
| ded74b0bb3 | |||
| ffee5faae8 | |||
| c1bb33ea4d | |||
| 6545e53803 | |||
| d30806590c | |||
| 823174de4d | |||
| d5f9160441 | |||
| 37df1d92d8 | |||
| dabac81824 | |||
| bb62c281f5 | |||
| 3565bf8d00 | |||
| f4c17a202b | |||
| 9a4b0b9823 | |||
| 9107565d06 | |||
| 4fda3314d8 | |||
| e906485b07 | |||
| 1daf242c8b | |||
| 967010ae8c | |||
| d03022b9ca | |||
| 6d67ac2a1b | |||
| 2d425af1b1 | |||
| ed19693f50 | |||
| ee27444098 | |||
| 194c1dbbd8 | |||
| a9eac7eab4 | |||
| 7c28683f08 | |||
| c18e849451 | |||
| a665517151 | |||
| 0d3c899528 | |||
| 863eebe7bd | |||
| 3c5350ba92 | |||
| c52c95f97f | |||
| 85a5139665 | |||
| ad4b09cdb8 | |||
| e594c788e4 | |||
| f84dc1e908 | |||
| 826aaa0e9b | |||
| 1328be7c29 | |||
| ff4f32d58f | |||
| 462ac1efcf | |||
| 1332f400f7 | |||
| a292c04ddb | |||
| 37c4513cf9 | |||
| b584c21ca4 | |||
| c92507341d | |||
| 9f0feaa2e1 | |||
| 0f830aa431 | |||
| 5f05cb4817 | |||
| 4778039dd2 | |||
| b180770b1d | |||
| 4f5c2cbccc | |||
| cb21a40490 | |||
| 5025b4d094 | |||
| 509e25cf04 | |||
| 135c1fce90 | |||
| d517fd3d77 | |||
| 3894add8a9 | |||
| edd90d46c7 | |||
| c22e377a6d | |||
| 3f0d0075f8 | |||
| f8c9f11f06 | |||
| 80319add55 | |||
| edde4f55e0 | |||
| e37bb50628 | |||
| 5bd0437eed | |||
| e5239b9859 |
@@ -26,3 +26,15 @@ Roberto Hashioka <roberto_hashioka@hotmail.com>
|
||||
Konstantin Pelykh <kpelykh@zettaset.com>
|
||||
David Sissitka <me@dsissitka.com>
|
||||
Nolan Darilek <nolan@thewordnerd.info>
|
||||
<mastahyeti@gmail.com> <mastahyeti@users.noreply.github.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
Jordan Arentsen <blissdev@gmail.com>
|
||||
Daniel Garcia <daniel@danielgarcia.info>
|
||||
Miguel Angel Fernández <elmendalerenda@gmail.com>
|
||||
Bhiraj Butala <abhiraj.butala@gmail.com>
|
||||
Faiz Khan <faizkhan00@gmail.com>
|
||||
Victor Lyuboslavsky <victor@victoreda.com>
|
||||
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
|
||||
Matthew Mueller <mattmuelle@gmail.com>
|
||||
<mosoni@ebay.com> <mohitsoni1989@gmail.com>
|
||||
Shih-Yuan Lee <fourdollars@gmail.com>
|
||||
|
||||
@@ -5,64 +5,98 @@
|
||||
#
|
||||
Al Tobey <al@ooyala.com>
|
||||
Alex Gaynor <alex.gaynor@gmail.com>
|
||||
Alexander Larsson <alexl@redhat.com>
|
||||
Alexey Shamrin <shamrin@gmail.com>
|
||||
Andrea Luzzardi <aluzzardi@gmail.com>
|
||||
Andreas Savvides <andreas@editd.com>
|
||||
Andreas Tiefenthaler <at@an-ti.eu>
|
||||
Andrew Macgregor <andrew.macgregor@agworld.com.au>
|
||||
Andrew Munsell <andrew@wizardapps.net>
|
||||
Andrews Medina <andrewsmedina@gmail.com>
|
||||
Andy Rothfusz <github@metaliveblog.com>
|
||||
Andy Smith <github@anarkystic.com>
|
||||
Anthony Bishopric <git@anthonybishopric.com>
|
||||
Antony Messerli <amesserl@rackspace.com>
|
||||
Asbjørn Enge <asbjorn@hanafjedle.net>
|
||||
Barry Allard <barry.allard@gmail.com>
|
||||
Ben Toews <mastahyeti@gmail.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
Bhiraj Butala <abhiraj.butala@gmail.com>
|
||||
Bouke Haarsma <bouke@webatoom.nl>
|
||||
Brandon Liu <bdon@bdon.org>
|
||||
Brandon Philips <brandon@ifup.co>
|
||||
Brian McCallister <brianm@skife.org>
|
||||
Brian Olsen <brian@maven-group.org>
|
||||
Brian Shumate <brian@couchbase.com>
|
||||
Briehan Lombaard <briehan.lombaard@gmail.com>
|
||||
Bruno Bigras <bigras.bruno@gmail.com>
|
||||
Caleb Spare <cespare@gmail.com>
|
||||
Calen Pennington <cale@edx.org>
|
||||
Charles Hooper <charles.hooper@dotcloud.com>
|
||||
Christopher Currie <codemonkey+github@gmail.com>
|
||||
Colin Dunklau <colin.dunklau@gmail.com>
|
||||
Colin Rice <colin@daedrum.net>
|
||||
Dan Buch <d.buch@modcloth.com>
|
||||
Daniel Garcia <daniel@danielgarcia.info>
|
||||
Daniel Gasienica <daniel@gasienica.ch>
|
||||
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com>
|
||||
Daniel Nordberg <dnordberg@gmail.com>
|
||||
Daniel Robinson <gottagetmac@gmail.com>
|
||||
Daniel Von Fange <daniel@leancoder.com>
|
||||
Daniel YC Lin <dlin.tw@gmail.com>
|
||||
David Calavera <david.calavera@gmail.com>
|
||||
David Sissitka <me@dsissitka.com>
|
||||
Deni Bertovic <deni@kset.org>
|
||||
Dominik Honnef <dominik@honnef.co>
|
||||
Don Spaulding <donspauldingii@gmail.com>
|
||||
Dr Nic Williams <drnicwilliams@gmail.com>
|
||||
Dražen Lučanin <kermit666@gmail.com>
|
||||
Elias Probst <mail@eliasprobst.eu>
|
||||
Emily Rose <emily@contactvibe.com>
|
||||
Eric Hanchrow <ehanchrow@ine.com>
|
||||
Eric Myhre <hash@exultant.us>
|
||||
Erno Hopearuoho <erno.hopearuoho@gmail.com>
|
||||
Evan Phoenix <evan@fallingsnow.net>
|
||||
Evan Wies <evan@neomantra.net>
|
||||
ezbercih <cem.ezberci@gmail.com>
|
||||
Fabrizio Regini <freegenie@gmail.com>
|
||||
Faiz Khan <faizkhan00@gmail.com>
|
||||
Fareed Dudhia <fareeddudhia@googlemail.com>
|
||||
Flavio Castelli <fcastelli@suse.com>
|
||||
Francisco Souza <f@souza.cc>
|
||||
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
|
||||
Gabriel Monroy <gabriel@opdemand.com>
|
||||
Gareth Rushgrove <gareth@morethanseven.net>
|
||||
Greg Thornton <xdissent@me.com>
|
||||
Guillaume J. Charmes <guillaume.charmes@dotcloud.com>
|
||||
Guruprasad <lgp171188@gmail.com>
|
||||
Harley Laue <losinggeneration@gmail.com>
|
||||
Hector Castro <hectcastro@gmail.com>
|
||||
Hunter Blanks <hunter@twilio.com>
|
||||
Isao Jonas <isao.jonas@gmail.com>
|
||||
James Carr <james.r.carr@gmail.com>
|
||||
Jason McVetta <jason.mcvetta@gmail.com>
|
||||
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
|
||||
Jeff Lindsay <progrium@gmail.com>
|
||||
Jeremy Grosser <jeremy@synack.me>
|
||||
Jim Alateras <jima@comware.com.au>
|
||||
Jimmy Cuadra <jimmy@jimmycuadra.com>
|
||||
Joe Van Dyk <joe@tanga.com>
|
||||
Joffrey F <joffrey@dotcloud.com>
|
||||
Johan Euphrosine <proppy@google.com>
|
||||
John Costa <john.costa@gmail.com>
|
||||
Jon Wedaman <jweede@gmail.com>
|
||||
Jonas Pfenniger <jonas@pfenniger.name>
|
||||
Jonathan Mueller <j.mueller@apoveda.ch>
|
||||
Jonathan Rudenberg <jonathan@titanous.com>
|
||||
Joost Cassee <joost@cassee.net>
|
||||
Jordan Arentsen <blissdev@gmail.com>
|
||||
Joseph Anthony Pasquale Holsten <joseph@josephholsten.com>
|
||||
Julien Barbier <write0@gmail.com>
|
||||
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
|
||||
Karan Lyons <karan@karanlyons.com>
|
||||
Karl Grzeszczak <karl@karlgrz.com>
|
||||
Kawsar Saiyeed <kawsar.saiyeed@projiris.com>
|
||||
Keli Hu <dev@keli.hu>
|
||||
Ken Cochrane <kencochrane@gmail.com>
|
||||
Kevin Clark <kevin.clark@gmail.com>
|
||||
@@ -71,28 +105,45 @@ kim0 <email.ahmedkamal@googlemail.com>
|
||||
Kimbro Staken <kstaken@kstaken.com>
|
||||
Kiran Gangadharan <kiran.daredevil@gmail.com>
|
||||
Konstantin Pelykh <kpelykh@zettaset.com>
|
||||
Kyle Conroy <kyle.j.conroy@gmail.com>
|
||||
Laurie Voss <github@seldo.com>
|
||||
Louis Opter <kalessin@kalessin.fr>
|
||||
Manuel Meurer <manuel@krautcomputing.com>
|
||||
Marco Hennings <marco.hennings@freiheit.com>
|
||||
Marcus Farkas <toothlessgear@finitebox.com>
|
||||
Marcus Ramberg <marcus@nordaaker.com>
|
||||
Mark McGranaghan <mmcgrana@gmail.com>
|
||||
Martin Redmond <mrtodo@gmail.com>
|
||||
Marko Mikulicic <mmikulicic@gmail.com>
|
||||
Markus Fix <lispmeister@gmail.com>
|
||||
Martin Redmond <martin@tinychat.com>
|
||||
Matt Apperson <me@mattapperson.com>
|
||||
Matt Bachmann <bachmann.matt@gmail.com>
|
||||
Matthew Mueller <mattmuelle@gmail.com>
|
||||
Maxim Treskin <zerthurd@gmail.com>
|
||||
meejah <meejah@meejah.ca>
|
||||
Michael Crosby <crosby.michael@gmail.com>
|
||||
Michael Gorsuch <gorsuch@github.com>
|
||||
Miguel Angel Fernández <elmendalerenda@gmail.com>
|
||||
Mike Gaffney <mike@uberu.com>
|
||||
Mikhail Sobolev <mss@mawhrin.net>
|
||||
Mohit Soni <mosoni@ebay.com>
|
||||
Morten Siebuhr <sbhr@sbhr.dk>
|
||||
Nan Monnand Deng <monnand@gmail.com>
|
||||
Nate Jones <nate@endot.org>
|
||||
Nelson Chen <crazysim@gmail.com>
|
||||
Niall O'Higgins <niallo@unworkable.org>
|
||||
Nick Payne <nick@kurai.co.uk>
|
||||
Nick Stenning <nick.stenning@digital.cabinet-office.gov.uk>
|
||||
Nick Stinemates <nick@stinemates.org>
|
||||
Nolan Darilek <nolan@thewordnerd.info>
|
||||
odk- <github@odkurzacz.org>
|
||||
Pascal Borreli <pascal@borreli.com>
|
||||
Paul Bowsher <pbowsher@globalpersonals.co.uk>
|
||||
Paul Hammond <paul@paulhammond.org>
|
||||
Phil Spitler <pspitler@gmail.com>
|
||||
Piotr Bogdan <ppbogdan@gmail.com>
|
||||
pysqz <randomq@126.com>
|
||||
Ramon van Alteren <ramon@vanalteren.nl>
|
||||
Renato Riccieri Santos Zannon <renato.riccieri@gmail.com>
|
||||
Rhys Hiltner <rhys@twitch.tv>
|
||||
Robert Obryk <robryk@gmail.com>
|
||||
@@ -100,14 +151,21 @@ Roberto Hashioka <roberto_hashioka@hotmail.com>
|
||||
Ryan Fowler <rwfowler@gmail.com>
|
||||
Sam Alba <sam.alba@gmail.com>
|
||||
Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
|
||||
Sean P. Kane <skane@newrelic.com>
|
||||
Shawn Siefkas <shawn.siefkas@meredith.com>
|
||||
Shih-Yuan Lee <fourdollars@gmail.com>
|
||||
Silas Sewell <silas@sewell.org>
|
||||
Solomon Hykes <solomon@dotcloud.com>
|
||||
Song Gao <song@gao.io>
|
||||
Sridatta Thatipamala <sthatipamala@gmail.com>
|
||||
Sridhar Ratnakumar <sridharr@activestate.com>
|
||||
Steeve Morin <steeve.morin@gmail.com>
|
||||
Stefan Praszalowicz <stefan@greplin.com>
|
||||
Thatcher Peskens <thatcher@dotcloud.com>
|
||||
Thermionix <bond711@gmail.com>
|
||||
Thijs Terlouw <thijsterlouw@gmail.com>
|
||||
Thomas Bikeev <thomas.bikeev@mac.com>
|
||||
Thomas Frössman <thomasf@jossystem.se>
|
||||
Thomas Hansen <thomas.hansen@gmail.com>
|
||||
Tianon Gravi <admwiggin@gmail.com>
|
||||
Tim Terhorst <mynamewastaken+git@gmail.com>
|
||||
@@ -115,8 +173,16 @@ Tobias Bieniek <Tobias.Bieniek@gmx.de>
|
||||
Tobias Schmidt <ts@soundcloud.com>
|
||||
Tobias Schwab <tobias.schwab@dynport.de>
|
||||
Tom Hulihan <hulihan.tom159@gmail.com>
|
||||
Tommaso Visconti <tommaso.visconti@gmail.com>
|
||||
Tyler Brock <tyler.brock@gmail.com>
|
||||
unclejack <unclejacksons@gmail.com>
|
||||
Victor Coisne <victor.coisne@dotcloud.com>
|
||||
Victor Lyuboslavsky <victor@victoreda.com>
|
||||
Victor Vieux <victor.vieux@dotcloud.com>
|
||||
Vincent Bernat <bernat@luffy.cx>
|
||||
Vivek Agarwal <me@vivek.im>
|
||||
Vladimir Kirillov <proger@wilab.org.ua>
|
||||
Walter Stanish <walter@pratyeka.org>
|
||||
Wes Morgan <cap10morgan@gmail.com>
|
||||
Will Dietz <w@wdtz.org>
|
||||
Zaiste! <oh@zaiste.net>
|
||||
|
||||
@@ -1,5 +1,83 @@
|
||||
# Changelog
|
||||
|
||||
## 0.6.4 (2013-10-15)
|
||||
- Runtime: Add cleanup of container when Start() fails
|
||||
- Testing: Catch errClosing error when TCP and UDP proxies are terminated
|
||||
- Testing: Add aggregated docker-ci email report
|
||||
- Testing: Remove a few errors in tests
|
||||
* Contrib: Reorganize contributed completion scripts to add zsh completion
|
||||
* Contrib: Add vim syntax highlighting for Dockerfiles from @honza
|
||||
* Runtime: Add better comments to utils/stdcopy.go
|
||||
- Testing: add cleanup to remove leftover containers
|
||||
* Documentation: Document how to edit and release docs
|
||||
* Documentation: Add initial draft of the Docker infrastructure doc
|
||||
* Contrib: Add mkimage-arch.sh
|
||||
- Builder: Abort build if mergeConfig returns an error and fix duplicate error message
|
||||
- Runtime: Remove error messages which are not actually errors
|
||||
* Testing: Only run certain tests with TESTFLAGS='-run TestName' make.sh
|
||||
* Testing: Prevent docker-ci to test closing PRs
|
||||
- Documentation: Minor updates to postgresql_service.rst
|
||||
* Testing: Add nightly release to docker-ci
|
||||
* Hack: Improve network performance for VirtualBox
|
||||
* Hack: Add vagrant user to the docker group
|
||||
* Runtime: Add utils.Errorf for error logging
|
||||
- Packaging: Remove deprecated packaging directory
|
||||
* Hack: Revamp install.sh to be usable by more people, and to use official install methods whenever possible (apt repo, portage tree, etc.)
|
||||
- Hack: Fix contrib/mkimage-debian.sh apt caching prevention
|
||||
* Documentation: Clarify LGTM process to contributors
|
||||
- Documentation: Small fixes to parameter names in docs for ADD command
|
||||
* Runtime: Record termination time in state.
|
||||
- Registry: Use correct auth config when logging in.
|
||||
- Documentation: Corrected error in the package name
|
||||
* Documentation: Document what `vagrant up` is actually doing
|
||||
- Runtime: Fix `docker rm` with volumes
|
||||
- Runtime: Use empty string so TempDir uses the OS's temp dir automatically
|
||||
- Runtime: Make sure to close the network allocators
|
||||
* Testing: Replace panic by log.Fatal in tests
|
||||
+ Documentation: improve doc search results
|
||||
- Runtime: Fix some error cases where a HTTP body might not be closed
|
||||
* Hack: Add proper bash completion for "docker push"
|
||||
* Documentation: Add devenvironment link to CONTRIBUTING.md
|
||||
* Documentation: Cleanup whitespace in API 1.5 docs
|
||||
* Documentation: use angle brackets in MAINTAINER example email
|
||||
- Testing: Increase TestRunDetach timeout
|
||||
* Documentation: Fix help text for -v option
|
||||
+ Hack: Added Dockerfile.tmLanguage to contrib
|
||||
+ Runtime: Autorestart containers by default
|
||||
* Testing: Adding more tests around auth.ResolveAuthConfig
|
||||
* Hack: Configured FPM to make /etc/init/docker.conf a config file
|
||||
* Hack: Add xz utils as a runtime dep
|
||||
* Documentation: Add `apt-get install curl` to Ubuntu docs
|
||||
* Documentation: Remove Gentoo install notes about #1422 workaround
|
||||
* Documentation: Fix Ping endpoint documentation
|
||||
* Runtime: Bump vendor kr/pty to commit 3b1f6487b (syscall.O_NOCTTY)
|
||||
* Runtime: lxc: Allow set_file_cap capability in container
|
||||
* Documentation: Update archlinux.rst
|
||||
- Documentation: Fix ironic typo in changelog
|
||||
* Documentation: Add explanation for export restrictions
|
||||
* Hack: Add cleanup/refactor portion of #2010 for hack and Dockerfile updates
|
||||
+ Documentation: Changes to a new style for the docs. Includes version switcher.
|
||||
* Documentation: Formatting, add information about multiline json
|
||||
+ Hack: Add contrib/mkimage-centos.sh back (from #1621), and associated documentation link
|
||||
- Runtime: Fix panic with wrong dockercfg file
|
||||
- Runtime: Fix the attach behavior with -i
|
||||
* Documentation: Add .dockercfg doc
|
||||
- Runtime: Move run -rm to the cli only
|
||||
* Hack: Enable SSH Agent forwarding in Vagrant VM
|
||||
+ Runtime: Add -rm to docker run for removing a container on exit
|
||||
* Documentation: Improve registry and index REST API documentation
|
||||
* Runtime: Split stdout stderr
|
||||
- Documentation: Replace deprecated upgrading reference to docker-latest.tgz, which hasn't been updated since 0.5.3
|
||||
* Documentation: Update Gentoo installation documentation now that we're in the portage tree proper
|
||||
- Registry: Fix the error message so it is the same as the regex
|
||||
* Runtime: Always create a new session for the container
|
||||
* Hack: Add several of the small make.sh fixes from #1920, and make the output more consistent and contributor-friendly
|
||||
* Documentation: Various command fixes in postgres example
|
||||
* Documentation: Cleanup and reorganize docs and tooling for contributors and maintainers
|
||||
- Documentation: Minor spelling correction of protocoll -> protocol
|
||||
* Hack: Several small tweaks/fixes for contrib/mkimage-debian.sh
|
||||
+ Hack: Add @tianon to hack/MAINTAINERS
|
||||
|
||||
## 0.6.3 (2013-09-23)
|
||||
* Packaging: Update tar vendor dependency
|
||||
- Client: Fix detach issue
|
||||
|
||||
+19
-2
@@ -3,6 +3,10 @@
|
||||
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.
|
||||
|
||||
## Build Environment
|
||||
|
||||
For instructions on setting up your development environment, please see our dedicated [dev environment setup docs](http://docs.docker.io/en/latest/contributing/devenvironment/).
|
||||
|
||||
## Contribution guidelines
|
||||
|
||||
### Pull requests are always welcome
|
||||
@@ -55,8 +59,10 @@ 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
|
||||
your branch before submitting a pull request.
|
||||
|
||||
Make sure you include relevant updates or additions to documentation when
|
||||
creating or modifying features.
|
||||
Update the documentation when creating or modifying features. Test
|
||||
your documentation changes for clarity, concision, and correctness, as
|
||||
well as a clean docmuent build. See ``docs/README.md`` for more
|
||||
information on building the docs and how docs get released.
|
||||
|
||||
Write clean code. Universally formatted code promotes ease of writing, reading,
|
||||
and maintenance. Always run `go fmt` before committing your changes. Most
|
||||
@@ -89,6 +95,17 @@ name and email address match your git configuration. The AUTHORS file is
|
||||
regenerated occasionally from the git commit history, so a mismatch may result
|
||||
in your changes being overwritten.
|
||||
|
||||
### Approval
|
||||
|
||||
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
|
||||
|
||||
For more details see [MAINTAINERS.md](hack/MAINTAINERS.md)
|
||||
|
||||
### How can I become a maintainer?
|
||||
|
||||
|
||||
+10
-7
@@ -12,7 +12,7 @@
|
||||
#
|
||||
#
|
||||
# # Run the test suite:
|
||||
# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker go test -v
|
||||
# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh test
|
||||
#
|
||||
# # Publish a release:
|
||||
# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined \
|
||||
@@ -33,14 +33,16 @@ run apt-get update
|
||||
run apt-get install -y -q curl
|
||||
run apt-get install -y -q git
|
||||
run apt-get install -y -q mercurial
|
||||
run apt-get install -y -q build-essential libsqlite3-dev
|
||||
run apt-get install -y -q build-essential
|
||||
|
||||
# Install Go
|
||||
run curl -s https://go.googlecode.com/files/go1.2rc1.src.tar.gz | tar -v -C /usr/local -xz
|
||||
run cd /usr/local/go/src && ./make.bash && go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std
|
||||
env PATH /usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
|
||||
# Install Go from source (for eventual cross-compiling)
|
||||
env CGO_ENABLED 0
|
||||
run curl -s https://go.googlecode.com/files/go1.1.2.src.tar.gz | tar -v -C / -xz && mv /go /goroot
|
||||
run cd /goroot/src && ./make.bash
|
||||
env GOROOT /goroot
|
||||
env PATH $PATH:/goroot/bin
|
||||
env GOPATH /go:/go/src/github.com/dotcloud/docker/vendor
|
||||
run cd /tmp && echo 'package main' > t.go && go test -a -i -v
|
||||
|
||||
# Ubuntu stuff
|
||||
run apt-get install -y -q ruby1.9.3 rubygems libffi-dev
|
||||
run gem install --no-rdoc --no-ri fpm
|
||||
@@ -55,6 +57,7 @@ run /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_
|
||||
# Runtime dependencies
|
||||
run apt-get install -y -q iptables
|
||||
run apt-get install -y -q lxc
|
||||
run apt-get install -y -q aufs-tools
|
||||
|
||||
volume /var/lib/docker
|
||||
workdir /go/src/github.com/dotcloud/docker
|
||||
|
||||
@@ -15,23 +15,16 @@ 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
|
||||
* Clean up the Makefile, it's a mess
|
||||
* docker build: on non-existent local path for ADD, don't show full absolute path on the host
|
||||
* mount into /dockerinit rather than /sbin/init
|
||||
* docker tag foo REPO:TAG
|
||||
* use size header for progress bar in pull
|
||||
* Clean up context upload in build!!!
|
||||
* Parallel pull
|
||||
* Ensure /proc/sys/net/ipv4/ip_forward is 1
|
||||
* Force DNS to public!
|
||||
* Always generate a resolv.conf per container, to avoid changing resolv.conf under thne container's feet
|
||||
* Save metadata with import/export
|
||||
* Save metadata with import/export (#1974)
|
||||
* Upgrade dockerd without stopping containers
|
||||
* bring back git revision info, looks like it was lost
|
||||
* Simple command to remove all untagged images
|
||||
* Simple command to remove all untagged images (`docker rmi $(docker images | awk '/^<none>/ { print $3 }')`)
|
||||
* Simple command to clean up containers for disk space
|
||||
* Caching after an ADD
|
||||
* entry point config
|
||||
* bring back git revision info, looks like it was lost
|
||||
* Caching after an ADD (#880)
|
||||
* Clean up the ProgressReader api, it's a PITA to use
|
||||
* Use netlink instead of iproute2/iptables (#925)
|
||||
|
||||
Vendored
+4
@@ -39,6 +39,8 @@ Vagrant::Config.run do |config|
|
||||
"echo 'Installation of VBox Guest Additions is proceeding in the background.'; " \
|
||||
"echo '\"vagrant reload\" can be used in about 2 minutes to activate the new guest additions.'; "
|
||||
end
|
||||
# Add vagrant user to the docker group
|
||||
pkg_cmd << "usermod -a -G docker vagrant; "
|
||||
# Activate new kernel
|
||||
pkg_cmd << "shutdown -r +1; "
|
||||
config.vm.provision :shell, :inline => pkg_cmd
|
||||
@@ -78,6 +80,8 @@ Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
config.vm.box = BOX_NAME
|
||||
config.vm.box_url = BOX_URI
|
||||
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
|
||||
vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
"github.com/dotcloud/docker/gograph"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/gorilla/mux"
|
||||
"io"
|
||||
@@ -15,7 +14,6 @@ import (
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
@@ -71,9 +69,12 @@ func httpError(w http.ResponseWriter, err error) {
|
||||
statusCode = http.StatusUnauthorized
|
||||
} else if strings.Contains(err.Error(), "hasn't been activated") {
|
||||
statusCode = http.StatusForbidden
|
||||
}
|
||||
utils.Debugf("[error %d] %s", statusCode, err)
|
||||
http.Error(w, err.Error(), statusCode)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
|
||||
http.Error(w, err.Error(), statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||
@@ -104,7 +105,7 @@ func getBoolParam(value string) (bool, error) {
|
||||
func matchesContentType(contentType, expectedType string) bool {
|
||||
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
utils.Debugf("Error parsing media type: %s error: %s", contentType, err.Error())
|
||||
utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
|
||||
}
|
||||
return err == nil && mimetype == expectedType
|
||||
}
|
||||
@@ -135,7 +136,6 @@ func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
name = decodeName(name)
|
||||
if err := srv.ContainerKill(name); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -148,10 +148,9 @@ func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
name = decodeName(name)
|
||||
|
||||
if err := srv.ContainerExport(name, w); err != nil {
|
||||
utils.Debugf("%s", err)
|
||||
utils.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -196,7 +195,7 @@ func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
|
||||
_, err = wf.Write(b)
|
||||
if err != nil {
|
||||
// On error, evict the listener
|
||||
utils.Debugf("%s", err)
|
||||
utils.Errorf("%s", err)
|
||||
srv.Lock()
|
||||
delete(srv.listeners, r.RemoteAddr)
|
||||
srv.Unlock()
|
||||
@@ -350,8 +349,8 @@ func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Req
|
||||
return err
|
||||
}
|
||||
config := &Config{}
|
||||
if err := json.NewDecoder(r.Body).Decode(config); err != nil {
|
||||
utils.Debugf("%s", err)
|
||||
if err := json.NewDecoder(r.Body).Decode(config); err != nil && err != io.EOF {
|
||||
utils.Errorf("%s", err)
|
||||
}
|
||||
repo := r.Form.Get("repo")
|
||||
tag := r.Form.Get("tag")
|
||||
@@ -516,19 +515,16 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
|
||||
return err
|
||||
}
|
||||
|
||||
if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
|
||||
if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
|
||||
out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns))
|
||||
config.Dns = defaultDns
|
||||
}
|
||||
|
||||
id, warnings, err := srv.ContainerCreate(config)
|
||||
id, err := srv.ContainerCreate(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.ID = id
|
||||
for _, warning := range warnings {
|
||||
out.Warnings = append(out.Warnings, warning)
|
||||
}
|
||||
|
||||
if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
|
||||
log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
|
||||
@@ -559,7 +555,6 @@ func postContainersRestart(srv *Server, version float64, w http.ResponseWriter,
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
name = decodeName(name)
|
||||
if err := srv.ContainerRestart(name, t); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -575,18 +570,12 @@ func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *ht
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
name = decodeName(name)
|
||||
|
||||
removeVolume, err := getBoolParam(r.Form.Get("v"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
removeLink, err := getBoolParam(r.Form.Get("link"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := srv.ContainerDestroy(name, removeVolume, removeLink); err != nil {
|
||||
if err := srv.ContainerDestroy(name, removeVolume); err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
@@ -632,12 +621,7 @@ func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
var err error
|
||||
name := vars["name"]
|
||||
name = decodeName(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := srv.ContainerStart(name, hostConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -658,7 +642,6 @@ func postContainersStop(srv *Server, version float64, w http.ResponseWriter, r *
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
name = decodeName(name)
|
||||
|
||||
if err := srv.ContainerStop(name, t); err != nil {
|
||||
return err
|
||||
@@ -672,8 +655,6 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
name = decodeName(name)
|
||||
|
||||
status, err := srv.ContainerWait(name)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -733,7 +714,6 @@ func postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
name = decodeName(name)
|
||||
|
||||
c, err := srv.ContainerInspect(name)
|
||||
if err != nil {
|
||||
@@ -815,7 +795,7 @@ func wsContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *
|
||||
defer ws.Close()
|
||||
|
||||
if err := srv.ContainerAttach(name, logs, stream, stdin, stdout, stderr, ws, ws, ws); err != nil {
|
||||
utils.Debugf("Error: %s", err)
|
||||
utils.Errorf("Error: %s", err)
|
||||
}
|
||||
})
|
||||
h.ServeHTTP(w, r)
|
||||
@@ -928,8 +908,7 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
|
||||
b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput, !noCache, rm)
|
||||
id, err := b.Build(context)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "Error build: %s\n", err)
|
||||
return err
|
||||
return fmt.Errorf("Error build: %s", err)
|
||||
}
|
||||
if repoName != "" {
|
||||
srv.runtime.repositories.Set(repoName, tag, id, false)
|
||||
@@ -961,7 +940,7 @@ func postContainersCopy(srv *Server, version float64, w http.ResponseWriter, r *
|
||||
}
|
||||
|
||||
if err := srv.ContainerCopy(name, copyData.Resource, w); err != nil {
|
||||
utils.Debugf("%s", err.Error())
|
||||
utils.Errorf("%s", err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -996,7 +975,7 @@ func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute s
|
||||
if err != nil {
|
||||
version = APIVERSION
|
||||
}
|
||||
if srv.runtime.config.EnableCors {
|
||||
if srv.enableCors {
|
||||
writeCorsHeaders(w, r)
|
||||
}
|
||||
|
||||
@@ -1006,69 +985,12 @@ func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute s
|
||||
}
|
||||
|
||||
if err := handlerFunc(srv, version, w, r, mux.Vars(r)); err != nil {
|
||||
utils.Debugf("Error: %s", err)
|
||||
utils.Errorf("Error: %s", err)
|
||||
httpError(w, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getContainersLinks(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
runtime := srv.runtime
|
||||
|
||||
out := []APILink{}
|
||||
err := runtime.containerGraph.Walk("/", func(p string, e *gograph.Entity) error {
|
||||
if container := runtime.Get(e.ID()); container != nil {
|
||||
out = append(out, APILink{
|
||||
Path: p,
|
||||
ContainerID: container.ID,
|
||||
Image: runtime.repositories.ImageName(container.Image),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}, -1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeJSON(w, http.StatusOK, out)
|
||||
}
|
||||
|
||||
func postContainerLink(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
values := make(map[string]string)
|
||||
if matchesContentType(r.Header.Get("Content-Type"), "application/json") && r.Body != nil {
|
||||
defer r.Body.Close()
|
||||
|
||||
dec := json.NewDecoder(r.Body)
|
||||
if err := dec.Decode(&values); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Invalid json body")
|
||||
}
|
||||
currentName := values["currentName"]
|
||||
newName := values["newName"]
|
||||
|
||||
if currentName == "" {
|
||||
return fmt.Errorf("currentName cannot be empty")
|
||||
}
|
||||
if newName == "" {
|
||||
return fmt.Errorf("newName cannot be empty")
|
||||
}
|
||||
|
||||
if err := srv.runtime.RenameLink(currentName, newName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeName(name string) string {
|
||||
s, _ := url.QueryUnescape(name)
|
||||
return s
|
||||
}
|
||||
|
||||
func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
||||
r := mux.NewRouter()
|
||||
|
||||
@@ -1089,7 +1011,6 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
||||
"/containers/{name:.*}/json": getContainersByName,
|
||||
"/containers/{name:.*}/top": getContainersTop,
|
||||
"/containers/{name:.*}/attach/ws": wsContainersAttach,
|
||||
"/containers/links": getContainersLinks,
|
||||
},
|
||||
"POST": {
|
||||
"/auth": postAuth,
|
||||
@@ -1108,7 +1029,6 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
||||
"/containers/{name:.*}/resize": postContainersResize,
|
||||
"/containers/{name:.*}/attach": postContainersAttach,
|
||||
"/containers/{name:.*}/copy": postContainersCopy,
|
||||
"/containers/link": postContainerLink,
|
||||
},
|
||||
"DELETE": {
|
||||
"/containers/{name:.*}": deleteContainers,
|
||||
|
||||
+17
-15
@@ -1,5 +1,7 @@
|
||||
package docker
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type APIHistory struct {
|
||||
ID string `json:"Id"`
|
||||
Tags []string `json:",omitempty"`
|
||||
@@ -50,18 +52,17 @@ type APIContainers struct {
|
||||
Ports []APIPort
|
||||
SizeRw int64
|
||||
SizeRootFs int64
|
||||
Names []string
|
||||
}
|
||||
|
||||
func (self *APIContainers) ToLegacy() APIContainersOld {
|
||||
return APIContainersOld{
|
||||
ID: self.ID,
|
||||
Image: self.Image,
|
||||
Command: self.Command,
|
||||
Created: self.Created,
|
||||
Status: self.Status,
|
||||
Ports: displayablePorts(self.Ports),
|
||||
SizeRw: self.SizeRw,
|
||||
ID: self.ID,
|
||||
Image: self.Image,
|
||||
Command: self.Command,
|
||||
Created: self.Created,
|
||||
Status: self.Status,
|
||||
Ports: displayablePorts(self.Ports),
|
||||
SizeRw: self.SizeRw,
|
||||
SizeRootFs: self.SizeRootFs,
|
||||
}
|
||||
}
|
||||
@@ -95,7 +96,14 @@ type APIPort struct {
|
||||
PrivatePort int64
|
||||
PublicPort int64
|
||||
Type string
|
||||
IP string
|
||||
}
|
||||
|
||||
func (port *APIPort) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]interface{}{
|
||||
"PrivatePort": port.PrivatePort,
|
||||
"PublicPort": port.PublicPort,
|
||||
"Type": port.Type,
|
||||
})
|
||||
}
|
||||
|
||||
type APIVersion struct {
|
||||
@@ -121,9 +129,3 @@ type APICopy struct {
|
||||
Resource string
|
||||
HostPath string
|
||||
}
|
||||
|
||||
type APILink struct {
|
||||
Path string
|
||||
ContainerID string
|
||||
Image string
|
||||
}
|
||||
|
||||
+45
-20
@@ -5,6 +5,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"net"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -40,6 +42,25 @@ func TestGetBoolParam(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TesthttpError(t *testing.T) {
|
||||
r := httptest.NewRecorder()
|
||||
|
||||
httpError(r, fmt.Errorf("No such method"))
|
||||
if r.Code != http.StatusNotFound {
|
||||
t.Fatalf("Expected %d, got %d", http.StatusNotFound, r.Code)
|
||||
}
|
||||
|
||||
httpError(r, fmt.Errorf("This accound hasn't been activated"))
|
||||
if r.Code != http.StatusForbidden {
|
||||
t.Fatalf("Expected %d, got %d", http.StatusForbidden, r.Code)
|
||||
}
|
||||
|
||||
httpError(r, fmt.Errorf("Some error"))
|
||||
if r.Code != http.StatusInternalServerError {
|
||||
t.Fatalf("Expected %d, got %d", http.StatusInternalServerError, r.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVersion(t *testing.T) {
|
||||
var err error
|
||||
runtime := mkRuntime(t)
|
||||
@@ -243,7 +264,11 @@ func TestGetImagesJSON(t *testing.T) {
|
||||
t.Fatalf("Error expected, received none")
|
||||
}
|
||||
|
||||
httpError(r4, err)
|
||||
if !strings.HasPrefix(err.Error(), "Bad parameter") {
|
||||
t.Fatalf("Error should starts with \"Bad parameter\"")
|
||||
}
|
||||
http.Error(r4, err.Error(), http.StatusBadRequest)
|
||||
|
||||
if r4.Code != http.StatusBadRequest {
|
||||
t.Fatalf("%d Bad Request expected, received %d\n", http.StatusBadRequest, r4.Code)
|
||||
}
|
||||
@@ -321,7 +346,7 @@ func TestGetContainersJSON(t *testing.T) {
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"echo", "test"},
|
||||
})
|
||||
@@ -358,7 +383,7 @@ func TestGetContainersExport(t *testing.T) {
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
// Create a container and remove a file
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"touch", "/test"},
|
||||
@@ -408,7 +433,7 @@ func TestGetContainersChanges(t *testing.T) {
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
// Create a container and remove a file
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/rm", "/etc/passwd"},
|
||||
@@ -454,7 +479,7 @@ func TestGetContainersTop(t *testing.T) {
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/sh", "-c", "cat"},
|
||||
@@ -536,7 +561,7 @@ func TestGetContainersByName(t *testing.T) {
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
// Create a container and remove a file
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"echo", "test"},
|
||||
@@ -567,7 +592,7 @@ func TestPostCommit(t *testing.T) {
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
// Create a container and remove a file
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"touch", "/test"},
|
||||
@@ -661,7 +686,7 @@ func TestPostContainersKill(t *testing.T) {
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/cat"},
|
||||
@@ -703,7 +728,7 @@ func TestPostContainersRestart(t *testing.T) {
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/cat"},
|
||||
@@ -757,7 +782,7 @@ func TestPostContainersStart(t *testing.T) {
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/cat"},
|
||||
@@ -776,6 +801,8 @@ func TestPostContainersStart(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := postContainersStart(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -807,7 +834,7 @@ func TestPostContainersStop(t *testing.T) {
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/cat"},
|
||||
@@ -854,7 +881,7 @@ func TestPostContainersWait(t *testing.T) {
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/sleep", "1"},
|
||||
@@ -896,7 +923,7 @@ func TestPostContainersAttach(t *testing.T) {
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/cat"},
|
||||
@@ -985,7 +1012,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/sh", "-c", "/bin/cat >&2"},
|
||||
@@ -1077,7 +1104,7 @@ func TestDeleteContainers(t *testing.T) {
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"touch", "/test"},
|
||||
})
|
||||
@@ -1115,8 +1142,7 @@ func TestOptionsRoute(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
|
||||
runtime.config.EnableCors = true
|
||||
srv := &Server{runtime: runtime}
|
||||
srv := &Server{runtime: runtime, enableCors: true}
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
router, err := createRouter(srv, false)
|
||||
@@ -1139,8 +1165,7 @@ func TestGetEnabledCors(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
|
||||
runtime.config.EnableCors = true
|
||||
srv := &Server{runtime: runtime}
|
||||
srv := &Server{runtime: runtime, enableCors: true}
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
|
||||
@@ -1267,7 +1292,7 @@ func TestPostContainersCopy(t *testing.T) {
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
// Create a container and remove a file
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"touch", "/test.txt"},
|
||||
|
||||
+83
-7
@@ -75,20 +75,31 @@ func TestCreateAccount(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSameAuthDataPostSave(t *testing.T) {
|
||||
func setupTempConfigFile() (*ConfigFile, error) {
|
||||
root, err := ioutil.TempDir("", "docker-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
configFile := &ConfigFile{
|
||||
rootPath: root,
|
||||
Configs: make(map[string]AuthConfig, 1),
|
||||
Configs: make(map[string]AuthConfig),
|
||||
}
|
||||
|
||||
configFile.Configs["testIndex"] = AuthConfig{
|
||||
Username: "docker-user",
|
||||
Password: "docker-pass",
|
||||
Email: "docker@docker.io",
|
||||
for _, registry := range []string{"testIndex", IndexServerAddress()} {
|
||||
configFile.Configs[registry] = AuthConfig{
|
||||
Username: "docker-user",
|
||||
Password: "docker-pass",
|
||||
Email: "docker@docker.io",
|
||||
}
|
||||
}
|
||||
|
||||
return configFile, nil
|
||||
}
|
||||
|
||||
func TestSameAuthDataPostSave(t *testing.T) {
|
||||
configFile, err := setupTempConfigFile()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = SaveConfig(configFile)
|
||||
@@ -110,3 +121,68 @@ func TestSameAuthDataPostSave(t *testing.T) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveAuthConfigIndexServer(t *testing.T) {
|
||||
configFile, err := setupTempConfigFile()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, registry := range []string{"", IndexServerAddress()} {
|
||||
resolved := configFile.ResolveAuthConfig(registry)
|
||||
if resolved != configFile.Configs[IndexServerAddress()] {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveAuthConfigFullURL(t *testing.T) {
|
||||
configFile, err := setupTempConfigFile()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
registryAuth := AuthConfig{
|
||||
Username: "foo-user",
|
||||
Password: "foo-pass",
|
||||
Email: "foo@example.com",
|
||||
}
|
||||
localAuth := AuthConfig{
|
||||
Username: "bar-user",
|
||||
Password: "bar-pass",
|
||||
Email: "bar@example.com",
|
||||
}
|
||||
configFile.Configs["https://registry.example.com/v1/"] = registryAuth
|
||||
configFile.Configs["http://localhost:8000/v1/"] = localAuth
|
||||
|
||||
validRegistries := map[string][]string{
|
||||
"https://registry.example.com/v1/": {
|
||||
"https://registry.example.com/v1/",
|
||||
"http://registry.example.com/v1/",
|
||||
"registry.example.com",
|
||||
"registry.example.com/v1/",
|
||||
},
|
||||
"http://localhost:8000/v1/": {
|
||||
"https://localhost:8000/v1/",
|
||||
"http://localhost:8000/v1/",
|
||||
"localhost:8000",
|
||||
"localhost:8000/v1/",
|
||||
},
|
||||
}
|
||||
|
||||
for configKey, registries := range validRegistries {
|
||||
for _, registry := range registries {
|
||||
var (
|
||||
configured AuthConfig
|
||||
ok bool
|
||||
)
|
||||
resolved := configFile.ResolveAuthConfig(registry)
|
||||
if configured, ok = configFile.Configs[configKey]; !ok {
|
||||
t.Fail()
|
||||
}
|
||||
if resolved.Email != configured.Email {
|
||||
t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-8
@@ -187,9 +187,6 @@ func (b *buildFile) CmdCmd(args string) error {
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdExpose(args string) error {
|
||||
if strings.Contains(args, ":") {
|
||||
return fmt.Errorf("EXPOSE cannot be used to bind to a host ip or port")
|
||||
}
|
||||
ports := strings.Split(args, " ")
|
||||
b.config.PortSpecs = append(ports, b.config.PortSpecs...)
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
|
||||
@@ -335,7 +332,7 @@ func (b *buildFile) CmdAdd(args string) error {
|
||||
|
||||
b.config.Image = b.image
|
||||
// Create the container and start it
|
||||
container, _, err := b.runtime.Create(b.config)
|
||||
container, err := b.runtime.Create(b.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -370,7 +367,7 @@ func (b *buildFile) run() (string, error) {
|
||||
b.config.Image = b.image
|
||||
|
||||
// Create the container and start it
|
||||
c, _, err := b.runtime.Create(b.config)
|
||||
c, err := b.runtime.Create(b.config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -426,7 +423,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
|
||||
}
|
||||
}
|
||||
|
||||
container, _, err := b.runtime.Create(b.config)
|
||||
container, err := b.runtime.Create(b.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -461,9 +458,8 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
|
||||
var lineContinuation = regexp.MustCompile(`\s*\\\s*\n`)
|
||||
|
||||
func (b *buildFile) Build(context io.Reader) (string, error) {
|
||||
// FIXME: @creack any reason for using /tmp instead of ""?
|
||||
// FIXME: @creack "name" is a terrible variable name
|
||||
name, err := ioutil.TempDir("/tmp", "docker-build")
|
||||
name, err := ioutil.TempDir("", "docker-build")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
+21
-92
@@ -98,7 +98,6 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
||||
{"kill", "Kill a running container"},
|
||||
{"login", "Register or Login to the docker registry server"},
|
||||
{"logs", "Fetch the logs of a container"},
|
||||
{"ls", "List links for containers"},
|
||||
{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
|
||||
{"top", "Lookup the running processes of a container"},
|
||||
{"ps", "List containers"},
|
||||
@@ -304,7 +303,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
||||
}
|
||||
|
||||
cli.LoadConfigFile()
|
||||
authconfig, ok := cli.configFile.Configs[auth.IndexServerAddress()]
|
||||
authconfig, ok := cli.configFile.Configs[serverAddress]
|
||||
if !ok {
|
||||
authconfig = auth.AuthConfig{}
|
||||
}
|
||||
@@ -419,7 +418,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
|
||||
var out APIVersion
|
||||
err = json.Unmarshal(body, &out)
|
||||
if err != nil {
|
||||
utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
|
||||
utils.Errorf("Error unmarshal: body: %s, err: %s\n", body, err)
|
||||
return err
|
||||
}
|
||||
if out.Version != "" {
|
||||
@@ -511,8 +510,7 @@ func (cli *DockerCli) CmdStop(args ...string) error {
|
||||
v.Set("t", strconv.Itoa(*nSeconds))
|
||||
|
||||
for _, name := range cmd.Args() {
|
||||
encName := cleanName(name)
|
||||
_, _, err := cli.call("POST", "/containers/"+encName+"/stop?"+v.Encode(), nil)
|
||||
_, _, err := cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
} else {
|
||||
@@ -537,7 +535,6 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
|
||||
v.Set("t", strconv.Itoa(*nSeconds))
|
||||
|
||||
for _, name := range cmd.Args() {
|
||||
name = cleanName(name)
|
||||
_, _, err := cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
@@ -560,8 +557,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
||||
|
||||
var encounteredError error
|
||||
for _, name := range args {
|
||||
encName := cleanName(name)
|
||||
_, _, err := cli.call("POST", "/containers/"+encName+"/start", nil)
|
||||
_, _, err := cli.call("POST", "/containers/"+name+"/start", nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
encounteredError = fmt.Errorf("Error: failed to start one or more containers")
|
||||
@@ -583,7 +579,6 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||
}
|
||||
fmt.Fprintf(cli.out, "[")
|
||||
for i, name := range args {
|
||||
name = cleanName(name)
|
||||
if i > 0 {
|
||||
fmt.Fprintf(cli.out, ",")
|
||||
}
|
||||
@@ -745,8 +740,6 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
|
||||
func (cli *DockerCli) CmdRm(args ...string) error {
|
||||
cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
|
||||
v := cmd.Bool("v", false, "Remove the volumes associated to the container")
|
||||
link := cmd.Bool("link", false, "Remove the specified link and not the underlying container")
|
||||
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -758,12 +751,8 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
||||
if *v {
|
||||
val.Set("v", "1")
|
||||
}
|
||||
if *link {
|
||||
val.Set("link", "1")
|
||||
}
|
||||
for _, name := range cmd.Args() {
|
||||
encName := cleanName(name)
|
||||
_, _, err := cli.call("DELETE", "/containers/"+encName+"?"+val.Encode(), nil)
|
||||
_, _, err := cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
} else {
|
||||
@@ -785,7 +774,6 @@ func (cli *DockerCli) CmdKill(args ...string) error {
|
||||
}
|
||||
|
||||
for _, name := range args {
|
||||
name = cleanName(name)
|
||||
_, _, err := cli.call("POST", "/containers/"+name+"/kill", nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
@@ -1029,10 +1017,10 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
||||
func displayablePorts(ports []APIPort) string {
|
||||
result := []string{}
|
||||
for _, port := range ports {
|
||||
if port.IP == "" {
|
||||
result = append(result, fmt.Sprintf("%d/%s", port.PublicPort, port.Type))
|
||||
if port.Type == "tcp" {
|
||||
result = append(result, fmt.Sprintf("%d->%d", port.PublicPort, port.PrivatePort))
|
||||
} else {
|
||||
result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type))
|
||||
result = append(result, fmt.Sprintf("%d->%d/%s", port.PublicPort, port.PrivatePort, port.Type))
|
||||
}
|
||||
}
|
||||
sort.Strings(result)
|
||||
@@ -1085,7 +1073,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
||||
}
|
||||
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||
if !*quiet {
|
||||
fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
|
||||
fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS")
|
||||
if *size {
|
||||
fmt.Fprintln(w, "\tSIZE")
|
||||
} else {
|
||||
@@ -1094,12 +1082,11 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
||||
}
|
||||
|
||||
for _, out := range outs {
|
||||
names := strings.Join(out.Names, ",")
|
||||
if !*quiet {
|
||||
if *noTrunc {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports), names)
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports))
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports), names)
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports))
|
||||
}
|
||||
if *size {
|
||||
if out.SizeRootFs > 0 {
|
||||
@@ -1125,58 +1112,6 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdLs(args ...string) error {
|
||||
cmd := Subcmd("ls", "", "List links for containers")
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
body, _, err := cli.call("GET", "/containers/links", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var links []APILink
|
||||
if err := json.Unmarshal(body, &links); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||
fmt.Fprintf(w, "NAME\tID\tIMAGE")
|
||||
fmt.Fprintf(w, "\n")
|
||||
|
||||
sortLinks(links, func(i, j APILink) bool {
|
||||
return len(i.Path) < len(j.Path)
|
||||
})
|
||||
for _, link := range links {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s", link.Path, link.ContainerID, link.Image)
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
w.Flush()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdLink(args ...string) error {
|
||||
cmd := Subcmd("link", "CURRENT_NAME NEW_NAME", "Link the container with a new name")
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
}
|
||||
if cmd.NArg() != 2 {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
body := map[string]string{
|
||||
"currentName": cmd.Arg(0),
|
||||
"newName": cmd.Arg(1),
|
||||
}
|
||||
|
||||
_, _, err := cli.call("POST", "/containers/link", body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdCommit(args ...string) error {
|
||||
cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes")
|
||||
flComment := cmd.String("m", "", "Commit message")
|
||||
@@ -1294,9 +1229,8 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
name := cleanName(cmd.Arg(0))
|
||||
|
||||
if err := cli.hijack("POST", "/containers/"+name+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err); err != nil {
|
||||
if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -1311,9 +1245,8 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
name := cmd.Arg(0)
|
||||
name = cleanName(name)
|
||||
body, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
|
||||
|
||||
body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1600,7 +1533,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||
if config.AttachStdin || config.AttachStdout || config.AttachStderr {
|
||||
if config.Tty {
|
||||
if err := cli.monitorTtySize(runResult.ID); err != nil {
|
||||
utils.Debugf("Error monitoring TTY size: %s\n", err)
|
||||
utils.Errorf("Error monitoring TTY size: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1865,11 +1798,11 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
||||
}
|
||||
if tcpc, ok := rwc.(*net.TCPConn); ok {
|
||||
if err := tcpc.CloseWrite(); err != nil {
|
||||
utils.Debugf("Couldn't send EOF: %s\n", err)
|
||||
utils.Errorf("Couldn't send EOF: %s\n", err)
|
||||
}
|
||||
} else if unixc, ok := rwc.(*net.UnixConn); ok {
|
||||
if err := unixc.CloseWrite(); err != nil {
|
||||
utils.Debugf("Couldn't send EOF: %s\n", err)
|
||||
utils.Errorf("Couldn't send EOF: %s\n", err)
|
||||
}
|
||||
}
|
||||
// Discard errors due to pipe interruption
|
||||
@@ -1878,14 +1811,14 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
||||
|
||||
if stdout != nil {
|
||||
if err := <-receiveStdout; err != nil {
|
||||
utils.Debugf("Error receiveStdout: %s", err)
|
||||
utils.Errorf("Error receiveStdout: %s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !cli.isTerminal {
|
||||
if err := <-sendStdin; err != nil {
|
||||
utils.Debugf("Error sendStdin: %s", err)
|
||||
utils.Errorf("Error sendStdin: %s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1899,7 +1832,7 @@ func (cli *DockerCli) getTtySize() (int, int) {
|
||||
}
|
||||
ws, err := term.GetWinsize(cli.terminalFd)
|
||||
if err != nil {
|
||||
utils.Debugf("Error getting size: %s", err)
|
||||
utils.Errorf("Error getting size: %s", err)
|
||||
if ws == nil {
|
||||
return 0, 0
|
||||
}
|
||||
@@ -1916,7 +1849,7 @@ func (cli *DockerCli) resizeTty(id string) {
|
||||
v.Set("h", strconv.Itoa(height))
|
||||
v.Set("w", strconv.Itoa(width))
|
||||
if _, _, err := cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil); err != nil {
|
||||
utils.Debugf("Error resize: %s", err)
|
||||
utils.Errorf("Error resize: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1987,10 +1920,6 @@ func getExitCode(cli *DockerCli, containerId string) (int, error) {
|
||||
return c.State.ExitCode, nil
|
||||
}
|
||||
|
||||
func cleanName(name string) string {
|
||||
return strings.Replace(name, "/", "%252F", -1)
|
||||
}
|
||||
|
||||
func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
|
||||
var (
|
||||
isTerminal = false
|
||||
|
||||
+3
-2
@@ -400,7 +400,7 @@ func TestRunDetach(t *testing.T) {
|
||||
})
|
||||
|
||||
// wait for CmdRun to return
|
||||
setTimeout(t, "Waiting for CmdRun timed out", 5*time.Second, func() {
|
||||
setTimeout(t, "Waiting for CmdRun timed out", 15*time.Second, func() {
|
||||
<-ch
|
||||
})
|
||||
|
||||
@@ -458,7 +458,7 @@ func TestAttachDetach(t *testing.T) {
|
||||
})
|
||||
|
||||
// wait for CmdRun to return
|
||||
setTimeout(t, "Waiting for CmdAttach timed out", 5*time.Second, func() {
|
||||
setTimeout(t, "Waiting for CmdAttach timed out", 15*time.Second, func() {
|
||||
<-ch
|
||||
})
|
||||
|
||||
@@ -545,6 +545,7 @@ func TestAttachDisconnect(t *testing.T) {
|
||||
|
||||
// Expected behaviour: container gets deleted automatically after exit
|
||||
func TestRunAutoRemove(t *testing.T) {
|
||||
t.Skip("Fixme. Skipping test for now, race condition")
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
type DaemonConfig struct {
|
||||
Pidfile string
|
||||
GraphPath string
|
||||
ProtoAddresses []string
|
||||
AutoRestart bool
|
||||
EnableCors bool
|
||||
Dns []string
|
||||
EnableIptables bool
|
||||
BridgeIface string
|
||||
DefaultIp net.IP
|
||||
}
|
||||
+128
-244
@@ -16,6 +16,7 @@ import (
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -57,8 +58,6 @@ type Container struct {
|
||||
// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
|
||||
// Easier than migrating older container configs :)
|
||||
VolumesRW map[string]bool
|
||||
|
||||
activeLinks map[string]*Link
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@@ -71,8 +70,7 @@ type Config struct {
|
||||
AttachStdin bool
|
||||
AttachStdout bool
|
||||
AttachStderr bool
|
||||
PortSpecs []string // Deprecated - Can be in the format of 8080/tcp
|
||||
ExposedPorts map[Port]struct{}
|
||||
PortSpecs []string
|
||||
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||
OpenStdin bool // Open stdin
|
||||
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
|
||||
@@ -92,8 +90,6 @@ type HostConfig struct {
|
||||
Binds []string
|
||||
ContainerIDFile string
|
||||
LxcConf []KeyValuePair
|
||||
PortBindings map[Port][]PortBinding
|
||||
Links []string
|
||||
}
|
||||
|
||||
type BindMap struct {
|
||||
@@ -111,34 +107,6 @@ type KeyValuePair struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
type PortBinding struct {
|
||||
HostIp string
|
||||
HostPort string
|
||||
}
|
||||
|
||||
// 80/tcp
|
||||
type Port string
|
||||
|
||||
func (p Port) Proto() string {
|
||||
return strings.Split(string(p), "/")[1]
|
||||
}
|
||||
|
||||
func (p Port) Port() string {
|
||||
return strings.Split(string(p), "/")[0]
|
||||
}
|
||||
|
||||
func (p Port) Int() int {
|
||||
i, err := parsePort(p.Port())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func NewPort(proto, port string) Port {
|
||||
return Port(fmt.Sprintf("%s/%s", port, proto))
|
||||
}
|
||||
|
||||
func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
|
||||
cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
|
||||
if os.Getenv("TEST") != "" {
|
||||
@@ -167,11 +135,8 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
|
||||
|
||||
flCpuShares := cmd.Int64("c", 0, "CPU shares (relative weight)")
|
||||
|
||||
var flPublish ListOpts
|
||||
cmd.Var(&flPublish, "p", "Publish a container's port to the host (use 'docker port' to see the actual mapping)")
|
||||
|
||||
var flExpose ListOpts
|
||||
cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host")
|
||||
var flPorts ListOpts
|
||||
cmd.Var(&flPorts, "p", "Expose a container's port to the host (use 'docker port' to see the actual mapping)")
|
||||
|
||||
var flEnv ListOpts
|
||||
cmd.Var(&flEnv, "e", "Set environment variables")
|
||||
@@ -190,9 +155,6 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
|
||||
var flLxcOpts ListOpts
|
||||
cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
|
||||
|
||||
var flLinks ListOpts
|
||||
cmd.Var(&flLinks, "link", "Add link to another container (containerid:alias)")
|
||||
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil, nil, cmd, err
|
||||
}
|
||||
@@ -258,28 +220,10 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
|
||||
hostname = parts[0]
|
||||
domainname = parts[1]
|
||||
}
|
||||
|
||||
ports, portBindings, err := parsePortSpecs(flPublish)
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
}
|
||||
|
||||
// Merge in exposed ports to the map of published ports
|
||||
for _, e := range flExpose {
|
||||
if strings.Contains(e, ":") {
|
||||
return nil, nil, cmd, fmt.Errorf("Invalid port format for -expose: %s", e)
|
||||
}
|
||||
p := NewPort(splitProtoPort(e))
|
||||
if _, exists := ports[p]; !exists {
|
||||
ports[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
config := &Config{
|
||||
Hostname: *flHostname,
|
||||
Hostname: hostname,
|
||||
Domainname: domainname,
|
||||
PortSpecs: nil, // Deprecated
|
||||
ExposedPorts: ports,
|
||||
PortSpecs: flPorts,
|
||||
User: *flUser,
|
||||
Tty: *flTty,
|
||||
NetworkDisabled: !*flNetwork,
|
||||
@@ -299,13 +243,10 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
|
||||
Privileged: *flPrivileged,
|
||||
WorkingDir: *flWorkingDir,
|
||||
}
|
||||
|
||||
hostConfig := &HostConfig{
|
||||
Binds: binds,
|
||||
ContainerIDFile: *flContainerIDFile,
|
||||
LxcConf: lxcConf,
|
||||
PortBindings: portBindings,
|
||||
Links: flLinks,
|
||||
}
|
||||
|
||||
if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
|
||||
@@ -320,38 +261,36 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
|
||||
return config, hostConfig, cmd, nil
|
||||
}
|
||||
|
||||
type PortMapping map[string]string // Deprecated
|
||||
type PortMapping map[string]string
|
||||
|
||||
type NetworkSettings struct {
|
||||
IPAddress string
|
||||
IPPrefixLen int
|
||||
Gateway string
|
||||
Bridge string
|
||||
PortMapping map[string]PortMapping // Deprecated
|
||||
Ports map[Port][]PortBinding
|
||||
PortMapping map[string]PortMapping
|
||||
}
|
||||
|
||||
// returns a more easy to process description of the port mapping defined in the settings
|
||||
func (settings *NetworkSettings) PortMappingAPI() []APIPort {
|
||||
var mapping []APIPort
|
||||
for port, bindings := range settings.Ports {
|
||||
p, _ := parsePort(port.Port())
|
||||
if len(bindings) == 0 {
|
||||
mapping = append(mapping, APIPort{
|
||||
PublicPort: int64(p),
|
||||
Type: port.Proto(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
for _, binding := range bindings {
|
||||
p, _ := parsePort(port.Port())
|
||||
h, _ := parsePort(binding.HostPort)
|
||||
mapping = append(mapping, APIPort{
|
||||
PrivatePort: int64(p),
|
||||
PublicPort: int64(h),
|
||||
Type: port.Proto(),
|
||||
IP: binding.HostIp,
|
||||
})
|
||||
}
|
||||
for private, public := range settings.PortMapping["Tcp"] {
|
||||
pubint, _ := strconv.ParseInt(public, 0, 0)
|
||||
privint, _ := strconv.ParseInt(private, 0, 0)
|
||||
mapping = append(mapping, APIPort{
|
||||
PrivatePort: privint,
|
||||
PublicPort: pubint,
|
||||
Type: "tcp",
|
||||
})
|
||||
}
|
||||
for private, public := range settings.PortMapping["Udp"] {
|
||||
pubint, _ := strconv.ParseInt(public, 0, 0)
|
||||
privint, _ := strconv.ParseInt(private, 0, 0)
|
||||
mapping = append(mapping, APIPort{
|
||||
PrivatePort: privint,
|
||||
PublicPort: pubint,
|
||||
Type: "udp",
|
||||
})
|
||||
}
|
||||
return mapping
|
||||
}
|
||||
@@ -451,9 +390,9 @@ func (container *Container) startPty() error {
|
||||
// Copy the PTYs to our broadcasters
|
||||
go func() {
|
||||
defer container.stdout.CloseWriters()
|
||||
utils.Debugf("[startPty] Begin of stdout pipe")
|
||||
utils.Debugf("startPty: begin of stdout pipe")
|
||||
io.Copy(container.stdout, ptyMaster)
|
||||
utils.Debugf("[startPty] End of stdout pipe")
|
||||
utils.Debugf("startPty: end of stdout pipe")
|
||||
}()
|
||||
|
||||
// stdin
|
||||
@@ -462,9 +401,9 @@ func (container *Container) startPty() error {
|
||||
container.cmd.SysProcAttr.Setctty = true
|
||||
go func() {
|
||||
defer container.stdin.Close()
|
||||
utils.Debugf("[startPty] Begin of stdin pipe")
|
||||
utils.Debugf("startPty: begin of stdin pipe")
|
||||
io.Copy(ptyMaster, container.stdin)
|
||||
utils.Debugf("[startPty] End of stdin pipe")
|
||||
utils.Debugf("startPty: end of stdin pipe")
|
||||
}()
|
||||
}
|
||||
if err := container.cmd.Start(); err != nil {
|
||||
@@ -484,9 +423,9 @@ func (container *Container) start() error {
|
||||
}
|
||||
go func() {
|
||||
defer stdin.Close()
|
||||
utils.Debugf("Begin of stdin pipe [start]")
|
||||
utils.Debugf("start: begin of stdin pipe")
|
||||
io.Copy(stdin, container.stdin)
|
||||
utils.Debugf("End of stdin pipe [start]")
|
||||
utils.Debugf("start: end of stdin pipe")
|
||||
}()
|
||||
}
|
||||
return container.cmd.Start()
|
||||
@@ -503,8 +442,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||
errors <- err
|
||||
} else {
|
||||
go func() {
|
||||
utils.Debugf("[start] attach stdin\n")
|
||||
defer utils.Debugf("[end] attach stdin\n")
|
||||
utils.Debugf("attach: stdin: begin")
|
||||
defer utils.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()
|
||||
@@ -522,7 +461,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||
_, err = io.Copy(cStdin, stdin)
|
||||
}
|
||||
if err != nil {
|
||||
utils.Debugf("[error] attach stdin: %s\n", err)
|
||||
utils.Errorf("attach: stdin: %s", err)
|
||||
}
|
||||
// Discard error, expecting pipe error
|
||||
errors <- nil
|
||||
@@ -536,8 +475,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||
} else {
|
||||
cStdout = p
|
||||
go func() {
|
||||
utils.Debugf("[start] attach stdout\n")
|
||||
defer utils.Debugf("[end] attach stdout\n")
|
||||
utils.Debugf("attach: stdout: begin")
|
||||
defer utils.Debugf("attach: stdout: end")
|
||||
// If we are in StdinOnce mode, then close stdin
|
||||
if container.Config.StdinOnce && stdin != nil {
|
||||
defer stdin.Close()
|
||||
@@ -546,8 +485,11 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||
defer stdinCloser.Close()
|
||||
}
|
||||
_, err := io.Copy(stdout, cStdout)
|
||||
if err == io.ErrClosedPipe {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
utils.Debugf("[error] attach stdout: %s\n", err)
|
||||
utils.Errorf("attach: stdout: %s", err)
|
||||
}
|
||||
errors <- err
|
||||
}()
|
||||
@@ -557,9 +499,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||
if stdinCloser != nil {
|
||||
defer stdinCloser.Close()
|
||||
}
|
||||
|
||||
if cStdout, err := container.StdoutPipe(); err != nil {
|
||||
utils.Debugf("Error stdout pipe")
|
||||
utils.Errorf("attach: stdout pipe: %s", err)
|
||||
} else {
|
||||
io.Copy(&utils.NopWriter{}, cStdout)
|
||||
}
|
||||
@@ -572,8 +513,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||
} else {
|
||||
cStderr = p
|
||||
go func() {
|
||||
utils.Debugf("[start] attach stderr\n")
|
||||
defer utils.Debugf("[end] attach stderr\n")
|
||||
utils.Debugf("attach: stderr: begin")
|
||||
defer utils.Debugf("attach: stderr: end")
|
||||
// If we are in StdinOnce mode, then close stdin
|
||||
if container.Config.StdinOnce && stdin != nil {
|
||||
defer stdin.Close()
|
||||
@@ -582,8 +523,11 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||
defer stdinCloser.Close()
|
||||
}
|
||||
_, err := io.Copy(stderr, cStderr)
|
||||
if err == io.ErrClosedPipe {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
utils.Debugf("[error] attach stderr: %s\n", err)
|
||||
utils.Errorf("attach: stderr: %s", err)
|
||||
}
|
||||
errors <- err
|
||||
}()
|
||||
@@ -595,7 +539,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||
}
|
||||
|
||||
if cStderr, err := container.StderrPipe(); err != nil {
|
||||
utils.Debugf("Error stdout pipe")
|
||||
utils.Errorf("attach: stdout pipe: %s", err)
|
||||
} else {
|
||||
io.Copy(&utils.NopWriter{}, cStderr)
|
||||
}
|
||||
@@ -609,24 +553,29 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||
if cStderr != nil {
|
||||
defer cStderr.Close()
|
||||
}
|
||||
// FIXME: how do clean up the stdin goroutine without the unwanted side effect
|
||||
// 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("Waiting for job %d/%d\n", i+1, nJobs)
|
||||
utils.Debugf("attach: waiting for job %d/%d", i+1, nJobs)
|
||||
if err := <-errors; err != nil {
|
||||
utils.Debugf("Job %d returned error %s. Aborting all jobs\n", i+1, err)
|
||||
utils.Errorf("attach: job %d returned error %s, aborting all jobs", i+1, err)
|
||||
return err
|
||||
}
|
||||
utils.Debugf("Job %d completed successfully\n", i+1)
|
||||
utils.Debugf("attach: job %d completed successfully", i+1)
|
||||
}
|
||||
utils.Debugf("All jobs completed successfully\n")
|
||||
utils.Debugf("attach: all jobs completed successfully")
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (container *Container) Start(hostConfig *HostConfig) error {
|
||||
func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
||||
container.State.Lock()
|
||||
defer container.State.Unlock()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
container.cleanup()
|
||||
}
|
||||
}()
|
||||
|
||||
if hostConfig == nil { // in docker start of docker restart we want to reuse previous HostConfigFile
|
||||
hostConfig, _ = container.ReadHostConfig()
|
||||
@@ -641,7 +590,7 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
||||
if container.runtime.networkManager.disabled {
|
||||
container.Config.NetworkDisabled = true
|
||||
} else {
|
||||
if err := container.allocateNetwork(hostConfig); err != nil {
|
||||
if err := container.allocateNetwork(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -831,46 +780,6 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
||||
"-e", "container=lxc",
|
||||
"-e", "HOSTNAME="+container.Config.Hostname,
|
||||
)
|
||||
|
||||
// Init any links between the parent and children
|
||||
runtime := container.runtime
|
||||
|
||||
children, err := runtime.Children(fmt.Sprintf("/%s", container.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(children) > 0 {
|
||||
container.activeLinks = make(map[string]*Link, len(children))
|
||||
|
||||
// If we encounter an error make sure that we rollback any network
|
||||
// config and ip table changes
|
||||
rollback := func() {
|
||||
for _, link := range container.activeLinks {
|
||||
link.Disable()
|
||||
}
|
||||
container.activeLinks = nil
|
||||
}
|
||||
|
||||
for p, child := range children {
|
||||
link, err := NewLink(container, child, p, runtime.networkManager.bridgeIface)
|
||||
if err != nil {
|
||||
rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
container.activeLinks[link.Alias()] = link
|
||||
if err := link.Enable(); err != nil {
|
||||
rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
for _, envVar := range link.ToEnv() {
|
||||
params = append(params, "-e", envVar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if container.Config.WorkingDir != "" {
|
||||
workingDir := path.Clean(container.Config.WorkingDir)
|
||||
utils.Debugf("[working dir] working dir is %s", workingDir)
|
||||
@@ -949,9 +858,14 @@ func (container *Container) Output() (output []byte, err error) {
|
||||
return output, err
|
||||
}
|
||||
|
||||
// StdinPipe() returns a pipe connected to the standard input of the container's
|
||||
// active process.
|
||||
//
|
||||
// Container.StdinPipe returns a WriteCloser which can be used to feed data
|
||||
// to the standard input of the container's active process.
|
||||
// Container.StdoutPipe and Container.StderrPipe each return a ReadCloser
|
||||
// which can be used to retrieve the standard output (and error) generated
|
||||
// by the container's active process. The output (and error) are actually
|
||||
// copied and delivered to all StdoutPipe and StderrPipe consumers, using
|
||||
// a kind of "broadcaster".
|
||||
|
||||
func (container *Container) StdinPipe() (io.WriteCloser, error) {
|
||||
return container.stdinPipe, nil
|
||||
}
|
||||
@@ -968,7 +882,7 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) {
|
||||
return utils.NewBufReader(reader), nil
|
||||
}
|
||||
|
||||
func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
|
||||
func (container *Container) allocateNetwork() error {
|
||||
if container.Config.NetworkDisabled {
|
||||
return nil
|
||||
}
|
||||
@@ -995,64 +909,41 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
|
||||
}
|
||||
}
|
||||
|
||||
if container.Config.PortSpecs != nil {
|
||||
utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", "))
|
||||
if err := migratePortMappings(container.Config); err != nil {
|
||||
var portSpecs []string
|
||||
if !container.State.Ghost {
|
||||
portSpecs = container.Config.PortSpecs
|
||||
} else {
|
||||
for backend, frontend := range container.NetworkSettings.PortMapping["Tcp"] {
|
||||
portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/tcp", frontend, backend))
|
||||
}
|
||||
for backend, frontend := range container.NetworkSettings.PortMapping["Udp"] {
|
||||
portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/udp", frontend, backend))
|
||||
}
|
||||
}
|
||||
|
||||
container.NetworkSettings.PortMapping = make(map[string]PortMapping)
|
||||
container.NetworkSettings.PortMapping["Tcp"] = make(PortMapping)
|
||||
container.NetworkSettings.PortMapping["Udp"] = make(PortMapping)
|
||||
for _, spec := range portSpecs {
|
||||
nat, err := iface.AllocatePort(spec)
|
||||
if err != nil {
|
||||
iface.Release()
|
||||
return err
|
||||
}
|
||||
container.Config.PortSpecs = nil
|
||||
proto := strings.Title(nat.Proto)
|
||||
backend, frontend := strconv.Itoa(nat.Backend), strconv.Itoa(nat.Frontend)
|
||||
container.NetworkSettings.PortMapping[proto][backend] = frontend
|
||||
}
|
||||
|
||||
portSpecs := make(map[Port]struct{})
|
||||
bindings := make(map[Port][]PortBinding)
|
||||
|
||||
if !container.State.Ghost {
|
||||
if container.Config.ExposedPorts != nil {
|
||||
portSpecs = container.Config.ExposedPorts
|
||||
}
|
||||
if hostConfig.PortBindings != nil {
|
||||
bindings = hostConfig.PortBindings
|
||||
}
|
||||
} else {
|
||||
if container.NetworkSettings.Ports != nil {
|
||||
for port, binding := range container.NetworkSettings.Ports {
|
||||
portSpecs[port] = struct{}{}
|
||||
bindings[port] = binding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
container.NetworkSettings.PortMapping = nil
|
||||
|
||||
for port := range portSpecs {
|
||||
binding := bindings[port]
|
||||
for i := 0; i < len(binding); i++ {
|
||||
b := binding[i]
|
||||
nat, err := iface.AllocatePort(port, b)
|
||||
if err != nil {
|
||||
iface.Release()
|
||||
return err
|
||||
}
|
||||
utils.Debugf("Allocate port: %s:%s->%s", nat.Binding.HostIp, port, nat.Binding.HostPort)
|
||||
binding[i] = nat.Binding
|
||||
}
|
||||
bindings[port] = binding
|
||||
}
|
||||
container.SaveHostConfig(hostConfig)
|
||||
|
||||
container.NetworkSettings.Ports = bindings
|
||||
container.network = iface
|
||||
|
||||
container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
|
||||
container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
|
||||
container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
|
||||
container.NetworkSettings.Gateway = iface.Gateway.String()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) releaseNetwork() {
|
||||
if container.Config.NetworkDisabled {
|
||||
if container.Config.NetworkDisabled || container.network == nil {
|
||||
return
|
||||
}
|
||||
container.network.Release()
|
||||
@@ -1076,20 +967,23 @@ func (container *Container) waitLxc() error {
|
||||
|
||||
func (container *Container) monitor(hostConfig *HostConfig) {
|
||||
// Wait for the program to exit
|
||||
utils.Debugf("Waiting for process")
|
||||
|
||||
// If the command does not exists, try to wait via lxc
|
||||
// If the command does not exist, try to wait via lxc
|
||||
// (This probably happens only for ghost containers, i.e. containers that were running when Docker started)
|
||||
if container.cmd == nil {
|
||||
utils.Debugf("monitor: waiting for container %s using waitLxc", container.ID)
|
||||
if err := container.waitLxc(); err != nil {
|
||||
utils.Debugf("%s: Process: %s", container.ID, err)
|
||||
utils.Errorf("monitor: while waiting for container %s, waitLxc had a problem: %s", container.ID, err)
|
||||
}
|
||||
} else {
|
||||
utils.Debugf("monitor: waiting for container %s using cmd.Wait", container.ID)
|
||||
if err := container.cmd.Wait(); err != nil {
|
||||
// Discard the error as any signals or non 0 returns will generate an error
|
||||
utils.Debugf("%s: Process: %s", container.ID, err)
|
||||
// Since non-zero exit status and signal terminations will cause err to be non-nil,
|
||||
// we have to actually discard it. Still, log it anyway, just in case.
|
||||
utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID)
|
||||
}
|
||||
}
|
||||
utils.Debugf("Process finished")
|
||||
utils.Debugf("monitor: container %s finished", container.ID)
|
||||
|
||||
exitCode := -1
|
||||
if container.cmd != nil {
|
||||
@@ -1104,36 +998,7 @@ func (container *Container) monitor(hostConfig *HostConfig) {
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
container.releaseNetwork()
|
||||
|
||||
// Disable all active links
|
||||
if container.activeLinks != nil {
|
||||
for _, link := range container.activeLinks {
|
||||
link.Disable()
|
||||
}
|
||||
}
|
||||
|
||||
if container.Config.OpenStdin {
|
||||
if err := container.stdin.Close(); err != nil {
|
||||
utils.Debugf("%s: Error close stdin: %s", container.ID, err)
|
||||
}
|
||||
}
|
||||
if err := container.stdout.CloseWriters(); err != nil {
|
||||
utils.Debugf("%s: Error close stdout: %s", container.ID, err)
|
||||
}
|
||||
if err := container.stderr.CloseWriters(); err != nil {
|
||||
utils.Debugf("%s: Error close stderr: %s", container.ID, err)
|
||||
}
|
||||
|
||||
if container.ptyMaster != nil {
|
||||
if err := container.ptyMaster.Close(); err != nil {
|
||||
utils.Debugf("%s: Error closing Pty master: %s", container.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := container.Unmount(); err != nil {
|
||||
log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
|
||||
}
|
||||
container.cleanup()
|
||||
|
||||
// Re-create a brand new stdin pipe once the container exited
|
||||
if container.Config.OpenStdin {
|
||||
@@ -1154,6 +1019,31 @@ func (container *Container) monitor(hostConfig *HostConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
func (container *Container) cleanup() {
|
||||
container.releaseNetwork()
|
||||
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.ptyMaster != nil {
|
||||
if err := container.ptyMaster.Close(); err != nil {
|
||||
utils.Errorf("%s: Error closing Pty master: %s", container.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := container.Unmount(); err != nil {
|
||||
log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (container *Container) kill() error {
|
||||
if !container.State.Running {
|
||||
return nil
|
||||
@@ -1402,9 +1292,3 @@ func (container *Container) Copy(resource string) (Archive, error) {
|
||||
}
|
||||
return TarFilter(basePath, Uncompressed, filter)
|
||||
}
|
||||
|
||||
// Returns true if the container exposes a certain port
|
||||
func (container *Container) Exposes(p Port) bool {
|
||||
_, exists := container.Config.ExposedPorts[p]
|
||||
return exists
|
||||
}
|
||||
|
||||
+35
-35
@@ -18,7 +18,7 @@ import (
|
||||
func TestIDFormat(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container1, _, err := runtime.Create(
|
||||
container1, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
|
||||
@@ -388,7 +388,7 @@ func TestRun(t *testing.T) {
|
||||
func TestOutput(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"echo", "-n", "foobar"},
|
||||
@@ -411,7 +411,7 @@ func TestKillDifferentUser(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"cat"},
|
||||
OpenStdin: true,
|
||||
@@ -471,7 +471,7 @@ func TestCreateVolume(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c, _, err := runtime.Create(config)
|
||||
c, err := runtime.Create(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -486,7 +486,7 @@ func TestCreateVolume(t *testing.T) {
|
||||
func TestKill(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"sleep", "2"},
|
||||
},
|
||||
@@ -530,7 +530,7 @@ func TestExitCode(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
|
||||
trueContainer, _, err := runtime.Create(&Config{
|
||||
trueContainer, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/true", ""},
|
||||
})
|
||||
@@ -545,7 +545,7 @@ func TestExitCode(t *testing.T) {
|
||||
t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode)
|
||||
}
|
||||
|
||||
falseContainer, _, err := runtime.Create(&Config{
|
||||
falseContainer, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/false", ""},
|
||||
})
|
||||
@@ -564,7 +564,7 @@ func TestExitCode(t *testing.T) {
|
||||
func TestRestart(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"echo", "-n", "foobar"},
|
||||
},
|
||||
@@ -594,7 +594,7 @@ func TestRestart(t *testing.T) {
|
||||
func TestRestartStdin(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"cat"},
|
||||
|
||||
@@ -672,7 +672,7 @@ func TestUser(t *testing.T) {
|
||||
defer nuke(runtime)
|
||||
|
||||
// Default user must be root
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"id"},
|
||||
},
|
||||
@@ -690,7 +690,7 @@ func TestUser(t *testing.T) {
|
||||
}
|
||||
|
||||
// Set a username
|
||||
container, _, err = runtime.Create(&Config{
|
||||
container, err = runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"id"},
|
||||
|
||||
@@ -710,7 +710,7 @@ func TestUser(t *testing.T) {
|
||||
}
|
||||
|
||||
// Set a UID
|
||||
container, _, err = runtime.Create(&Config{
|
||||
container, err = runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"id"},
|
||||
|
||||
@@ -730,7 +730,7 @@ func TestUser(t *testing.T) {
|
||||
}
|
||||
|
||||
// Set a different user by uid
|
||||
container, _, err = runtime.Create(&Config{
|
||||
container, err = runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"id"},
|
||||
|
||||
@@ -752,7 +752,7 @@ func TestUser(t *testing.T) {
|
||||
}
|
||||
|
||||
// Set a different user by username
|
||||
container, _, err = runtime.Create(&Config{
|
||||
container, err = runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"id"},
|
||||
|
||||
@@ -772,7 +772,7 @@ func TestUser(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test an wrong username
|
||||
container, _, err = runtime.Create(&Config{
|
||||
container, err = runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"id"},
|
||||
|
||||
@@ -793,7 +793,7 @@ func TestMultipleContainers(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
|
||||
container1, _, err := runtime.Create(&Config{
|
||||
container1, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"sleep", "2"},
|
||||
},
|
||||
@@ -803,7 +803,7 @@ func TestMultipleContainers(t *testing.T) {
|
||||
}
|
||||
defer runtime.Destroy(container1)
|
||||
|
||||
container2, _, err := runtime.Create(&Config{
|
||||
container2, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"sleep", "2"},
|
||||
},
|
||||
@@ -847,7 +847,7 @@ func TestMultipleContainers(t *testing.T) {
|
||||
func TestStdin(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"cat"},
|
||||
|
||||
@@ -892,7 +892,7 @@ func TestStdin(t *testing.T) {
|
||||
func TestTty(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"cat"},
|
||||
|
||||
@@ -937,7 +937,7 @@ func TestTty(t *testing.T) {
|
||||
func TestEnv(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"env"},
|
||||
},
|
||||
@@ -986,7 +986,7 @@ func TestEnv(t *testing.T) {
|
||||
func TestEntrypoint(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Entrypoint: []string{"/bin/echo"},
|
||||
@@ -1009,7 +1009,7 @@ func TestEntrypoint(t *testing.T) {
|
||||
func TestEntrypointNoCmd(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Entrypoint: []string{"/bin/echo", "foobar"},
|
||||
@@ -1060,7 +1060,7 @@ func TestLXCConfig(t *testing.T) {
|
||||
cpuMin := 100
|
||||
cpuMax := 10000
|
||||
cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/true"},
|
||||
|
||||
@@ -1084,7 +1084,7 @@ func TestLXCConfig(t *testing.T) {
|
||||
func TestCustomLxcConfig(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/true"},
|
||||
|
||||
@@ -1115,7 +1115,7 @@ func BenchmarkRunSequencial(b *testing.B) {
|
||||
runtime := mkRuntime(b)
|
||||
defer nuke(runtime)
|
||||
for i := 0; i < b.N; i++ {
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"echo", "-n", "foo"},
|
||||
},
|
||||
@@ -1147,7 +1147,7 @@ func BenchmarkRunParallel(b *testing.B) {
|
||||
complete := make(chan error)
|
||||
tasks = append(tasks, complete)
|
||||
go func(i int, complete chan error) {
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"echo", "-n", "foo"},
|
||||
},
|
||||
@@ -1297,7 +1297,7 @@ func TestBindMounts(t *testing.T) {
|
||||
func TestVolumesFromReadonlyMount(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
container, _, err := runtime.Create(
|
||||
container, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/echo", "-n", "foobar"},
|
||||
@@ -1316,7 +1316,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
container2, _, err := runtime.Create(
|
||||
container2, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/echo", "-n", "foobar"},
|
||||
@@ -1352,7 +1352,7 @@ func TestRestartWithVolumes(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"echo", "-n", "foobar"},
|
||||
Volumes: map[string]struct{}{"/test": {}},
|
||||
@@ -1395,7 +1395,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"},
|
||||
Volumes: map[string]struct{}{"/test": {}},
|
||||
@@ -1422,7 +1422,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
container2, _, err := runtime.Create(
|
||||
container2, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"cat", "/test/foo"},
|
||||
@@ -1463,7 +1463,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c, _, err := runtime.Create(config)
|
||||
c, err := runtime.Create(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1529,7 +1529,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
|
||||
container, _, err := runtime.Create(&Config{
|
||||
container, err := runtime.Create(&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"},
|
||||
Volumes: map[string]struct{}{"/test": {}},
|
||||
@@ -1556,7 +1556,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
container2, _, err := runtime.Create(
|
||||
container2, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"sh", "-c", "echo -n bar > /other/foo"},
|
||||
@@ -1577,7 +1577,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
container3, _, err := runtime.Create(
|
||||
container3, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
Cmd: []string{"/bin/echo", "-n", "foobar"},
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# [PackageDev] target_format: plist, ext: tmLanguage
|
||||
---
|
||||
name: Dockerfile
|
||||
scopeName: source.dockerfile
|
||||
uuid: a39d8795-59d2-49af-aa00-fe74ee29576e
|
||||
|
||||
patterns:
|
||||
# Keywords
|
||||
- name: keyword.control.dockerfile
|
||||
match: ^\s*(FROM|MAINTAINER|RUN|CMD|EXPOSE|ENV|ADD)\s
|
||||
- name: keyword.operator.dockerfile
|
||||
match: ^\s*(ENTRYPOINT|VOLUME|USER|WORKDIR)\s
|
||||
# String
|
||||
- name: string.quoted.double.dockerfile
|
||||
begin: "\""
|
||||
end: "\""
|
||||
patterns:
|
||||
- name: constant.character.escaped.dockerfile
|
||||
match: \\.
|
||||
# Comment
|
||||
- name: comment.block.dockerfile
|
||||
match: ^\s*#.*$
|
||||
...
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Dockerfile</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*(FROM|MAINTAINER|RUN|CMD|EXPOSE|ENV|ADD)\s</string>
|
||||
<key>name</key>
|
||||
<string>keyword.control.dockerfile</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*(ENTRYPOINT|VOLUME|USER|WORKDIR)\s</string>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.dockerfile</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>"</string>
|
||||
<key>end</key>
|
||||
<string>"</string>
|
||||
<key>name</key>
|
||||
<string>string.quoted.double.dockerfile</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\.</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escaped.dockerfile</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*#.*$</string>
|
||||
<key>name</key>
|
||||
<string>comment.block.dockerfile</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>scopeName</key>
|
||||
<string>source.dockerfile</string>
|
||||
<key>uuid</key>
|
||||
<string>a39d8795-59d2-49af-aa00-fe74ee29576e</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
Asbjorn Enge <asbjorn@hanafjedle.net> (@asbjornenge)
|
||||
@@ -0,0 +1,9 @@
|
||||
# Dockerfile.tmLanguage
|
||||
|
||||
Pretty basic Dockerfile.tmLanguage for Sublime Text syntax highlighting.
|
||||
|
||||
PR's with syntax updates, suggestions etc. are all very much appreciated!
|
||||
|
||||
I'll get to making this installable via Package Control soon!
|
||||
|
||||
enjoy.
|
||||
@@ -1,2 +1 @@
|
||||
Kawsar Saiyeed <kawsar.saiyeed@projiris.com> (@KSid)
|
||||
Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
|
||||
Regular → Executable
+1
-1
@@ -341,7 +341,7 @@ _docker_pull()
|
||||
|
||||
_docker_push()
|
||||
{
|
||||
return
|
||||
__docker_image_repos
|
||||
}
|
||||
|
||||
_docker_restart()
|
||||
Executable
+242
@@ -0,0 +1,242 @@
|
||||
#compdef docker
|
||||
#
|
||||
# zsh completion for docker (http://docker.io)
|
||||
#
|
||||
# version: 0.2.2
|
||||
# author: Felix Riedel
|
||||
# license: BSD License
|
||||
# github: https://github.com/felixr/docker-zsh-completion
|
||||
#
|
||||
|
||||
__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)}'
|
||||
}
|
||||
|
||||
__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
|
||||
}
|
||||
|
||||
__docker_runningcontainers() {
|
||||
local expl
|
||||
declare -a containers
|
||||
|
||||
containers=(${(f)"$(docker ps | __parse_docker_list)"})
|
||||
_describe -t containers-active "Running Containers" containers
|
||||
}
|
||||
|
||||
__docker_containers () {
|
||||
__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)}')"})
|
||||
_describe -t docker-images "Images" images
|
||||
}
|
||||
|
||||
__docker_tags() {
|
||||
local expl
|
||||
declare -a tags
|
||||
tags=(${(f)"$(docker images | awk '(NR>1){print $2}'| sort | uniq)"})
|
||||
_describe -t docker-tags "tags" tags
|
||||
}
|
||||
|
||||
__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
|
||||
fi
|
||||
|
||||
local searchterm cachename
|
||||
searchterm="${words[$CURRENT]%/}"
|
||||
cachename=_docker-search-$searchterm
|
||||
|
||||
local expl
|
||||
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}')"})
|
||||
_store_cache ${cachename#_} result
|
||||
fi
|
||||
_wanted dockersearch expl 'Available images' compadd -a result
|
||||
}
|
||||
|
||||
__docker_caching_policy()
|
||||
{
|
||||
# oldp=( "$1"(Nmh+24) ) # 24 hour
|
||||
oldp=( "$1"(Nmh+1) ) # 24 hour
|
||||
(( $#oldp ))
|
||||
}
|
||||
|
||||
|
||||
__docker_repositories () {
|
||||
local expl
|
||||
declare -a repos
|
||||
repos=(${(f)"$(docker images | sed -e '1d' -e 's/[ ].*//' | sort | uniq)"})
|
||||
_describe -t docker-repos "Repositories" repos
|
||||
}
|
||||
|
||||
__docker_commands () {
|
||||
# local -a _docker_subcommands
|
||||
local cache_policy
|
||||
|
||||
zstyle -s ":completion:${curcontext}:" cache-policy cache_policy
|
||||
if [[ -z "$cache_policy" ]]; then
|
||||
zstyle ":completion:${curcontext}:" cache-policy __docker_caching_policy
|
||||
fi
|
||||
|
||||
if ( [[ ${+_docker_subcommands} -eq 0 ]] || _cache_invalid docker_subcommands) \
|
||||
&& ! _retrieve_cache docker_subcommands;
|
||||
then
|
||||
_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')
|
||||
_store_cache docker_subcommands _docker_subcommands
|
||||
fi
|
||||
_describe -t docker-commands "docker command" _docker_subcommands
|
||||
}
|
||||
|
||||
__docker_subcommand () {
|
||||
local -a _command_args
|
||||
case "$words[1]" in
|
||||
(attach|wait)
|
||||
_arguments ':containers:__docker_runningcontainers'
|
||||
;;
|
||||
(build)
|
||||
_arguments \
|
||||
'-t=-:repository:__docker_repositories' \
|
||||
':path or URL:_directories'
|
||||
;;
|
||||
(commit)
|
||||
_arguments \
|
||||
':container:__docker_containers' \
|
||||
':repository:__docker_repositories' \
|
||||
':tag: '
|
||||
;;
|
||||
(diff|export|logs)
|
||||
_arguments '*:containers:__docker_containers'
|
||||
;;
|
||||
(history)
|
||||
_arguments '*:images:__docker_images'
|
||||
;;
|
||||
(images)
|
||||
_arguments \
|
||||
'-a[Show all images]' \
|
||||
':repository:__docker_repositories'
|
||||
;;
|
||||
(inspect)
|
||||
_arguments '*:containers:__docker_containers'
|
||||
;;
|
||||
(history)
|
||||
_arguments ':images:__docker_images'
|
||||
;;
|
||||
(insert)
|
||||
_arguments '1:containers:__docker_containers' \
|
||||
'2:URL:(http:// file://)' \
|
||||
'3:file:_files'
|
||||
;;
|
||||
(kill)
|
||||
_arguments '*:containers:__docker_runningcontainers'
|
||||
;;
|
||||
(port)
|
||||
_arguments '1:containers:__docker_runningcontainers'
|
||||
;;
|
||||
(start)
|
||||
_arguments '*:containers:__docker_stoppedcontainers'
|
||||
;;
|
||||
(rm)
|
||||
_arguments '-v[Remove the volumes associated to the container]' \
|
||||
'*:containers:__docker_stoppedcontainers'
|
||||
;;
|
||||
(rmi)
|
||||
_arguments '-v[Remove the volumes associated to the container]' \
|
||||
'*: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'
|
||||
;;
|
||||
(ps)
|
||||
_arguments '-a[Show all containers. Only running containers are shown by default]' \
|
||||
'-h[Show help]' \
|
||||
'-beforeId=-[Show only container created before Id, include non-running one]:containers:__docker_containers' \
|
||||
'-n=-[Show n last created containers, include non-running one]:n:(1 5 10 25 50)'
|
||||
;;
|
||||
(tag)
|
||||
_arguments \
|
||||
'-f[force]'\
|
||||
':image:__docker_images'\
|
||||
':repository:__docker_repositories' \
|
||||
':tag:__docker_tags'
|
||||
;;
|
||||
(run)
|
||||
_arguments \
|
||||
'-a=-[Attach to stdin, stdout or stderr]:toggle:(true false)' \
|
||||
'-c=-[CPU shares (relative weight)]:CPU shares: ' \
|
||||
'-d[Detached mode: leave the container running in the background]' \
|
||||
'*-dns=[Set custom dns servers]:dns server: ' \
|
||||
'*-e=[Set environment variables]:environment variable: ' \
|
||||
'-entrypoint=-[Overwrite the default entrypoint of the image]:entry point: ' \
|
||||
'-h=-[Container host name]:hostname:_hosts' \
|
||||
'-i[Keep stdin open even if not attached]' \
|
||||
'-m=-[Memory limit (in bytes)]:limit: ' \
|
||||
'*-p=-[Expose a container''s port to the host]:port:_ports' \
|
||||
'-t=-[Allocate a pseudo-tty]:toggle:(true false)' \
|
||||
'-u=-[Username or UID]:user:_users' \
|
||||
'*-v=-[Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)]:volume: '\
|
||||
'-volumes-from=-[Mount volumes from the specified container]:volume: ' \
|
||||
'(-):images:__docker_images' \
|
||||
'(-):command: _command_names -e' \
|
||||
'*::arguments: _normal'
|
||||
;;
|
||||
(pull|search)
|
||||
_arguments ':name:__docker_search'
|
||||
;;
|
||||
(help)
|
||||
_arguments ':subcommand:__docker_commands'
|
||||
;;
|
||||
(*)
|
||||
_message 'Unknown sub command'
|
||||
esac
|
||||
|
||||
}
|
||||
|
||||
_docker () {
|
||||
local curcontext="$curcontext" state line
|
||||
typeset -A opt_args
|
||||
|
||||
_arguments -C \
|
||||
'-H=-[tcp://host:port to bind/connect to]:socket: ' \
|
||||
'(-): :->command' \
|
||||
'(-)*:: :->option-or-argument'
|
||||
|
||||
if (( CURRENT == 1 )); then
|
||||
|
||||
fi
|
||||
case $state in
|
||||
(command)
|
||||
__docker_commands
|
||||
;;
|
||||
(option-or-argument)
|
||||
curcontext=${curcontext%:*:*}:docker-$words[1]:
|
||||
__docker_subcommand
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker "$@"
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/bin/sh
|
||||
# This script is meant for quick & easy install via 'curl URL-OF-SCRIPT | sh'
|
||||
# Original version by Jeff Lindsay <progrium@gmail.com>
|
||||
# Revamped by Jerome Petazzoni <jerome@dotcloud.com>
|
||||
#
|
||||
# This script canonical location is https://get.docker.io/; to update it, run:
|
||||
# s3cmd put -m text/x-shellscript -P install.sh s3://get.docker.io/index
|
||||
|
||||
echo "Ensuring basic dependencies are installed..."
|
||||
apt-get -qq update
|
||||
apt-get -qq install lxc wget
|
||||
|
||||
echo "Looking in /proc/filesystems to see if we have AUFS support..."
|
||||
if grep -q aufs /proc/filesystems
|
||||
then
|
||||
echo "Found."
|
||||
else
|
||||
echo "Ahem, it looks like the current kernel does not support AUFS."
|
||||
echo "Let's see if we can load the AUFS module with modprobe..."
|
||||
if modprobe aufs
|
||||
then
|
||||
echo "Module loaded."
|
||||
else
|
||||
echo "Ahem, things didn't turn out as expected."
|
||||
KPKG=linux-image-extra-$(uname -r)
|
||||
echo "Trying to install $KPKG..."
|
||||
if apt-get -qq install $KPKG
|
||||
then
|
||||
echo "Installed."
|
||||
else
|
||||
echo "Oops, we couldn't install the -extra kernel."
|
||||
echo "Are you sure you are running a supported version of Ubuntu?"
|
||||
echo "Proceeding anyway, but Docker will probably NOT WORK!"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Downloading docker binary to /usr/local/bin..."
|
||||
curl -s https://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-latest \
|
||||
> /usr/local/bin/docker
|
||||
chmod +x /usr/local/bin/docker
|
||||
|
||||
if [ -f /etc/init/dockerd.conf ]
|
||||
then
|
||||
echo "Upstart script already exists."
|
||||
else
|
||||
echo "Creating /etc/init/dockerd.conf..."
|
||||
cat >/etc/init/dockerd.conf <<EOF
|
||||
description "Docker daemon"
|
||||
start on filesystem and started lxc-net
|
||||
stop on runlevel [!2345]
|
||||
respawn
|
||||
exec /usr/local/bin/docker -d
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "Starting dockerd..."
|
||||
start dockerd > /dev/null
|
||||
|
||||
echo "Done."
|
||||
echo
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
# Generate a minimal filesystem for archlinux and load it into the local
|
||||
# docker as "archlinux"
|
||||
# requires root
|
||||
set -e
|
||||
|
||||
PACSTRAP=$(which pacstrap)
|
||||
[ "$PACSTRAP" ] || {
|
||||
echo "Could not find pacstrap. Run pacman -S arch-install-scripts"
|
||||
exit 1
|
||||
}
|
||||
EXPECT=$(which expect)
|
||||
[ "$EXPECT" ] || {
|
||||
echo "Could not find expect. Run pacman -S expect"
|
||||
exit 1
|
||||
}
|
||||
|
||||
ROOTFS=~/rootfs-arch-$$-$RANDOM
|
||||
mkdir $ROOTFS
|
||||
|
||||
#packages to ignore for space savings
|
||||
PKGIGNORE=linux,jfsutils,lvm2,cryptsetup,groff,man-db,man-pages,mdadm,pciutils,pcmciautils,reiserfsprogs,s-nail,xfsprogs
|
||||
|
||||
expect -c "
|
||||
set timeout 60
|
||||
spawn pacstrap -c -d -G -i $ROOTFS base haveged --ignore $PKGIGNORE
|
||||
expect {
|
||||
\"Install anyway\" { send "n\\r"; exp_continue }
|
||||
\"(default=all)\" { send "\\r"; exp_continue }
|
||||
\"Proceed with installation?\" { send "\\r"; exp_continue }
|
||||
\"skip the above package\" {send "y\\r"; exp_continue }
|
||||
\"checking\" { exp_continue }
|
||||
\"loading\" { exp_continue }
|
||||
\"installing\" { exp_continue }
|
||||
}"
|
||||
|
||||
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"
|
||||
cat > $ROOTFS/etc/locale.gen <<DELIM
|
||||
en_US.UTF-8 UTF-8
|
||||
en_US ISO-8859-1
|
||||
DELIM
|
||||
arch-chroot $ROOTFS locale-gen
|
||||
arch-chroot $ROOTFS /bin/sh -c 'echo "Server = http://mirrors.kernel.org/archlinux/\$repo/os/\$arch" > /etc/pacman.d/mirrorlist'
|
||||
|
||||
# udev doesn't work in containers, rebuild /dev
|
||||
DEV=${ROOTFS}/dev
|
||||
mv ${DEV} ${DEV}.old
|
||||
mkdir -p ${DEV}
|
||||
mknod -m 666 ${DEV}/null c 1 3
|
||||
mknod -m 666 ${DEV}/zero c 1 5
|
||||
mknod -m 666 ${DEV}/random c 1 8
|
||||
mknod -m 666 ${DEV}/urandom c 1 9
|
||||
mkdir -m 755 ${DEV}/pts
|
||||
mkdir -m 1777 ${DEV}/shm
|
||||
mknod -m 666 ${DEV}/tty c 5 0
|
||||
mknod -m 600 ${DEV}/console c 5 1
|
||||
mknod -m 666 ${DEV}/tty0 c 4 0
|
||||
mknod -m 666 ${DEV}/full c 1 7
|
||||
mknod -m 600 ${DEV}/initctl p
|
||||
mknod -m 666 ${DEV}/ptmx c 5 2
|
||||
|
||||
tar -C $ROOTFS -c . | docker import - archlinux
|
||||
docker run -i -t archlinux echo Success.
|
||||
rm -rf $ROOTFS
|
||||
@@ -10,12 +10,18 @@ variant='minbase'
|
||||
include='iproute,iputils-ping'
|
||||
|
||||
repo="$1"
|
||||
suite="${2:-$stableSuite}"
|
||||
suite="$2"
|
||||
mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
|
||||
|
||||
if [ ! "$repo" ]; then
|
||||
echo >&2 "usage: $0 repo [suite [mirror]]"
|
||||
if [ ! "$repo" ] || [ ! "$suite" ]; then
|
||||
echo >&2 "usage: $0 repo suite [mirror]"
|
||||
echo >&2
|
||||
echo >&2 " ie: $0 tianon/debian squeeze"
|
||||
echo >&2 " $0 tianon/debian squeeze http://ftp.uk.debian.org/debian/"
|
||||
echo >&2
|
||||
echo >&2 " ie: $0 tianon/ubuntu precise"
|
||||
echo >&2 " $0 tianon/ubuntu precise http://mirrors.melbourne.co.uk/ubuntu/"
|
||||
echo >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -33,8 +39,12 @@ sudo debootstrap --verbose --variant="$variant" --include="$include" "$suite" "$
|
||||
cd "$target"
|
||||
|
||||
# prevent init scripts from running during install/update
|
||||
# policy-rc.d (for most scripts)
|
||||
echo $'#!/bin/sh\nexit 101' | sudo tee usr/sbin/policy-rc.d > /dev/null
|
||||
sudo chmod +x usr/sbin/policy-rc.d
|
||||
# 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
|
||||
|
||||
# shrink the image, since apt makes us fat (wheezy: ~157.5MB vs ~120MB)
|
||||
@@ -42,9 +52,16 @@ sudo chroot . apt-get clean
|
||||
|
||||
# while we're at it, apt is unnecessarily slow inside containers
|
||||
# this forces dpkg not to call sync() after package extraction and speeds up install
|
||||
# the benefit is huge on spinning disks, and the penalty is nonexistent on SSD or decent server virtualization
|
||||
echo 'force-unsafe-io' | sudo tee etc/dpkg/dpkg.cfg.d/02apt-speedup > /dev/null
|
||||
# we don't need an apt cache in a container
|
||||
echo 'Acquire::http {No-Cache=True;};' | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null
|
||||
# we want to effectively run "apt-get clean" after every install to keep images small
|
||||
echo 'DPkg::Post-Invoke {"/bin/rm -f /var/cache/apt/archives/*.deb || true";};' | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null
|
||||
|
||||
# helpful undo lines for each the above tweaks (for lack of a better home to keep track of them):
|
||||
# rm /usr/sbin/policy-rc.d
|
||||
# rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl
|
||||
# rm /etc/dpkg/dpkg.cfg.d/02apt-speedup
|
||||
# rm /etc/apt/apt.conf.d/no-cache
|
||||
|
||||
# create the image (and tag $repo:$suite)
|
||||
sudo tar -c . | docker import - $repo $suite
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2013 Honza Pokorny
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. 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.
|
||||
|
||||
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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
@@ -0,0 +1,23 @@
|
||||
dockerfile.vim
|
||||
==============
|
||||
|
||||
Syntax highlighting for Dockerfiles
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Via pathogen, the usual way...
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
The syntax highlighting includes:
|
||||
|
||||
* The directives (e.g. `FROM`)
|
||||
* Strings
|
||||
* Comments
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD, short and sweet
|
||||
@@ -0,0 +1,18 @@
|
||||
*dockerfile.txt* Syntax highlighting for Dockerfiles
|
||||
|
||||
Author: Honza Pokorny <http://honza.ca>
|
||||
License: BSD
|
||||
|
||||
INSTALLATION *installation*
|
||||
|
||||
Drop it on your Pathogen path and you're all set.
|
||||
|
||||
FEATURES *features*
|
||||
|
||||
The syntax highlighting includes:
|
||||
|
||||
* The directives (e.g. FROM)
|
||||
* Strings
|
||||
* Comments
|
||||
|
||||
vim:tw=78:et:ft=help:norl:
|
||||
@@ -0,0 +1 @@
|
||||
au BufNewFile,BufRead Dockerfile set filetype=dockerfile
|
||||
@@ -0,0 +1,22 @@
|
||||
" dockerfile.vim - Syntax highlighting for Dockerfiles
|
||||
" Maintainer: Honza Pokorny <http://honza.ca>
|
||||
" Version: 0.5
|
||||
|
||||
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
let b:current_syntax = "dockerfile"
|
||||
|
||||
syntax case ignore
|
||||
|
||||
syntax match dockerfileKeyword /\v^\s*(FROM|MAINTAINER|RUN|CMD|EXPOSE|ENV|ADD)\s/
|
||||
syntax match dockerfileKeyword /\v^\s*(ENTRYPOINT|VOLUME|USER|WORKDIR)\s/
|
||||
highlight link dockerfileKeyword Keyword
|
||||
|
||||
syntax region dockerfileString start=/\v"/ skip=/\v\\./ end=/\v"/
|
||||
highlight link dockerfileString String
|
||||
|
||||
syntax match dockerfileComment "\v^\s*#.*$"
|
||||
highlight link dockerfileComment Comment
|
||||
+17
-35
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
@@ -30,7 +29,7 @@ func main() {
|
||||
flVersion := flag.Bool("v", false, "Print version information and quit")
|
||||
flDaemon := flag.Bool("d", false, "Daemon mode")
|
||||
flDebug := flag.Bool("D", false, "Debug mode")
|
||||
flAutoRestart := flag.Bool("r", false, "Restart previously running containers")
|
||||
flAutoRestart := flag.Bool("r", true, "Restart previously running containers")
|
||||
bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge. Use 'none' to disable container networking")
|
||||
pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
|
||||
flGraphPath := flag.String("g", "/var/lib/docker", "Path to graph storage base dir.")
|
||||
@@ -38,11 +37,7 @@ func main() {
|
||||
flDns := flag.String("dns", "", "Set custom dns servers")
|
||||
flHosts := docker.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)}
|
||||
flag.Var(&flHosts, "H", "tcp://host:port to bind/connect to or unix://path/to/socket to use")
|
||||
flEnableIptables := flag.Bool("iptables", true, "Disable iptables within docker")
|
||||
flDefaultIp := flag.String("ip", "0.0.0.0", "Default ip address to use when binding a containers ports")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *flVersion {
|
||||
showVersion()
|
||||
return
|
||||
@@ -54,9 +49,10 @@ func main() {
|
||||
flHosts[i] = utils.ParseHost(docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT, flHost)
|
||||
}
|
||||
|
||||
bridge := docker.DefaultNetworkBridge
|
||||
if *bridgeName != "" {
|
||||
bridge = *bridgeName
|
||||
docker.NetworkBridgeIface = *bridgeName
|
||||
} else {
|
||||
docker.NetworkBridgeIface = docker.DefaultNetworkBridge
|
||||
}
|
||||
if *flDebug {
|
||||
os.Setenv("DEBUG", "1")
|
||||
@@ -68,25 +64,7 @@ func main() {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
var dns []string
|
||||
if *flDns != "" {
|
||||
dns = []string{*flDns}
|
||||
}
|
||||
|
||||
ip := net.ParseIP(*flDefaultIp)
|
||||
|
||||
config := &docker.DaemonConfig{
|
||||
Pidfile: *pidfile,
|
||||
GraphPath: *flGraphPath,
|
||||
AutoRestart: *flAutoRestart,
|
||||
EnableCors: *flEnableCors,
|
||||
Dns: dns,
|
||||
EnableIptables: *flEnableIptables,
|
||||
BridgeIface: bridge,
|
||||
ProtoAddresses: flHosts,
|
||||
DefaultIp: ip,
|
||||
}
|
||||
if err := daemon(config); err != nil {
|
||||
if err := daemon(*pidfile, *flGraphPath, flHosts, *flAutoRestart, *flEnableCors, *flDns); err != nil {
|
||||
log.Fatal(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
@@ -137,26 +115,30 @@ func removePidFile(pidfile string) {
|
||||
}
|
||||
}
|
||||
|
||||
func daemon(config *docker.DaemonConfig) error {
|
||||
if err := createPidFile(config.Pidfile); err != nil {
|
||||
func daemon(pidfile string, flGraphPath string, protoAddrs []string, autoRestart, enableCors bool, flDns string) error {
|
||||
if err := createPidFile(pidfile); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer removePidFile(config.Pidfile)
|
||||
defer removePidFile(pidfile)
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM))
|
||||
go func() {
|
||||
sig := <-c
|
||||
log.Printf("Received signal '%v', exiting\n", sig)
|
||||
removePidFile(config.Pidfile)
|
||||
removePidFile(pidfile)
|
||||
os.Exit(0)
|
||||
}()
|
||||
server, err := docker.NewServer(config)
|
||||
var dns []string
|
||||
if flDns != "" {
|
||||
dns = []string{flDns}
|
||||
}
|
||||
server, err := docker.NewServer(flGraphPath, autoRestart, enableCors, dns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chErrors := make(chan error, len(config.ProtoAddresses))
|
||||
for _, protoAddr := range config.ProtoAddresses {
|
||||
chErrors := make(chan error, len(protoAddrs))
|
||||
for _, protoAddr := range protoAddrs {
|
||||
protoAddrParts := strings.SplitN(protoAddr, "://", 2)
|
||||
if protoAddrParts[0] == "unix" {
|
||||
syscall.Unlink(protoAddrParts[1])
|
||||
@@ -172,7 +154,7 @@ func daemon(config *docker.DaemonConfig) error {
|
||||
chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true)
|
||||
}()
|
||||
}
|
||||
for i := 0; i < len(config.ProtoAddresses); i += 1 {
|
||||
for i := 0; i < len(protoAddrs); i += 1 {
|
||||
err := <-chErrors
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+86
-24
@@ -1,38 +1,93 @@
|
||||
Docker Documentation
|
||||
====================
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
This is your definite place to contribute to the docker documentation. After each push to master the documentation
|
||||
is automatically generated and made available on [docs.docker.io](http://docs.docker.io)
|
||||
Overview
|
||||
--------
|
||||
|
||||
Each of the .rst files under sources reflects a page on the documentation.
|
||||
The source for Docker documentation is here under ``sources/`` in the
|
||||
form of .rst files. These files use
|
||||
[reStructuredText](http://docutils.sourceforge.net/rst.html)
|
||||
formatting with [Sphinx](http://sphinx-doc.org/) extensions for
|
||||
structure, cross-linking and indexing.
|
||||
|
||||
Installation
|
||||
------------
|
||||
The HTML files are built and hosted on
|
||||
[readthedocs.org](https://readthedocs.org/projects/docker/), appearing
|
||||
via proxy on https://docs.docker.io. The HTML files update
|
||||
automatically after each change to the master or release branch of the
|
||||
[docker files on GitHub](https://github.com/dotcloud/docker) thanks to
|
||||
post-commit hooks. The "release" branch maps to the "latest"
|
||||
documentation and the "master" branch maps to the "master"
|
||||
documentation.
|
||||
|
||||
**Warning**: The "master" documentation may include features not yet
|
||||
part of any official docker release. "Master" docs should be used only
|
||||
for understanding bleeding-edge development and "latest" should be
|
||||
used for the latest official release.
|
||||
|
||||
If you need to manually trigger a build of an existing branch, then
|
||||
you can do that through the [readthedocs
|
||||
interface](https://readthedocs.org/builds/docker/). If you would like
|
||||
to add new build targets, including new branches or tags, then you
|
||||
must contact one of the existing maintainers and get your
|
||||
readthedocs.org account added to the maintainers list, or just file an
|
||||
issue on GitHub describing the branch/tag and why it needs to be added
|
||||
to the docs, and one of the maintainers will add it for you.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
To edit and test the docs, you'll need to install the Sphinx tool and
|
||||
its dependencies. There are two main ways to install this tool:
|
||||
|
||||
Native Installation
|
||||
...................
|
||||
|
||||
* Work in your own fork of the code, we accept pull requests.
|
||||
* Install sphinx: `pip install sphinx`
|
||||
* Mac OS X: `[sudo] pip-2.7 install sphinx`)
|
||||
* Mac OS X: `[sudo] pip-2.7 install sphinx`
|
||||
* Install sphinx httpdomain contrib package: `pip install sphinxcontrib-httpdomain`
|
||||
* Mac OS X: `[sudo] pip-2.7 install sphinxcontrib-httpdomain`
|
||||
* If pip is not available you can probably install it using your favorite package manager as **python-pip**
|
||||
|
||||
Alternative Installation: Docker Container
|
||||
..........................................
|
||||
|
||||
If you're running ``docker`` on your development machine then you may
|
||||
find it easier and cleaner to use the Dockerfile. This installs Sphinx
|
||||
in a container, adds the local ``docs/`` directory and builds the HTML
|
||||
docs inside the container, even starting a simple HTTP server on port
|
||||
8000 so that you can connect and see your changes. Just run ``docker
|
||||
build .`` and run the resulting image. This is the equivalent to
|
||||
``make clean server`` since each container starts clean.
|
||||
|
||||
Usage
|
||||
-----
|
||||
* Change the `.rst` files with your favorite editor to your liking.
|
||||
* Run `make docs` to clean up old files and generate new ones.
|
||||
* Your static website can now be found in the `_build` directory.
|
||||
* To preview what you have generated run `make server` and open http://localhost:8000/ in your favorite browser.
|
||||
* Follow the contribution guidelines (``../CONTRIBUTING.md``)
|
||||
* Work in your own fork of the code, we accept pull requests.
|
||||
* Change the ``.rst`` files with your favorite editor -- try to keep the
|
||||
lines short and respect RST and Sphinx conventions.
|
||||
* Run ``make clean docs`` to clean up old files and generate new ones,
|
||||
or just ``make docs`` to update after small changes.
|
||||
* Your static website can now be found in the ``_build`` directory.
|
||||
* To preview what you have generated run ``make server`` and open
|
||||
http://localhost:8000/ in your favorite browser.
|
||||
|
||||
``make clean docs`` must complete without any warnings or errors.
|
||||
|
||||
Working using GitHub's file editor
|
||||
----------------------------------
|
||||
Alternatively, for small changes and typo's you might want to use GitHub's built in file editor. It allows
|
||||
you to preview your changes right online. Just be careful not to create many commits.
|
||||
|
||||
Alternatively, for small changes and typos you might want to use
|
||||
GitHub's built in file editor. It allows you to preview your changes
|
||||
right online (though there can be some differences between GitHub
|
||||
markdown and Sphinx RST). Just be careful not to create many commits.
|
||||
|
||||
Images
|
||||
------
|
||||
When you need to add images, try to make them as small as possible (e.g. as gif).
|
||||
|
||||
When you need to add images, try to make them as small as possible
|
||||
(e.g. as gif). Usually images should go in the same directory as the
|
||||
.rst file which references them, or in a subdirectory if one already
|
||||
exists.
|
||||
|
||||
Notes
|
||||
-----
|
||||
@@ -41,7 +96,7 @@ lessc ``lessc main.less`` or watched using watch-lessc ``watch-lessc -i main.les
|
||||
|
||||
Guides on using sphinx
|
||||
----------------------
|
||||
* To make links to certain pages create a link target like so:
|
||||
* To make links to certain sections create a link target like so:
|
||||
|
||||
```
|
||||
.. _hello_world:
|
||||
@@ -52,7 +107,10 @@ Guides on using sphinx
|
||||
This is.. (etc.)
|
||||
```
|
||||
|
||||
The ``_hello_world:`` will make it possible to link to this position (page and marker) from all other pages.
|
||||
The ``_hello_world:`` will make it possible to link to this position
|
||||
(page and section heading) from all other pages. See the [Sphinx
|
||||
docs](http://sphinx-doc.org/markup/inline.html#role-ref) for more
|
||||
information and examples.
|
||||
|
||||
* Notes, warnings and alarms
|
||||
|
||||
@@ -68,13 +126,17 @@ Guides on using sphinx
|
||||
|
||||
* Code examples
|
||||
|
||||
Start without $, so it's easy to copy and paste.
|
||||
* Start without $, so it's easy to copy and paste.
|
||||
* Use "sudo" with docker to ensure that your command is runnable
|
||||
even if they haven't [used the *docker*
|
||||
group](http://docs.docker.io/en/latest/use/basics/#why-sudo).
|
||||
|
||||
Manpages
|
||||
--------
|
||||
|
||||
* To make the manpages, simply run 'make man'. Please note there is a bug in spinx 1.1.3 which makes this fail.
|
||||
Upgrade to the latest version of sphinx.
|
||||
* Then preview the manpage by running `man _build/man/docker.1`, where _build/man/docker.1 is the path to the generated
|
||||
manfile
|
||||
* The manpages are also autogenerated by our hosted readthedocs here: http://docs-docker.dotcloud.com/projects/docker/downloads/
|
||||
* To make the manpages, run ``make man``. Please note there is a bug
|
||||
in spinx 1.1.3 which makes this fail. Upgrade to the latest version
|
||||
of Sphinx.
|
||||
* Then preview the manpage by running ``man _build/man/docker.1``,
|
||||
where ``_build/man/docker.1`` is the path to the generated manfile
|
||||
|
||||
|
||||
@@ -34,8 +34,13 @@ Calling /images/<name>/insert is the same as calling
|
||||
You can still call an old version of the api using
|
||||
/v1.0/images/<name>/insert
|
||||
|
||||
v1.6
|
||||
****
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.6`
|
||||
*****************************
|
||||
|
||||
What's new
|
||||
----------
|
||||
@@ -49,8 +54,13 @@ What's new
|
||||
stderr are merged.
|
||||
|
||||
|
||||
v1.5
|
||||
****
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.5`
|
||||
*****************************
|
||||
|
||||
What's new
|
||||
----------
|
||||
@@ -71,8 +81,13 @@ What's new
|
||||
dicts each containing `PublicPort`, `PrivatePort` and `Type` describing a
|
||||
port mapping.
|
||||
|
||||
v1.4
|
||||
****
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.4`
|
||||
*****************************
|
||||
|
||||
What's new
|
||||
----------
|
||||
@@ -89,11 +104,16 @@ What's new
|
||||
|
||||
**New!** Image's name added in the events
|
||||
|
||||
:doc:`docker_remote_api_v1.3`
|
||||
*****************************
|
||||
v1.3
|
||||
****
|
||||
|
||||
docker v0.5.0 51f6c4a_
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.3`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
@@ -126,11 +146,16 @@ Start containers (/containers/<id>/start):
|
||||
- You can now pass host-specific configuration (e.g. bind mounts) in
|
||||
the POST body for start calls
|
||||
|
||||
:doc:`docker_remote_api_v1.2`
|
||||
*****************************
|
||||
v1.2
|
||||
****
|
||||
|
||||
docker v0.4.2 2e7649b_
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.2`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
@@ -156,11 +181,16 @@ The client should send it's authConfig as POST on each call of
|
||||
deleted/untagged.
|
||||
|
||||
|
||||
:doc:`docker_remote_api_v1.1`
|
||||
*****************************
|
||||
v1.1
|
||||
****
|
||||
|
||||
docker v0.4.0 a8ae398_
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.1`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
@@ -180,12 +210,16 @@ What's new
|
||||
{"error":"Invalid..."}
|
||||
...
|
||||
|
||||
|
||||
:doc:`docker_remote_api_v1.0`
|
||||
*****************************
|
||||
v1.0
|
||||
****
|
||||
|
||||
docker v0.3.4 8d73740_
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.0`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -463,7 +463,7 @@ Ancestry
|
||||
2.4 Status
|
||||
----------
|
||||
|
||||
.. http:get /v1/_ping
|
||||
.. http:get:: /v1/_ping
|
||||
|
||||
Check status of the registry. This endpoint is also used to determine if
|
||||
the registry supports SSL.
|
||||
@@ -479,9 +479,6 @@ Ancestry
|
||||
|
||||
""
|
||||
|
||||
:parameter namespace: namespace for the repo
|
||||
:parameter repository: name for the repo
|
||||
|
||||
**Example Response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
@@ -54,14 +54,10 @@ Available Commands
|
||||
|
||||
.. include:: command/kill.rst
|
||||
|
||||
.. include:: command/link.rst
|
||||
|
||||
.. include:: command/login.rst
|
||||
|
||||
.. include:: command/logs.rst
|
||||
|
||||
.. include:: command/ls.rst
|
||||
|
||||
.. include:: command/port.rst
|
||||
|
||||
.. include:: command/ps.rst
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
:title: Link Command
|
||||
:description: Add a link or rename the link for a container
|
||||
:keywords: link, docker, container, documentation, link, links
|
||||
|
||||
============================================================================
|
||||
``link`` -- Add a link or rename the link for a container
|
||||
============================================================================
|
||||
|
||||
::
|
||||
|
||||
Usage: docker link CURRENT_NAME NEW_NAME
|
||||
|
||||
Link a container to a new name.
|
||||
|
||||
|
||||
Examples:
|
||||
--------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ docker link /59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc /redis
|
||||
$ docker ls
|
||||
NAME ID IMAGE
|
||||
/redis 59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc crosbymichael/redis:latest
|
||||
/59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc 59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc crosbymichael/redis:latest
|
||||
|
||||
|
||||
This will create a new link for the existing name ``/59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc``
|
||||
with the new name ``/redis`` so that we can new reference the same container under the new name ``/redis``.
|
||||
@@ -1,29 +0,0 @@
|
||||
:title: Ls Command
|
||||
:description: Ls all links for containers
|
||||
:keywords: ls, docker, container, documentation, link, links
|
||||
|
||||
============================================================================
|
||||
``ls`` -- List all links for containers
|
||||
============================================================================
|
||||
|
||||
::
|
||||
|
||||
Usage: docker ls
|
||||
|
||||
List all links for containers and display the relationship between parent
|
||||
and child containers.
|
||||
|
||||
|
||||
Examples:
|
||||
--------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ docker ls
|
||||
NAME ID IMAGE
|
||||
/redis 39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89 crosbymichael/redis:latest
|
||||
/webapp cffb86ffa80b11cd8777d300759ee53c4e61729431c30ec9552dd9e6d3abc87d demo:latest
|
||||
/webapp/redis 39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89 crosbymichael/redis:latest
|
||||
|
||||
This will display all links and the names that you can use the reference the link. Parent child
|
||||
relationships are also displayed with ls.
|
||||
@@ -11,26 +11,3 @@
|
||||
Usage: docker rm [OPTIONS] CONTAINER
|
||||
|
||||
Remove one or more containers
|
||||
-link="": Remove the link instead of the actual container
|
||||
|
||||
|
||||
Examples:
|
||||
--------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ docker rm /redis
|
||||
/redis
|
||||
|
||||
|
||||
This will remove the container referenced under the link ``/redis``.
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ docker rm -link /webapp/redis
|
||||
/webapp/redis
|
||||
|
||||
|
||||
This will remove the underlying link between ``/webapp`` and the ``/redis`` containers removing all
|
||||
network communication.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
Run a command in a new container
|
||||
|
||||
-a=map[]: Attach to stdin, stdout or stderr
|
||||
-a=map[]: Attach to stdin, stdout or stderr.
|
||||
-c=0: CPU shares (relative weight)
|
||||
-cidfile="": Write the container ID to the file
|
||||
-d=false: Detached mode: Run container in the background, print new container id
|
||||
@@ -27,13 +27,11 @@
|
||||
-t=false: Allocate a pseudo-tty
|
||||
-u="": Username or UID
|
||||
-dns=[]: Set custom dns servers for the container
|
||||
-v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "host-dir" is missing, then docker creates a new volume
|
||||
-volumes-from="": Mount all volumes from the given container
|
||||
-entrypoint="": Overwrite the default entrypoint set by the image
|
||||
-v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume.
|
||||
-volumes-from="": Mount all volumes from the given container.
|
||||
-entrypoint="": Overwrite the default entrypoint set by the image.
|
||||
-w="": Working directory inside the container
|
||||
-lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
|
||||
-expose=[]: Expose a port from the container without publishing it to your host
|
||||
-link="": Add link to another container (containerid:alias)
|
||||
|
||||
Examples
|
||||
--------
|
||||
@@ -84,34 +82,4 @@ working directory, by changing into the directory to the value
|
||||
returned by ``pwd``. So this combination executes the command
|
||||
using the container, but inside the current working directory.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -p 127.0.0.0::80 ubuntu bash
|
||||
|
||||
This the ``-p`` flag now allows you to bind a port to a specific
|
||||
interface of the host machine. In this example port ``80`` of the
|
||||
container will have a dynamically allocated port bound to 127.0.0.1
|
||||
of the host.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -p 127.0.0.1:80:80 ubuntu bash
|
||||
|
||||
This will bind port ``80`` of the container to port ``80`` on 127.0.0.1 of your
|
||||
host machine.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -expose 80 ubuntu bash
|
||||
|
||||
This will expose port ``80`` of the container for use within a link
|
||||
without publishing the port to the host system's interfaces.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -link /redis:redis ubuntu bash
|
||||
|
||||
The ``-link`` flag will link the container named ``/redis`` into the
|
||||
newly created container with the alias ``redis``. The new container
|
||||
can access the network and environment of the redis container via
|
||||
environment variables.
|
||||
|
||||
@@ -124,7 +124,7 @@ You can run an interactive session in the newly built container:
|
||||
|
||||
|
||||
|
||||
.. note:: The binary is availalbe outside the container in the directory ``./bundles/<version>-dev/binary/``.
|
||||
.. note:: The binary is available outside the container in the directory ``./bundles/<version>-dev/binary/``. You can swap your host docker executable with this binary for live testing - for example, on ubuntu: ``sudo service docker stop ; sudo cp $(which docker) $(which docker)_ ; sudo cp ./bundles/<version>-dev/binary/docker-<version>-dev $(which docker);sudo service docker start``.
|
||||
|
||||
|
||||
**Need More Help?**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
:title: Docker Examples
|
||||
:description: Examples on how to use Docker
|
||||
:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql, link
|
||||
:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql
|
||||
|
||||
|
||||
.. _example_list:
|
||||
@@ -24,4 +24,3 @@ to more substantial services like you might find in production.
|
||||
postgresql_service
|
||||
mongodb
|
||||
running_riak_service
|
||||
linking_into_redis
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
:title: Linking to an Redis container
|
||||
:description: Running redis linked into your web app
|
||||
:keywords: docker, example, networking, redis, link
|
||||
|
||||
.. _linking_redis:
|
||||
|
||||
Linking Redis
|
||||
=============
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
Building a redis container to link as a child of our web application.
|
||||
|
||||
Building the redis container
|
||||
----------------------------
|
||||
|
||||
We will use a pre-build version of redis from the index under
|
||||
the name ``crosbymichael/redis``. If you are interested in the
|
||||
Dockerfile that was used to build this container here it is.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Build redis from source
|
||||
# Make sure you have the redis source code checked out in
|
||||
# the same directory as this Dockerfile
|
||||
FROM ubuntu
|
||||
|
||||
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
|
||||
RUN apt-get update
|
||||
RUN apt-get upgrade -y
|
||||
|
||||
RUN apt-get install -y gcc make g++ build-essential libc6-dev tcl
|
||||
|
||||
ADD . /redis
|
||||
|
||||
RUN (cd /redis && make)
|
||||
RUN (cd /redis && make test)
|
||||
|
||||
RUN mkdir -p /redis-data
|
||||
VOLUME ["/redis-data"]
|
||||
EXPOSE 6379
|
||||
|
||||
ENTRYPOINT ["/redis/src/redis-server"]
|
||||
CMD ["--dir", "/redis-data"]
|
||||
|
||||
|
||||
We need to ``EXPOSE`` the default port of 6379 so that our link knows what ports
|
||||
to connect to our redis container on. If you do not expose any ports for the
|
||||
image then docker will not be able to establish the link between containers.
|
||||
|
||||
|
||||
Run the redis container
|
||||
-----------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -d -e PASSWORD=docker crosbymichael/redis --requirepass=docker
|
||||
|
||||
This will run our redis container using the default port of 6379 and using
|
||||
as password to secure our service. Next we will link the redis container to
|
||||
a new name using ``docker link`` and ``docker ls``.
|
||||
|
||||
|
||||
Linking an existing container
|
||||
-----------------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker ls
|
||||
|
||||
NAME ID IMAGE
|
||||
/39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89 39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89 crosbymichael/redis:latest
|
||||
|
||||
|
||||
Docker will automatically create an initial link with the container's id but
|
||||
because the is long and not very user friendly we can link the container with
|
||||
a new name.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker link /39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89 /redis
|
||||
|
||||
docker ls
|
||||
|
||||
NAME ID IMAGE
|
||||
/redis 39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89 crosbymichael/redis:latest
|
||||
/39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89 39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89 crosbymichael/redis:latest
|
||||
|
||||
Now we can reference our running redis service using the friendly name ``/redis``.
|
||||
We can issue all the commands that you would expect; start, stop, attach, using the new name.
|
||||
|
||||
Linking redis as a child
|
||||
------------------------
|
||||
|
||||
Next we can start a new web application that has a dependency on redis and apply a link
|
||||
to connect both containers. If you noticed when running our redis service we did not use
|
||||
the ``-p`` option to publish the redis port to the host system. Redis exposed port 6379
|
||||
but we did not publish the port. This allows docker to prevent all network traffic to
|
||||
the redis container except when explicitly specified within a link. This is a big win
|
||||
for security.
|
||||
|
||||
|
||||
Now lets start our web application with a link into redis.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -t -i -link /redis:db ubuntu bash
|
||||
|
||||
root@4c01db0b339c:/# env
|
||||
|
||||
HOSTNAME=4c01db0b339c
|
||||
DB_NAME=/4c01db0b339cf19958731255a796ee072040a652f51652a4ade190ab8c27006f/db
|
||||
TERM=xterm
|
||||
DB_PORT=tcp://172.17.0.8:6379
|
||||
DB_PORT_6379_TCP=tcp://172.17.0.8:6379
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
PWD=/
|
||||
DB_ENV_PASSWORD=dockerpass
|
||||
SHLVL=1
|
||||
HOME=/
|
||||
container=lxc
|
||||
_=/usr/bin/env
|
||||
root@4c01db0b339c:/#
|
||||
|
||||
|
||||
When we inspect the environment of the linked container we can see a few extra environment
|
||||
variables have been added. When you specified ``-link /redis:db`` you are telling docker
|
||||
to link the container named ``/redis`` into this new container with the alias ``db``.
|
||||
Environment variables are prefixed with the alias so that the parent container can access
|
||||
network and environment information from the child.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# The name of the child container
|
||||
DB_NAME=/4c01db0b339cf19958731255a796ee072040a652f51652a4ade190ab8c27006f/db
|
||||
# The default protocol, ip, and port of the service running in the container
|
||||
DB_PORT=tcp://172.17.0.8:6379
|
||||
# A specific protocol, ip, and port of various services
|
||||
DB_PORT_6379_TCP=tcp://172.17.0.8:6379
|
||||
# Get environment variables of the container
|
||||
DB_ENV_PASSWORD=dockerpass
|
||||
|
||||
|
||||
Accessing the network information along with the environment of the child container allows
|
||||
us to easily connect to the redis service on the specific ip and port and use the password
|
||||
specified in the environment.
|
||||
@@ -39,12 +39,12 @@ Update its dependencies.
|
||||
|
||||
apt-get update
|
||||
|
||||
Install ``python-software-properies``.
|
||||
Install ``python-software-properties``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
apt-get install python-software-properties
|
||||
apt-get install software-properties-common
|
||||
apt-get -y install python-software-properties
|
||||
apt-get -y install software-properties-common
|
||||
|
||||
Add Pitti's PostgreSQL repository. It contains the most recent stable release
|
||||
of PostgreSQL i.e. ``9.2``.
|
||||
@@ -77,7 +77,8 @@ role.
|
||||
|
||||
Adjust PostgreSQL configuration so that remote connections to the
|
||||
database are possible. Make sure that inside
|
||||
``/etc/postgresql/9.2/main/pg_hba.conf`` you have following line:
|
||||
``/etc/postgresql/9.2/main/pg_hba.conf`` you have following line (you will need
|
||||
to install an editor, e.g. ``apt-get install vim``):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -90,9 +91,17 @@ uncomment ``listen_addresses`` so it is as follows:
|
||||
|
||||
listen_addresses='*'
|
||||
|
||||
*Note:* this PostgreSQL setup is for development only purposes. Refer
|
||||
to PostgreSQL documentation how to fine-tune these settings so that it
|
||||
is enough secure.
|
||||
.. note::
|
||||
|
||||
This PostgreSQL setup is for development only purposes. Refer
|
||||
to PostgreSQL documentation how to fine-tune these settings so that it
|
||||
is enough secure.
|
||||
|
||||
Exit.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
exit
|
||||
|
||||
Create an image and assign it a name. ``<container_id>`` is in the
|
||||
Bash prompt; you can also locate it using ``docker ps -a``.
|
||||
@@ -111,7 +120,9 @@ Finally, run PostgreSQL server via ``docker``.
|
||||
-D /var/lib/postgresql/9.2/main \
|
||||
-c config_file=/etc/postgresql/9.2/main/postgresql.conf')
|
||||
|
||||
Connect the PostgreSQL server using ``psql``.
|
||||
Connect the PostgreSQL server using ``psql`` (You will need postgres installed
|
||||
on the machine. For ubuntu, use something like
|
||||
``sudo apt-get install postgresql``).
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -128,7 +139,7 @@ As before, create roles or databases if needed.
|
||||
docker=# CREATE DATABASE foo OWNER=docker;
|
||||
CREATE DATABASE
|
||||
|
||||
Additionally, publish there your newly created image on Docker Index.
|
||||
Additionally, publish your newly created image on Docker Index.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -149,10 +160,11 @@ container starts.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker commit <container_id> <your username> postgresql -run='{"Cmd": \
|
||||
sudo docker commit -run='{"Cmd": \
|
||||
["/bin/su", "postgres", "-c", "/usr/lib/postgresql/9.2/bin/postgres -D \
|
||||
/var/lib/postgresql/9.2/main -c \
|
||||
config_file=/etc/postgresql/9.2/main/postgresql.conf"], "PortSpecs": ["5432"]}'
|
||||
config_file=/etc/postgresql/9.2/main/postgresql.conf"], "PortSpecs": ["5432"]}' \
|
||||
<container_id> <your username>/postgresql
|
||||
|
||||
From now on, just type ``docker run <your username>/postgresql`` and
|
||||
PostgreSQL should automatically start.
|
||||
|
||||
@@ -44,10 +44,10 @@ Security Group to allow SSH.** By default all incoming ports to your
|
||||
new instance will be blocked by the AWS Security Group, so you might
|
||||
just get timeouts when you try to connect.
|
||||
|
||||
Installing with ``get.docker.io`` (as above) will create a service
|
||||
named ``dockerd``. You may want to set up a :ref:`docker group
|
||||
<dockergroup>` and add the *ubuntu* user to it so that you don't have
|
||||
to use ``sudo`` for every Docker command.
|
||||
Installing with ``get.docker.io`` (as above) will create a service named
|
||||
``lxc-docker``. It will also set up a :ref:`docker group <dockergroup>` and you
|
||||
may want to add the *ubuntu* user to it so that you don't have to use ``sudo``
|
||||
for every Docker command.
|
||||
|
||||
Once you've got Docker installed, you're ready to try it out -- head
|
||||
on over to the :doc:`../use/basics` or :doc:`../examples/index` section.
|
||||
|
||||
@@ -96,16 +96,3 @@ Or, to enable it more permanently:
|
||||
.. code-block:: bash
|
||||
|
||||
echo net.ipv4.ip_forward = 1 | sudo tee /etc/sysctl.d/docker.conf
|
||||
|
||||
fork/exec /usr/sbin/lxc-start: operation not permitted
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Unfortunately, Gentoo suffers from `issue #1422
|
||||
<https://github.com/dotcloud/docker/issues/1422>`_, meaning that after every
|
||||
fresh start of docker, the first docker run fails due to some tricky terminal
|
||||
issues, so be sure to run something trivial (such as ``docker run -i -t busybox
|
||||
echo hi``) before attempting to run anything important.
|
||||
|
||||
There is a tentative (and very hacky) workaround for this in the OpenRC init
|
||||
script, and it can be enabled by modifying the appropriate value in
|
||||
``/etc/conf.d/docker`` after successful installation.
|
||||
|
||||
@@ -75,7 +75,7 @@ Docker is available as a Debian package, which makes installation easy.
|
||||
|
||||
# Add the Docker repository key to your local keychain
|
||||
# using apt-key finger you can check the fingerprint matches 36A1 D786 9245 C895 0F96 6E92 D857 6A8B A88D 21E9
|
||||
sudo sh -c "curl https://get.docker.io/gpg | apt-key add -"
|
||||
sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -"
|
||||
|
||||
# Add the Docker repository to your apt sources list.
|
||||
sudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
|
||||
@@ -129,7 +129,7 @@ to follow them again.*
|
||||
|
||||
# Add the Docker repository key to your local keychain
|
||||
# using apt-key finger you can check the fingerprint matches 36A1 D786 9245 C895 0F96 6E92 D857 6A8B A88D 21E9
|
||||
sudo sh -c "curl https://get.docker.io/gpg | apt-key add -"
|
||||
sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -"
|
||||
|
||||
# Add the Docker repository to your apt sources list.
|
||||
sudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
|
||||
|
||||
@@ -53,9 +53,7 @@ Spin it up
|
||||
|
||||
* Download the 'official' Precise64 base ubuntu virtual machine image from vagrantup.com
|
||||
* Boot this image in virtualbox
|
||||
* Add the `Docker PPA sources <https://launchpad.net/~dotcloud/+archive/lxc-docker>`_ to /etc/apt/sources.lst
|
||||
* Update your sources
|
||||
* Install lxc-docker
|
||||
* Follow official :ref:`ubuntu_linux` installation path
|
||||
|
||||
You now have a Ubuntu Virtual Machine running with docker pre-installed.
|
||||
|
||||
|
||||
@@ -54,8 +54,14 @@ Docker evaluates the instructions in a Dockerfile in order. **The
|
||||
first instruction must be `FROM`** in order to specify the
|
||||
:ref:`base_image_def` from which you are building.
|
||||
|
||||
Docker will ignore **comment lines** *beginning* with ``#``. A comment
|
||||
marker anywhere in the rest of the line will be treated as an argument.
|
||||
Docker will treat lines that *begin* with ``#`` as a comment. A ``#``
|
||||
marker anywhere else in the line will be treated as an argument. This
|
||||
allows statements like:
|
||||
|
||||
::
|
||||
|
||||
# Comment
|
||||
RUN echo 'we are running some # of cool things'
|
||||
|
||||
3. Instructions
|
||||
===============
|
||||
@@ -226,10 +232,10 @@ The copy obeys the following rules:
|
||||
with conflicts resolved in favor of 2) on a file-by-file basis.
|
||||
|
||||
* If ``<src>`` is any other kind of file, it is copied individually
|
||||
along with its metadata. In this case, if ``<dst>`` ends with a
|
||||
along with its metadata. In this case, if ``<dest>`` ends with a
|
||||
trailing slash ``/``, it will be considered a directory and the
|
||||
contents of ``<src>`` will be written at ``<dst>/base(<src>)``.
|
||||
* If ``<dst>`` does not end with a trailing slash, it will be
|
||||
contents of ``<src>`` will be written at ``<dest>/base(<src>)``.
|
||||
* If ``<dest>`` does not end with a trailing slash, it will be
|
||||
considered a regular file and the contents of ``<src>`` will be
|
||||
written at ``<dst>``.
|
||||
* If ``<dest>`` doesn't exist, it is created along with all missing
|
||||
@@ -321,7 +327,7 @@ the command given by ``CMD`` is executed.
|
||||
# VERSION 0.0.1
|
||||
|
||||
FROM ubuntu
|
||||
MAINTAINER Guillaume J. Charmes "guillaume@dotcloud.com"
|
||||
MAINTAINER Guillaume J. Charmes <guillaume@dotcloud.com>
|
||||
|
||||
# make sure the package repository is up to date
|
||||
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
|
||||
|
||||
Vendored
+28
-6
@@ -6,6 +6,9 @@
|
||||
<title>{{ meta['title'] if meta and meta['title'] else title }} - Docker Documentation</title>
|
||||
<meta name="description" content="{{ meta['description'] if meta }}" />
|
||||
<meta name="keywords" content="{{ meta['keywords'] if meta }}" />
|
||||
<!-- Swiftype tags: https://swiftype.com/documentation/meta_tags -->
|
||||
<meta property='st:popularity' content='4' />
|
||||
<meta property='st:type' content='docker_doc' />
|
||||
|
||||
{%- set url_root = pathto('', 1) %}
|
||||
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
|
||||
@@ -25,11 +28,20 @@
|
||||
+ ['//fonts.googleapis.com/css?family=Cabin:400,700,400italic']
|
||||
%}
|
||||
|
||||
{%- if pagename == 'index' %}
|
||||
<link rel="canonical" href="http://docs.docker.io/en/latest/">
|
||||
{% else %}
|
||||
<link rel="canonical" href="http://docs.docker.io/en/latest/{{ pagename }}/">
|
||||
{% endif %}
|
||||
{#
|
||||
This part is hopefully complex because things like |cut '/index/' are not available in spinx jinja
|
||||
and will make it crash. (and we need index/ out.
|
||||
#}
|
||||
<link rel="canonical" href="http://docs.docker.io/en/latest/
|
||||
{%- for word in pagename.split('/') -%}
|
||||
{%- if word != 'index' -%}
|
||||
{%- if word != '' -%}
|
||||
{{ word }}/
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
">
|
||||
|
||||
{%- for cssfile in css_files %}
|
||||
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
|
||||
{%- endfor %}
|
||||
@@ -124,7 +136,14 @@
|
||||
<small>Current version:</small>
|
||||
<ul class="inline">
|
||||
{% for slug, url in versions %}
|
||||
<li class="alternative"><a href="{{ url }}{{ pagename }}/" title="Switch to {{ slug }}">{{ slug }}</a></li>
|
||||
<li class="alternative"><a href="{{ url }}{%- for word in pagename.split('/') -%}
|
||||
{%- if word != 'index' -%}
|
||||
{%- if word != '' -%}
|
||||
{{ word }}/
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}"
|
||||
title="Switch to {{ slug }}">{{ slug }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -152,6 +171,9 @@
|
||||
Swiftype.resultContainingElement = '#st-results-container';
|
||||
Swiftype.attachElement = '#st-search-input';
|
||||
Swiftype.renderStyle = "overlay";
|
||||
// from https://swiftype.com/questions/how-can-i-make-more-popular-content-rank-higher
|
||||
// Use "page" for now -- they don't subgroup by document type yet.
|
||||
Swiftype.searchFunctionalBoosts = {"page": {"popularity": "linear"}};
|
||||
|
||||
var script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
|
||||
+2
-1
@@ -397,7 +397,8 @@ dt:hover > a.headerlink {
|
||||
.admonition .admonition-title {
|
||||
font-size: larger;
|
||||
}
|
||||
.admonition.warning {
|
||||
.admonition.warning,
|
||||
.admonition.danger {
|
||||
border-color: #ac0004;
|
||||
}
|
||||
.admonition.note {
|
||||
|
||||
+1
-1
@@ -594,7 +594,7 @@ dt:hover > a.headerlink {
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
&.warning, &.danger {
|
||||
border-color: #ac0004;
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
||||
@@ -1,456 +0,0 @@
|
||||
package gograph
|
||||
|
||||
import (
|
||||
_ "code.google.com/p/gosqlite/sqlite3"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
const (
|
||||
createEntityTable = `
|
||||
CREATE TABLE IF NOT EXISTS entity (
|
||||
id text NOT NULL PRIMARY KEY
|
||||
);`
|
||||
|
||||
createEdgeTable = `
|
||||
CREATE TABLE IF NOT EXISTS edge (
|
||||
"entity_id" text NOT NULL,
|
||||
"parent_id" text NULL,
|
||||
"name" text NOT NULL,
|
||||
CONSTRAINT "parent_fk" FOREIGN KEY ("parent_id") REFERENCES "entity" ("id"),
|
||||
CONSTRAINT "entity_fk" FOREIGN KEY ("entity_id") REFERENCES "entity" ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "name_parent_ix" ON "edge" (parent_id, name);
|
||||
`
|
||||
)
|
||||
|
||||
// Entity with a unique id
|
||||
type Entity struct {
|
||||
id string
|
||||
}
|
||||
|
||||
// An Edge connects two entities together
|
||||
type Edge struct {
|
||||
EntityID string
|
||||
Name string
|
||||
ParentID string
|
||||
}
|
||||
|
||||
type Entities map[string]*Entity
|
||||
type Edges []*Edge
|
||||
|
||||
type WalkFunc func(fullPath string, entity *Entity) error
|
||||
|
||||
// Graph database for storing entities and their relationships
|
||||
type Database struct {
|
||||
dbPath string
|
||||
rootID string
|
||||
}
|
||||
|
||||
// Create a new graph database initialized with a root entity
|
||||
func NewDatabase(dbPath, rootId string) (*Database, error) {
|
||||
db := &Database{dbPath, rootId}
|
||||
if _, err := os.Stat(dbPath); err == nil {
|
||||
return db, nil
|
||||
}
|
||||
conn, err := db.openConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if _, err := conn.Exec(createEntityTable); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := conn.Exec(createEdgeTable); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rollback := func() {
|
||||
conn.Exec("ROLLBACK")
|
||||
}
|
||||
|
||||
// Create root entities
|
||||
if _, err := conn.Exec("BEGIN"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := conn.Exec("INSERT INTO entity (id) VALUES (?);", rootId); err != nil {
|
||||
rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := conn.Exec("INSERT INTO edge (entity_id, name) VALUES(?,?);", rootId, "/"); err != nil {
|
||||
rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := conn.Exec("COMMIT"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// Set the entity id for a given path
|
||||
func (db *Database) Set(fullPath, id string) (*Entity, error) {
|
||||
conn, err := db.openConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
rollback := func() {
|
||||
conn.Exec("ROLLBACK")
|
||||
}
|
||||
if _, err := conn.Exec("BEGIN"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var entityId string
|
||||
if err := conn.QueryRow("SELECT id FROM entity WHERE id = ?;", id).Scan(&entityId); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
if _, err := conn.Exec("INSERT INTO entity (id) VALUES(?);", id); err != nil {
|
||||
rollback()
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
rollback()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
e := &Entity{id}
|
||||
|
||||
parentPath, name := splitPath(fullPath)
|
||||
if err := db.setEdge(conn, parentPath, name, e); err != nil {
|
||||
rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := conn.Exec("COMMIT"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func (db *Database) setEdge(conn *sql.DB, parentPath, name string, e *Entity) error {
|
||||
parent, err := db.get(conn, parentPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if parent.id == e.id {
|
||||
return fmt.Errorf("Cannot set self as child")
|
||||
}
|
||||
|
||||
if _, err := conn.Exec("INSERT INTO edge (parent_id, name, entity_id) VALUES (?,?,?);", parent.id, name, e.id); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the root "/" entity for the database
|
||||
func (db *Database) RootEntity() *Entity {
|
||||
return &Entity{
|
||||
id: db.rootID,
|
||||
}
|
||||
}
|
||||
|
||||
// Return the entity for a given path
|
||||
func (db *Database) Get(name string) *Entity {
|
||||
conn, err := db.openConn()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
e, err := db.get(conn, name)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (db *Database) get(conn *sql.DB, name string) (*Entity, error) {
|
||||
e := db.RootEntity()
|
||||
// We always know the root name so return it if
|
||||
// it is requested
|
||||
if name == "/" {
|
||||
return e, nil
|
||||
}
|
||||
|
||||
parts := split(name)
|
||||
for i := 1; i < len(parts); i++ {
|
||||
p := parts[i]
|
||||
|
||||
next := db.child(conn, e, p)
|
||||
if next == nil {
|
||||
return nil, fmt.Errorf("Cannot find child")
|
||||
}
|
||||
e = next
|
||||
}
|
||||
return e, nil
|
||||
|
||||
}
|
||||
|
||||
// List all entities by from the name
|
||||
// The key will be the full path of the entity
|
||||
func (db *Database) List(name string, depth int) Entities {
|
||||
out := Entities{}
|
||||
conn, err := db.openConn()
|
||||
if err != nil {
|
||||
return out
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
for c := range db.children(conn, name, depth) {
|
||||
out[c.FullPath] = c.Entity
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
|
||||
conn, err := db.openConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
for c := range db.children(conn, name, depth) {
|
||||
if err := walkFunc(c.FullPath, c.Entity); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the refrence count for a specified id
|
||||
func (db *Database) Refs(id string) int {
|
||||
conn, err := db.openConn()
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
var count int
|
||||
if err := conn.QueryRow("SELECT COUNT(*) FROM edge WHERE entity_id = ?;", id).Scan(&count); err != nil {
|
||||
return 0
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// Return all the id's path references
|
||||
func (db *Database) RefPaths(id string) Edges {
|
||||
refs := Edges{}
|
||||
conn, err := db.openConn()
|
||||
if err != nil {
|
||||
return refs
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
rows, err := conn.Query("SELECT name, parent_id FROM edge WHERE entity_id = ?;", id)
|
||||
if err != nil {
|
||||
return refs
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var name string
|
||||
var parentId string
|
||||
if err := rows.Scan(&name, &parentId); err != nil {
|
||||
return refs
|
||||
}
|
||||
refs = append(refs, &Edge{
|
||||
EntityID: id,
|
||||
Name: name,
|
||||
ParentID: parentId,
|
||||
})
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
// Delete the reference to an entity at a given path
|
||||
func (db *Database) Delete(name string) error {
|
||||
if name == "/" {
|
||||
return fmt.Errorf("Cannot delete root entity")
|
||||
}
|
||||
conn, err := db.openConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
parentPath, n := splitPath(name)
|
||||
parent, err := db.get(conn, parentPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := conn.Exec("DELETE FROM edge WHERE parent_id = ? AND name = ?;", parent.id, n); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove the entity with the specified id
|
||||
// Walk the graph to make sure all references to the entity
|
||||
// are removed and return the number of references removed
|
||||
func (db *Database) Purge(id string) (int, error) {
|
||||
conn, err := db.openConn()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
rollback := func() {
|
||||
conn.Exec("ROLLBACK")
|
||||
}
|
||||
|
||||
if _, err := conn.Exec("BEGIN"); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
// Delete all edges
|
||||
rows, err := conn.Exec("DELETE FROM edge WHERE entity_id = ?;", id)
|
||||
if err != nil {
|
||||
rollback()
|
||||
return -1, err
|
||||
}
|
||||
|
||||
changes, err := rows.RowsAffected()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
// Delete entity
|
||||
if _, err := conn.Exec("DELETE FROM entity where id = ?;", id); err != nil {
|
||||
rollback()
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if _, err := conn.Exec("COMMIT"); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return int(changes), nil
|
||||
}
|
||||
|
||||
// Rename an edge for a given path
|
||||
func (db *Database) Rename(currentName, newName string) error {
|
||||
parentPath, name := splitPath(currentName)
|
||||
newParentPath, newEdgeName := splitPath(newName)
|
||||
|
||||
if parentPath != newParentPath {
|
||||
return fmt.Errorf("Cannot rename when root paths do not match %s != %s", parentPath, newParentPath)
|
||||
}
|
||||
|
||||
conn, err := db.openConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
parent, err := db.get(conn, parentPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := conn.Exec("UPDATE edge SET name = ? WHERE parent_id = ? AND name = ?;", newEdgeName, parent.id, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i, err := rows.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i == 0 {
|
||||
return fmt.Errorf("Cannot locate edge for %s %s", parent.id, name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WalkMeta struct {
|
||||
Parent *Entity
|
||||
Entity *Entity
|
||||
FullPath string
|
||||
Edge *Edge
|
||||
}
|
||||
|
||||
func (db *Database) children(conn *sql.DB, name string, depth int) <-chan WalkMeta {
|
||||
out := make(chan WalkMeta)
|
||||
e, err := db.get(conn, name)
|
||||
if err != nil {
|
||||
close(out)
|
||||
return out
|
||||
}
|
||||
|
||||
go func() {
|
||||
rows, err := conn.Query("SELECT entity_id, name FROM edge where parent_id = ?;", e.id)
|
||||
if err != nil {
|
||||
close(out)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var entityId, entityName string
|
||||
if err := rows.Scan(&entityId, &entityName); err != nil {
|
||||
// Log error
|
||||
continue
|
||||
}
|
||||
child := &Entity{entityId}
|
||||
edge := &Edge{
|
||||
ParentID: e.id,
|
||||
Name: entityName,
|
||||
EntityID: child.id,
|
||||
}
|
||||
|
||||
meta := WalkMeta{
|
||||
Parent: e,
|
||||
Entity: child,
|
||||
FullPath: path.Join(name, edge.Name),
|
||||
Edge: edge,
|
||||
}
|
||||
|
||||
out <- meta
|
||||
if depth == 0 {
|
||||
continue
|
||||
}
|
||||
nDepth := depth
|
||||
if depth != -1 {
|
||||
nDepth -= 1
|
||||
}
|
||||
sc := db.children(conn, meta.FullPath, nDepth)
|
||||
for c := range sc {
|
||||
out <- c
|
||||
}
|
||||
}
|
||||
close(out)
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
// Return the entity based on the parent path and name
|
||||
func (db *Database) child(conn *sql.DB, parent *Entity, name string) *Entity {
|
||||
var id string
|
||||
if err := conn.QueryRow("SELECT entity_id FROM edge WHERE parent_id = ? AND name = ?;", parent.id, name).Scan(&id); err != nil {
|
||||
return nil
|
||||
}
|
||||
return &Entity{id}
|
||||
}
|
||||
|
||||
func (db *Database) openConn() (*sql.DB, error) {
|
||||
return sql.Open("sqlite3", db.dbPath)
|
||||
}
|
||||
|
||||
// Return the id used to reference this entity
|
||||
func (e *Entity) ID() string {
|
||||
return e.id
|
||||
}
|
||||
|
||||
// Return the paths sorted by depth
|
||||
func (e Entities) Paths() []string {
|
||||
out := make([]string, len(e))
|
||||
var i int
|
||||
for k := range e {
|
||||
out[i] = k
|
||||
i++
|
||||
}
|
||||
sortByDepth(out)
|
||||
|
||||
return out
|
||||
}
|
||||
@@ -1,452 +0,0 @@
|
||||
package gograph
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func newTestDb(t *testing.T) *Database {
|
||||
db, err := NewDatabase(path.Join(os.TempDir(), "sqlite.db"), "0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func destroyTestDb(db *Database) {
|
||||
os.Remove(db.dbPath)
|
||||
}
|
||||
|
||||
func TestNewDatabase(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
if db == nil {
|
||||
t.Fatal("Datbase should not be nil")
|
||||
}
|
||||
defer destroyTestDb(db)
|
||||
}
|
||||
|
||||
func TestCreateRootEnity(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
root := db.RootEntity()
|
||||
if root == nil {
|
||||
t.Fatal("Root entity should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRootEntity(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
e := db.Get("/")
|
||||
if e == nil {
|
||||
t.Fatal("Entity should not be nil")
|
||||
}
|
||||
if e.ID() != "0" {
|
||||
t.Fatalf("Enity id should be 0, got %s", e.ID())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetEntityWithDifferentName(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
db.Set("/test", "1")
|
||||
if _, err := db.Set("/other", "1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateChild(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
child, err := db.Set("/db", "1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if child == nil {
|
||||
t.Fatal("Child should not be nil")
|
||||
}
|
||||
if child.ID() != "1" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestListAllRootChildren(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
for i := 1; i < 6; i++ {
|
||||
a := strconv.Itoa(i)
|
||||
if _, err := db.Set("/"+a, a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
entries := db.List("/", -1)
|
||||
if len(entries) != 5 {
|
||||
t.Fatalf("Expect 5 entries for / got %d", len(entries))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListAllSubChildren(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
_, err := db.Set("/webapp", "1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
child2, err := db.Set("/db", "2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
child4, err := db.Set("/logs", "4")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/db/logs", child4.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
child3, err := db.Set("/sentry", "3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
entries := db.List("/webapp", 1)
|
||||
if len(entries) != 3 {
|
||||
t.Fatalf("Expect 3 entries for / got %d", len(entries))
|
||||
}
|
||||
|
||||
entries = db.List("/webapp", 0)
|
||||
if len(entries) != 2 {
|
||||
t.Fatalf("Expect 2 entries for / got %d", len(entries))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddSelfAsChild(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
child, err := db.Set("/test", "1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/test/other", child.ID()); err == nil {
|
||||
t.Fatal("Error should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddChildToNonExistantRoot(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
if _, err := db.Set("/myapp", "1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := db.Set("/myapp/proxy/db", "2"); err == nil {
|
||||
t.Fatal("Error should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalkAll(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
_, err := db.Set("/webapp", "1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
child2, err := db.Set("/db", "2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
child4, err := db.Set("/db/logs", "4")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/logs", child4.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
child3, err := db.Set("/sentry", "3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
child5, err := db.Set("/gograph", "5")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := db.Walk("/", func(p string, e *Entity) error {
|
||||
t.Logf("Path: %s Entity: %s", p, e.ID())
|
||||
return nil
|
||||
}, -1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEntityByPath(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
_, err := db.Set("/webapp", "1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
child2, err := db.Set("/db", "2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
child4, err := db.Set("/logs", "4")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/db/logs", child4.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
child3, err := db.Set("/sentry", "3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
child5, err := db.Set("/gograph", "5")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
entity := db.Get("/webapp/db/logs")
|
||||
if entity == nil {
|
||||
t.Fatal("Entity should not be nil")
|
||||
}
|
||||
if entity.ID() != "4" {
|
||||
t.Fatalf("Expected to get entity with id 4, got %s", entity.ID())
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnitiesPaths(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
_, err := db.Set("/webapp", "1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
child2, err := db.Set("/db", "2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
child4, err := db.Set("/logs", "4")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/db/logs", child4.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
child3, err := db.Set("/sentry", "3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
child5, err := db.Set("/gograph", "5")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out := db.List("/", -1)
|
||||
for _, p := range out.Paths() {
|
||||
t.Log(p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteRootEntity(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
if err := db.Delete("/"); err == nil {
|
||||
t.Fatal("Error should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteEntity(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
_, err := db.Set("/webapp", "1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
child2, err := db.Set("/db", "2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
child4, err := db.Set("/logs", "4")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/db/logs", child4.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
child3, err := db.Set("/sentry", "3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
child5, err := db.Set("/gograph", "5")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := db.Delete("/webapp/sentry"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
entity := db.Get("/webapp/sentry")
|
||||
if entity != nil {
|
||||
t.Fatal("Entity /webapp/sentry should be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountRefs(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
db.Set("/webapp", "1")
|
||||
|
||||
if db.Refs("1") != 1 {
|
||||
t.Fatal("Expect reference count to be 1")
|
||||
}
|
||||
|
||||
db.Set("/db", "2")
|
||||
db.Set("/webapp/db", "2")
|
||||
if db.Refs("2") != 2 {
|
||||
t.Fatal("Expect reference count to be 2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPurgeId(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
db.Set("/webapp", "1")
|
||||
|
||||
if db.Refs("1") != 1 {
|
||||
t.Fatal("Expect reference count to be 1")
|
||||
}
|
||||
|
||||
db.Set("/db", "2")
|
||||
db.Set("/webapp/db", "2")
|
||||
|
||||
count, err := db.Purge("2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if count != 2 {
|
||||
t.Fatal("Expected 2 references to be removed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRename(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
db.Set("/webapp", "1")
|
||||
|
||||
if db.Refs("1") != 1 {
|
||||
t.Fatal("Expect reference count to be 1")
|
||||
}
|
||||
|
||||
db.Set("/db", "2")
|
||||
db.Set("/webapp/db", "2")
|
||||
|
||||
if db.Get("/webapp/db") == nil {
|
||||
t.Fatal("Cannot find entity at path /webapp/db")
|
||||
}
|
||||
|
||||
if err := db.Rename("/webapp/db", "/webapp/newdb"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if db.Get("/webapp/db") != nil {
|
||||
t.Fatal("Entity should not exist at /webapp/db")
|
||||
}
|
||||
if db.Get("/webapp/newdb") == nil {
|
||||
t.Fatal("Cannot find entity at path /webapp/newdb")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCreateMultipleNames(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
db.Set("/db", "1")
|
||||
if _, err := db.Set("/myapp", "1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
db.Walk("/", func(p string, e *Entity) error {
|
||||
t.Logf("%s\n", p)
|
||||
return nil
|
||||
}, -1)
|
||||
}
|
||||
|
||||
func TestRefPaths(t *testing.T) {
|
||||
db := newTestDb(t)
|
||||
defer destroyTestDb(db)
|
||||
|
||||
db.Set("/webapp", "1")
|
||||
|
||||
db.Set("/db", "2")
|
||||
db.Set("/webapp/db", "2")
|
||||
|
||||
refs := db.RefPaths("2")
|
||||
if len(refs) != 2 {
|
||||
t.Fatalf("Expected reference count to be 2, got %d", len(refs))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package gograph
|
||||
|
||||
import "sort"
|
||||
|
||||
type pathSorter struct {
|
||||
paths []string
|
||||
by func(i, j string) bool
|
||||
}
|
||||
|
||||
func sortByDepth(paths []string) {
|
||||
s := &pathSorter{paths, func(i, j string) bool {
|
||||
return pathDepth(i) > pathDepth(j)
|
||||
}}
|
||||
sort.Sort(s)
|
||||
}
|
||||
|
||||
func (s *pathSorter) Len() int {
|
||||
return len(s.paths)
|
||||
}
|
||||
|
||||
func (s *pathSorter) Swap(i, j int) {
|
||||
s.paths[i], s.paths[j] = s.paths[j], s.paths[i]
|
||||
}
|
||||
|
||||
func (s *pathSorter) Less(i, j int) bool {
|
||||
return s.by(s.paths[i], s.paths[j])
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package gograph
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
paths := []string{
|
||||
"/",
|
||||
"/myreallylongname",
|
||||
"/app/db",
|
||||
}
|
||||
|
||||
sortByDepth(paths)
|
||||
|
||||
if len(paths) != 3 {
|
||||
t.Fatalf("Expected 3 parts got %d", len(paths))
|
||||
}
|
||||
|
||||
if paths[0] != "/app/db" {
|
||||
t.Fatalf("Expected /app/db got %s", paths[0])
|
||||
}
|
||||
if paths[1] != "/myreallylongname" {
|
||||
t.Fatalf("Expected /myreallylongname got %s", paths[1])
|
||||
}
|
||||
if paths[2] != "/" {
|
||||
t.Fatalf("Expected / got %s", paths[2])
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package gograph
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Split p on /
|
||||
func split(p string) []string {
|
||||
return strings.Split(p, "/")
|
||||
}
|
||||
|
||||
// Returns the depth or number of / in a given path
|
||||
func pathDepth(p string) int {
|
||||
parts := split(p)
|
||||
if len(parts) == 2 && parts[1] == "" {
|
||||
return 1
|
||||
}
|
||||
return len(parts)
|
||||
}
|
||||
|
||||
func splitPath(p string) (parent, name string) {
|
||||
if p[0] != '/' {
|
||||
p = "/" + p
|
||||
}
|
||||
parent, name = path.Split(p)
|
||||
l := len(parent)
|
||||
if parent[l-1] == '/' {
|
||||
parent = parent[:l-1]
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -116,6 +116,7 @@ To run properly, docker needs the following software to be installed at runtime:
|
||||
* iptables version 1.4 or later
|
||||
* The lxc utility scripts (http://lxc.sourceforge.net) version 0.8 or later.
|
||||
* Git version 1.7 or later
|
||||
* XZ Utils 4.9 or later
|
||||
|
||||
## Kernel dependencies
|
||||
|
||||
|
||||
@@ -57,7 +57,13 @@ EXAMPLES:
|
||||
|
||||
FIXME
|
||||
|
||||
### 5. Commit and create a pull request to the "release" branch
|
||||
### 5. Test the docs
|
||||
|
||||
Make sure that your tree includes documentation for any modified or
|
||||
new features, syntax or semantic changes. Instructions for building
|
||||
the docs are in ``docs/README.md``
|
||||
|
||||
### 6. Commit and create a pull request to the "release" branch
|
||||
|
||||
```bash
|
||||
git add CHANGELOG.md
|
||||
@@ -65,9 +71,9 @@ git commit -m "Bump version to $VERSION"
|
||||
git push origin bump_$VERSION
|
||||
```
|
||||
|
||||
### 6. Get 2 other maintainers to validate the pull request
|
||||
### 7. Get 2 other maintainers to validate the pull request
|
||||
|
||||
### 7. Merge the pull request and apply tags
|
||||
### 8. Merge the pull request and apply tags
|
||||
|
||||
```bash
|
||||
git checkout release
|
||||
@@ -78,7 +84,13 @@ git push
|
||||
git push --tags
|
||||
```
|
||||
|
||||
### 8. Publish binaries
|
||||
Merging the pull request to the release branch will automatically
|
||||
update the documentation on the "latest" revision of the docs. You
|
||||
should see the updated docs 5-10 minutes after the merge. The docs
|
||||
will appear on http://docs.docker.io/. For more information about
|
||||
documentation releases, see ``docs/README.md``
|
||||
|
||||
### 9. Publish binaries
|
||||
|
||||
To run this you will need access to the release credentials.
|
||||
Get them from [the infrastructure maintainers](
|
||||
@@ -100,6 +112,6 @@ use get-nightly.docker.io for general testing, and once everything is fine,
|
||||
switch to get.docker.io).
|
||||
|
||||
|
||||
### 9. Rejoice!
|
||||
### 10. Rejoice!
|
||||
|
||||
Congratulations! You're done.
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
docker-ci github pull request
|
||||
=============================
|
||||
docker-ci
|
||||
=========
|
||||
|
||||
docker-ci is our buildbot continuous integration server,
|
||||
building and testing docker, hosted on EC2 and reachable at
|
||||
http://docker-ci.dotcloud.com
|
||||
|
||||
|
||||
Deployment
|
||||
==========
|
||||
|
||||
# Load AWS credentials
|
||||
export AWS_ACCESS_KEY_ID=''
|
||||
export AWS_SECRET_ACCESS_KEY=''
|
||||
export AWS_KEYPAIR_NAME=''
|
||||
export AWS_SSH_PRIVKEY=''
|
||||
|
||||
# Load buildbot credentials and config
|
||||
export BUILDBOT_PWD=''
|
||||
export IRC_PWD=''
|
||||
export IRC_CHANNEL='docker-dev'
|
||||
export SMTP_USER=''
|
||||
export SMTP_PWD=''
|
||||
export EMAIL_RCP=''
|
||||
|
||||
# Load registry test credentials
|
||||
export REGISTRY_USER=''
|
||||
export REGISTRY_PWD=''
|
||||
|
||||
cd docker/testing
|
||||
vagrant up --provider=aws
|
||||
|
||||
|
||||
github pull request
|
||||
===================
|
||||
|
||||
The entire docker pull request test workflow is event driven by github. Its
|
||||
usage is fully automatic and the results are logged in docker-ci.dotcloud.com
|
||||
@@ -13,3 +46,11 @@ buildbot (0.8.7p1) was patched using ./testing/buildbot/github.py, so it
|
||||
can understand the PR data github sends to it. Originally PR #1603 (ee64e099e0)
|
||||
implemented this capability. Also we added a new scheduler to exclusively filter
|
||||
PRs. and the 'pullrequest' builder to rebase the PR on top of master and test it.
|
||||
|
||||
|
||||
nighthly release
|
||||
================
|
||||
|
||||
The nightly release process is done by buildbot, running a DinD container that downloads
|
||||
the docker repository and builds the release container. The resulting docker
|
||||
binary is then tested, and if everything is fine, the release is done.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# VERSION: 0.22
|
||||
# DOCKER-VERSION 0.6.3
|
||||
# AUTHOR: Daniel Mizyrycki <daniel@dotcloud.com>
|
||||
# DESCRIPTION: Deploy docker-ci on Amazon EC2
|
||||
# COMMENTS:
|
||||
# CONFIG_JSON is an environment variable json string loaded as:
|
||||
#
|
||||
# export CONFIG_JSON='
|
||||
# { "AWS_TAG": "EC2_instance_name",
|
||||
# "AWS_ACCESS_KEY": "EC2_access_key",
|
||||
# "AWS_SECRET_KEY": "EC2_secret_key",
|
||||
# "DOCKER_CI_PUB": "$(cat docker-ci_ssh_public_key.pub)",
|
||||
# "DOCKER_CI_KEY": "$(cat docker-ci_ssh_private_key.key)",
|
||||
# "BUILDBOT_PWD": "Buildbot_server_password",
|
||||
# "IRC_PWD": "Buildbot_IRC_password",
|
||||
# "SMTP_USER": "SMTP_server_user",
|
||||
# "SMTP_PWD": "SMTP_server_password",
|
||||
# "PKG_ACCESS_KEY": "Docker_release_S3_bucket_access_key",
|
||||
# "PKG_SECRET_KEY": "Docker_release_S3_bucket_secret_key",
|
||||
# "PKG_GPG_PASSPHRASE": "Docker_release_gpg_passphrase",
|
||||
# "INDEX_AUTH": "Index_encripted_user_password",
|
||||
# "REGISTRY_USER": "Registry_test_user",
|
||||
# "REGISTRY_PWD": "Registry_test_password",
|
||||
# "REGISTRY_BUCKET": "Registry_S3_bucket_name",
|
||||
# "REGISTRY_ACCESS_KEY": "Registry_S3_bucket_access_key",
|
||||
# "REGISTRY_SECRET_KEY": "Registry_S3_bucket_secret_key",
|
||||
# "IRC_CHANNEL": "Buildbot_IRC_channel",
|
||||
# "EMAIL_RCP": "Buildbot_mailing_receipient" }'
|
||||
#
|
||||
#
|
||||
# TO_BUILD: docker build -t docker-ci .
|
||||
# TO_DEPLOY: docker run -e CONFIG_JSON="${CONFIG_JSON}" docker-ci
|
||||
|
||||
from ubuntu:12.04
|
||||
|
||||
run echo 'deb http://archive.ubuntu.com/ubuntu precise main universe' > /etc/apt/sources.list
|
||||
run apt-get update; apt-get install -y python2.7 python-dev python-pip ssh rsync less vim
|
||||
run pip install boto fabric
|
||||
|
||||
# Add deployment code and set default container command
|
||||
add . /docker-ci
|
||||
cmd "/docker-ci/deployment.py"
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
=======
|
||||
testing
|
||||
=======
|
||||
|
||||
This directory contains docker-ci testing related files.
|
||||
|
||||
|
||||
Buildbot
|
||||
========
|
||||
|
||||
Buildbot is a continuous integration system designed to automate the
|
||||
build/test cycle. By automatically rebuilding and testing the tree each time
|
||||
something has changed, build problems are pinpointed quickly, before other
|
||||
developers are inconvenienced by the failure.
|
||||
|
||||
We are running buildbot in Amazon's EC2 to verify docker passes all
|
||||
tests when commits get pushed to the master branch and building
|
||||
nightly releases using Docker in Docker awesome implementation made
|
||||
by Jerome Petazzoni.
|
||||
|
||||
https://github.com/jpetazzo/dind
|
||||
|
||||
Docker's buildbot instance is at http://docker-ci.dotcloud.com/waterfall
|
||||
|
||||
For deployment instructions, please take a look at
|
||||
hack/infrastructure/docker-ci/Dockerfile
|
||||
@@ -0,0 +1 @@
|
||||
Buildbot configuration and setup files
|
||||
@@ -86,12 +86,16 @@ def getChanges(request, options = None):
|
||||
the http request object
|
||||
"""
|
||||
payload = json.loads(request.args['payload'][0])
|
||||
if 'pull_request' in payload:
|
||||
user = payload['repository']['owner']['login']
|
||||
repo = payload['repository']['name']
|
||||
repo_url = payload['repository']['html_url']
|
||||
else:
|
||||
user = payload['repository']['owner']['name']
|
||||
import urllib,datetime
|
||||
fname = str(datetime.datetime.now()).replace(' ','_').replace(':','-')[:19]
|
||||
open('github_{0}.json'.format(fname),'w').write(json.dumps(json.loads(urllib.unquote(request.args['payload'][0])), sort_keys = True, indent = 2))
|
||||
|
||||
if 'pull_request' in payload:
|
||||
user = payload['pull_request']['user']['login']
|
||||
repo = payload['pull_request']['head']['repo']['name']
|
||||
repo_url = payload['pull_request']['head']['repo']['html_url']
|
||||
else:
|
||||
user = payload['repository']['owner']['name']
|
||||
repo = payload['repository']['name']
|
||||
repo_url = payload['repository']['url']
|
||||
project = request.args.get('project', None)
|
||||
@@ -115,7 +119,7 @@ def process_change(payload, user, repo, repo_url, project):
|
||||
Hook.
|
||||
"""
|
||||
changes = []
|
||||
|
||||
|
||||
newrev = payload['after'] if 'after' in payload else payload['pull_request']['head']['sha']
|
||||
refname = payload['ref'] if 'ref' in payload else payload['pull_request']['head']['ref']
|
||||
|
||||
@@ -130,10 +134,13 @@ def process_change(payload, user, repo, repo_url, project):
|
||||
log.msg("Branch `%s' deleted, ignoring" % branch)
|
||||
return []
|
||||
else:
|
||||
if 'pull_request' in payload:
|
||||
changes = [{
|
||||
'category' : 'github_pullrequest',
|
||||
'who' : user,
|
||||
if 'pull_request' in payload:
|
||||
if payload['action'] == 'closed':
|
||||
log.msg("PR#{} closed, ignoring".format(payload['number']))
|
||||
return []
|
||||
changes = [{
|
||||
'category' : 'github_pullrequest',
|
||||
'who' : '{0} - PR#{1}'.format(user,payload['number']),
|
||||
'files' : [],
|
||||
'comments' : payload['pull_request']['title'],
|
||||
'revision' : newrev,
|
||||
@@ -142,7 +149,7 @@ def process_change(payload, user, repo, repo_url, project):
|
||||
'revlink' : '{0}/commit/{1}'.format(repo_url,newrev),
|
||||
'repository' : repo_url,
|
||||
'project' : project }]
|
||||
return changes
|
||||
return changes
|
||||
for commit in payload['commits']:
|
||||
files = []
|
||||
if 'added' in commit:
|
||||
@@ -166,4 +173,3 @@ def process_change(payload, user, repo, repo_url, project):
|
||||
project = project)
|
||||
changes.append(chdict)
|
||||
return changes
|
||||
|
||||
@@ -20,7 +20,8 @@ TEST_PWD = 'docker' # Credential to authenticate build triggers
|
||||
BUILDER_NAME = 'docker'
|
||||
GITHUB_DOCKER = 'github.com/dotcloud/docker'
|
||||
BUILDBOT_PATH = '/data/buildbot'
|
||||
DOCKER_PATH = '/data/docker'
|
||||
DOCKER_PATH = '/go/src/github.com/dotcloud/docker'
|
||||
DOCKER_CI_PATH = '/docker-ci'
|
||||
BUILDER_PATH = '/data/buildbot/slave/{0}/build'.format(BUILDER_NAME)
|
||||
PULL_REQUEST_PATH = '/data/buildbot/slave/pullrequest/build'
|
||||
|
||||
@@ -45,49 +46,41 @@ c['slavePortnum'] = PORT_MASTER
|
||||
|
||||
# Schedulers
|
||||
c['schedulers'] = [ForceScheduler(name='trigger', builderNames=[BUILDER_NAME,
|
||||
'index','registry','coverage'])]
|
||||
'index','registry','coverage','nightlyrelease'])]
|
||||
c['schedulers'] += [SingleBranchScheduler(name="all",
|
||||
change_filter=filter.ChangeFilter(branch='master'), treeStableTimer=None,
|
||||
builderNames=[BUILDER_NAME])]
|
||||
c['schedulers'] += [SingleBranchScheduler(name='pullrequest',
|
||||
change_filter=filter.ChangeFilter(category='github_pullrequest'), treeStableTimer=None,
|
||||
builderNames=['pullrequest'])]
|
||||
c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['coverage'],
|
||||
hour=0, minute=30)]
|
||||
c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['nightlyrelease'],
|
||||
hour=7, minute=00)]
|
||||
c['schedulers'] += [Nightly(name='every4hrs', branch=None, builderNames=['registry','index'],
|
||||
hour=range(0,24,4), minute=15)]
|
||||
|
||||
# Builders
|
||||
# Docker commit test
|
||||
factory = BuildFactory()
|
||||
factory.addStep(ShellCommand(description='Docker',logEnviron=False,usePTY=True,
|
||||
command=["sh", "-c", Interpolate("cd ..; rm -rf build; mkdir build; "
|
||||
"cp -r {2}-dependencies/src {0}; export GOPATH={0}; go get {3}; cd {1}; "
|
||||
"git reset --hard %(src::revision)s; go test -v".format(
|
||||
BUILDER_PATH, BUILDER_PATH+'/src/'+GITHUB_DOCKER, DOCKER_PATH, GITHUB_DOCKER))]))
|
||||
c['builders'] = [BuilderConfig(name=BUILDER_NAME,slavenames=['buildworker'],
|
||||
factory.addStep(ShellCommand(description='Docker', logEnviron=False,
|
||||
usePTY=True, command=['sh', '-c', Interpolate(
|
||||
'{0}/docker-test/test_docker.sh %(src::revision)s'.format(DOCKER_CI_PATH))]))
|
||||
c['builders'] = [BuilderConfig(name='docker',slavenames=['buildworker'],
|
||||
factory=factory)]
|
||||
|
||||
# Docker pull request test
|
||||
factory = BuildFactory()
|
||||
factory.addStep(ShellCommand(description='pull_request',logEnviron=False,usePTY=True,
|
||||
command=["sh", "-c", Interpolate("cd ..; rm -rf build; mkdir build; "
|
||||
"cp -r {2}-dependencies/src {0}; export GOPATH={0}; go get {3}; cd {1}; "
|
||||
"git fetch %(src::repository)s %(src::branch)s:PR-%(src::branch)s; "
|
||||
"git checkout %(src::revision)s; git rebase master; go test -v".format(
|
||||
PULL_REQUEST_PATH, PULL_REQUEST_PATH+'/src/'+GITHUB_DOCKER, DOCKER_PATH, GITHUB_DOCKER))]))
|
||||
factory.addStep(ShellCommand(description='pull_request', logEnviron=False,
|
||||
usePTY=True, command=['sh', '-c', Interpolate(
|
||||
'{0}/docker-test/test_docker.sh %(src::revision)s %(src::repository)s'
|
||||
' %(src::branch)s'.format(DOCKER_CI_PATH))]))
|
||||
c['builders'] += [BuilderConfig(name='pullrequest',slavenames=['buildworker'],
|
||||
factory=factory)]
|
||||
|
||||
# Docker coverage test
|
||||
coverage_cmd = ('GOPATH=`pwd` go get -d github.com/dotcloud/docker\n'
|
||||
'GOPATH=`pwd` go get github.com/axw/gocov/gocov\n'
|
||||
'sudo -E GOPATH=`pwd` ./bin/gocov test -deps -exclude-goroot -v'
|
||||
' -exclude github.com/gorilla/context,github.com/gorilla/mux,github.com/kr/pty,'
|
||||
'code.google.com/p/go.net/websocket github.com/dotcloud/docker | ./bin/gocov report')
|
||||
factory = BuildFactory()
|
||||
factory.addStep(ShellCommand(description='Coverage',logEnviron=False,usePTY=True,
|
||||
command=coverage_cmd))
|
||||
factory.addStep(ShellCommand(description='Coverage', logEnviron=False,
|
||||
usePTY=True, command='{0}/docker-coverage/coverage-docker.sh'.format(
|
||||
DOCKER_CI_PATH)))
|
||||
c['builders'] += [BuilderConfig(name='coverage',slavenames=['buildworker'],
|
||||
factory=factory)]
|
||||
|
||||
@@ -95,8 +88,8 @@ c['builders'] += [BuilderConfig(name='coverage',slavenames=['buildworker'],
|
||||
factory = BuildFactory()
|
||||
factory.addStep(ShellCommand(description='registry', logEnviron=False,
|
||||
command='. {0}/master/credentials.cfg; '
|
||||
'{1}/testing/functionaltests/test_registry.sh'.format(BUILDBOT_PATH,
|
||||
DOCKER_PATH), usePTY=True))
|
||||
'/docker-ci/functionaltests/test_registry.sh'.format(BUILDBOT_PATH),
|
||||
usePTY=True))
|
||||
c['builders'] += [BuilderConfig(name='registry',slavenames=['buildworker'],
|
||||
factory=factory)]
|
||||
|
||||
@@ -109,6 +102,14 @@ factory.addStep(ShellCommand(description='index', logEnviron=False,
|
||||
c['builders'] += [BuilderConfig(name='index',slavenames=['buildworker'],
|
||||
factory=factory)]
|
||||
|
||||
# Docker nightly release
|
||||
nightlyrelease_cmd = ('docker run -i -t -privileged -lxc-conf=lxc.aa_profile=unconfined'
|
||||
' -e AWS_S3_BUCKET=test.docker.io dockerbuilder')
|
||||
factory = BuildFactory()
|
||||
factory.addStep(ShellCommand(description='NightlyRelease',logEnviron=False,usePTY=True,
|
||||
command=nightlyrelease_cmd))
|
||||
c['builders'] += [BuilderConfig(name='nightlyrelease',slavenames=['buildworker'],
|
||||
factory=factory)]
|
||||
|
||||
# Status
|
||||
authz_cfg = authz.Authz(auth=auth.BasicAuth([(TEST_USER, TEST_PWD)]),
|
||||
@@ -6,16 +6,22 @@
|
||||
|
||||
USER=$1
|
||||
CFG_PATH=$2
|
||||
BUILDBOT_PWD=$3
|
||||
IRC_PWD=$4
|
||||
IRC_CHANNEL=$5
|
||||
SMTP_USER=$6
|
||||
SMTP_PWD=$7
|
||||
EMAIL_RCP=$8
|
||||
DOCKER_PATH=$3
|
||||
BUILDBOT_PWD=$4
|
||||
IRC_PWD=$5
|
||||
IRC_CHANNEL=$6
|
||||
SMTP_USER=$7
|
||||
SMTP_PWD=$8
|
||||
EMAIL_RCP=$9
|
||||
REGISTRY_USER=${10}
|
||||
REGISTRY_PWD=${11}
|
||||
REGISTRY_BUCKET=${12}
|
||||
REGISTRY_ACCESS_KEY=${13}
|
||||
REGISTRY_SECRET_KEY=${14}
|
||||
BUILDBOT_PATH="/data/buildbot"
|
||||
DOCKER_PATH="/data/docker"
|
||||
SLAVE_NAME="buildworker"
|
||||
SLAVE_SOCKET="localhost:9989"
|
||||
|
||||
export PATH="/bin:sbin:/usr/bin:/usr/sbin:/usr/local/bin"
|
||||
|
||||
function run { su $USER -c "$1"; }
|
||||
@@ -35,6 +41,10 @@ run "sed -i -E 's#(SMTP_USER = ).+#\1\"$SMTP_USER\"#' master/master.cfg"
|
||||
run "sed -i -E 's#(SMTP_PWD = ).+#\1\"$SMTP_PWD\"#' master/master.cfg"
|
||||
run "sed -i -E 's#(EMAIL_RCP = ).+#\1\"$EMAIL_RCP\"#' master/master.cfg"
|
||||
run "buildslave create-slave slave $SLAVE_SOCKET $SLAVE_NAME $BUILDBOT_PWD"
|
||||
run "echo 'export DOCKER_CREDS=\"$REGISTRY_USER:$REGISTRY_PWD\"' > $BUILDBOT_PATH/master/credentials.cfg"
|
||||
run "echo 'export S3_BUCKET=\"$REGISTRY_BUCKET\"' >> $BUILDBOT_PATH/master/credentials.cfg"
|
||||
run "echo 'export S3_ACCESS_KEY=\"$REGISTRY_ACCESS_KEY\"' >> $BUILDBOT_PATH/master/credentials.cfg"
|
||||
run "echo 'export S3_SECRET_KEY=\"$REGISTRY_SECRET_KEY\"' >> $BUILDBOT_PATH/master/credentials.cfg"
|
||||
|
||||
# Patch github webstatus to capture pull requests
|
||||
cp $CFG_PATH/github.py /usr/local/lib/python2.7/dist-packages/buildbot/status/web/hooks
|
||||
Executable
+155
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os, sys, re, json, base64
|
||||
from boto.ec2.connection import EC2Connection
|
||||
from subprocess import call
|
||||
from fabric import api
|
||||
from fabric.api import cd, run, put, sudo
|
||||
from os import environ as env
|
||||
from time import sleep
|
||||
|
||||
# Remove SSH private key as it needs more processing
|
||||
CONFIG = json.loads(re.sub(r'("DOCKER_CI_KEY".+?"(.+?)",)','',
|
||||
env['CONFIG_JSON'], flags=re.DOTALL))
|
||||
|
||||
# Populate environment variables
|
||||
for key in CONFIG:
|
||||
env[key] = CONFIG[key]
|
||||
|
||||
# Load SSH private key
|
||||
env['DOCKER_CI_KEY'] = re.sub('^.+"DOCKER_CI_KEY".+?"(.+?)".+','\\1',
|
||||
env['CONFIG_JSON'],flags=re.DOTALL)
|
||||
|
||||
|
||||
AWS_TAG = env.get('AWS_TAG','docker-ci')
|
||||
AWS_KEY_NAME = 'dotcloud-dev' # Same as CONFIG_JSON['DOCKER_CI_PUB']
|
||||
AWS_AMI = 'ami-d582d6bc' # Ubuntu 13.04
|
||||
AWS_REGION = 'us-east-1'
|
||||
AWS_TYPE = 'm1.small'
|
||||
AWS_SEC_GROUPS = 'gateway'
|
||||
AWS_IMAGE_USER = 'ubuntu'
|
||||
DOCKER_PATH = '/go/src/github.com/dotcloud/docker'
|
||||
DOCKER_CI_PATH = '/docker-ci'
|
||||
CFG_PATH = '{}/buildbot'.format(DOCKER_CI_PATH)
|
||||
|
||||
|
||||
class AWS_EC2:
|
||||
'''Amazon EC2'''
|
||||
def __init__(self, access_key, secret_key):
|
||||
'''Set default API parameters'''
|
||||
self.handler = EC2Connection(access_key, secret_key)
|
||||
def create_instance(self, tag, instance_type):
|
||||
reservation = self.handler.run_instances(**instance_type)
|
||||
instance = reservation.instances[0]
|
||||
sleep(10)
|
||||
while instance.state != 'running':
|
||||
sleep(5)
|
||||
instance.update()
|
||||
print "Instance state: %s" % (instance.state)
|
||||
instance.add_tag("Name",tag)
|
||||
print "instance %s done!" % (instance.id)
|
||||
return instance.ip_address
|
||||
def get_instances(self):
|
||||
return self.handler.get_all_instances()
|
||||
def get_tags(self):
|
||||
return dict([(i.instances[0].id, i.instances[0].tags['Name'])
|
||||
for i in self.handler.get_all_instances() if i.instances[0].tags])
|
||||
def del_instance(self, instance_id):
|
||||
self.handler.terminate_instances(instance_ids=[instance_id])
|
||||
|
||||
|
||||
def json_fmt(data):
|
||||
'''Format json output'''
|
||||
return json.dumps(data, sort_keys = True, indent = 2)
|
||||
|
||||
|
||||
# Create EC2 API handler
|
||||
ec2 = AWS_EC2(env['AWS_ACCESS_KEY'], env['AWS_SECRET_KEY'])
|
||||
|
||||
# Stop processing if AWS_TAG exists on EC2
|
||||
if AWS_TAG in ec2.get_tags().values():
|
||||
print ('Instance: {} already deployed. Not further processing.'
|
||||
.format(AWS_TAG))
|
||||
exit(1)
|
||||
|
||||
ip = ec2.create_instance(AWS_TAG, {'image_id':AWS_AMI, 'instance_type':AWS_TYPE,
|
||||
'security_groups':[AWS_SEC_GROUPS], 'key_name':AWS_KEY_NAME})
|
||||
|
||||
# Wait 30 seconds for the machine to boot
|
||||
sleep(30)
|
||||
|
||||
# Create docker-ci ssh private key so docker-ci docker container can communicate
|
||||
# with its EC2 instance
|
||||
os.makedirs('/root/.ssh')
|
||||
open('/root/.ssh/id_rsa','w').write(env['DOCKER_CI_KEY'])
|
||||
os.chmod('/root/.ssh/id_rsa',0600)
|
||||
open('/root/.ssh/config','w').write('StrictHostKeyChecking no\n')
|
||||
|
||||
api.env.host_string = ip
|
||||
api.env.user = AWS_IMAGE_USER
|
||||
api.env.key_filename = '/root/.ssh/id_rsa'
|
||||
|
||||
# Correct timezone
|
||||
sudo('echo "America/Los_Angeles" >/etc/timezone')
|
||||
sudo('dpkg-reconfigure --frontend noninteractive tzdata')
|
||||
|
||||
# Load public docker-ci key
|
||||
sudo("echo '{}' >> /root/.ssh/authorized_keys".format(env['DOCKER_CI_PUB']))
|
||||
|
||||
# Create docker nightly release credentials file
|
||||
credentials = {
|
||||
'AWS_ACCESS_KEY': env['PKG_ACCESS_KEY'],
|
||||
'AWS_SECRET_KEY': env['PKG_SECRET_KEY'],
|
||||
'GPG_PASSPHRASE': env['PKG_GPG_PASSPHRASE'],
|
||||
'INDEX_AUTH': env['INDEX_AUTH']}
|
||||
open(DOCKER_CI_PATH + '/nightlyrelease/release_credentials.json', 'w').write(
|
||||
base64.b64encode(json.dumps(credentials)))
|
||||
|
||||
# Transfer docker
|
||||
sudo('mkdir -p ' + DOCKER_CI_PATH)
|
||||
sudo('chown {}.{} {}'.format(AWS_IMAGE_USER, AWS_IMAGE_USER, DOCKER_CI_PATH))
|
||||
call('/usr/bin/rsync -aH {} {}@{}:{}'.format(DOCKER_CI_PATH, AWS_IMAGE_USER, ip,
|
||||
os.path.dirname(DOCKER_CI_PATH)), shell=True)
|
||||
|
||||
# Install Docker and Buildbot dependencies
|
||||
sudo('addgroup docker')
|
||||
sudo('usermod -a -G docker ubuntu')
|
||||
sudo('mkdir /mnt/docker; ln -s /mnt/docker /var/lib/docker')
|
||||
sudo('wget -q -O - https://get.docker.io/gpg | apt-key add -')
|
||||
sudo('echo deb https://get.docker.io/ubuntu docker main >'
|
||||
' /etc/apt/sources.list.d/docker.list')
|
||||
sudo('echo -e "deb http://archive.ubuntu.com/ubuntu raring main universe\n'
|
||||
'deb http://us.archive.ubuntu.com/ubuntu/ raring-security main universe\n"'
|
||||
' > /etc/apt/sources.list; apt-get update')
|
||||
sudo('DEBIAN_FRONTEND=noninteractive apt-get install -q -y wget python-dev'
|
||||
' python-pip supervisor git mercurial linux-image-extra-$(uname -r)'
|
||||
' aufs-tools make libfontconfig libevent-dev')
|
||||
sudo('wget -O - https://go.googlecode.com/files/go1.1.2.linux-amd64.tar.gz | '
|
||||
'tar -v -C /usr/local -xz; ln -s /usr/local/go/bin/go /usr/bin/go')
|
||||
sudo('GOPATH=/go go get -d github.com/dotcloud/docker')
|
||||
sudo('pip install -r {}/requirements.txt'.format(CFG_PATH))
|
||||
|
||||
# Install docker and testing dependencies
|
||||
sudo('apt-get install -y -q lxc-docker')
|
||||
sudo('curl -s https://phantomjs.googlecode.com/files/'
|
||||
'phantomjs-1.9.1-linux-x86_64.tar.bz2 | tar jx -C /usr/bin'
|
||||
' --strip-components=2 phantomjs-1.9.1-linux-x86_64/bin/phantomjs')
|
||||
|
||||
#### FIXME. Temporarily install docker with proper apparmor handling
|
||||
sudo('stop docker')
|
||||
sudo('wget -q -O /usr/bin/docker http://test.docker.io/test/docker')
|
||||
sudo('start docker')
|
||||
|
||||
# Build docker-ci containers
|
||||
sudo('cd {}; docker build -t docker .'.format(DOCKER_PATH))
|
||||
sudo('cd {}/nightlyrelease; docker build -t dockerbuilder .'.format(
|
||||
DOCKER_CI_PATH))
|
||||
|
||||
# Setup buildbot
|
||||
sudo('mkdir /data')
|
||||
sudo('{0}/setup.sh root {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10}'
|
||||
' {11} {12}'.format(CFG_PATH, DOCKER_PATH, env['BUILDBOT_PWD'],
|
||||
env['IRC_PWD'], env['IRC_CHANNEL'], env['SMTP_USER'],
|
||||
env['SMTP_PWD'], env['EMAIL_RCP'], env['REGISTRY_USER'],
|
||||
env['REGISTRY_PWD'], env['REGISTRY_BUCKET'], env['REGISTRY_ACCESS_KEY'],
|
||||
env['REGISTRY_SECRET_KEY']))
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
# Generate a random string of $1 characters
|
||||
function random {
|
||||
cat /dev/urandom | tr -cd 'a-f0-9' | head -c $1
|
||||
}
|
||||
|
||||
# Compute test paths
|
||||
BASE_PATH=`pwd`/test_docker_$(random 12)
|
||||
DOCKER_PATH=$BASE_PATH/go/src/github.com/dotcloud/docker
|
||||
export GOPATH=$BASE_PATH/go:$DOCKER_PATH/vendor
|
||||
|
||||
# Fetch latest master
|
||||
mkdir -p $DOCKER_PATH
|
||||
cd $DOCKER_PATH
|
||||
git init .
|
||||
git fetch -q http://github.com/dotcloud/docker master
|
||||
git reset --hard FETCH_HEAD
|
||||
|
||||
# Fetch go coverage
|
||||
cd $BASE_PATH/go
|
||||
GOPATH=$BASE_PATH/go go get github.com/axw/gocov/gocov
|
||||
sudo -E GOPATH=$GOPATH ./bin/gocov test -deps -exclude-goroot -v\
|
||||
-exclude github.com/gorilla/context,github.com/gorilla/mux,github.com/kr/pty,\
|
||||
code.google.com/p/go.net/websocket,github.com/dotcloud/tar\
|
||||
github.com/dotcloud/docker | ./bin/gocov report; exit_status=$?
|
||||
|
||||
# Cleanup testing directory
|
||||
rm -rf $BASE_PATH
|
||||
|
||||
exit $exit_status
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
COMMIT=${1-HEAD}
|
||||
REPO=${2-http://github.com/dotcloud/docker}
|
||||
BRANCH=${3-master}
|
||||
|
||||
# Generate a random string of $1 characters
|
||||
function random {
|
||||
cat /dev/urandom | tr -cd 'a-f0-9' | head -c $1
|
||||
}
|
||||
|
||||
# Compute test paths
|
||||
BASE_PATH=`pwd`/test_docker_$(random 12)
|
||||
DOCKER_PATH=$BASE_PATH/go/src/github.com/dotcloud/docker
|
||||
export GOPATH=$BASE_PATH/go:$DOCKER_PATH/vendor
|
||||
|
||||
# Fetch latest master
|
||||
mkdir -p $DOCKER_PATH
|
||||
cd $DOCKER_PATH
|
||||
git init .
|
||||
git fetch -q http://github.com/dotcloud/docker master
|
||||
git reset --hard FETCH_HEAD
|
||||
|
||||
# Merge commit
|
||||
git fetch -q "$REPO" "$BRANCH"
|
||||
git merge --no-edit $COMMIT || exit 1
|
||||
|
||||
# Test commit
|
||||
go test -v; exit_status=$?
|
||||
|
||||
# Cleanup testing directory
|
||||
rm -rf $BASE_PATH
|
||||
|
||||
exit $exit_status
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
# Cleanup
|
||||
rm -rf docker-registry
|
||||
|
||||
# Setup the environment
|
||||
export SETTINGS_FLAVOR=test
|
||||
export DOCKER_REGISTRY_CONFIG=config_test.yml
|
||||
|
||||
# Get latest docker registry
|
||||
git clone -q https://github.com/dotcloud/docker-registry.git
|
||||
cd docker-registry
|
||||
|
||||
# Get dependencies
|
||||
pip install -q -r requirements.txt
|
||||
pip install -q -r test-requirements.txt
|
||||
pip install -q tox
|
||||
|
||||
# Run registry tests
|
||||
tox || exit 1
|
||||
export PYTHONPATH=$(pwd)/docker-registry
|
||||
python -m unittest discover -p s3.py -s test || exit 1
|
||||
python -m unittest discover -p workflow.py -s test
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# VERSION: 1.2
|
||||
# DOCKER-VERSION 0.6.3
|
||||
# AUTHOR: Daniel Mizyrycki <daniel@dotcloud.com>
|
||||
# DESCRIPTION: Build docker nightly release using Docker in Docker.
|
||||
# REFERENCES: This code reuses the excellent implementation of docker in docker
|
||||
# made by Jerome Petazzoni. https://github.com/jpetazzo/dind
|
||||
# COMMENTS:
|
||||
# release_credentials.json is a base64 json encoded file containing:
|
||||
# { "AWS_ACCESS_KEY": "Test_docker_AWS_S3_bucket_id",
|
||||
# "AWS_SECRET_KEY='Test_docker_AWS_S3_bucket_key'
|
||||
# "GPG_PASSPHRASE='Test_docker_GPG_passphrase_signature'
|
||||
# "INDEX_AUTH='Encripted_index_authentication' }
|
||||
# TO_BUILD: docker build -t dockerbuilder .
|
||||
# TO_RELEASE: docker run -i -t -privileged -lxc-conf="lxc.aa_profile = unconfined" -e AWS_S3_BUCKET="test.docker.io" dockerbuilder
|
||||
|
||||
from docker
|
||||
maintainer Daniel Mizyrycki <daniel@dotcloud.com>
|
||||
|
||||
# Add docker dependencies and downloading packages
|
||||
run echo 'deb http://archive.ubuntu.com/ubuntu precise main universe' > /etc/apt/sources.list
|
||||
run apt-get update; apt-get install -y -q wget python2.7
|
||||
|
||||
# Add production docker binary
|
||||
run wget -q -O /usr/bin/docker http://get.docker.io/builds/Linux/x86_64/docker-latest; chmod +x /usr/bin/docker
|
||||
|
||||
#### FIXME. Temporarily install docker with proper apparmor handling
|
||||
run wget -q -O /usr/bin/docker http://test.docker.io/test/docker; chmod +x /usr/bin/docker
|
||||
|
||||
# Add proto docker builder
|
||||
add ./dockerbuild /usr/bin/dockerbuild
|
||||
run chmod +x /usr/bin/dockerbuild
|
||||
|
||||
# Add release credentials
|
||||
add ./release_credentials.json /root/release_credentials.json
|
||||
|
||||
# Launch build process in a container
|
||||
cmd dockerbuild
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Variables AWS_ACCESS_KEY, AWS_SECRET_KEY, PG_PASSPHRASE and INDEX_AUTH
|
||||
# are decoded from /root/release_credentials.json
|
||||
# Variable AWS_S3_BUCKET is passed to the environment from docker run -e
|
||||
|
||||
# Enable debugging
|
||||
set -x
|
||||
|
||||
# Fetch docker master branch
|
||||
rm -rf /go/src/github.com/dotcloud/docker
|
||||
cd /
|
||||
git clone -q http://github.com/dotcloud/docker /go/src/github.com/dotcloud/docker
|
||||
cd /go/src/github.com/dotcloud/docker
|
||||
|
||||
echo FIXME. Temporarily add Jerome changeset with proper apparmor handling
|
||||
git fetch http://github.com/jpetazzo/docker escape-apparmor-confinement:escape-apparmor-confinement
|
||||
git rebase --onto master master escape-apparmor-confinement
|
||||
|
||||
# Launch docker daemon using dind inside the container
|
||||
./hack/dind /usr/bin/docker -d &
|
||||
sleep 5
|
||||
|
||||
# Add an uncommitted change to generate a timestamped release
|
||||
date > timestamp
|
||||
|
||||
# Build the docker package using /Dockerfile
|
||||
docker build -t docker .
|
||||
|
||||
# Run Docker unittests binary and Ubuntu package
|
||||
docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh || exit 1
|
||||
|
||||
# Turn debug off to load credentials from the environment
|
||||
set +x
|
||||
eval $(cat /root/release_credentials.json | python -c '
|
||||
import sys,json,base64;
|
||||
d=json.loads(base64.b64decode(sys.stdin.read()));
|
||||
exec("""for k in d: print "export {0}=\\"{1}\\"".format(k,d[k])""")')
|
||||
echo '{"https://index.docker.io/v1/":{"auth":"'$INDEX_AUTH'","email":"engineering@dotcloud.com"}}' > /.dockercfg
|
||||
set -x
|
||||
|
||||
# Push docker nightly
|
||||
echo docker run -i -t -privileged -e AWS_S3_BUCKET=$AWS_S3_BUCKET -e AWS_ACCESS_KEY=XXXXX -e AWS_SECRET_KEY=XXXXX -e GPG_PASSPHRASE=XXXXX release hack/release.sh
|
||||
set +x
|
||||
docker run -i -t -privileged -e AWS_S3_BUCKET=$AWS_S3_BUCKET -e AWS_ACCESS_KEY=$AWS_ACCESS_KEY -e AWS_SECRET_KEY=$AWS_SECRET_KEY -e GPG_PASSPHRASE=$GPG_PASSPHRASE release hack/release.sh
|
||||
@@ -0,0 +1 @@
|
||||
eyAiQVdTX0FDQ0VTU19LRVkiOiAiIiwKICAiQVdTX1NFQ1JFVF9LRVkiOiAiIiwKICAiR1BHX1BBU1NQSFJBU0UiOiAiIiwKICAiSU5ERVhfQVVUSCI6ICIiIH0=
|
||||
@@ -0,0 +1,28 @@
|
||||
# VERSION: 0.22
|
||||
# DOCKER-VERSION 0.6.3
|
||||
# AUTHOR: Daniel Mizyrycki <daniel@dotcloud.com>
|
||||
# DESCRIPTION: Generate docker-ci daily report
|
||||
# COMMENTS: The build process is initiated by deployment.py
|
||||
Report configuration is passed through ./credentials.json at
|
||||
# deployment time.
|
||||
# TO_BUILD: docker build -t report .
|
||||
# TO_DEPLOY: docker run report
|
||||
|
||||
from ubuntu:12.04
|
||||
maintainer Daniel Mizyrycki <daniel@dotcloud.com>
|
||||
|
||||
env PYTHONPATH /report
|
||||
|
||||
|
||||
# Add report dependencies
|
||||
run echo 'deb http://archive.ubuntu.com/ubuntu precise main universe' > \
|
||||
/etc/apt/sources.list
|
||||
run apt-get update; apt-get install -y python2.7 python-pip ssh rsync
|
||||
|
||||
# Set San Francisco timezone
|
||||
run echo "America/Los_Angeles" >/etc/timezone
|
||||
run dpkg-reconfigure --frontend noninteractive tzdata
|
||||
|
||||
# Add report code and set default container command
|
||||
add . /report
|
||||
cmd "/report/report.py"
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''Deploy docker-ci report container on Digital Ocean.
|
||||
Usage:
|
||||
export CONFIG_JSON='
|
||||
{ "DROPLET_NAME": "Digital_Ocean_dropplet_name",
|
||||
"DO_CLIENT_ID": "Digital_Ocean_client_id",
|
||||
"DO_API_KEY": "Digital_Ocean_api_key",
|
||||
"DOCKER_KEY_ID": "Digital_Ocean_ssh_key_id",
|
||||
"DOCKER_CI_KEY_PATH": "docker-ci_private_key_path",
|
||||
"DOCKER_CI_PUB": "$(cat docker-ci_ssh_public_key.pub)",
|
||||
"DOCKER_CI_ADDRESS" "user@docker-ci_fqdn_server",
|
||||
"SMTP_USER": "SMTP_server_user",
|
||||
"SMTP_PWD": "SMTP_server_password",
|
||||
"EMAIL_SENDER": "Buildbot_mailing_sender",
|
||||
"EMAIL_RCP": "Buildbot_mailing_receipient" }'
|
||||
python deployment.py
|
||||
'''
|
||||
|
||||
import re, json, requests, base64
|
||||
from fabric import api
|
||||
from fabric.api import cd, run, put, sudo
|
||||
from os import environ as env
|
||||
from time import sleep
|
||||
from datetime import datetime
|
||||
|
||||
# Populate environment variables
|
||||
CONFIG = json.loads(env['CONFIG_JSON'])
|
||||
for key in CONFIG:
|
||||
env[key] = CONFIG[key]
|
||||
|
||||
# Load DOCKER_CI_KEY
|
||||
env['DOCKER_CI_KEY'] = open(env['DOCKER_CI_KEY_PATH']).read()
|
||||
|
||||
DROPLET_NAME = env.get('DROPLET_NAME','report')
|
||||
TIMEOUT = 120 # Seconds before timeout droplet creation
|
||||
IMAGE_ID = 894856 # Docker on Ubuntu 13.04
|
||||
REGION_ID = 4 # New York 2
|
||||
SIZE_ID = 66 # memory 512MB
|
||||
DO_IMAGE_USER = 'root' # Image user on Digital Ocean
|
||||
API_URL = 'https://api.digitalocean.com/'
|
||||
|
||||
|
||||
class digital_ocean():
|
||||
|
||||
def __init__(self, key, client):
|
||||
'''Set default API parameters'''
|
||||
self.key = key
|
||||
self.client = client
|
||||
self.api_url = API_URL
|
||||
|
||||
def api(self, cmd_path, api_arg={}):
|
||||
'''Make api call'''
|
||||
api_arg.update({'api_key':self.key, 'client_id':self.client})
|
||||
resp = requests.get(self.api_url + cmd_path, params=api_arg).text
|
||||
resp = json.loads(resp)
|
||||
if resp['status'] != 'OK':
|
||||
raise Exception(resp['error_message'])
|
||||
return resp
|
||||
|
||||
def droplet_data(self, name):
|
||||
'''Get droplet data'''
|
||||
data = self.api('droplets')
|
||||
data = [droplet for droplet in data['droplets']
|
||||
if droplet['name'] == name]
|
||||
return data[0] if data else {}
|
||||
|
||||
def json_fmt(data):
|
||||
'''Format json output'''
|
||||
return json.dumps(data, sort_keys = True, indent = 2)
|
||||
|
||||
|
||||
do = digital_ocean(env['DO_API_KEY'], env['DO_CLIENT_ID'])
|
||||
|
||||
# Get DROPLET_NAME data
|
||||
data = do.droplet_data(DROPLET_NAME)
|
||||
|
||||
# Stop processing if DROPLET_NAME exists on Digital Ocean
|
||||
if data:
|
||||
print ('Droplet: {} already deployed. Not further processing.'
|
||||
.format(DROPLET_NAME))
|
||||
exit(1)
|
||||
|
||||
# Create droplet
|
||||
do.api('droplets/new', {'name':DROPLET_NAME, 'region_id':REGION_ID,
|
||||
'image_id':IMAGE_ID, 'size_id':SIZE_ID,
|
||||
'ssh_key_ids':[env['DOCKER_KEY_ID']]})
|
||||
|
||||
# Wait for droplet to be created.
|
||||
start_time = datetime.now()
|
||||
while (data.get('status','') != 'active' and (
|
||||
datetime.now()-start_time).seconds < TIMEOUT):
|
||||
data = do.droplet_data(DROPLET_NAME)
|
||||
print data['status']
|
||||
sleep(3)
|
||||
|
||||
# Wait for the machine to boot
|
||||
sleep(15)
|
||||
|
||||
# Get droplet IP
|
||||
ip = str(data['ip_address'])
|
||||
print 'droplet: {} ip: {}'.format(DROPLET_NAME, ip)
|
||||
|
||||
api.env.host_string = ip
|
||||
api.env.user = DO_IMAGE_USER
|
||||
api.env.key_filename = env['DOCKER_CI_KEY_PATH']
|
||||
|
||||
# Correct timezone
|
||||
sudo('echo "America/Los_Angeles" >/etc/timezone')
|
||||
sudo('dpkg-reconfigure --frontend noninteractive tzdata')
|
||||
|
||||
# Load JSON_CONFIG environment for Dockerfile
|
||||
CONFIG_JSON= base64.b64encode(
|
||||
'{{"DOCKER_CI_PUB": "{DOCKER_CI_PUB}",'
|
||||
' "DOCKER_CI_KEY": "{DOCKER_CI_KEY}",'
|
||||
' "DOCKER_CI_ADDRESS": "{DOCKER_CI_ADDRESS}",'
|
||||
' "SMTP_USER": "{SMTP_USER}",'
|
||||
' "SMTP_PWD": "{SMTP_PWD}",'
|
||||
' "EMAIL_SENDER": "{EMAIL_SENDER}",'
|
||||
' "EMAIL_RCP": "{EMAIL_RCP}"}}'.format(**env))
|
||||
|
||||
run('mkdir -p /data/report')
|
||||
put('./', '/data/report')
|
||||
with cd('/data/report'):
|
||||
run('chmod 700 report.py')
|
||||
run('echo "{}" > credentials.json'.format(CONFIG_JSON))
|
||||
run('docker build -t report .')
|
||||
run('rm credentials.json')
|
||||
run("echo -e '30 09 * * * /usr/bin/docker run report\n' |"
|
||||
" /usr/bin/crontab -")
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
'''CONFIG_JSON is a json encoded string base64 environment variable. It is used
|
||||
to clone docker-ci database, generate docker-ci report and submit it by email.
|
||||
CONFIG_JSON data comes from the file /report/credentials.json inserted in this
|
||||
container by deployment.py:
|
||||
|
||||
{ "DOCKER_CI_PUB": "$(cat docker-ci_ssh_public_key.pub)",
|
||||
"DOCKER_CI_KEY": "$(cat docker-ci_ssh_private_key.key)",
|
||||
"DOCKER_CI_ADDRESS": "user@docker-ci_fqdn_server",
|
||||
"SMTP_USER": "SMTP_server_user",
|
||||
"SMTP_PWD": "SMTP_server_password",
|
||||
"EMAIL_SENDER": "Buildbot_mailing_sender",
|
||||
"EMAIL_RCP": "Buildbot_mailing_receipient" } '''
|
||||
|
||||
import os, re, json, sqlite3, datetime, base64
|
||||
import smtplib
|
||||
from datetime import timedelta
|
||||
from subprocess import call
|
||||
from os import environ as env
|
||||
|
||||
TODAY = datetime.date.today()
|
||||
|
||||
# Load credentials to the environment
|
||||
env['CONFIG_JSON'] = base64.b64decode(open('/report/credentials.json').read())
|
||||
|
||||
# Remove SSH private key as it needs more processing
|
||||
CONFIG = json.loads(re.sub(r'("DOCKER_CI_KEY".+?"(.+?)",)','',
|
||||
env['CONFIG_JSON'], flags=re.DOTALL))
|
||||
|
||||
# Populate environment variables
|
||||
for key in CONFIG:
|
||||
env[key] = CONFIG[key]
|
||||
|
||||
# Load SSH private key
|
||||
env['DOCKER_CI_KEY'] = re.sub('^.+"DOCKER_CI_KEY".+?"(.+?)".+','\\1',
|
||||
env['CONFIG_JSON'],flags=re.DOTALL)
|
||||
|
||||
# Prevent rsync to validate host on first connection to docker-ci
|
||||
os.makedirs('/root/.ssh')
|
||||
open('/root/.ssh/id_rsa','w').write(env['DOCKER_CI_KEY'])
|
||||
os.chmod('/root/.ssh/id_rsa',0600)
|
||||
open('/root/.ssh/config','w').write('StrictHostKeyChecking no\n')
|
||||
|
||||
|
||||
# Sync buildbot database from docker-ci
|
||||
call('rsync {}:/data/buildbot/master/state.sqlite .'.format(
|
||||
env['DOCKER_CI_ADDRESS']), shell=True)
|
||||
|
||||
class SQL:
|
||||
def __init__(self, database_name):
|
||||
sql = sqlite3.connect(database_name)
|
||||
# Use column names as keys for fetchall rows
|
||||
sql.row_factory = sqlite3.Row
|
||||
sql = sql.cursor()
|
||||
self.sql = sql
|
||||
|
||||
def query(self,query_statement):
|
||||
return self.sql.execute(query_statement).fetchall()
|
||||
|
||||
sql = SQL("state.sqlite")
|
||||
|
||||
|
||||
class Report():
|
||||
|
||||
def __init__(self,period='',date=''):
|
||||
self.data = []
|
||||
self.period = 'date' if not period else period
|
||||
self.date = str(TODAY) if not date else date
|
||||
self.compute()
|
||||
|
||||
def compute(self):
|
||||
'''Compute report'''
|
||||
if self.period == 'week':
|
||||
self.week_report(self.date)
|
||||
else:
|
||||
self.date_report(self.date)
|
||||
|
||||
|
||||
def date_report(self,date):
|
||||
'''Create a date test report'''
|
||||
builds = []
|
||||
# Get a queryset with all builds from date
|
||||
rows = sql.query('SELECT * FROM builds JOIN buildrequests'
|
||||
' WHERE builds.brid=buildrequests.id and'
|
||||
' date(start_time, "unixepoch", "localtime") = "{0}"'
|
||||
' GROUP BY number'.format(date))
|
||||
build_names = sorted(set([row['buildername'] for row in rows]))
|
||||
# Create a report build line for a given build
|
||||
for build_name in build_names:
|
||||
tried = len([row['buildername']
|
||||
for row in rows if row['buildername'] == build_name])
|
||||
fail_tests = [row['buildername'] for row in rows if (
|
||||
row['buildername'] == build_name and row['results'] != 0)]
|
||||
fail = len(fail_tests)
|
||||
fail_details = ''
|
||||
fail_pct = int(100.0*fail/tried) if tried != 0 else 100
|
||||
builds.append({'name': build_name, 'tried': tried, 'fail': fail,
|
||||
'fail_pct': fail_pct, 'fail_details':fail_details})
|
||||
if builds:
|
||||
self.data.append({'date': date, 'builds': builds})
|
||||
|
||||
|
||||
def week_report(self,date):
|
||||
'''Add the week's date test reports to report.data'''
|
||||
date = datetime.datetime.strptime(date,'%Y-%m-%d').date()
|
||||
last_monday = date - datetime.timedelta(days=date.weekday())
|
||||
week_dates = [last_monday + timedelta(days=x) for x in range(7,-1,-1)]
|
||||
for date in week_dates:
|
||||
self.date_report(str(date))
|
||||
|
||||
def render_text(self):
|
||||
'''Return rendered report in text format'''
|
||||
retval = ''
|
||||
fail_tests = {}
|
||||
for builds in self.data:
|
||||
retval += 'Test date: {0}\n'.format(builds['date'],retval)
|
||||
table = ''
|
||||
for build in builds['builds']:
|
||||
table += ('Build {name:15} Tried: {tried:4} '
|
||||
' Failures: {fail:4} ({fail_pct}%)\n'.format(**build))
|
||||
if build['name'] in fail_tests:
|
||||
fail_tests[build['name']] += build['fail_details']
|
||||
else:
|
||||
fail_tests[build['name']] = build['fail_details']
|
||||
retval += '{0}\n'.format(table)
|
||||
retval += '\n Builds failing'
|
||||
for fail_name in fail_tests:
|
||||
retval += '\n' + fail_name + '\n'
|
||||
for (fail_id,fail_url,rn_tests,nr_errors,log_errors,
|
||||
tracelog_errors) in fail_tests[fail_name]:
|
||||
retval += fail_url + '\n'
|
||||
retval += '\n\n'
|
||||
return retval
|
||||
|
||||
|
||||
# Send email
|
||||
smtp_from = env['EMAIL_SENDER']
|
||||
subject = '[docker-ci] Daily report for {}'.format(str(TODAY))
|
||||
msg = "From: {}\r\nTo: {}\r\nSubject: {}\r\n\r\n".format(
|
||||
smtp_from, env['EMAIL_RCP'], subject)
|
||||
msg = msg + Report('week').render_text()
|
||||
server = smtplib.SMTP_SSL('smtp.mailgun.org')
|
||||
server.login(env['SMTP_USER'], env['SMTP_PWD'])
|
||||
server.sendmail(smtp_from, env['EMAIL_RCP'], msg)
|
||||
@@ -0,0 +1,103 @@
|
||||
# Docker Server Overview
|
||||
This is an overview of the Docker infrastructure
|
||||
|
||||
## Docker Git Repo
|
||||
The Docker source code lives on github.com under the dotCloud account.
|
||||
https://github.com/dotcloud/docker
|
||||
|
||||
## DNS
|
||||
We are using dyn.com for our DNS server for the docker.io domain.
|
||||
It is using the dotCloud account.
|
||||
|
||||
### DNS Redirect
|
||||
We have a DNS redirect in dyn.com that will automatically redirect
|
||||
docker.io to www.docker.io
|
||||
|
||||
## email
|
||||
Email is sent via dotCloud account on MailGun.com
|
||||
|
||||
## CDN
|
||||
We are using a CDN in front of some of the docker.io domains to help improve
|
||||
proformance. The CDN is Cloudflare, using a Pro account.
|
||||
|
||||
*This is currently disabled due to an issue with slow performance during pull
|
||||
in some regions of the world.*
|
||||
|
||||
### CDN Domains
|
||||
- www.docker.io
|
||||
- test.docker.io
|
||||
- registry-1.docker.io
|
||||
- debug.docker.io
|
||||
- cdn-registry-1.docker.io
|
||||
|
||||
## edge-docker.dotcloud.com
|
||||
All of the Docker applications that live on dotCloud go through their own
|
||||
load balancer, and this is where SSL is terminated as well.
|
||||
|
||||
## www.docker.io
|
||||
This is hosted under the docker account on dotCloud's PaaS.
|
||||
|
||||
### Source Code
|
||||
The source code for the website lives here:
|
||||
https://github.com/dotcloud/www.docker.io
|
||||
|
||||
## Docker Registry
|
||||
The registry is where the image data is store.
|
||||
|
||||
### URL:
|
||||
- registry-1.docker.io
|
||||
- cdn-registry-1.docker.io
|
||||
|
||||
There are two urls, one is behind a CDN the other isn't this is because when
|
||||
you pull, you pull from the CDN url, to help with pull speeds. We don't push
|
||||
through the CDN as well, because it doesn't help us, so we bypass it.
|
||||
|
||||
### Data Store:
|
||||
The data store for the registry is using Amazon S3 in a bucket under the docker
|
||||
aws account.
|
||||
|
||||
### Source Code
|
||||
The source code for the registry lives here: https://github.com/dotcloud/docker-registry
|
||||
|
||||
### Hosted:
|
||||
Hosted on the Docker account on dotCloud's PaaS
|
||||
|
||||
## index.docker.io
|
||||
This is the docker index, it stores all of the meta information about the
|
||||
docker images, but all data is stored in the registry.
|
||||
|
||||
### Source Code:
|
||||
Not available
|
||||
|
||||
### Hosted:
|
||||
Hosted on the Docker account on dotCloud's PaaS
|
||||
|
||||
## blog.docker.io
|
||||
This is a wordpress based Docker blog.
|
||||
|
||||
### URL:
|
||||
http://blog.docker.io
|
||||
|
||||
### Source Code:
|
||||
https://github.com/dotcloud/blog.docker.io
|
||||
|
||||
## docs.docker.io
|
||||
This is where all of the documentation for docker lives.
|
||||
|
||||
### Hosted:
|
||||
This website is hosted on ReadTheDocs.org.
|
||||
|
||||
### Updates
|
||||
These docs get automatically updated when the Docker repo on github has
|
||||
new commits. It does this via a webhook.
|
||||
|
||||
### Proxy:
|
||||
This is a simple dotcloud app, with its main and only purpose to forward
|
||||
http (and https) requests to docker.readthedocs.org.
|
||||
|
||||
https://github.com/dotcloud/docker-docs-dotcloud-proxy
|
||||
|
||||
## get.docker.io
|
||||
This is the docker repository where we store images
|
||||
|
||||
TODO: need more here. jerome?
|
||||
Executable
+136
@@ -0,0 +1,136 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
#
|
||||
# This script is meant for quick & easy install via:
|
||||
# 'curl -sL https://get.docker.io/ | sh'
|
||||
# or:
|
||||
# 'wget -qO- https://get.docker.io/ | sh'
|
||||
#
|
||||
#
|
||||
# Docker Maintainers:
|
||||
# To update this script on https://get.docker.io,
|
||||
# use hack/release.sh during a normal release,
|
||||
# or the following one-liner for script hotfixes:
|
||||
# s3cmd put --acl-public -P hack/install.sh s3://get.docker.io/index
|
||||
#
|
||||
|
||||
url='https://get.docker.io/'
|
||||
|
||||
command_exists() {
|
||||
command -v "$@" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
case "$(uname -m)" in
|
||||
*64)
|
||||
;;
|
||||
*)
|
||||
echo >&2 'Error: you are not using a 64bit platform.'
|
||||
echo >&2 'Docker currently only supports 64bit platforms.'
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if command_exists docker || command_exists lxc-docker; then
|
||||
echo >&2 'Warning: "docker" or "lxc-docker" command appears to already exist.'
|
||||
echo >&2 'Please ensure that you do not already have docker installed.'
|
||||
echo >&2 'You may press Ctrl+C now to abort this process and rectify this situation.'
|
||||
( set -x; sleep 20 )
|
||||
fi
|
||||
|
||||
sh_c='sh -c'
|
||||
if [ "$(whoami 2>/dev/null || true)" != 'root' ]; then
|
||||
if command_exists sudo; then
|
||||
sh_c='sudo sh -c'
|
||||
elif command_exists su; then
|
||||
sh_c='su -c'
|
||||
else
|
||||
echo >&2 'Error: this installer needs the ability to run commands as root.'
|
||||
echo >&2 'We are unable to find either "sudo" or "su" available to make this happen.'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
curl=''
|
||||
if command_exists curl; then
|
||||
curl='curl -sL'
|
||||
elif command_exists wget; then
|
||||
curl='wget -qO-'
|
||||
elif command_exists busybox && busybox --list-modules | grep -q wget; then
|
||||
curl='busybox wget -qO-'
|
||||
fi
|
||||
|
||||
# perform some very rudimentary platform detection
|
||||
lsb_dist=''
|
||||
if command_exists lsb_release; then
|
||||
lsb_dist="$(lsb_release -si)"
|
||||
fi
|
||||
if [ -z "$lsb_dist" ] && [ -r /etc/lsb-release ]; then
|
||||
lsb_dist="$(. /etc/lsb-release && echo "$DISTRIB_ID")"
|
||||
fi
|
||||
if [ -z "$lsb_dist" ] && [ -r /etc/debian_version ]; then
|
||||
lsb_dist='Debian'
|
||||
fi
|
||||
|
||||
case "$lsb_dist" in
|
||||
Ubuntu|Debian)
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# TODO remove this comment/section once device-mapper lands
|
||||
echo 'Warning: Docker currently requires AUFS support in the kernel.'
|
||||
echo 'Please ensure that your kernel includes such support.'
|
||||
( set -x; sleep 10 )
|
||||
|
||||
if [ ! -e /usr/lib/apt/methods/https ]; then
|
||||
( set -x; $sh_c 'sleep 3; apt-get update; apt-get install -y -q apt-transport-https' )
|
||||
fi
|
||||
if [ -z "$curl" ]; then
|
||||
( set -x; $sh_c 'sleep 3; apt-get update; apt-get install -y -q curl' )
|
||||
curl='curl -sL'
|
||||
fi
|
||||
(
|
||||
set -x
|
||||
$sh_c "$curl ${url}gpg | apt-key add -"
|
||||
$sh_c "echo deb ${url}ubuntu docker main > /etc/apt/sources.list.d/docker.list"
|
||||
$sh_c 'sleep 3; apt-get update; apt-get install -y -q lxc-docker'
|
||||
)
|
||||
if command_exists docker && [ -e /var/run/docker.sock ]; then
|
||||
(
|
||||
set -x
|
||||
$sh_c 'docker run busybox echo "Docker has been successfully installed!"'
|
||||
)
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
|
||||
Gentoo)
|
||||
if [ "$url" = "https://test.docker.io/" ]; then
|
||||
echo >&2
|
||||
echo >&2 ' You appear to be trying to install the latest nightly build in Gentoo.'
|
||||
echo >&2 ' The portage tree should contain the latest stable release of Docker, but'
|
||||
echo >&2 ' if you want something more recent, you can always use the live ebuild'
|
||||
echo >&2 ' provided in the "docker" overlay available via layman. For more'
|
||||
echo >&2 ' instructions, please see the following URL:'
|
||||
echo >&2 ' https://github.com/tianon/docker-overlay#using-this-overlay'
|
||||
echo >&2 ' After adding the "docker" overlay, you should be able to:'
|
||||
echo >&2 ' emerge -av =app-emulation/docker-9999'
|
||||
echo >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(
|
||||
set -x
|
||||
$sh_c 'sleep 3; emerge app-emulation/docker'
|
||||
)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
echo >&2
|
||||
echo >&2 ' Either your platform is not easily detectable, is not supported by this'
|
||||
echo >&2 ' installer script (yet - PRs welcome!), or does not yet have a package for'
|
||||
echo >&2 ' Docker. Please visit the following URL for more detailed installation'
|
||||
echo >&2 ' instructions:'
|
||||
echo >&2
|
||||
echo >&2 ' http://docs.docker.io/en/latest/installation/'
|
||||
echo >&2
|
||||
exit 1
|
||||
+1
-2
@@ -44,8 +44,7 @@ if [ -n "$(git status --porcelain)" ]; then
|
||||
fi
|
||||
|
||||
# Use these flags when compiling the tests and final binary
|
||||
LDFLAGS='-X main.GITCOMMIT "'$GITCOMMIT'" -X main.VERSION "'$VERSION'" -w -linkmode external -extldflags "-lpthread -static -Wl,--unresolved-symbols=ignore-in-object-files"'
|
||||
BUILDFLAGS='-tags netgo'
|
||||
LDFLAGS="-X main.GITCOMMIT $GITCOMMIT -X main.VERSION $VERSION -d -w"
|
||||
|
||||
|
||||
bundle() {
|
||||
|
||||
+3
-3
@@ -2,6 +2,6 @@
|
||||
|
||||
DEST=$1
|
||||
|
||||
go build -o $DEST/docker-$VERSION -ldflags "$LDFLAGS" $BUILDFLAGS ./docker
|
||||
|
||||
echo "Created binary: $DEST/docker-$VERSION"
|
||||
if go build -o $DEST/docker-$VERSION -ldflags "$LDFLAGS" ./docker; then
|
||||
echo "Created binary: $DEST/docker-$VERSION"
|
||||
fi
|
||||
|
||||
+6
-1
@@ -3,13 +3,18 @@ DEST=$1
|
||||
set -e
|
||||
|
||||
# Run Docker's test suite, including sub-packages, and store their output as a bundle
|
||||
# If $TESTFLAGS is set in the environment, it is passed as extra arguments to 'go test'.
|
||||
# You can use this to select certain tests to run, eg.
|
||||
#
|
||||
# TESTFLAGS='-run ^TestBuild$' ./hack/make.sh test
|
||||
#
|
||||
bundle_test() {
|
||||
{
|
||||
date
|
||||
for test_dir in $(find_test_dirs); do (
|
||||
set -x
|
||||
cd $test_dir
|
||||
go test -v -ldflags "$LDFLAGS" $BUILDFLAGS
|
||||
go test -v -ldflags "$LDFLAGS" $TESTFLAGS
|
||||
) done
|
||||
} 2>&1 | tee $DEST/test.log
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ EOF
|
||||
--replaces lxc-docker-virtual-package \
|
||||
--url "$PACKAGE_URL" \
|
||||
--vendor "$PACKAGE_VENDOR" \
|
||||
--config-files /etc/init/docker.conf \
|
||||
-t deb .
|
||||
mkdir empty
|
||||
fpm -s dir -C empty \
|
||||
@@ -89,6 +90,7 @@ EOF
|
||||
--maintainer "$PACKAGE_MAINTAINER" \
|
||||
--url "$PACKAGE_URL" \
|
||||
--vendor "$PACKAGE_VENDOR" \
|
||||
--config-files /etc/init/docker.conf \
|
||||
-t deb .
|
||||
)
|
||||
}
|
||||
|
||||
+13
-13
@@ -67,7 +67,14 @@ write_to_s3() {
|
||||
}
|
||||
|
||||
s3_url() {
|
||||
echo "http://$BUCKET.s3.amazonaws.com"
|
||||
case "$BUCKET" in
|
||||
get.docker.io|test.docker.io)
|
||||
echo "https://$BUCKET"
|
||||
;;
|
||||
*)
|
||||
echo "http://$BUCKET.s3.amazonaws.com"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Upload the 'ubuntu' bundle to S3:
|
||||
@@ -125,13 +132,13 @@ EOF
|
||||
s3cmd --acl-public sync $APTDIR/ s3://$BUCKET/ubuntu/
|
||||
cat <<EOF | write_to_s3 s3://$BUCKET/ubuntu/info
|
||||
# Add the repository to your APT sources
|
||||
echo deb $(s3_url $BUCKET)/ubuntu docker main > /etc/apt/sources.list.d/docker.list
|
||||
echo deb $(s3_url)/ubuntu docker main > /etc/apt/sources.list.d/docker.list
|
||||
# Then import the repository key
|
||||
curl $(s3_url $BUCKET)/gpg | apt-key add -
|
||||
curl $(s3_url)/gpg | apt-key add -
|
||||
# Install docker
|
||||
apt-get update ; apt-get install -y lxc-docker
|
||||
EOF
|
||||
echo "APT repository uploaded. Instructions available at $(s3_url $BUCKET)/ubuntu/info"
|
||||
echo "APT repository uploaded. Instructions available at $(s3_url)/ubuntu/info"
|
||||
}
|
||||
|
||||
# Upload a static binary to S3
|
||||
@@ -141,7 +148,7 @@ release_binary() {
|
||||
s3cmd --acl-public put bundles/$VERSION/binary/docker-$VERSION $S3DIR/docker-$VERSION
|
||||
cat <<EOF | write_to_s3 s3://$BUCKET/builds/info
|
||||
# To install, run the following command as root:
|
||||
curl -O http://$BUCKET.s3.amazonaws.com/builds/Linux/x86_64/docker-$VERSION && chmod +x docker-$VERSION && sudo mv docker-$VERSION /usr/local/bin/docker
|
||||
curl -O $(s3_url)/builds/Linux/x86_64/docker-$VERSION && chmod +x docker-$VERSION && sudo mv docker-$VERSION /usr/local/bin/docker
|
||||
# Then start docker in daemon mode:
|
||||
sudo /usr/local/bin/docker -d
|
||||
EOF
|
||||
@@ -155,14 +162,7 @@ EOF
|
||||
|
||||
# Upload the index script
|
||||
release_index() {
|
||||
(
|
||||
if [ "$BUCKET" != "get.docker.io" ]
|
||||
then
|
||||
sed s,https://get.docker.io/,http://$BUCKET.s3.amazonaws.com/, contrib/install.sh
|
||||
else
|
||||
cat contrib/install.sh
|
||||
fi
|
||||
) | write_to_s3 s3://$BUCKET/index
|
||||
sed "s,https://get.docker.io/,$(s3_url)/," hack/install.sh | write_to_s3 s3://$BUCKET/index
|
||||
}
|
||||
|
||||
release_test() {
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ git_clone () {
|
||||
)
|
||||
}
|
||||
|
||||
git_clone github.com/kr/pty 27435c699
|
||||
git_clone github.com/kr/pty 3b1f6487b
|
||||
|
||||
git_clone github.com/gorilla/context/ 708054d61e5
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
||||
@@ -1,105 +0,0 @@
|
||||
package iptables
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Action string
|
||||
|
||||
const (
|
||||
Add Action = "-A"
|
||||
Delete Action = "-D"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrIptablesNotFound = errors.New("Iptables not found")
|
||||
nat = []string{"-t", "nat"}
|
||||
)
|
||||
|
||||
type Chain struct {
|
||||
Name string
|
||||
Bridge string
|
||||
}
|
||||
|
||||
func NewChain(name, bridge string) (*Chain, error) {
|
||||
if err := Raw("-t", "nat", "-N", name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chain := &Chain{
|
||||
Name: name,
|
||||
Bridge: bridge,
|
||||
}
|
||||
|
||||
if err := chain.Prerouting(Add, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil {
|
||||
return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
|
||||
}
|
||||
if err := chain.Output(Add, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil {
|
||||
return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
|
||||
}
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
func RemoveExistingChain(name string) error {
|
||||
chain := &Chain{
|
||||
Name: name,
|
||||
}
|
||||
return chain.Remove()
|
||||
}
|
||||
|
||||
func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr string, dest_port int) error {
|
||||
return Raw("-t", "nat", fmt.Sprint(action), c.Name,
|
||||
"-p", proto,
|
||||
"-d", ip.String(),
|
||||
"--dport", strconv.Itoa(port),
|
||||
"!", "-i", c.Bridge,
|
||||
"-j", "DNAT",
|
||||
"--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port)))
|
||||
}
|
||||
|
||||
func (c *Chain) Prerouting(action Action, args ...string) error {
|
||||
a := append(nat, fmt.Sprint(action), "PREROUTING")
|
||||
if len(args) > 0 {
|
||||
a = append(a, args...)
|
||||
}
|
||||
return Raw(append(a, "-j", c.Name)...)
|
||||
}
|
||||
|
||||
func (c *Chain) Output(action Action, args ...string) error {
|
||||
a := append(nat, fmt.Sprint(action), "OUTPUT")
|
||||
if len(args) > 0 {
|
||||
a = append(a, args...)
|
||||
}
|
||||
return Raw(append(a, "-j", c.Name)...)
|
||||
}
|
||||
|
||||
func (c *Chain) Remove() error {
|
||||
// Ignore errors - This could mean the chains were never set up
|
||||
c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6
|
||||
|
||||
c.Prerouting(Delete)
|
||||
c.Output(Delete)
|
||||
|
||||
Raw("-t", "nat", "-F", c.Name)
|
||||
Raw("-t", "nat", "-X", c.Name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Raw(args ...string) error {
|
||||
path, err := exec.LookPath("iptables")
|
||||
if err != nil {
|
||||
return ErrIptablesNotFound
|
||||
}
|
||||
if err := exec.Command(path, args...).Run(); err != nil {
|
||||
return fmt.Errorf("iptables failed: iptables %v", strings.Join(args, " "))
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package iptables
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIptables(t *testing.T) {
|
||||
if err := Raw("-L"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
path := os.Getenv("PATH")
|
||||
os.Setenv("PATH", "")
|
||||
defer os.Setenv("PATH", path)
|
||||
if err := Raw("-L"); err == nil {
|
||||
t.Fatal("Not finding iptables in the PATH should cause an error")
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/iptables"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Link struct {
|
||||
ParentIP string
|
||||
ChildIP string
|
||||
Name string
|
||||
BridgeInterface string
|
||||
ChildEnvironment []string
|
||||
Ports []Port
|
||||
IsEnabled bool
|
||||
}
|
||||
|
||||
func NewLink(parent, child *Container, name, bridgeInterface string) (*Link, error) {
|
||||
if parent.ID == child.ID {
|
||||
return nil, fmt.Errorf("Cannot link to self: %s == %s", parent.ID, child.ID)
|
||||
}
|
||||
if !child.State.Running {
|
||||
return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.ID, name)
|
||||
}
|
||||
|
||||
ports := make([]Port, len(child.Config.ExposedPorts))
|
||||
var i int
|
||||
for p := range child.Config.ExposedPorts {
|
||||
ports[i] = p
|
||||
i++
|
||||
}
|
||||
|
||||
l := &Link{
|
||||
BridgeInterface: bridgeInterface,
|
||||
Name: name,
|
||||
ChildIP: child.NetworkSettings.IPAddress,
|
||||
ParentIP: parent.NetworkSettings.IPAddress,
|
||||
ChildEnvironment: child.Config.Env,
|
||||
Ports: ports,
|
||||
}
|
||||
return l, nil
|
||||
|
||||
}
|
||||
|
||||
func (l *Link) Alias() string {
|
||||
_, alias := path.Split(l.Name)
|
||||
return alias
|
||||
}
|
||||
|
||||
func (l *Link) ToEnv() []string {
|
||||
env := []string{}
|
||||
alias := strings.ToUpper(l.Alias())
|
||||
|
||||
if p := l.getDefaultPort(); p != nil {
|
||||
env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
|
||||
}
|
||||
|
||||
// Load exposed ports into the environment
|
||||
for _, p := range l.Ports {
|
||||
env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
|
||||
}
|
||||
|
||||
// Load the linked container's name into the environment
|
||||
env = append(env, fmt.Sprintf("%s_NAME=%s", alias, l.Name))
|
||||
|
||||
if l.ChildEnvironment != nil {
|
||||
for _, v := range l.ChildEnvironment {
|
||||
parts := strings.Split(v, "=")
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
// Ignore a few variables that are added during docker build
|
||||
if parts[0] == "HOME" || parts[0] == "PATH" {
|
||||
continue
|
||||
}
|
||||
env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, parts[0], parts[1]))
|
||||
}
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
// Default port rules
|
||||
func (l *Link) getDefaultPort() *Port {
|
||||
var p Port
|
||||
i := len(l.Ports)
|
||||
|
||||
if i == 0 {
|
||||
return nil
|
||||
} else if i > 1 {
|
||||
sortPorts(l.Ports, func(ip, jp Port) bool {
|
||||
// If the two ports have the same number, tcp takes priority
|
||||
// Sort in desc order
|
||||
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
|
||||
})
|
||||
}
|
||||
p = l.Ports[0]
|
||||
return &p
|
||||
}
|
||||
|
||||
func (l *Link) Enable() error {
|
||||
if err := l.toggle("-I", false); err != nil {
|
||||
return err
|
||||
}
|
||||
l.IsEnabled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Link) Disable() {
|
||||
// We do not care about erros here because the link may not
|
||||
// exist in iptables
|
||||
l.toggle("-D", true)
|
||||
|
||||
l.IsEnabled = false
|
||||
}
|
||||
|
||||
func (l *Link) toggle(action string, ignoreErrors bool) error {
|
||||
for _, p := range l.Ports {
|
||||
if err := iptables.Raw(action, "FORWARD",
|
||||
"-i", l.BridgeInterface, "-o", l.BridgeInterface,
|
||||
"-p", p.Proto(),
|
||||
"-s", l.ParentIP,
|
||||
"--dport", p.Port(),
|
||||
"-d", l.ChildIP,
|
||||
"-j", "ACCEPT"); !ignoreErrors && err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.Raw(action, "FORWARD",
|
||||
"-i", l.BridgeInterface, "-o", l.BridgeInterface,
|
||||
"-p", p.Proto(),
|
||||
"-s", l.ChildIP,
|
||||
"--sport", p.Port(),
|
||||
"-d", l.ParentIP,
|
||||
"-j", "ACCEPT"); !ignoreErrors && err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func newMockLinkContainer(id string, ip string) *Container {
|
||||
return &Container{
|
||||
Config: &Config{},
|
||||
ID: id,
|
||||
NetworkSettings: &NetworkSettings{
|
||||
IPAddress: ip,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkNew(t *testing.T) {
|
||||
toID := GenerateID()
|
||||
fromID := GenerateID()
|
||||
|
||||
from := newMockLinkContainer(fromID, "172.0.17.2")
|
||||
from.Config.Env = []string{}
|
||||
from.State = State{Running: true}
|
||||
ports := make(map[Port]struct{})
|
||||
|
||||
ports[Port("6379/tcp")] = struct{}{}
|
||||
|
||||
from.Config.ExposedPorts = ports
|
||||
|
||||
to := newMockLinkContainer(toID, "172.0.17.3")
|
||||
|
||||
link, err := NewLink(to, from, "/db/docker", "172.0.17.1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if link == nil {
|
||||
t.FailNow()
|
||||
}
|
||||
if link.Name != "/db/docker" {
|
||||
t.Fail()
|
||||
}
|
||||
if link.Alias() != "docker" {
|
||||
t.Fail()
|
||||
}
|
||||
if link.ParentIP != "172.0.17.3" {
|
||||
t.Fail()
|
||||
}
|
||||
if link.ChildIP != "172.0.17.2" {
|
||||
t.Fail()
|
||||
}
|
||||
if link.BridgeInterface != "172.0.17.1" {
|
||||
t.Fail()
|
||||
}
|
||||
for _, p := range link.Ports {
|
||||
if p != Port("6379/tcp") {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkEnv(t *testing.T) {
|
||||
toID := GenerateID()
|
||||
fromID := GenerateID()
|
||||
|
||||
from := newMockLinkContainer(fromID, "172.0.17.2")
|
||||
from.Config.Env = []string{"PASSWORD=gordon"}
|
||||
from.State = State{Running: true}
|
||||
ports := make(map[Port]struct{})
|
||||
|
||||
ports[Port("6379/tcp")] = struct{}{}
|
||||
|
||||
from.Config.ExposedPorts = ports
|
||||
|
||||
to := newMockLinkContainer(toID, "172.0.17.3")
|
||||
|
||||
link, err := NewLink(to, from, "/db/docker", "172.0.17.1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rawEnv := link.ToEnv()
|
||||
env := make(map[string]string, len(rawEnv))
|
||||
for _, e := range rawEnv {
|
||||
parts := strings.Split(e, "=")
|
||||
if len(parts) != 2 {
|
||||
t.FailNow()
|
||||
}
|
||||
env[parts[0]] = parts[1]
|
||||
}
|
||||
if env["DOCKER_PORT"] != "tcp://172.0.17.2:6379" {
|
||||
t.Fatalf("Expected tcp://172.0.17.2:6379, got %s", env["DOCKER_PORT"])
|
||||
}
|
||||
if env["DOCKER_PORT_6379_TCP"] != "tcp://172.0.17.2:6379" {
|
||||
t.Fatalf("Expected tcp://172.0.17.2:6379, got %s", env["DOCKER_PORT_6379_TCP"])
|
||||
}
|
||||
if env["DOCKER_NAME"] != "/db/docker" {
|
||||
t.Fatalf("Expected /db/docker, got %s", env["DOCKER_NAME"])
|
||||
}
|
||||
if env["DOCKER_ENV_PASSWORD"] != "gordon" {
|
||||
t.Fatalf("Expected gordon, got %s", env["DOCKER_ENV_PASSWORD"])
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -111,7 +111,7 @@ lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,{{ if ind
|
||||
# (Note: 'lxc.cap.keep' is coming soon and should replace this under the
|
||||
# security principle 'deny all unless explicitly permitted', see
|
||||
# http://sourceforge.net/mailarchive/message.php?msg_id=31054627 )
|
||||
lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod setfcap setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config
|
||||
lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config
|
||||
{{end}}
|
||||
|
||||
# limits
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
func Unmount(target string) error {
|
||||
if err := exec.Command("auplink", target, "flush").Run(); err != nil {
|
||||
utils.Debugf("[warning]: couldn't run auplink before unmount: %s", err)
|
||||
utils.Errorf("[warning]: couldn't run auplink before unmount: %s", err)
|
||||
}
|
||||
if err := syscall.Unmount(target, 0); err != nil {
|
||||
return err
|
||||
|
||||
+208
-118
@@ -4,8 +4,6 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/iptables"
|
||||
"github.com/dotcloud/docker/proxy"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"log"
|
||||
"net"
|
||||
@@ -15,6 +13,8 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var NetworkBridgeIface string
|
||||
|
||||
const (
|
||||
DefaultNetworkBridge = "docker0"
|
||||
DisableNetworkBridge = "none"
|
||||
@@ -81,6 +81,18 @@ func ip(args ...string) (string, error) {
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// Wrapper around the iptables command
|
||||
func iptables(args ...string) error {
|
||||
path, err := exec.LookPath("iptables")
|
||||
if err != nil {
|
||||
return fmt.Errorf("command not found: iptables")
|
||||
}
|
||||
if err := exec.Command(path, args...).Run(); err != nil {
|
||||
return fmt.Errorf("iptables failed: iptables %v", strings.Join(args, " "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkRouteOverlaps(routes string, dockerNetwork *net.IPNet) error {
|
||||
utils.Debugf("Routes:\n\n%s", routes)
|
||||
for _, line := range strings.Split(routes, "\n") {
|
||||
@@ -112,7 +124,7 @@ func checkRouteOverlaps(routes string, dockerNetwork *net.IPNet) error {
|
||||
// CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`,
|
||||
// and attempts to configure it with an address which doesn't conflict with any other interface on the host.
|
||||
// If it can't find an address which doesn't conflict, it will return an error.
|
||||
func CreateBridgeIface(config *DaemonConfig) error {
|
||||
func CreateBridgeIface(ifaceName string) error {
|
||||
addrs := []string{
|
||||
// Here we don't follow the convention of using the 1st IP of the range for the gateway.
|
||||
// This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges.
|
||||
@@ -151,29 +163,23 @@ func CreateBridgeIface(config *DaemonConfig) error {
|
||||
}
|
||||
}
|
||||
if ifaceAddr == "" {
|
||||
return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", config.BridgeIface, config.BridgeIface)
|
||||
return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", ifaceName, ifaceName)
|
||||
}
|
||||
utils.Debugf("Creating bridge %s with network %s", config.BridgeIface, ifaceAddr)
|
||||
utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
|
||||
|
||||
if output, err := ip("link", "add", config.BridgeIface, "type", "bridge"); err != nil {
|
||||
if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil {
|
||||
return fmt.Errorf("Error creating bridge: %s (output: %s)", err, output)
|
||||
}
|
||||
|
||||
if output, err := ip("addr", "add", ifaceAddr, "dev", config.BridgeIface); err != nil {
|
||||
if output, err := ip("addr", "add", ifaceAddr, "dev", ifaceName); err != nil {
|
||||
return fmt.Errorf("Unable to add private network: %s (%s)", err, output)
|
||||
}
|
||||
if output, err := ip("link", "set", config.BridgeIface, "up"); err != nil {
|
||||
if output, err := ip("link", "set", ifaceName, "up"); err != nil {
|
||||
return fmt.Errorf("Unable to start network bridge: %s (%s)", err, output)
|
||||
}
|
||||
if config.EnableIptables {
|
||||
if err := iptables.Raw("-t", "nat", "-A", "POSTROUTING", "-s", ifaceAddr,
|
||||
"!", "-d", ifaceAddr, "-j", "MASQUERADE"); err != nil {
|
||||
return fmt.Errorf("Unable to enable network bridge NAT: %s", err)
|
||||
}
|
||||
// Prevent inter-container communication by default
|
||||
if err := iptables.Raw("-A", "FORWARD", "-i", config.BridgeIface, "-o", config.BridgeIface, "-j", "DROP"); err != nil {
|
||||
return fmt.Errorf("Unable to prevent intercontainer communication: %s", err)
|
||||
}
|
||||
if err := iptables("-t", "nat", "-A", "POSTROUTING", "-s", ifaceAddr,
|
||||
"!", "-d", ifaceAddr, "-j", "MASQUERADE"); err != nil {
|
||||
return fmt.Errorf("Unable to enable network bridge NAT: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -210,27 +216,58 @@ func getIfaceAddr(name string) (net.Addr, error) {
|
||||
// It keeps track of all mappings and is able to unmap at will
|
||||
type PortMapper struct {
|
||||
tcpMapping map[int]*net.TCPAddr
|
||||
tcpProxies map[int]proxy.Proxy
|
||||
tcpProxies map[int]Proxy
|
||||
udpMapping map[int]*net.UDPAddr
|
||||
udpProxies map[int]proxy.Proxy
|
||||
|
||||
iptables *iptables.Chain
|
||||
defaultIp net.IP
|
||||
udpProxies map[int]Proxy
|
||||
}
|
||||
|
||||
func (mapper *PortMapper) Map(ip net.IP, port int, backendAddr net.Addr) error {
|
||||
func (mapper *PortMapper) cleanup() error {
|
||||
// Ignore errors - This could mean the chains were never set up
|
||||
iptables("-t", "nat", "-D", "PREROUTING", "-m", "addrtype", "--dst-type", "LOCAL", "-j", "DOCKER")
|
||||
iptables("-t", "nat", "-D", "OUTPUT", "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8", "-j", "DOCKER")
|
||||
iptables("-t", "nat", "-D", "OUTPUT", "-m", "addrtype", "--dst-type", "LOCAL", "-j", "DOCKER") // Created in versions <= 0.1.6
|
||||
// Also cleanup rules created by older versions, or -X might fail.
|
||||
iptables("-t", "nat", "-D", "PREROUTING", "-j", "DOCKER")
|
||||
iptables("-t", "nat", "-D", "OUTPUT", "-j", "DOCKER")
|
||||
iptables("-t", "nat", "-F", "DOCKER")
|
||||
iptables("-t", "nat", "-X", "DOCKER")
|
||||
mapper.tcpMapping = make(map[int]*net.TCPAddr)
|
||||
mapper.tcpProxies = make(map[int]Proxy)
|
||||
mapper.udpMapping = make(map[int]*net.UDPAddr)
|
||||
mapper.udpProxies = make(map[int]Proxy)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mapper *PortMapper) setup() error {
|
||||
if err := iptables("-t", "nat", "-N", "DOCKER"); err != nil {
|
||||
return fmt.Errorf("Failed to create DOCKER chain: %s", err)
|
||||
}
|
||||
if err := iptables("-t", "nat", "-A", "PREROUTING", "-m", "addrtype", "--dst-type", "LOCAL", "-j", "DOCKER"); err != nil {
|
||||
return fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
|
||||
}
|
||||
if err := iptables("-t", "nat", "-A", "OUTPUT", "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8", "-j", "DOCKER"); err != nil {
|
||||
return fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mapper *PortMapper) iptablesForward(rule string, port int, proto string, dest_addr string, dest_port int) error {
|
||||
return iptables("-t", "nat", rule, "DOCKER", "-p", proto, "--dport", strconv.Itoa(port),
|
||||
"!", "-i", NetworkBridgeIface,
|
||||
"-j", "DNAT", "--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port)))
|
||||
}
|
||||
|
||||
func (mapper *PortMapper) Map(port int, backendAddr net.Addr) error {
|
||||
if _, isTCP := backendAddr.(*net.TCPAddr); isTCP {
|
||||
backendPort := backendAddr.(*net.TCPAddr).Port
|
||||
backendIP := backendAddr.(*net.TCPAddr).IP
|
||||
if mapper.iptables != nil {
|
||||
if err := mapper.iptables.Forward(iptables.Add, ip, port, "tcp", backendIP.String(), backendPort); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mapper.iptablesForward("-A", port, "tcp", backendIP.String(), backendPort); err != nil {
|
||||
return err
|
||||
}
|
||||
mapper.tcpMapping[port] = backendAddr.(*net.TCPAddr)
|
||||
proxy, err := proxy.NewProxy(&net.TCPAddr{IP: ip, Port: port}, backendAddr)
|
||||
proxy, err := NewProxy(&net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: port}, backendAddr)
|
||||
if err != nil {
|
||||
mapper.Unmap(ip, port, "tcp")
|
||||
mapper.Unmap(port, "tcp")
|
||||
return err
|
||||
}
|
||||
mapper.tcpProxies[port] = proxy
|
||||
@@ -238,15 +275,13 @@ func (mapper *PortMapper) Map(ip net.IP, port int, backendAddr net.Addr) error {
|
||||
} else {
|
||||
backendPort := backendAddr.(*net.UDPAddr).Port
|
||||
backendIP := backendAddr.(*net.UDPAddr).IP
|
||||
if mapper.iptables != nil {
|
||||
if err := mapper.iptables.Forward(iptables.Add, ip, port, "udp", backendIP.String(), backendPort); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mapper.iptablesForward("-A", port, "udp", backendIP.String(), backendPort); err != nil {
|
||||
return err
|
||||
}
|
||||
mapper.udpMapping[port] = backendAddr.(*net.UDPAddr)
|
||||
proxy, err := proxy.NewProxy(&net.UDPAddr{IP: ip, Port: port}, backendAddr)
|
||||
proxy, err := NewProxy(&net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: port}, backendAddr)
|
||||
if err != nil {
|
||||
mapper.Unmap(ip, port, "udp")
|
||||
mapper.Unmap(port, "udp")
|
||||
return err
|
||||
}
|
||||
mapper.udpProxies[port] = proxy
|
||||
@@ -255,7 +290,7 @@ func (mapper *PortMapper) Map(ip net.IP, port int, backendAddr net.Addr) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mapper *PortMapper) Unmap(ip net.IP, port int, proto string) error {
|
||||
func (mapper *PortMapper) Unmap(port int, proto string) error {
|
||||
if proto == "tcp" {
|
||||
backendAddr, ok := mapper.tcpMapping[port]
|
||||
if !ok {
|
||||
@@ -265,10 +300,8 @@ func (mapper *PortMapper) Unmap(ip net.IP, port int, proto string) error {
|
||||
proxy.Close()
|
||||
delete(mapper.tcpProxies, port)
|
||||
}
|
||||
if mapper.iptables != nil {
|
||||
if err := mapper.iptables.Forward(iptables.Delete, ip, port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mapper.iptablesForward("-D", port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(mapper.tcpMapping, port)
|
||||
} else {
|
||||
@@ -280,37 +313,21 @@ func (mapper *PortMapper) Unmap(ip net.IP, port int, proto string) error {
|
||||
proxy.Close()
|
||||
delete(mapper.udpProxies, port)
|
||||
}
|
||||
if mapper.iptables != nil {
|
||||
if err := mapper.iptables.Forward(iptables.Delete, ip, port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mapper.iptablesForward("-D", port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(mapper.udpMapping, port)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newPortMapper(config *DaemonConfig) (*PortMapper, error) {
|
||||
// We can always try removing the iptables
|
||||
if err := iptables.RemoveExistingChain("DOCKER"); err != nil {
|
||||
func newPortMapper() (*PortMapper, error) {
|
||||
mapper := &PortMapper{}
|
||||
if err := mapper.cleanup(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var chain *iptables.Chain
|
||||
if config.EnableIptables {
|
||||
var err error
|
||||
chain, err = iptables.NewChain("DOCKER", config.BridgeIface)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to create DOCKER chain: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
mapper := &PortMapper{
|
||||
tcpMapping: make(map[int]*net.TCPAddr),
|
||||
tcpProxies: make(map[int]proxy.Proxy),
|
||||
udpMapping: make(map[int]*net.UDPAddr),
|
||||
udpProxies: make(map[int]proxy.Proxy),
|
||||
iptables: chain,
|
||||
defaultIp: config.DefaultIp,
|
||||
if err := mapper.setup(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mapper, nil
|
||||
}
|
||||
@@ -319,13 +336,20 @@ func newPortMapper(config *DaemonConfig) (*PortMapper, error) {
|
||||
type PortAllocator struct {
|
||||
sync.Mutex
|
||||
inUse map[int]struct{}
|
||||
fountain chan (int)
|
||||
fountain chan int
|
||||
quit chan bool
|
||||
}
|
||||
|
||||
func (alloc *PortAllocator) runFountain() {
|
||||
for {
|
||||
for port := portRangeStart; port < portRangeEnd; port++ {
|
||||
alloc.fountain <- port
|
||||
select {
|
||||
case alloc.fountain <- port:
|
||||
case quit := <-alloc.quit:
|
||||
if quit {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,10 +383,18 @@ func (alloc *PortAllocator) Acquire(port int) (int, error) {
|
||||
return port, nil
|
||||
}
|
||||
|
||||
func (alloc *PortAllocator) Close() error {
|
||||
alloc.quit <- true
|
||||
close(alloc.quit)
|
||||
close(alloc.fountain)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newPortAllocator() (*PortAllocator, error) {
|
||||
allocator := &PortAllocator{
|
||||
inUse: make(map[int]struct{}),
|
||||
fountain: make(chan int),
|
||||
quit: make(chan bool),
|
||||
}
|
||||
go allocator.runFountain()
|
||||
return allocator, nil
|
||||
@@ -374,6 +406,7 @@ type IPAllocator struct {
|
||||
queueAlloc chan allocatedIP
|
||||
queueReleased chan net.IP
|
||||
inUse map[int32]struct{}
|
||||
quit chan bool
|
||||
}
|
||||
|
||||
type allocatedIP struct {
|
||||
@@ -418,6 +451,10 @@ func (alloc *IPAllocator) run() {
|
||||
}
|
||||
|
||||
select {
|
||||
case quit := <-alloc.quit:
|
||||
if quit {
|
||||
return
|
||||
}
|
||||
case alloc.queueAlloc <- ip:
|
||||
alloc.inUse[newNum] = struct{}{}
|
||||
case released := <-alloc.queueReleased:
|
||||
@@ -450,12 +487,21 @@ func (alloc *IPAllocator) Release(ip net.IP) {
|
||||
alloc.queueReleased <- ip
|
||||
}
|
||||
|
||||
func (alloc *IPAllocator) Close() error {
|
||||
alloc.quit <- true
|
||||
close(alloc.quit)
|
||||
close(alloc.queueAlloc)
|
||||
close(alloc.queueReleased)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newIPAllocator(network *net.IPNet) *IPAllocator {
|
||||
alloc := &IPAllocator{
|
||||
network: network,
|
||||
queueAlloc: make(chan allocatedIP),
|
||||
queueReleased: make(chan net.IP),
|
||||
inUse: make(map[int32]struct{}),
|
||||
quit: make(chan bool),
|
||||
}
|
||||
|
||||
go alloc.run()
|
||||
@@ -473,56 +519,40 @@ type NetworkInterface struct {
|
||||
disabled bool
|
||||
}
|
||||
|
||||
// Allocate an external port and map it to the interface
|
||||
func (iface *NetworkInterface) AllocatePort(port Port, binding PortBinding) (*Nat, error) {
|
||||
// Allocate an external TCP port and map it to the interface
|
||||
func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) {
|
||||
|
||||
if iface.disabled {
|
||||
return nil, fmt.Errorf("Trying to allocate port for interface %v, which is disabled", iface) // FIXME
|
||||
}
|
||||
|
||||
ip := iface.manager.portMapper.defaultIp
|
||||
|
||||
if binding.HostIp != "" {
|
||||
ip = net.ParseIP(binding.HostIp)
|
||||
} else {
|
||||
binding.HostIp = ip.String()
|
||||
}
|
||||
|
||||
nat := &Nat{
|
||||
Port: port,
|
||||
Binding: binding,
|
||||
}
|
||||
|
||||
containerPort, err := parsePort(port.Port())
|
||||
nat, err := parseNat(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostPort, _ := parsePort(nat.Binding.HostPort)
|
||||
|
||||
if nat.Port.Proto() == "tcp" {
|
||||
extPort, err := iface.manager.tcpPortAllocator.Acquire(hostPort)
|
||||
if nat.Proto == "tcp" {
|
||||
extPort, err := iface.manager.tcpPortAllocator.Acquire(nat.Frontend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backend := &net.TCPAddr{IP: iface.IPNet.IP, Port: containerPort}
|
||||
if err := iface.manager.portMapper.Map(ip, extPort, backend); err != nil {
|
||||
backend := &net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}
|
||||
if err := iface.manager.portMapper.Map(extPort, backend); err != nil {
|
||||
iface.manager.tcpPortAllocator.Release(extPort)
|
||||
return nil, err
|
||||
}
|
||||
nat.Binding.HostPort = strconv.Itoa(extPort)
|
||||
nat.Frontend = extPort
|
||||
} else {
|
||||
extPort, err := iface.manager.udpPortAllocator.Acquire(hostPort)
|
||||
extPort, err := iface.manager.udpPortAllocator.Acquire(nat.Frontend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
backend := &net.UDPAddr{IP: iface.IPNet.IP, Port: containerPort}
|
||||
if err := iface.manager.portMapper.Map(ip, extPort, backend); err != nil {
|
||||
backend := &net.UDPAddr{IP: iface.IPNet.IP, Port: nat.Backend}
|
||||
if err := iface.manager.portMapper.Map(extPort, backend); err != nil {
|
||||
iface.manager.udpPortAllocator.Release(extPort)
|
||||
return nil, err
|
||||
}
|
||||
nat.Binding.HostPort = strconv.Itoa(extPort)
|
||||
nat.Frontend = extPort
|
||||
}
|
||||
iface.extPorts = append(iface.extPorts, nat)
|
||||
|
||||
@@ -530,37 +560,83 @@ func (iface *NetworkInterface) AllocatePort(port Port, binding PortBinding) (*Na
|
||||
}
|
||||
|
||||
type Nat struct {
|
||||
Port Port
|
||||
Binding PortBinding
|
||||
Proto string
|
||||
Frontend int
|
||||
Backend int
|
||||
}
|
||||
|
||||
func (n *Nat) String() string {
|
||||
return fmt.Sprintf("%s:%d:%d/%s", n.Binding.HostIp, n.Binding.HostPort, n.Port.Port(), n.Port.Proto())
|
||||
func parseNat(spec string) (*Nat, error) {
|
||||
var nat Nat
|
||||
|
||||
if strings.Contains(spec, "/") {
|
||||
specParts := strings.Split(spec, "/")
|
||||
if len(specParts) != 2 {
|
||||
return nil, fmt.Errorf("Invalid port format.")
|
||||
}
|
||||
proto := specParts[1]
|
||||
spec = specParts[0]
|
||||
if proto != "tcp" && proto != "udp" {
|
||||
return nil, fmt.Errorf("Invalid port format: unknown protocol %v.", proto)
|
||||
}
|
||||
nat.Proto = proto
|
||||
} else {
|
||||
nat.Proto = "tcp"
|
||||
}
|
||||
|
||||
if strings.Contains(spec, ":") {
|
||||
specParts := strings.Split(spec, ":")
|
||||
if len(specParts) != 2 {
|
||||
return nil, fmt.Errorf("Invalid port format.")
|
||||
}
|
||||
// If spec starts with ':', external and internal ports must be the same.
|
||||
// This might fail if the requested external port is not available.
|
||||
var sameFrontend bool
|
||||
if len(specParts[0]) == 0 {
|
||||
sameFrontend = true
|
||||
} else {
|
||||
front, err := strconv.ParseUint(specParts[0], 10, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nat.Frontend = int(front)
|
||||
}
|
||||
back, err := strconv.ParseUint(specParts[1], 10, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nat.Backend = int(back)
|
||||
if sameFrontend {
|
||||
nat.Frontend = nat.Backend
|
||||
}
|
||||
} else {
|
||||
port, err := strconv.ParseUint(spec, 10, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nat.Backend = int(port)
|
||||
}
|
||||
|
||||
return &nat, nil
|
||||
}
|
||||
|
||||
// Release: Network cleanup - release all resources
|
||||
func (iface *NetworkInterface) Release() {
|
||||
|
||||
if iface.disabled {
|
||||
return
|
||||
}
|
||||
|
||||
for _, nat := range iface.extPorts {
|
||||
hostPort, err := parsePort(nat.Binding.HostPort)
|
||||
if err != nil {
|
||||
log.Printf("Unable to get host port: %s", err)
|
||||
continue
|
||||
utils.Debugf("Unmaping %v/%v", nat.Proto, nat.Frontend)
|
||||
if err := iface.manager.portMapper.Unmap(nat.Frontend, nat.Proto); err != nil {
|
||||
log.Printf("Unable to unmap port %v/%v: %v", nat.Proto, nat.Frontend, err)
|
||||
}
|
||||
ip := net.ParseIP(nat.Binding.HostIp)
|
||||
utils.Debugf("Unmaping %s/%s", nat.Port.Proto, nat.Binding.HostPort)
|
||||
if err := iface.manager.portMapper.Unmap(ip, hostPort, nat.Port.Proto()); err != nil {
|
||||
log.Printf("Unable to unmap port %s: %s", nat, err)
|
||||
}
|
||||
if nat.Port.Proto() == "tcp" {
|
||||
if err := iface.manager.tcpPortAllocator.Release(hostPort); err != nil {
|
||||
log.Printf("Unable to release port %s", nat)
|
||||
if nat.Proto == "tcp" {
|
||||
if err := iface.manager.tcpPortAllocator.Release(nat.Frontend); err != nil {
|
||||
log.Printf("Unable to release port tcp/%v: %v", nat.Frontend, err)
|
||||
}
|
||||
} else if err := iface.manager.udpPortAllocator.Release(hostPort); err != nil {
|
||||
log.Printf("Unable to release port %s: %s", nat, err)
|
||||
} else if err := iface.manager.udpPortAllocator.Release(nat.Frontend); err != nil {
|
||||
log.Printf("Unable to release port udp/%v: %v", nat.Frontend, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,22 +691,35 @@ func (manager *NetworkManager) Allocate() (*NetworkInterface, error) {
|
||||
return iface, nil
|
||||
}
|
||||
|
||||
func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) {
|
||||
func (manager *NetworkManager) Close() error {
|
||||
err1 := manager.tcpPortAllocator.Close()
|
||||
err2 := manager.udpPortAllocator.Close()
|
||||
err3 := manager.ipAllocator.Close()
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return err3
|
||||
}
|
||||
|
||||
if config.BridgeIface == DisableNetworkBridge {
|
||||
func newNetworkManager(bridgeIface string) (*NetworkManager, error) {
|
||||
|
||||
if bridgeIface == DisableNetworkBridge {
|
||||
manager := &NetworkManager{
|
||||
disabled: true,
|
||||
}
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
addr, err := getIfaceAddr(config.BridgeIface)
|
||||
addr, err := getIfaceAddr(bridgeIface)
|
||||
if err != nil {
|
||||
// If the iface is not found, try to create it
|
||||
if err := CreateBridgeIface(config); err != nil {
|
||||
if err := CreateBridgeIface(bridgeIface); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr, err = getIfaceAddr(config.BridgeIface)
|
||||
addr, err = getIfaceAddr(bridgeIface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -648,18 +737,19 @@ func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
portMapper, err := newPortMapper(config)
|
||||
portMapper, err := newPortMapper()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
manager := &NetworkManager{
|
||||
bridgeIface: config.BridgeIface,
|
||||
bridgeIface: bridgeIface,
|
||||
bridgeNetwork: network,
|
||||
ipAllocator: ipAllocator,
|
||||
tcpPortAllocator: tcpPortAllocator,
|
||||
udpPortAllocator: udpPortAllocator,
|
||||
portMapper: portMapper,
|
||||
}
|
||||
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user