Compare commits

..

151 Commits

Author SHA1 Message Date
Michael Crosby e8da76605f Bump version to 0.6.4 2013-10-16 17:50:08 -07:00
Michael Crosby f16e60665b Merge branch 'master' into bump_0.6.4 2013-10-16 17:48:17 -07:00
Michael Crosby 3ba9893786 Merge pull request #2242 from jpetazzo/remove-spurious-errors-and-errclosedpipe
Fix error/debug messages in Container.Attach and recover from ErrClosedPipe
2013-10-16 17:27:23 -07:00
Guillaume J. Charmes 6ee2964a4f Merge pull request #2256 from dotcloud/discard_errno_0_errors
- Runtime: discard errno = 0 errors
2013-10-16 16:22:03 -07:00
Victor Vieux 49c62879b8 discard errno = 0 errors 2013-10-16 23:05:50 +00:00
Michael Crosby 34745ee0f4 Merge pull request #2246 from dotcloud/clean_when_start_fail
Add cleanup when container.Start fails
2013-10-16 15:06:08 -07:00
Victor Vieux d21bd5b964 Merge pull request #2251 from dotcloud/skip_autoremove_test
skip auto-remove test for now
2013-10-16 14:59:08 -07:00
Michael Crosby 82220ea2ad Merge pull request #2249 from jpetazzo/cleanup-errclosing-in-proxy
Catch errClosing error when TCP and UDP proxies are terminated.
2013-10-16 14:51:43 -07:00
Victor Vieux 655b16c712 skip auto-remove test for now 2013-10-16 21:47:20 +00:00
Michael Crosby eb2d0d4bf5 Merge pull request #2250 from dotcloud/revert-07-doc-changes
Revert "Fix #2115 Clean AUFS references. Also fixes some links to client...
2013-10-16 14:44:53 -07:00
Jérôme Petazzoni cc851dbb3f Catch errClosing error when TCP and UDP proxies are terminated. 2013-10-16 14:43:20 -07:00
Victor Vieux e40fd1e3e1 fix panic 2013-10-16 21:39:03 +00:00
Michael Crosby dab84f7e8b Revert "Fix #2115 Clean AUFS references. Also fixes some links to client libraries and mongo example."
This reverts commit f4c17a202b.
2013-10-16 14:38:24 -07:00
Daniel Mizyrycki 7df8ec2093 Merge pull request #2195 from mzdaniel/report
Add aggregated docker-ci email report
2013-10-16 14:29:24 -07:00
Daniel Mizyrycki a2ffa637ce testing infrastructure, PR #2195: Add aggregated docker-ci email report 2013-10-16 13:30:15 -07:00
Guillaume J. Charmes ed6ca109bf Refactor container.Start() 2013-10-16 13:12:56 -07:00
Victor Vieux 6e48b73955 Merge pull request #2247 from dotcloud/improve_tests_error_messages
Tests: Remove a few errors
2013-10-16 12:50:15 -07:00
Victor Vieux 664acd2971 Add clean if start fail 2013-10-16 19:48:24 +00:00
Victor Vieux 3acfc60028 change an errorf to a debugf 2013-10-16 19:45:10 +00:00
Victor Vieux 0e64a4d7e7 remove false alarm error 2013-10-16 19:38:12 +00:00
Victor Vieux 104a86936a api.go:108 Error parsing media type: error: mime: no media type
fix
2013-10-16 19:32:03 +00:00
Victor Vieux df697b4318 use httpError in a separate test 2013-10-16 19:23:47 +00:00
Tianon Gravi 27fc01271e Merge pull request #2219 from tianon/completion
Reorganize contributed completion scripts to add zsh completion
2013-10-16 11:40:56 -07:00
Tianon Gravi 97ab118fbb Merge pull request #2220 from tianon/vim
Add vim syntax highlighting for Dockerfiles from @honza
2013-10-16 11:40:42 -07:00
Jérôme Petazzoni fc659b68e4 Fix error/debug messages in Container.Attach and recover from ErrClosedPipe conditions. 2013-10-16 11:35:45 -07:00
Guillaume J. Charmes 99f6b43f4f Merge pull request #2243 from jpetazzo/comments-stdcopy
Add better comments to utils/stdcopy.go.
2013-10-16 11:17:15 -07:00
Victor Vieux 2b92aa71f9 Merge pull request #2244 from dotcloud/add_cleanup_leftover_container_tests
Tests: add cleanup to remove leftover containers
2013-10-16 11:09:48 -07:00
Victor Vieux f854529ae8 add cleanupin tests to remove leftover containers 2013-10-16 18:06:37 +00:00
Jérôme Petazzoni e32debcf5f Add better comments to utils/stdcopy.go. 2013-10-16 10:58:53 -07:00
Andy Rothfusz 0e08e1d695 Merge pull request #2236 from SvenDowideit/2209-comments-doc
#2209 - add the example @tianon gave to elaborate on the Comment docume...
2013-10-16 10:51:15 -07:00
Andy Rothfusz 65b62307c3 Merge pull request #2231 from metalivedev/1919-docreleaseinfo
Fix #1919 document how to edit and release docs.
2013-10-16 10:45:01 -07:00
Andy Rothfusz 03e5d94b12 Merge pull request #2090 from kencochrane/master
added initial draft of the Docker infrastructure doc
2013-10-16 10:44:22 -07:00
Tianon Gravi 0ca533ca35 Merge pull request #2233 from tmc/master
Add mkimage-arch.sh
2013-10-15 19:27:45 -07:00
Travis Cline d94a027da6 Add mkimage-arch.sh 2013-10-16 02:26:15 +00:00
Sven Dowideit 7250f94874 #2209 - add the example @tianon gave to elaborate on the Comment documentation 2013-10-16 11:12:47 +10:00
Michael Crosby 4196c704f0 Merge pull request #1943 from dotcloud/1940-prevent_crash_parseNat_build-fix
Abort build if mergeConfig returns an error and fix duplicate error message
2013-10-15 17:40:48 -07:00
Victor Vieux adb48ef319 Merge pull request #2235 from dotcloud/update_dockerfile
Update Dockerfile test comment
2013-10-15 17:32:48 -07:00
Victor Vieux ee7d9b0bde Update Dockerfile test comment 2013-10-16 00:31:34 +00:00
Victor Vieux 2f4526d5c4 Merge pull request #2221 from flavio/fix_broken_tests
Fixed broken tests
2013-10-15 17:26:19 -07:00
Victor Vieux 261285c2c2 Merge pull request #2232 from shykes/error-should-be-debug
Remove error messages which are not actually errors
2013-10-15 17:12:51 -07:00
Solomon Hykes cbc49d7d76 Remove error messages which are not actually errors 2013-10-15 23:12:27 +00:00
Solomon Hykes 4dc90405e3 Merge pull request #2230 from shykes/hack-select-test
hack: only run certain tests with TESTFLAGS='-run TestName' make.sh
2013-10-15 15:58:54 -07:00
Solomon Hykes b7abe9dab5 hack: only run certain tests with TESTFLAGS='-run TestName' make.sh 2013-10-15 22:57:40 +00:00
Andy Rothfusz cd455ca6fa Fix #1919 document how to edit and release docs. 2013-10-15 15:52:21 -07:00
Flavio Castelli ca56b35e53 Fixed broken tests
Some tests failed because the `auplink` binary was not found inside of the
"docker" container.
2013-10-15 11:44:56 +02:00
Tianon Gravi ace059e409 Add vim syntax highlighting for Dockerfiles from @honza
See https://github.com/honza/dockerfile.vim
See also https://twitter.com/_honza/status/390046317579620352
2013-10-15 03:35:13 -06:00
Tianon Gravi 9da2b3590e Add zsh completion from @felixr with permission
See https://github.com/felixr/docker-zsh-completion for the original.
See also https://github.com/dotcloud/docker/issues/2202#issuecomment-26313287

Fixes #2202
2013-10-15 03:04:06 -06:00
Tianon Gravi e99ab1df97 Move contrib/docker.bash to contrib/completion/bash/docker for better organization 2013-10-15 03:02:31 -06:00
Solomon Hykes fde157425c Merge pull request #2218 from tianon/ksid
Remove @KSid from contrib maintainers for now
2013-10-15 00:13:58 -07:00
Tianon Gravi a0e5657cb6 Remove @KSid from contrib maintainers for now
His availability has been very limited, so I'll take over completely for the short term until he can find more time to help out.
2013-10-15 00:58:34 -06:00
Daniel Mizyrycki f4a98b4598 Merge pull request #2211 from mzdaniel/2190-docker-ci
Prevent docker-ci to test closing PRs
2013-10-14 14:12:23 -07:00
Daniel Mizyrycki 53a07d5475 Prevent docker-ci to test closing PRs 2013-10-14 14:00:06 -07:00
Andy Rothfusz 93b258413f Merge pull request #2160 from iffy/patch-1
Minor updates to postgresql_service.rst
2013-10-14 11:59:57 -07:00
Andy Rothfusz c0bfb75e5f Merge pull request #2142 from SvenDowideit/patch-1
tell the new developer how they can replace the real docker binary with ...
2013-10-14 11:55:42 -07:00
Daniel Mizyrycki 620bc5b9e1 Merge pull request #1823 from dotcloud/1766-nightly-release
testing, issue #1766: Add nightly release to docker-ci
2013-10-11 17:38:06 -07:00
Daniel Mizyrycki 6d6097cddf Merge pull request #1813 from doubleyou/patch-1
Improve network performance for VirtualBox
2013-10-11 16:23:19 -07:00
Daniel Mizyrycki 42277d0329 Merge pull request #2178 from tianon/vagrant-docker-group
Add vagrant user to the docker group
2013-10-11 15:10:50 -07:00
Tianon Gravi d62df6b2b5 Add vagrant user to the docker group 2013-10-11 11:00:04 -06:00
Michael Crosby 97779ff7cf Merge pull request #2118 from karlgrz/936-enhance-logging
Add utils.Errorf for error logging
2013-10-11 08:51:37 -07:00
Michael Crosby 9afa48a92f Merge pull request #2169 from dotcloud/remove-deprecated-packaging
Remove deprecated packaging directory
2013-10-11 08:44:34 -07:00
Karl Grzeszczak ad723bbfe7 Initial steps to fix Issue #936
Use utils.Errorf instead of utils.Debugf
2013-10-11 08:04:40 -05:00
Solomon Hykes f15cd71bb8 Merge pull request #2037 from tianon/install-script-revamp
Revamp install.sh to be usable by more people, and to use official install methods whenever possible (apt repo, portage tree, etc.)
2013-10-10 23:56:13 -07:00
Tianon Gravi 94bf5b0011 Revamp install.sh to be usable by more people, and to use official install methods whenever possible (apt repo, portage tree, etc.), thus making it an official script and moving it to hack/ 2013-10-11 00:53:15 -06:00
Daniel Mizyrycki d542318d63 testing infrastructure, issue #1766: Dockerize deployment of docker-ci 2013-10-10 18:29:10 -07:00
Daniel Mizyrycki 6e3068700f testing infrastructure, issue #1766: Temporarily install docker with proper apparmor handling 2013-10-10 18:29:10 -07:00
Daniel Mizyrycki fac190959f testing, issue #1766: Upgrade tooling for Docker nightly release 2013-10-10 18:29:10 -07:00
Daniel Mizyrycki 1dcdc3deb7 testing, issue #1766: Add nightly release to docker-ci 2013-10-10 18:29:10 -07:00
Solomon Hykes ee256ce6bf Merge pull request #2038 from tianon/mkimage-debian-fixes
Fix contrib/mkimage-debian.sh apt caching prevention
2013-10-10 17:59:17 -07:00
Solomon Hykes 5fe699f813 Merge pull request #2166 from pnasrat/document-approval
Clarify LGTM process to contributors.
2013-10-10 17:55:32 -07:00
Solomon Hykes 7561e2570c Merge pull request #2159 from nathankleyn/fix-references-to-param-names
Small fixes to parameter names in docs for ADD command.
2013-10-10 17:53:57 -07:00
Guillaume J. Charmes 640ca68e4f Merge pull request #2128 from titanous/update-authors
Update AUTHORS
2013-10-10 16:40:33 -07:00
Guillaume J. Charmes f9b9dfc82d Merge pull request #2164 from pnasrat/add-finished-time
* Runtime: Record termination time in state.
2013-10-10 16:29:07 -07:00
Guillaume J. Charmes c978f0fa5c Merge pull request #2165 from jrydberg/master
- Registry: Use correct auth config when logging in.
2013-10-10 16:28:46 -07:00
Sven Dowideit d91bf33df3 tell the new developer how they can replace the real docker binary with the one they've been hacking on (#2142) 2013-10-11 08:47:32 +10:00
Paul Nasrat 310a174260 Clarify LGTM process to contributors. 2013-10-10 18:32:33 -04:00
Paul Nasrat 2eb404ab14 Record termination time in state.
Supercedes pull #1946
2013-10-10 14:47:25 -04:00
Ken Cochrane 60e426938e Merge pull request #2155 from jdubois/patch-1
- Documentation: Corrected error in the package name
2013-10-10 11:19:22 -07:00
Matt Haggard 9041d6a47b Minor updates to postgresql_service.rst
I've added the steps that were missing/wrong for me when I went through this just now.
2013-10-10 10:23:24 -06:00
Nathan Kleyn 8abd328f53 Small fixes to parameter names in docs for ADD command. 2013-10-10 12:14:18 +01:00
Solomon Hykes ae780fb5ea Remove deprecated packaging directory (distro-specific packaging is now managed downstream by their respective package maintainers) 2013-10-10 03:54:10 -07:00
Julien Dubois 152037f011 Corrected error in the package name 2013-10-10 10:10:33 +02:00
Andy Rothfusz 0367032bd1 Merge pull request #2144 from shamrin/patch-5
document what `vagrant up` is actually doing
2013-10-09 17:42:37 -07:00
Andy Rothfusz edca9f7e9c Merge pull request #2139 from metalivedev/2115-AUFScleanup
Fix #2115 Clean AUFS references.
2013-10-09 17:35:05 -07:00
Guillaume J. Charmes e19cc1e843 Merge pull request #2125 from dotcloud/2099_rm-v_fix
- Runtime: fix `docker rm` with volumes
2013-10-09 16:01:51 -07:00
Guillaume J. Charmes feabce3bcc Merge pull request #2119 from SvenDowideit/2117-hardcoded-tmp-dir
use empty string so TempDir uses the OS's temp dir automatically (Closes...
2013-10-09 16:00:40 -07:00
Michael Crosby ded74b0bb3 Merge pull request #2133 from dotcloud/improve_tests
Make sure to close the network allocators
2013-10-09 15:45:16 -07:00
Guillaume J. Charmes ffee5faae8 Merge pull request #2145 from dotcloud/remove-panic-test
* Runtime: replace panic by log.Fatal in tests
2013-10-09 15:37:08 -07:00
Andy Rothfusz c1bb33ea4d Merge pull request #2114 from metalivedev/2113-searchbump
Fix #2113 improve doc search results
2013-10-09 12:24:33 -07:00
Andy Rothfusz 6545e53803 Clean up javascript comment. 2013-10-09 12:22:14 -07:00
Victor Vieux d30806590c Merge pull request #2127 from titanous/close-body
Fix some error cases where a HTTP body might not be closed
2013-10-09 06:52:03 -07:00
Victor Vieux 823174de4d replace panic by log.Fatal in tests 2013-10-09 13:47:49 +00:00
Tianon Gravi d5f9160441 Merge pull request #1956 from tianon/bash-completion
Add proper bash completion for "docker push"
2013-10-09 05:47:55 -07:00
Alexey Shamrin 37df1d92d8 document what vagrant up is actually doing
* don't mention outdated PPA that Vagrantfile doesn't use
* point to official Ubuntu installation instructions that Vagrant appears to be following; should be future proof
2013-10-09 15:05:09 +04:00
Jonathan Rudenberg dabac81824 Update AUTHORS 2013-10-08 23:51:38 -04:00
Guillaume J. Charmes bb62c281f5 Merge pull request #2130 from tianon/swap-contrib-maintainers
Swap contrib MAINTAINERS
2013-10-08 17:57:52 -07:00
Andy Rothfusz 3565bf8d00 Merge pull request #2134 from tianon/contributing
Add devenvironment link to CONTRIBUTING.md
2013-10-08 17:36:56 -07:00
Andy Rothfusz f4c17a202b Fix #2115 Clean AUFS references. Also fixes some links to client libraries and mongo example. 2013-10-08 17:33:19 -07:00
Tianon Gravi 9a4b0b9823 Add devenvironment link to CONTRIBUTING.md 2013-10-08 16:48:19 -06:00
Guillaume J. Charmes 9107565d06 Make sure to close the network allocators 2013-10-08 15:42:02 -07:00
Tianon Gravi 4fda3314d8 Swap contrib MAINTAINERS 2013-10-08 15:15:58 -06:00
Jonathan Rudenberg e906485b07 Fix some error cases where a HTTP body might not be closed
Refs #2126
2013-10-08 15:35:00 -04:00
Victor Vieux 1daf242c8b fix rm -v 2013-10-08 16:35:47 +00:00
Johan Rydberg 967010ae8c Use correct auth config when logging in. 2013-10-08 06:46:29 +02:00
Sven Dowideit d03022b9ca use empty string so TempDir uses the OS's temp dir automatically (Closes #2117) 2013-10-08 13:24:20 +10:00
Andy Rothfusz 6d67ac2a1b Insert popularity and boost docs results. 2013-10-07 17:51:07 -07:00
Solomon Hykes 2d425af1b1 Merge pull request #2103 from tianon/clean-out-fixme
Clean out a few outdated FIXME items
2013-10-07 15:19:01 -07:00
Andy Rothfusz ed19693f50 Merge pull request #2106 from mastahyeti/api_1.5_docs_whitespace
Cleanup whitespace in API 1.5 docs
2013-10-07 13:23:55 -07:00
Tianon Gravi ee27444098 Fix contrib/mkimage-debian.sh apt caching prevention 2013-10-07 13:41:10 -06:00
Andy Rothfusz 194c1dbbd8 Merge pull request #2109 from shamrin/patch-4
[docs] use angle brackets in MAINTAINER example email
2013-10-07 12:18:44 -07:00
Alexey Shamrin a9eac7eab4 [docs] use angle brackets in MAINTAINER example email 2013-10-07 20:29:54 +04:00
Ben Toews 7c28683f08 Cleanup whitespace in API 1.5 docs
remove whitespace on blank lines
2013-10-06 19:52:18 -06:00
Daniel Mizyrycki c18e849451 Merge pull request #2061 from dotcloud/1948-test-detach
testing, issue #1948: Increase TestRunDetach timeout
2013-10-06 13:26:44 -07:00
Tianon Gravi a665517151 Clean out a few outdated FIXME items 2013-10-06 13:55:26 -06:00
Ken Cochrane 0d3c899528 added initial draft of the Docker infrastructure doc 2013-10-04 15:12:40 -04:00
Andy Rothfusz 863eebe7bd Merge pull request #2084 from makeusabrew/patch-1
Fix help text for -v option
2013-10-04 09:29:56 -07:00
Nick Payne 3c5350ba92 Fix help text for -v option 2013-10-04 10:26:57 +01:00
Tianon Gravi c52c95f97f Merge pull request #1876 from asbjornenge/master
Added Dockerfile.tmLanguage to contrib
2013-10-03 23:52:37 -07:00
Asbjørn Enge 85a5139665 Added Dockerfile.tmLanguage to contrib 2013-10-04 08:45:15 +02:00
Michael Crosby ad4b09cdb8 Merge pull request #1832 from dotcloud/26-default_r_flag
Autorestart containers by default
2013-10-03 15:59:58 -07:00
Victor Vieux e594c788e4 keep -r, but default true 2013-10-03 22:42:35 +00:00
Victor Vieux f84dc1e908 add missing error check 2013-10-03 22:33:00 +00:00
Michael Crosby 826aaa0e9b Merge pull request #1990 from modcloth-labs/more-tests-for-auth-registry-resolution
Adding more tests around `auth.ResolveAuthConfig`
2013-10-03 14:45:02 -07:00
Victor Vieux 1328be7c29 Add test 2013-10-03 15:21:14 +00:00
Andy Rothfusz ff4f32d58f Fix #2017 Merge branch '2017-api-details' 2013-10-02 18:20:44 -07:00
Andy Rothfusz 462ac1efcf Merge branch 'hint-api-details' of git://github.com/cdunklau/docker into 2017-api-details
Conflicts:
	docs/sources/api/docker_remote_api.rst
2013-10-02 18:18:46 -07:00
Solomon Hykes 1332f400f7 Merge pull request #2015 from dsissitka/1927
Configured FPM to make /etc/init/docker.conf a config file.
2013-10-02 18:18:16 -07:00
Solomon Hykes a292c04ddb Merge pull request #2069 from philips/add-xz-to-runtime-dependencies
fix(hack/PACKAGERS): add xz utils as a runtime dep
2013-10-02 17:37:56 -07:00
Andy Rothfusz 37c4513cf9 Merge pull request #2060 from mastahyeti/patch-1
Add `apt-get install curl` to Ubuntu docs
2013-10-02 16:46:00 -07:00
Andy Rothfusz b584c21ca4 Merge pull request #2063 from dhrp/doc-template-fix
Doc template fix
2013-10-02 16:31:47 -07:00
Andy Rothfusz c92507341d Merge pull request #2073 from tianon/gentoo-no-more-1422-hack
Remove Gentoo install notes about #1422 workaround
2013-10-02 16:24:11 -07:00
Andy Rothfusz 9f0feaa2e1 Merge pull request #2065 from kyleconroy/fix_ping_endpoint
Fix Ping endpoint documentation
2013-10-02 16:22:49 -07:00
Ben Toews 0f830aa431 Update ubuntu docs to use wget instead of curl
Ubuntu doesn't come with curl.

wget instead of curl

nicer wget syntax

remove /dev/null
2013-10-02 16:27:34 -06:00
Tianon Gravi 5f05cb4817 Remove Gentoo install notes about #1422 workaround 2013-10-02 16:06:22 -06:00
Michael Crosby 4778039dd2 Merge pull request #2066 from tianon/vendor-bump-kr-pty
Bump vendor kr/pty to commit 3b1f6487b (syscall.O_NOCTTY)
2013-10-02 14:44:07 -07:00
Guillaume J. Charmes b180770b1d Merge pull request #2014 from alexlarsson/allow_set_file_cap
* Runtime: lxc: Allow set_file_cap capability in container
2013-10-02 14:38:50 -07:00
Brandon Philips 4f5c2cbccc fix(hack/PACKAGERS): add xz utils as a runtime dep 2013-10-02 12:06:19 -07:00
Tianon Gravi cb21a40490 Bump vendor kr/pty to commit 3b1f6487b (syscall.O_NOCTTY) 2013-10-01 19:48:50 -06:00
Thatcher Peskens 5025b4d094 Cleanup 2013-10-01 16:32:56 -07:00
Thatcher Peskens 509e25cf04 Add/modified the canonical link to work with all /index/ links. Added style for "danger' 2013-10-01 16:26:14 -07:00
Daniel Mizyrycki 135c1fce90 testing, issue #1948: Increase TestAttachDetach and TestRunDetach timeout 2013-10-01 15:59:52 -07:00
Kyle Conroy d517fd3d77 Fix Ping endpoint documentation 2013-10-01 15:49:32 -07:00
Andy Rothfusz 3894add8a9 Merge pull request #2034 from metalivedev/1969-cherrypick-fixcommitformat
Fix #1969 formatting, add information about multiline json
2013-09-27 18:19:55 -07:00
Colin Dunklau edd90d46c7 Clearly point at detailed docs 2013-09-27 08:32:55 -05:00
David Sissitka c22e377a6d Used tabs for alignment when I should've used spaces. Fixed. 2013-09-26 13:56:59 -07:00
Colin Dunklau 3f0d0075f8 Hint for Remote API doc details in header links 2013-09-26 15:30:26 -05:00
David Sissitka f8c9f11f06 Configured FPM to make /etc/init/docker.conf a config file. 2013-09-26 12:52:43 -07:00
Alexander Larsson 80319add55 lxc: Allow set_file_cap capability in container
This means you're able to set the bits for capabilities on files
inside the container. This is needed for e.g. many fedora packages
as they use finegrained capabilities rather than setuid binaries.

This is safe as we're not adding capabilities really, since the
container is already allowed to create setuid binaries. Setuid
binaries are strictly more powerful that any capabilities (as root implies
all capabilities).

This doesn't mean the container can *gain* capabilities that it
doesn't already have though. The actual set of caps are strictly
decreasing.
2013-09-26 21:41:45 +02:00
Dan Buch edde4f55e0 Adding more tests around auth.ResolveAuthConfig
mostly because I've been failing at getting docker `0.6.2` working with
docker-registry `0.6.0` with regard to login and push.  I suspect there
may be some mismatched expectations between `auth.ResolveAuthConfig` and
`registry.ResolveRepositoryName`, but I haven't done enough research or
experimentation to think it's anything more than user error.  More tests
are good, no?
2013-09-24 12:36:50 -04:00
Tianon Gravi e37bb50628 Add proper bash completion for "docker push" 2013-09-20 21:22:51 -06:00
Victor Vieux 5bd0437eed abord build if mergeConfig returns an error and fix duplicate error message 2013-09-20 12:46:24 +00:00
Dmitry Demeshchuk e5239b9859 Improve network performance for VirtualBox
Without those lines, VitrualBox machines are being very slow with all network operations (tested only in OS X Lion, but they are supposed to work everywhere). See https://github.com/mitchellh/vagrant/issues/1807
2013-09-05 21:05:23 -07:00
160 changed files with 3807 additions and 7455 deletions
+12
View File
@@ -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>
+67 -1
View File
@@ -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>
+78
View File
@@ -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
View File
@@ -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
View File
@@ -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
+3 -10
View File
@@ -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)
+1 -1
View File
@@ -1 +1 @@
0.6.3-dev
0.6.4
Vendored
+4
View File
@@ -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
+19 -99
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
-17
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+9
View File
@@ -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
View File
@@ -1,2 +1 @@
Kawsar Saiyeed <kawsar.saiyeed@projiris.com> (@KSid)
Tianon Gravi <admwiggin@gmail.com> (@tianon)
+1 -1
View File
@@ -341,7 +341,7 @@ _docker_pull()
_docker_push()
{
return
__docker_image_repos
}
_docker_restart()
+242
View File
@@ -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 "$@"
-61
View File
@@ -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
+65
View File
@@ -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
+22 -5
View File
@@ -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
+22
View File
@@ -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.
+23
View File
@@ -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
+18
View File
@@ -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
+22
View File
@@ -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
View File
@@ -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
View File
@@ -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
+46 -12
View File
@@ -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
+1 -4
View File
@@ -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
-4
View File
@@ -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
-29
View File
@@ -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``.
-29
View File
@@ -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.
-23
View File
@@ -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.
+4 -36
View File
@@ -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.
+1 -1
View File
@@ -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 -2
View File
@@ -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.
+23 -11
View File
@@ -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.
+4 -4
View File
@@ -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.
-13
View File
@@ -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.
+2 -2
View File
@@ -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"
+1 -3
View File
@@ -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.
+12 -6
View File
@@ -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
+28 -6
View File
@@ -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
View File
@@ -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
View File
@@ -594,7 +594,7 @@ dt:hover > a.headerlink {
font-size: larger;
}
&.warning {
&.warning, &.danger {
border-color: #ac0004;
}
-1
View File
@@ -1 +0,0 @@
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
-456
View File
@@ -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
}
-452
View File
@@ -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))
}
}
-27
View File
@@ -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])
}
-29
View File
@@ -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])
}
}
-32
View File
@@ -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
}
+1
View File
@@ -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
+17 -5
View File
@@ -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.
+43 -2
View File
@@ -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.
+43
View File
@@ -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"
+26
View File
@@ -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
+155
View File
@@ -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
+35
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+103
View File
@@ -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?
+136
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1 +0,0 @@
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
-105
View File
@@ -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
}
-18
View File
@@ -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")
}
}
-141
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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