| ... | ... |
@@ -69,12 +69,14 @@ Gabriel Monroy <gabriel@opdemand.com> |
| 69 | 69 |
Gareth Rushgrove <gareth@morethanseven.net> |
| 70 | 70 |
Greg Thornton <xdissent@me.com> |
| 71 | 71 |
Guillaume J. Charmes <guillaume.charmes@dotcloud.com> |
| 72 |
+Gurjeet Singh <gurjeet@singh.im> |
|
| 72 | 73 |
Guruprasad <lgp171188@gmail.com> |
| 73 | 74 |
Harley Laue <losinggeneration@gmail.com> |
| 74 | 75 |
Hector Castro <hectcastro@gmail.com> |
| 75 | 76 |
Hunter Blanks <hunter@twilio.com> |
| 76 | 77 |
Isao Jonas <isao.jonas@gmail.com> |
| 77 | 78 |
James Carr <james.r.carr@gmail.com> |
| 79 |
+James Turnbull <james@lovedthanlost.net> |
|
| 78 | 80 |
Jason McVetta <jason.mcvetta@gmail.com> |
| 79 | 81 |
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com> |
| 80 | 82 |
Jeff Lindsay <progrium@gmail.com> |
| ... | ... |
@@ -140,6 +142,7 @@ odk- <github@odkurzacz.org> |
| 140 | 140 |
Pascal Borreli <pascal@borreli.com> |
| 141 | 141 |
Paul Bowsher <pbowsher@globalpersonals.co.uk> |
| 142 | 142 |
Paul Hammond <paul@paulhammond.org> |
| 143 |
+Paul Nasrat <pnasrat@gmail.com> |
|
| 143 | 144 |
Phil Spitler <pspitler@gmail.com> |
| 144 | 145 |
Piotr Bogdan <ppbogdan@gmail.com> |
| 145 | 146 |
pysqz <randomq@126.com> |
| ... | ... |
@@ -1,514 +1,783 @@ |
| 1 | 1 |
# Changelog |
| 2 | 2 |
|
| 3 |
+## 0.6.6 (2013-11-06) |
|
| 4 |
+ |
|
| 5 |
+#### Runtime |
|
| 6 |
+ |
|
| 7 |
+* Ensure container name on register |
|
| 8 |
+* Fix regression in /etc/hosts |
|
| 9 |
++ Add lock around write operations in graph |
|
| 10 |
+* Check if port is valid |
|
| 11 |
+* Fix restart runtime error with ghost container networking |
|
| 12 |
++ Added some more colors and animals to increase the pool of generated names |
|
| 13 |
+* Fix issues in docker inspect |
|
| 14 |
++ Escape apparmor confinement |
|
| 15 |
++ Set environment variables using a file. |
|
| 16 |
+* Prevent docker insert to erase something |
|
| 17 |
++ Prevent DNS server conflicts in CreateBridgeIface |
|
| 18 |
++ Validate bind mounts on the server side |
|
| 19 |
++ Use parent image config in docker build |
|
| 20 |
+* Fix regression in /etc/hosts |
|
| 21 |
+ |
|
| 22 |
+#### Client |
|
| 23 |
+ |
|
| 24 |
++ Add -P flag to publish all exposed ports |
|
| 25 |
++ Add -notrunc and -q flags to docker history |
|
| 26 |
+* Fix docker commit, tag and import usage |
|
| 27 |
++ Add stars, trusted builds and library flags in docker search |
|
| 28 |
+* Fix docker logs with tty |
|
| 29 |
+ |
|
| 30 |
+#### RemoteAPI |
|
| 31 |
+ |
|
| 32 |
+* Make /events API send headers immediately |
|
| 33 |
+* Do not split last column docker top |
|
| 34 |
++ Add size to history |
|
| 35 |
+ |
|
| 36 |
+#### Other |
|
| 37 |
+ |
|
| 38 |
++ Contrib: Desktop integration. Firefox usecase. |
|
| 39 |
++ Dockerfile: bump to go1.2rc3 |
|
| 40 |
+ |
|
| 3 | 41 |
## 0.6.5 (2013-10-29) |
| 4 |
-+ Runtime: Containers can now be named |
|
| 5 |
-+ Runtime: Containers can now be linked together for service discovery |
|
| 6 |
-+ Runtime: 'run -a', 'start -a' and 'attach' can forward signals to the container for better integration with process supervisors |
|
| 7 |
-+ Runtime: Automatically start crashed containers after a reboot |
|
| 8 |
-+ Runtime: Expose IP, port, and proto as separate environment vars for container links |
|
| 9 |
-* Runtime: Allow ports to be published to specific ips |
|
| 10 |
-* Runtime: Prohibit inter-container communication by default |
|
| 42 |
+ |
|
| 43 |
+#### Runtime |
|
| 44 |
+ |
|
| 45 |
++ Containers can now be named |
|
| 46 |
++ Containers can now be linked together for service discovery |
|
| 47 |
++ 'run -a', 'start -a' and 'attach' can forward signals to the container for better integration with process supervisors |
|
| 48 |
++ Automatically start crashed containers after a reboot |
|
| 49 |
++ Expose IP, port, and proto as separate environment vars for container links |
|
| 50 |
+* Allow ports to be published to specific ips |
|
| 51 |
+* Prohibit inter-container communication by default |
|
| 52 |
+- Ignore ErrClosedPipe for stdin in Container.Attach |
|
| 53 |
+- Remove unused field kernelVersion |
|
| 54 |
+* Fix issue when mounting subdirectories of /mnt in container |
|
| 55 |
+- Fix untag during removal of images |
|
| 56 |
+* Check return value of syscall.Chdir when changing working directory inside dockerinit |
|
| 57 |
+ |
|
| 58 |
+#### Client |
|
| 59 |
+ |
|
| 60 |
+- Only pass stdin to hijack when needed to avoid closed pipe errors |
|
| 61 |
+* Use less reflection in command-line method invocation |
|
| 62 |
+- Monitor the tty size after starting the container, not prior |
|
| 63 |
+- Remove useless os.Exit() calls after log.Fatal |
|
| 64 |
+ |
|
| 65 |
+#### Hack |
|
| 66 |
+ |
|
| 67 |
++ Add initial init scripts library and a safer Ubuntu packaging script that works for Debian |
|
| 68 |
+* Add -p option to invoke debootstrap with http_proxy |
|
| 69 |
+- Update install.sh with $sh_c to get sudo/su for modprobe |
|
| 70 |
+* Update all the mkimage scripts to use --numeric-owner as a tar argument |
|
| 71 |
+* Update hack/release.sh process to automatically invoke hack/make.sh and bail on build and test issues |
|
| 72 |
+ |
|
| 73 |
+#### Other |
|
| 74 |
+ |
|
| 11 | 75 |
* Documentation: Fix the flags for nc in example |
| 12 |
-- Client: Only pass stdin to hijack when needed to avoid closed pipe errors |
|
| 13 | 76 |
* Testing: Remove warnings and prevent mount issues |
| 14 |
-* Client: Use less reflection in command-line method invocation |
|
| 15 |
-- Runtime: Ignore ErrClosedPipe for stdin in Container.Attach |
|
| 16 | 77 |
- Testing: Change logic for tty resize to avoid warning in tests |
| 17 |
-- Client: Monitor the tty size after starting the container, not prior |
|
| 18 |
-- Hack: Update install.sh with $sh_c to get sudo/su for modprobe |
|
| 19 |
-- Client: Remove useless os.Exit() calls after log.Fatal |
|
| 20 |
-* Hack: Update all the mkimage scripts to use --numeric-owner as a tar argument |
|
| 21 |
-* Hack: Update hack/release.sh process to automatically invoke hack/make.sh and bail on build and test issues |
|
| 22 |
-+ Hack: Add initial init scripts library and a safer Ubuntu packaging script that works for Debian |
|
| 23 |
-- Runtime: Fix untag during removal of images |
|
| 24 |
-- Runtime: Remove unused field kernelVersion |
|
| 25 |
-* Hack: Add -p option to invoke debootstrap with http_proxy |
|
| 26 | 78 |
- Builder: Fix race condition in docker build with verbose output |
| 27 | 79 |
- Registry: Fix content-type for PushImageJSONIndex method |
| 28 |
-* Runtime: Fix issue when mounting subdirectories of /mnt in container |
|
| 29 |
-* Runtime: Check return value of syscall.Chdir when changing working directory inside dockerinit |
|
| 30 | 80 |
* Contrib: Improve helper tools to generate debian and Arch linux server images |
| 31 | 81 |
|
| 32 | 82 |
## 0.6.4 (2013-10-16) |
| 33 |
-- Runtime: Add cleanup of container when Start() fails |
|
| 34 |
-- Testing: Catch errClosing error when TCP and UDP proxies are terminated |
|
| 35 |
-- Testing: Add aggregated docker-ci email report |
|
| 36 |
-- Testing: Remove a few errors in tests |
|
| 37 |
-* Contrib: Reorganize contributed completion scripts to add zsh completion |
|
| 38 |
-* Contrib: Add vim syntax highlighting for Dockerfiles from @honza |
|
| 39 |
-* Runtime: Add better comments to utils/stdcopy.go |
|
| 40 |
-- Testing: add cleanup to remove leftover containers |
|
| 41 |
-* Documentation: Document how to edit and release docs |
|
| 42 |
-* Documentation: Add initial draft of the Docker infrastructure doc |
|
| 43 |
-* Contrib: Add mkimage-arch.sh |
|
| 83 |
+ |
|
| 84 |
+#### Runtime |
|
| 85 |
+ |
|
| 86 |
+- Add cleanup of container when Start() fails |
|
| 87 |
+* Add better comments to utils/stdcopy.go |
|
| 88 |
+* Add utils.Errorf for error logging |
|
| 89 |
++ Add -rm to docker run for removing a container on exit |
|
| 90 |
+- Remove error messages which are not actually errors |
|
| 91 |
+- Fix `docker rm` with volumes |
|
| 92 |
+- Fix some error cases where a HTTP body might not be closed |
|
| 93 |
+- Fix panic with wrong dockercfg file |
|
| 94 |
+- Fix the attach behavior with -i |
|
| 95 |
+* Record termination time in state. |
|
| 96 |
+- Use empty string so TempDir uses the OS's temp dir automatically |
|
| 97 |
+- Make sure to close the network allocators |
|
| 98 |
++ Autorestart containers by default |
|
| 99 |
+* Bump vendor kr/pty to commit 3b1f6487b `(syscall.O_NOCTTY)` |
|
| 100 |
+* lxc: Allow set_file_cap capability in container |
|
| 101 |
+- Move run -rm to the cli only |
|
| 102 |
+* Split stdout stderr |
|
| 103 |
+* Always create a new session for the container |
|
| 104 |
+ |
|
| 105 |
+#### Testing |
|
| 106 |
+ |
|
| 107 |
+- Add aggregated docker-ci email report |
|
| 108 |
+- Add cleanup to remove leftover containers |
|
| 109 |
+* Add nightly release to docker-ci |
|
| 110 |
+* Add more tests around auth.ResolveAuthConfig |
|
| 111 |
+- Remove a few errors in tests |
|
| 112 |
+- Catch errClosing error when TCP and UDP proxies are terminated |
|
| 113 |
+* Only run certain tests with TESTFLAGS='-run TestName' make.sh |
|
| 114 |
+* Prevent docker-ci to test closing PRs |
|
| 115 |
+* Replace panic by log.Fatal in tests |
|
| 116 |
+- Increase TestRunDetach timeout |
|
| 117 |
+ |
|
| 118 |
+#### Documentation |
|
| 119 |
+ |
|
| 120 |
+* Add initial draft of the Docker infrastructure doc |
|
| 121 |
+* Add devenvironment link to CONTRIBUTING.md |
|
| 122 |
+* Add `apt-get install curl` to Ubuntu docs |
|
| 123 |
+* Add explanation for export restrictions |
|
| 124 |
+* Add .dockercfg doc |
|
| 125 |
+* Remove Gentoo install notes about #1422 workaround |
|
| 126 |
+* Fix help text for -v option |
|
| 127 |
+* Fix Ping endpoint documentation |
|
| 128 |
+- Fix parameter names in docs for ADD command |
|
| 129 |
+- Fix ironic typo in changelog |
|
| 130 |
+* Various command fixes in postgres example |
|
| 131 |
+* Document how to edit and release docs |
|
| 132 |
+- Minor updates to `postgresql_service.rst` |
|
| 133 |
+* Clarify LGTM process to contributors |
|
| 134 |
+- Corrected error in the package name |
|
| 135 |
+* Document what `vagrant up` is actually doing |
|
| 136 |
++ improve doc search results |
|
| 137 |
+* Cleanup whitespace in API 1.5 docs |
|
| 138 |
+* use angle brackets in MAINTAINER example email |
|
| 139 |
+* Update archlinux.rst |
|
| 140 |
++ Changes to a new style for the docs. Includes version switcher. |
|
| 141 |
+* Formatting, add information about multiline json |
|
| 142 |
+* Improve registry and index REST API documentation |
|
| 143 |
+- Replace deprecated upgrading reference to docker-latest.tgz, which hasn't been updated since 0.5.3 |
|
| 144 |
+* Update Gentoo installation documentation now that we're in the portage tree proper |
|
| 145 |
+* Cleanup and reorganize docs and tooling for contributors and maintainers |
|
| 146 |
+- Minor spelling correction of protocoll -> protocol |
|
| 147 |
+ |
|
| 148 |
+#### Contrib |
|
| 149 |
+ |
|
| 150 |
+* Add vim syntax highlighting for Dockerfiles from @honza |
|
| 151 |
+* Add mkimage-arch.sh |
|
| 152 |
+* Reorganize contributed completion scripts to add zsh completion |
|
| 153 |
+ |
|
| 154 |
+#### Hack |
|
| 155 |
+ |
|
| 156 |
+* Add vagrant user to the docker group |
|
| 157 |
+* Add proper bash completion for "docker push" |
|
| 158 |
+* Add xz utils as a runtime dep |
|
| 159 |
+* Add cleanup/refactor portion of #2010 for hack and Dockerfile updates |
|
| 160 |
++ Add contrib/mkimage-centos.sh back (from #1621), and associated documentation link |
|
| 161 |
+* Add several of the small make.sh fixes from #1920, and make the output more consistent and contributor-friendly |
|
| 162 |
++ Add @tianon to hack/MAINTAINERS |
|
| 163 |
+* Improve network performance for VirtualBox |
|
| 164 |
+* Revamp install.sh to be usable by more people, and to use official install methods whenever possible (apt repo, portage tree, etc.) |
|
| 165 |
+- Fix contrib/mkimage-debian.sh apt caching prevention |
|
| 166 |
++ Added Dockerfile.tmLanguage to contrib |
|
| 167 |
+* Configured FPM to make /etc/init/docker.conf a config file |
|
| 168 |
+* Enable SSH Agent forwarding in Vagrant VM |
|
| 169 |
+* Several small tweaks/fixes for contrib/mkimage-debian.sh |
|
| 170 |
+ |
|
| 171 |
+#### Other |
|
| 172 |
+ |
|
| 44 | 173 |
- Builder: Abort build if mergeConfig returns an error and fix duplicate error message |
| 45 |
-- Runtime: Remove error messages which are not actually errors |
|
| 46 |
-* Testing: Only run certain tests with TESTFLAGS='-run TestName' make.sh |
|
| 47 |
-* Testing: Prevent docker-ci to test closing PRs |
|
| 48 |
-- Documentation: Minor updates to postgresql_service.rst |
|
| 49 |
-* Testing: Add nightly release to docker-ci |
|
| 50 |
-* Hack: Improve network performance for VirtualBox |
|
| 51 |
-* Hack: Add vagrant user to the docker group |
|
| 52 |
-* Runtime: Add utils.Errorf for error logging |
|
| 53 | 174 |
- Packaging: Remove deprecated packaging directory |
| 54 |
-* Hack: Revamp install.sh to be usable by more people, and to use official install methods whenever possible (apt repo, portage tree, etc.) |
|
| 55 |
-- Hack: Fix contrib/mkimage-debian.sh apt caching prevention |
|
| 56 |
-* Documentation: Clarify LGTM process to contributors |
|
| 57 |
-- Documentation: Small fixes to parameter names in docs for ADD command |
|
| 58 |
-* Runtime: Record termination time in state. |
|
| 59 | 175 |
- Registry: Use correct auth config when logging in. |
| 60 |
-- Documentation: Corrected error in the package name |
|
| 61 |
-* Documentation: Document what `vagrant up` is actually doing |
|
| 62 |
-- Runtime: Fix `docker rm` with volumes |
|
| 63 |
-- Runtime: Use empty string so TempDir uses the OS's temp dir automatically |
|
| 64 |
-- Runtime: Make sure to close the network allocators |
|
| 65 |
-* Testing: Replace panic by log.Fatal in tests |
|
| 66 |
-+ Documentation: improve doc search results |
|
| 67 |
-- Runtime: Fix some error cases where a HTTP body might not be closed |
|
| 68 |
-* Hack: Add proper bash completion for "docker push" |
|
| 69 |
-* Documentation: Add devenvironment link to CONTRIBUTING.md |
|
| 70 |
-* Documentation: Cleanup whitespace in API 1.5 docs |
|
| 71 |
-* Documentation: use angle brackets in MAINTAINER example email |
|
| 72 |
-- Testing: Increase TestRunDetach timeout |
|
| 73 |
-* Documentation: Fix help text for -v option |
|
| 74 |
-+ Hack: Added Dockerfile.tmLanguage to contrib |
|
| 75 |
-+ Runtime: Autorestart containers by default |
|
| 76 |
-* Testing: Adding more tests around auth.ResolveAuthConfig |
|
| 77 |
-* Hack: Configured FPM to make /etc/init/docker.conf a config file |
|
| 78 |
-* Hack: Add xz utils as a runtime dep |
|
| 79 |
-* Documentation: Add `apt-get install curl` to Ubuntu docs |
|
| 80 |
-* Documentation: Remove Gentoo install notes about #1422 workaround |
|
| 81 |
-* Documentation: Fix Ping endpoint documentation |
|
| 82 |
-* Runtime: Bump vendor kr/pty to commit 3b1f6487b (syscall.O_NOCTTY) |
|
| 83 |
-* Runtime: lxc: Allow set_file_cap capability in container |
|
| 84 |
-* Documentation: Update archlinux.rst |
|
| 85 |
-- Documentation: Fix ironic typo in changelog |
|
| 86 |
-* Documentation: Add explanation for export restrictions |
|
| 87 |
-* Hack: Add cleanup/refactor portion of #2010 for hack and Dockerfile updates |
|
| 88 |
-+ Documentation: Changes to a new style for the docs. Includes version switcher. |
|
| 89 |
-* Documentation: Formatting, add information about multiline json |
|
| 90 |
-+ Hack: Add contrib/mkimage-centos.sh back (from #1621), and associated documentation link |
|
| 91 |
-- Runtime: Fix panic with wrong dockercfg file |
|
| 92 |
-- Runtime: Fix the attach behavior with -i |
|
| 93 |
-* Documentation: Add .dockercfg doc |
|
| 94 |
-- Runtime: Move run -rm to the cli only |
|
| 95 |
-* Hack: Enable SSH Agent forwarding in Vagrant VM |
|
| 96 |
-+ Runtime: Add -rm to docker run for removing a container on exit |
|
| 97 |
-* Documentation: Improve registry and index REST API documentation |
|
| 98 |
-* Runtime: Split stdout stderr |
|
| 99 |
-- Documentation: Replace deprecated upgrading reference to docker-latest.tgz, which hasn't been updated since 0.5.3 |
|
| 100 |
-* Documentation: Update Gentoo installation documentation now that we're in the portage tree proper |
|
| 101 | 176 |
- Registry: Fix the error message so it is the same as the regex |
| 102 |
-* Runtime: Always create a new session for the container |
|
| 103 |
-* Hack: Add several of the small make.sh fixes from #1920, and make the output more consistent and contributor-friendly |
|
| 104 |
-* Documentation: Various command fixes in postgres example |
|
| 105 |
-* Documentation: Cleanup and reorganize docs and tooling for contributors and maintainers |
|
| 106 |
-- Documentation: Minor spelling correction of protocoll -> protocol |
|
| 107 |
-* Hack: Several small tweaks/fixes for contrib/mkimage-debian.sh |
|
| 108 |
-+ Hack: Add @tianon to hack/MAINTAINERS |
|
| 109 | 177 |
|
| 110 | 178 |
## 0.6.3 (2013-09-23) |
| 111 |
-* Packaging: Update tar vendor dependency |
|
| 179 |
+ |
|
| 180 |
+#### Packaging |
|
| 181 |
+ |
|
| 182 |
+* Add 'docker' group on install for ubuntu package |
|
| 183 |
+* Update tar vendor dependency |
|
| 184 |
+* Download apt key over HTTPS |
|
| 185 |
+ |
|
| 186 |
+#### Runtime |
|
| 187 |
+ |
|
| 188 |
+- Only copy and change permissions on non-bindmount volumes |
|
| 189 |
+* Allow multiple volumes-from |
|
| 190 |
+- Fix HTTP imports from STDIN |
|
| 191 |
+ |
|
| 192 |
+#### Documentation |
|
| 193 |
+ |
|
| 194 |
+* Update section on extracting the docker binary after build |
|
| 195 |
+* Update development environment docs for new build process |
|
| 196 |
+* Remove 'base' image from documentation |
|
| 197 |
+ |
|
| 198 |
+#### Other |
|
| 199 |
+ |
|
| 112 | 200 |
- Client: Fix detach issue |
| 113 |
-- Runtime: Only copy and change permissions on non-bindmount volumes |
|
| 114 | 201 |
- Registry: Update regular expression to match index |
| 115 |
-* Runtime: Allow multiple volumes-from |
|
| 116 |
-* Packaging: Download apt key over HTTPS |
|
| 117 |
-* Documentation: Update section on extracting the docker binary after build |
|
| 118 |
-* Documentation: Update development environment docs for new build process |
|
| 119 |
-* Documentation: Remove 'base' image from documentation |
|
| 120 |
-* Packaging: Add 'docker' group on install for ubuntu package |
|
| 121 |
-- Runtime: Fix HTTP imports from STDIN |
|
| 122 | 202 |
|
| 123 | 203 |
## 0.6.2 (2013-09-17) |
| 204 |
+ |
|
| 205 |
+#### Runtime |
|
| 206 |
+ |
|
| 207 |
++ Add domainname support |
|
| 208 |
++ Implement image filtering with path.Match |
|
| 209 |
+* Remove unnecesasry warnings |
|
| 210 |
+* Remove os/user dependency |
|
| 211 |
+* Only mount the hostname file when the config exists |
|
| 212 |
+* Handle signals within the `docker login` command |
|
| 213 |
+- UID and GID are now also applied to volumes |
|
| 214 |
+- `docker start` set error code upon error |
|
| 215 |
+- `docker run` set the same error code as the process started |
|
| 216 |
+ |
|
| 217 |
+#### Builder |
|
| 218 |
+ |
|
| 219 |
++ Add -rm option in order to remove intermediate containers |
|
| 220 |
+* Allow multiline for the RUN instruction |
|
| 221 |
+ |
|
| 222 |
+#### Registry |
|
| 223 |
+ |
|
| 224 |
+* Implement login with private registry |
|
| 225 |
+- Fix push issues |
|
| 226 |
+ |
|
| 227 |
+#### Other |
|
| 228 |
+ |
|
| 124 | 229 |
+ Hack: Vendor all dependencies |
| 125 |
-+ Builder: Add -rm option in order to remove intermediate containers |
|
| 126 |
-+ Runtime: Add domainname support |
|
| 127 |
-+ Runtime: Implement image filtering with path.Match |
|
| 128 |
-* Builder: Allow multiline for the RUN instruction |
|
| 129 |
-* Runtime: Remove unnecesasry warnings |
|
| 130 |
-* Runtime: Only mount the hostname file when the config exists |
|
| 131 |
-* Runtime: Handle signals within the `docker login` command |
|
| 132 |
-* Runtime: Remove os/user dependency |
|
| 133 |
-* Registry: Implement login with private registry |
|
| 134 | 230 |
* Remote API: Bump to v1.5 |
| 135 | 231 |
* Packaging: Break down hack/make.sh into small scripts, one per 'bundle': test, binary, ubuntu etc. |
| 136 |
-* Documentation: General improvements |
|
| 137 |
-- Runtime: UID and GID are now also applied to volumes |
|
| 138 |
-- Runtime: `docker start` set error code upon error |
|
| 139 |
-- Runtime: `docker run` set the same error code as the process started |
|
| 140 |
-- Registry: Fix push issues |
|
| 232 |
+* Documentation: General improvments |
|
| 141 | 233 |
|
| 142 | 234 |
## 0.6.1 (2013-08-23) |
| 143 |
-* Registry: Pass "meta" headers in API calls to the registry |
|
| 144 |
-- Packaging: Use correct upstart script with new build tool |
|
| 145 |
-- Packaging: Use libffi-dev, don't build it from sources |
|
| 146 |
-- Packaging: Removed duplicate mercurial install command |
|
| 235 |
+ |
|
| 236 |
+#### Registry |
|
| 237 |
+ |
|
| 238 |
+* Pass "meta" headers in API calls to the registry |
|
| 239 |
+ |
|
| 240 |
+#### Packaging |
|
| 241 |
+ |
|
| 242 |
+- Use correct upstart script with new build tool |
|
| 243 |
+- Use libffi-dev, don`t build it from sources |
|
| 244 |
+- Remove duplicate mercurial install command |
|
| 147 | 245 |
|
| 148 | 246 |
## 0.6.0 (2013-08-22) |
| 149 |
-- Runtime: Load authConfig only when needed and fix useless WARNING |
|
| 150 |
-+ Runtime: Add lxc-conf flag to allow custom lxc options |
|
| 151 |
-- Runtime: Fix race conditions in parallel pull |
|
| 152 |
-- Runtime: Improve CMD, ENTRYPOINT, and attach docs. |
|
| 153 |
-* Documentation: Small fix to docs regarding adding docker groups |
|
| 154 |
-* Documentation: Add MongoDB image example |
|
| 155 |
-+ Builder: Add USER instruction do Dockerfile |
|
| 156 |
-* Documentation: updated default -H docs |
|
| 157 |
-* Remote API: Sort Images by most recent creation date. |
|
| 158 |
-+ Builder: Add workdir support for the Buildfile |
|
| 159 |
-+ Runtime: Add an option to set the working directory |
|
| 160 |
-- Runtime: Show tag used when image is missing |
|
| 161 |
-* Documentation: Update readme with dependencies for building |
|
| 162 |
-* Documentation: Add instructions for creating and using the docker group |
|
| 163 |
-* Remote API: Reworking opaque requests in registry module |
|
| 164 |
-- Runtime: Fix Graph ByParent() to generate list of child images per parent image. |
|
| 165 |
-* Runtime: Add Image name to LogEvent tests |
|
| 166 |
-* Documentation: Add sudo to examples and installation to documentation |
|
| 167 |
-+ Hack: Bash Completion: Limit commands to containers of a relevant state |
|
| 168 |
-* Remote API: Add image name in /events |
|
| 169 |
-* Runtime: Apply volumes-from before creating volumes |
|
| 170 |
-- Runtime: Make docker run handle SIGINT/SIGTERM |
|
| 171 |
-- Runtime: Prevent crash when .dockercfg not readable |
|
| 172 |
-* Hack: Add docker dependencies coverage testing into docker-ci |
|
| 173 |
-+ Runtime: Add -privileged flag and relevant tests, docs, and examples |
|
| 174 |
-+ Packaging: Docker-brew 0.5.2 support and memory footprint reduction |
|
| 175 |
-- Runtime: Install script should be fetched over https, not http. |
|
| 176 |
-* Packaging: Add new docker dependencies into docker-ci |
|
| 177 |
-* Runtime: Use Go 1.1.2 for dockerbuilder |
|
| 178 |
-* Registry: Improve auth push |
|
| 179 |
-* Runtime: API, issue 1471: Use groups for socket permissions |
|
| 180 |
-* Documentation: PostgreSQL service example in documentation |
|
| 247 |
+ |
|
| 248 |
+#### Runtime |
|
| 249 |
+ |
|
| 250 |
++ Add lxc-conf flag to allow custom lxc options |
|
| 251 |
++ Add an option to set the working directory |
|
| 252 |
+* Add Image name to LogEvent tests |
|
| 253 |
++ Add -privileged flag and relevant tests, docs, and examples |
|
| 254 |
+* Add websocket support to /container/<name>/attach/ws |
|
| 255 |
+* Add warning when net.ipv4.ip_forwarding = 0 |
|
| 256 |
+* Add hostname to environment |
|
| 257 |
+* Add last stable version in `docker version` |
|
| 258 |
+- Fix race conditions in parallel pull |
|
| 259 |
+- Fix Graph ByParent() to generate list of child images per parent image. |
|
| 260 |
+- Fix typo: fmt.Sprint -> fmt.Sprintf |
|
| 261 |
+- Fix small \n error un docker build |
|
| 262 |
+* Fix to "Inject dockerinit at /.dockerinit" |
|
| 263 |
+* Fix #910. print user name to docker info output |
|
| 264 |
+* Use Go 1.1.2 for dockerbuilder |
|
| 265 |
+* Use ranged for loop on channels |
|
| 266 |
+- Use utils.ParseRepositoryTag instead of strings.Split(name, ":") in server.ImageDelete |
|
| 267 |
+- Improve CMD, ENTRYPOINT, and attach docs. |
|
| 268 |
+- Improve connect message with socket error |
|
| 269 |
+- Load authConfig only when needed and fix useless WARNING |
|
| 270 |
+- Show tag used when image is missing |
|
| 271 |
+* Apply volumes-from before creating volumes |
|
| 272 |
+- Make docker run handle SIGINT/SIGTERM |
|
| 273 |
+- Prevent crash when .dockercfg not readable |
|
| 274 |
+- Install script should be fetched over https, not http. |
|
| 275 |
+* API, issue 1471: Use groups for socket permissions |
|
| 276 |
+- Correctly detect IPv4 forwarding |
|
| 277 |
+* Mount /dev/shm as a tmpfs |
|
| 278 |
+- Switch from http to https for get.docker.io |
|
| 279 |
+* Let userland proxy handle container-bound traffic |
|
| 280 |
+* Updated the Docker CLI to specify a value for the "Host" header. |
|
| 281 |
+- Change network range to avoid conflict with EC2 DNS |
|
| 282 |
+- Reduce connect and read timeout when pinging the registry |
|
| 283 |
+* Parallel pull |
|
| 284 |
+- Handle ip route showing mask-less IP addresses |
|
| 285 |
+* Allow ENTRYPOINT without CMD |
|
| 286 |
+- Always consider localhost as a domain name when parsing the FQN repos name |
|
| 287 |
+* Refactor checksum |
|
| 288 |
+ |
|
| 289 |
+#### Documentation |
|
| 290 |
+ |
|
| 291 |
+* Add MongoDB image example |
|
| 292 |
+* Add instructions for creating and using the docker group |
|
| 293 |
+* Add sudo to examples and installation to documentation |
|
| 294 |
+* Add ufw doc |
|
| 295 |
+* Add a reference to ps -a |
|
| 296 |
+* Add information about Docker`s high level tools over LXC. |
|
| 297 |
+* Fix typo in docs for docker run -dns |
|
| 298 |
+* Fix a typo in the ubuntu installation guide |
|
| 299 |
+* Fix to docs regarding adding docker groups |
|
| 300 |
+* Update default -H docs |
|
| 301 |
+* Update readme with dependencies for building |
|
| 302 |
+* Update amazon.rst to explain that Vagrant is not necessary for running Docker on ec2 |
|
| 303 |
+* PostgreSQL service example in documentation |
|
| 304 |
+* Suggest installing linux-headers by default. |
|
| 305 |
+* Change the twitter handle |
|
| 306 |
+* Clarify Amazon EC2 installation |
|
| 307 |
+* 'Base' image is deprecated and should no longer be referenced in the docs. |
|
| 308 |
+* Move note about officially supported kernel |
|
| 309 |
+- Solved the logo being squished in Safari |
|
| 310 |
+ |
|
| 311 |
+#### Builder |
|
| 312 |
+ |
|
| 313 |
++ Add USER instruction do Dockerfile |
|
| 314 |
++ Add workdir support for the Buildfile |
|
| 315 |
+* Add no cache for docker build |
|
| 316 |
+- Fix docker build and docker events output |
|
| 317 |
+- Only count known instructions as build steps |
|
| 318 |
+- Make sure ENV instruction within build perform a commit each time |
|
| 319 |
+- Forbid certain paths within docker build ADD |
|
| 320 |
+- Repository name (and optionally a tag) in build usage |
|
| 321 |
+- Make sure ADD will create everything in 0755 |
|
| 322 |
+ |
|
| 323 |
+#### Remote API |
|
| 324 |
+ |
|
| 325 |
+* Sort Images by most recent creation date. |
|
| 326 |
+* Reworking opaque requests in registry module |
|
| 327 |
+* Add image name in /events |
|
| 328 |
+* Use mime pkg to parse Content-Type |
|
| 329 |
+* 650 http utils and user agent field |
|
| 330 |
+ |
|
| 331 |
+#### Hack |
|
| 332 |
+ |
|
| 333 |
++ Bash Completion: Limit commands to containers of a relevant state |
|
| 334 |
+* Add docker dependencies coverage testing into docker-ci |
|
| 335 |
+ |
|
| 336 |
+#### Packaging |
|
| 337 |
+ |
|
| 338 |
++ Docker-brew 0.5.2 support and memory footprint reduction |
|
| 339 |
+* Add new docker dependencies into docker-ci |
|
| 340 |
+- Revert "docker.upstart: avoid spawning a `sh` process" |
|
| 341 |
++ Docker-brew and Docker standard library |
|
| 342 |
++ Release docker with docker |
|
| 343 |
+* Fix the upstart script generated by get.docker.io |
|
| 344 |
+* Enabled the docs to generate manpages. |
|
| 345 |
+* Revert Bind daemon to 0.0.0.0 in Vagrant. |
|
| 346 |
+ |
|
| 347 |
+#### Register |
|
| 348 |
+ |
|
| 349 |
+* Improve auth push |
|
| 350 |
+* Registry unit tests + mock registry |
|
| 351 |
+ |
|
| 352 |
+#### Tests |
|
| 353 |
+ |
|
| 354 |
+* Improve TestKillDifferentUser to prevent timeout on buildbot |
|
| 355 |
+- Fix typo in TestBindMounts (runContainer called without image) |
|
| 356 |
+* Improve TestGetContainersTop so it does not rely on sleep |
|
| 357 |
+* Relax the lo interface test to allow iface index != 1 |
|
| 358 |
+* Add registry functional test to docker-ci |
|
| 359 |
+* Add some tests in server and utils |
|
| 360 |
+ |
|
| 361 |
+#### Other |
|
| 362 |
+ |
|
| 181 | 363 |
* Contrib: bash completion script |
| 182 |
-* Tests: Improve TestKillDifferentUser to prevent timeout on buildbot |
|
| 183 |
-* Documentation: Fix typo in docs for docker run -dns |
|
| 184 |
-* Documentation: Adding a reference to ps -a |
|
| 185 |
-- Runtime: Correctly detect IPv4 forwarding |
|
| 186 |
-- Packaging: Revert "docker.upstart: avoid spawning a `sh` process" |
|
| 187 |
-* Runtime: Use ranged for loop on channels |
|
| 188 |
-- Runtime: Fix typo: fmt.Sprint -> fmt.Sprintf |
|
| 189 |
-- Tests: Fix typo in TestBindMounts (runContainer called without image) |
|
| 190 |
-* Runtime: add websocket support to /container/<name>/attach/ws |
|
| 191 |
-* Runtime: Mount /dev/shm as a tmpfs |
|
| 192 |
-- Builder: Only count known instructions as build steps |
|
| 193 |
-- Builder: Fix docker build and docker events output |
|
| 194 |
-- Runtime: switch from http to https for get.docker.io |
|
| 195 |
-* Tests: Improve TestGetContainersTop so it does not rely on sleep |
|
| 196 |
-+ Packaging: Docker-brew and Docker standard library |
|
| 197 |
-* Testing: Add some tests in server and utils |
|
| 198 |
-+ Packaging: Release docker with docker |
|
| 199 |
-- Builder: Make sure ENV instruction within build perform a commit each time |
|
| 200 |
-* Packaging: Fix the upstart script generated by get.docker.io |
|
| 201 |
-- Runtime: fix small \n error un docker build |
|
| 202 |
-* Runtime: Let userland proxy handle container-bound traffic |
|
| 203 |
-* Runtime: Updated the Docker CLI to specify a value for the "Host" header. |
|
| 204 |
-* Runtime: Add warning when net.ipv4.ip_forwarding = 0 |
|
| 205 |
-* Registry: Registry unit tests + mock registry |
|
| 206 |
-* Runtime: fixed #910. print user name to docker info output |
|
| 207 |
-- Builder: Forbid certain paths within docker build ADD |
|
| 208 |
-- Runtime: change network range to avoid conflict with EC2 DNS |
|
| 209 |
-* Tests: Relax the lo interface test to allow iface index != 1 |
|
| 210 |
-* Documentation: Suggest installing linux-headers by default. |
|
| 211 |
-* Documentation: Change the twitter handle |
|
| 212 | 364 |
* Client: Add docker cp command and copy api endpoint to copy container files/folders to the host |
| 213 |
-* Remote API: Use mime pkg to parse Content-Type |
|
| 214 |
-- Runtime: Reduce connect and read timeout when pinging the registry |
|
| 215 |
-* Documentation: Update amazon.rst to explain that Vagrant is not necessary for running Docker on ec2 |
|
| 216 |
-* Packaging: Enabled the docs to generate manpages. |
|
| 217 |
-* Runtime: Parallel pull |
|
| 218 |
-- Runtime: Handle ip route showing mask-less IP addresses |
|
| 219 |
-* Documentation: Clarify Amazon EC2 installation |
|
| 220 |
-* Documentation: 'Base' image is deprecated and should no longer be referenced in the docs. |
|
| 221 |
-* Runtime: Fix to "Inject dockerinit at /.dockerinit" |
|
| 222 |
-* Runtime: Allow ENTRYPOINT without CMD |
|
| 223 |
-- Runtime: Always consider localhost as a domain name when parsing the FQN repos name |
|
| 224 |
-* Remote API: 650 http utils and user agent field |
|
| 225 |
-* Documentation: fix a typo in the ubuntu installation guide |
|
| 226 |
-- Builder: Repository name (and optionally a tag) in build usage |
|
| 227 |
-* Documentation: Move note about officially supported kernel |
|
| 228 |
-* Packaging: Revert "Bind daemon to 0.0.0.0 in Vagrant. |
|
| 229 |
-* Builder: Add no cache for docker build |
|
| 230 |
-* Runtime: Add hostname to environment |
|
| 231 |
-* Runtime: Add last stable version in `docker version` |
|
| 232 |
-- Builder: Make sure ADD will create everything in 0755 |
|
| 233 |
-* Documentation: Add ufw doc |
|
| 234 |
-* Tests: Add registry functional test to docker-ci |
|
| 235 |
-- Documentation: Solved the logo being squished in Safari |
|
| 236 |
-- Runtime: Use utils.ParseRepositoryTag instead of strings.Split(name, ":") in server.ImageDelete |
|
| 237 |
-* Runtime: Refactor checksum |
|
| 238 |
-- Runtime: Improve connect message with socket error |
|
| 239 |
-* Documentation: Added information about Docker's high level tools over LXC. |
|
| 240 |
-* Don't read from stdout when only attached to stdin |
|
| 365 |
+* Don`t read from stdout when only attached to stdin |
|
| 241 | 366 |
|
| 242 | 367 |
## 0.5.3 (2013-08-13) |
| 243 |
-* Runtime: Use docker group for socket permissions |
|
| 244 |
-- Runtime: Spawn shell within upstart script |
|
| 245 |
-- Builder: Make sure ENV instruction within build perform a commit each time |
|
| 246 |
-- Runtime: Handle ip route showing mask-less IP addresses |
|
| 247 |
-- Runtime: Add hostname to environment |
|
| 368 |
+ |
|
| 369 |
+#### Runtime |
|
| 370 |
+ |
|
| 371 |
+* Use docker group for socket permissions |
|
| 372 |
+- Spawn shell within upstart script |
|
| 373 |
+- Handle ip route showing mask-less IP addresses |
|
| 374 |
+- Add hostname to environment |
|
| 375 |
+ |
|
| 376 |
+#### Builder |
|
| 377 |
+ |
|
| 378 |
+- Make sure ENV instruction within build perform a commit each time |
|
| 248 | 379 |
|
| 249 | 380 |
## 0.5.2 (2013-08-08) |
| 250 |
- * Builder: Forbid certain paths within docker build ADD |
|
| 251 |
- - Runtime: Change network range to avoid conflict with EC2 DNS |
|
| 252 |
- * API: Change daemon to listen on unix socket by default |
|
| 381 |
+ |
|
| 382 |
+* Builder: Forbid certain paths within docker build ADD |
|
| 383 |
+- Runtime: Change network range to avoid conflict with EC2 DNS |
|
| 384 |
+* API: Change daemon to listen on unix socket by default |
|
| 253 | 385 |
|
| 254 | 386 |
## 0.5.1 (2013-07-30) |
| 255 |
- + API: Docker client now sets useragent (RFC 2616) |
|
| 256 |
- + Runtime: Add `ps` args to `docker top` |
|
| 257 |
- + Runtime: Add support for container ID files (pidfile like) |
|
| 258 |
- + Runtime: Add container=lxc in default env |
|
| 259 |
- + Runtime: Support networkless containers with `docker run -n` and `docker -d -b=none` |
|
| 260 |
- + API: Add /events endpoint |
|
| 261 |
- + Builder: ADD command now understands URLs |
|
| 262 |
- + Builder: CmdAdd and CmdEnv now respect Dockerfile-set ENV variables |
|
| 263 |
- * Hack: Simplify unit tests with helpers |
|
| 264 |
- * Hack: Improve docker.upstart event |
|
| 265 |
- * Hack: Add coverage testing into docker-ci |
|
| 266 |
- * Runtime: Stdout/stderr logs are now stored in the same file as JSON |
|
| 267 |
- * Runtime: Allocate a /16 IP range by default, with fallback to /24. Try 12 ranges instead of 3. |
|
| 268 |
- * Runtime: Change .dockercfg format to json and support multiple auth remote |
|
| 269 |
- - Runtime: Do not override volumes from config |
|
| 270 |
- - Runtime: Fix issue with EXPOSE override |
|
| 271 |
- - Builder: Create directories with 755 instead of 700 within ADD instruction |
|
| 387 |
+ |
|
| 388 |
+#### Runtime |
|
| 389 |
+ |
|
| 390 |
++ Add `ps` args to `docker top` |
|
| 391 |
++ Add support for container ID files (pidfile like) |
|
| 392 |
++ Add container=lxc in default env |
|
| 393 |
++ Support networkless containers with `docker run -n` and `docker -d -b=none` |
|
| 394 |
+* Stdout/stderr logs are now stored in the same file as JSON |
|
| 395 |
+* Allocate a /16 IP range by default, with fallback to /24. Try 12 ranges instead of 3. |
|
| 396 |
+* Change .dockercfg format to json and support multiple auth remote |
|
| 397 |
+- Do not override volumes from config |
|
| 398 |
+- Fix issue with EXPOSE override |
|
| 399 |
+ |
|
| 400 |
+#### API |
|
| 401 |
+ |
|
| 402 |
++ Docker client now sets useragent (RFC 2616) |
|
| 403 |
++ Add /events endpoint |
|
| 404 |
+ |
|
| 405 |
+#### Builder |
|
| 406 |
+ |
|
| 407 |
++ ADD command now understands URLs |
|
| 408 |
++ CmdAdd and CmdEnv now respect Dockerfile-set ENV variables |
|
| 409 |
+- Create directories with 755 instead of 700 within ADD instruction |
|
| 410 |
+ |
|
| 411 |
+#### Hack |
|
| 412 |
+ |
|
| 413 |
+* Simplify unit tests with helpers |
|
| 414 |
+* Improve docker.upstart event |
|
| 415 |
+* Add coverage testing into docker-ci |
|
| 272 | 416 |
|
| 273 | 417 |
## 0.5.0 (2013-07-17) |
| 274 |
- + Runtime: List all processes running inside a container with 'docker top' |
|
| 275 |
- + Runtime: Host directories can be mounted as volumes with 'docker run -v' |
|
| 276 |
- + Runtime: Containers can expose public UDP ports (eg, '-p 123/udp') |
|
| 277 |
- + Runtime: Optionally specify an exact public port (eg. '-p 80:4500') |
|
| 278 |
- + Registry: New image naming scheme inspired by Go packaging convention allows arbitrary combinations of registries |
|
| 279 |
- + Builder: ENTRYPOINT instruction sets a default binary entry point to a container |
|
| 280 |
- + Builder: VOLUME instruction marks a part of the container as persistent data |
|
| 281 |
- * Builder: 'docker build' displays the full output of a build by default |
|
| 282 |
- * Runtime: 'docker login' supports additional options |
|
| 283 |
- - Runtime: Dont save a container's hostname when committing an image. |
|
| 284 |
- - Registry: Fix issues when uploading images to a private registry |
|
| 418 |
+ |
|
| 419 |
+#### Runtime |
|
| 420 |
+ |
|
| 421 |
++ List all processes running inside a container with 'docker top' |
|
| 422 |
++ Host directories can be mounted as volumes with 'docker run -v' |
|
| 423 |
++ Containers can expose public UDP ports (eg, '-p 123/udp') |
|
| 424 |
++ Optionally specify an exact public port (eg. '-p 80:4500') |
|
| 425 |
+* 'docker login' supports additional options |
|
| 426 |
+- Dont save a container`s hostname when committing an image. |
|
| 427 |
+ |
|
| 428 |
+#### Registry |
|
| 429 |
+ |
|
| 430 |
++ New image naming scheme inspired by Go packaging convention allows arbitrary combinations of registries |
|
| 431 |
+- Fix issues when uploading images to a private registry |
|
| 432 |
+ |
|
| 433 |
+#### Builder |
|
| 434 |
+ |
|
| 435 |
++ ENTRYPOINT instruction sets a default binary entry point to a container |
|
| 436 |
++ VOLUME instruction marks a part of the container as persistent data |
|
| 437 |
+* 'docker build' displays the full output of a build by default |
|
| 285 | 438 |
|
| 286 | 439 |
## 0.4.8 (2013-07-01) |
| 287 |
- + Builder: New build operation ENTRYPOINT adds an executable entry point to the container. |
|
| 288 |
- - Runtime: Fix a bug which caused 'docker run -d' to no longer print the container ID. |
|
| 289 |
- - Tests: Fix issues in the test suite |
|
| 440 |
+ |
|
| 441 |
++ Builder: New build operation ENTRYPOINT adds an executable entry point to the container. - Runtime: Fix a bug which caused 'docker run -d' to no longer print the container ID. |
|
| 442 |
+- Tests: Fix issues in the test suite |
|
| 290 | 443 |
|
| 291 | 444 |
## 0.4.7 (2013-06-28) |
| 292 |
- * Registry: easier push/pull to a custom registry |
|
| 293 |
- * Remote API: the progress bar updates faster when downloading and uploading large files |
|
| 294 |
- - Remote API: fix a bug in the optional unix socket transport |
|
| 295 |
- * Runtime: improve detection of kernel version |
|
| 296 |
- + Runtime: host directories can be mounted as volumes with 'docker run -b' |
|
| 297 |
- - Runtime: fix an issue when only attaching to stdin |
|
| 298 |
- * Runtime: use 'tar --numeric-owner' to avoid uid mismatch across multiple hosts |
|
| 299 |
- * Hack: improve test suite and dev environment |
|
| 300 |
- * Hack: remove dependency on unit tests on 'os/user' |
|
| 301 |
- + Documentation: add terminology section |
|
| 445 |
+ |
|
| 446 |
+#### Remote API |
|
| 447 |
+ |
|
| 448 |
+* The progress bar updates faster when downloading and uploading large files |
|
| 449 |
+- Fix a bug in the optional unix socket transport |
|
| 450 |
+ |
|
| 451 |
+#### Runtime |
|
| 452 |
+ |
|
| 453 |
+* Improve detection of kernel version |
|
| 454 |
++ Host directories can be mounted as volumes with 'docker run -b' |
|
| 455 |
+- fix an issue when only attaching to stdin |
|
| 456 |
+* Use 'tar --numeric-owner' to avoid uid mismatch across multiple hosts |
|
| 457 |
+ |
|
| 458 |
+#### Hack |
|
| 459 |
+ |
|
| 460 |
+* Improve test suite and dev environment |
|
| 461 |
+* Remove dependency on unit tests on 'os/user' |
|
| 462 |
+ |
|
| 463 |
+#### Other |
|
| 464 |
+ |
|
| 465 |
+* Registry: easier push/pull to a custom registry |
|
| 466 |
++ Documentation: add terminology section |
|
| 302 | 467 |
|
| 303 | 468 |
## 0.4.6 (2013-06-22) |
| 304 |
- - Runtime: fix a bug which caused creation of empty images (and volumes) to crash. |
|
| 469 |
+ |
|
| 470 |
+- Runtime: fix a bug which caused creation of empty images (and volumes) to crash. |
|
| 305 | 471 |
|
| 306 | 472 |
## 0.4.5 (2013-06-21) |
| 307 |
- + Builder: 'docker build git://URL' fetches and builds a remote git repository |
|
| 308 |
- * Runtime: 'docker ps -s' optionally prints container size |
|
| 309 |
- * Tests: Improved and simplified |
|
| 310 |
- - Runtime: fix a regression introduced in 0.4.3 which caused the logs command to fail. |
|
| 311 |
- - Builder: fix a regression when using ADD with single regular file. |
|
| 473 |
+ |
|
| 474 |
++ Builder: 'docker build git://URL' fetches and builds a remote git repository |
|
| 475 |
+* Runtime: 'docker ps -s' optionally prints container size |
|
| 476 |
+* Tests: Improved and simplified |
|
| 477 |
+- Runtime: fix a regression introduced in 0.4.3 which caused the logs command to fail. |
|
| 478 |
+- Builder: fix a regression when using ADD with single regular file. |
|
| 312 | 479 |
|
| 313 | 480 |
## 0.4.4 (2013-06-19) |
| 314 |
- - Builder: fix a regression introduced in 0.4.3 which caused builds to fail on new clients. |
|
| 481 |
+ |
|
| 482 |
+- Builder: fix a regression introduced in 0.4.3 which caused builds to fail on new clients. |
|
| 315 | 483 |
|
| 316 | 484 |
## 0.4.3 (2013-06-19) |
| 317 |
- + Builder: ADD of a local file will detect tar archives and unpack them |
|
| 318 |
- * Runtime: Remove bsdtar dependency |
|
| 319 |
- * Runtime: Add unix socket and multiple -H support |
|
| 320 |
- * Runtime: Prevent rm of running containers |
|
| 321 |
- * Runtime: Use go1.1 cookiejar |
|
| 322 |
- * Builder: ADD improvements: use tar for copy + automatically unpack local archives |
|
| 323 |
- * Builder: ADD uses tar/untar for copies instead of calling 'cp -ar' |
|
| 324 |
- * Builder: nicer output for 'docker build' |
|
| 325 |
- * Builder: fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented. |
|
| 326 |
- * Client: HumanReadable ProgressBar sizes in pull |
|
| 327 |
- * Client: Fix docker version's git commit output |
|
| 328 |
- * API: Send all tags on History API call |
|
| 329 |
- * API: Add tag lookup to history command. Fixes #882 |
|
| 330 |
- - Runtime: Fix issue detaching from running TTY container |
|
| 331 |
- - Runtime: Forbid parralel push/pull for a single image/repo. Fixes #311 |
|
| 332 |
- - Runtime: Fix race condition within Run command when attaching. |
|
| 333 |
- - Builder: fix a bug which caused builds to fail if ADD was the first command |
|
| 334 |
- - Documentation: fix missing command in irc bouncer example |
|
| 485 |
+ |
|
| 486 |
+#### Builder |
|
| 487 |
+ |
|
| 488 |
++ ADD of a local file will detect tar archives and unpack them |
|
| 489 |
+* ADD improvements: use tar for copy + automatically unpack local archives |
|
| 490 |
+* ADD uses tar/untar for copies instead of calling 'cp -ar' |
|
| 491 |
+* Fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented. |
|
| 492 |
+- Fix a bug which caused builds to fail if ADD was the first command |
|
| 493 |
+* Nicer output for 'docker build' |
|
| 494 |
+ |
|
| 495 |
+#### Runtime |
|
| 496 |
+ |
|
| 497 |
+* Remove bsdtar dependency |
|
| 498 |
+* Add unix socket and multiple -H support |
|
| 499 |
+* Prevent rm of running containers |
|
| 500 |
+* Use go1.1 cookiejar |
|
| 501 |
+- Fix issue detaching from running TTY container |
|
| 502 |
+- Forbid parralel push/pull for a single image/repo. Fixes #311 |
|
| 503 |
+- Fix race condition within Run command when attaching. |
|
| 504 |
+ |
|
| 505 |
+#### Client |
|
| 506 |
+ |
|
| 507 |
+* HumanReadable ProgressBar sizes in pull |
|
| 508 |
+* Fix docker version`s git commit output |
|
| 509 |
+ |
|
| 510 |
+#### API |
|
| 511 |
+ |
|
| 512 |
+* Send all tags on History API call |
|
| 513 |
+* Add tag lookup to history command. Fixes #882 |
|
| 514 |
+ |
|
| 515 |
+#### Documentation |
|
| 516 |
+ |
|
| 517 |
+- Fix missing command in irc bouncer example |
|
| 335 | 518 |
|
| 336 | 519 |
## 0.4.2 (2013-06-17) |
| 337 |
- - Packaging: Bumped version to work around an Ubuntu bug |
|
| 520 |
+ |
|
| 521 |
+- Packaging: Bumped version to work around an Ubuntu bug |
|
| 338 | 522 |
|
| 339 | 523 |
## 0.4.1 (2013-06-17) |
| 340 |
- + Remote Api: Add flag to enable cross domain requests |
|
| 341 |
- + Remote Api/Client: Add images and containers sizes in docker ps and docker images |
|
| 342 |
- + Runtime: Configure dns configuration host-wide with 'docker -d -dns' |
|
| 343 |
- + Runtime: Detect faulty DNS configuration and replace it with a public default |
|
| 344 |
- + Runtime: allow docker run <name>:<id> |
|
| 345 |
- + Runtime: you can now specify public port (ex: -p 80:4500) |
|
| 346 |
- * Client: allow multiple params in inspect |
|
| 347 |
- * Client: Print the container id before the hijack in `docker run` |
|
| 348 |
- * Registry: add regexp check on repo's name |
|
| 349 |
- * Registry: Move auth to the client |
|
| 350 |
- * Runtime: improved image removal to garbage-collect unreferenced parents |
|
| 351 |
- * Vagrantfile: Add the rest api port to vagrantfile's port_forward |
|
| 352 |
- * Upgrade to Go 1.1 |
|
| 353 |
- - Builder: don't ignore last line in Dockerfile when it doesn't end with \n |
|
| 354 |
- - Registry: Remove login check on pull |
|
| 524 |
+ |
|
| 525 |
+#### Remote Api |
|
| 526 |
+ |
|
| 527 |
++ Add flag to enable cross domain requests |
|
| 528 |
++ Add images and containers sizes in docker ps and docker images |
|
| 529 |
+ |
|
| 530 |
+#### Runtime |
|
| 531 |
+ |
|
| 532 |
++ Configure dns configuration host-wide with 'docker -d -dns' |
|
| 533 |
++ Detect faulty DNS configuration and replace it with a public default |
|
| 534 |
++ Allow docker run <name>:<id> |
|
| 535 |
++ You can now specify public port (ex: -p 80:4500) |
|
| 536 |
+* Improved image removal to garbage-collect unreferenced parents |
|
| 537 |
+ |
|
| 538 |
+#### Client |
|
| 539 |
+ |
|
| 540 |
+* Allow multiple params in inspect |
|
| 541 |
+* Print the container id before the hijack in `docker run` |
|
| 542 |
+ |
|
| 543 |
+#### Registry |
|
| 544 |
+ |
|
| 545 |
+* Add regexp check on repo`s name |
|
| 546 |
+* Move auth to the client |
|
| 547 |
+- Remove login check on pull |
|
| 548 |
+ |
|
| 549 |
+#### Other |
|
| 550 |
+ |
|
| 551 |
+* Vagrantfile: Add the rest api port to vagrantfile`s port_forward |
|
| 552 |
+* Upgrade to Go 1.1 |
|
| 553 |
+- Builder: don`t ignore last line in Dockerfile when it doesn`t end with \n |
|
| 355 | 554 |
|
| 356 | 555 |
## 0.4.0 (2013-06-03) |
| 357 |
- + Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile |
|
| 358 |
- + Introducing Remote API: control Docker programmatically using a simple HTTP/json API |
|
| 359 |
- * Runtime: various reliability and usability improvements |
|
| 556 |
+ |
|
| 557 |
+#### Builder |
|
| 558 |
+ |
|
| 559 |
++ Introducing Builder |
|
| 560 |
++ 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile |
|
| 561 |
+ |
|
| 562 |
+#### Remote API |
|
| 563 |
+ |
|
| 564 |
++ Introducing Remote API |
|
| 565 |
++ control Docker programmatically using a simple HTTP/json API |
|
| 566 |
+ |
|
| 567 |
+#### Runtime |
|
| 568 |
+ |
|
| 569 |
+* Various reliability and usability improvements |
|
| 360 | 570 |
|
| 361 | 571 |
## 0.3.4 (2013-05-30) |
| 362 |
- + Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile |
|
| 363 |
- + Builder: 'docker build -t FOO' applies the tag FOO to the newly built container. |
|
| 364 |
- + Runtime: interactive TTYs correctly handle window resize |
|
| 365 |
- * Runtime: fix how configuration is merged between layers |
|
| 366 |
- + Remote API: split stdout and stderr on 'docker run' |
|
| 367 |
- + Remote API: optionally listen on a different IP and port (use at your own risk) |
|
| 368 |
- * Documentation: improved install instructions. |
|
| 572 |
+ |
|
| 573 |
+#### Builder |
|
| 574 |
+ |
|
| 575 |
++ 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile |
|
| 576 |
++ 'docker build -t FOO' applies the tag FOO to the newly built container. |
|
| 577 |
+ |
|
| 578 |
+#### Runtime |
|
| 579 |
+ |
|
| 580 |
++ Interactive TTYs correctly handle window resize |
|
| 581 |
+* Fix how configuration is merged between layers |
|
| 582 |
+ |
|
| 583 |
+#### Remote API |
|
| 584 |
+ |
|
| 585 |
++ Split stdout and stderr on 'docker run' |
|
| 586 |
++ Optionally listen on a different IP and port (use at your own risk) |
|
| 587 |
+ |
|
| 588 |
+#### Documentation |
|
| 589 |
+ |
|
| 590 |
+* Improved install instructions. |
|
| 369 | 591 |
|
| 370 | 592 |
## 0.3.3 (2013-05-23) |
| 371 |
- - Registry: Fix push regression |
|
| 372 |
- - Various bugfixes |
|
| 593 |
+ |
|
| 594 |
+- Registry: Fix push regression |
|
| 595 |
+- Various bugfixes |
|
| 373 | 596 |
|
| 374 | 597 |
## 0.3.2 (2013-05-09) |
| 375 |
- * Runtime: Store the actual archive on commit |
|
| 376 |
- * Registry: Improve the checksum process |
|
| 377 |
- * Registry: Use the size to have a good progress bar while pushing |
|
| 378 |
- * Registry: Use the actual archive if it exists in order to speed up the push |
|
| 379 |
- - Registry: Fix error 400 on push |
|
| 598 |
+ |
|
| 599 |
+#### Registry |
|
| 600 |
+ |
|
| 601 |
+* Improve the checksum process |
|
| 602 |
+* Use the size to have a good progress bar while pushing |
|
| 603 |
+* Use the actual archive if it exists in order to speed up the push |
|
| 604 |
+- Fix error 400 on push |
|
| 605 |
+ |
|
| 606 |
+#### Runtime |
|
| 607 |
+ |
|
| 608 |
+* Store the actual archive on commit |
|
| 380 | 609 |
|
| 381 | 610 |
## 0.3.1 (2013-05-08) |
| 382 |
- + Builder: Implement the autorun capability within docker builder |
|
| 383 |
- + Builder: Add caching to docker builder |
|
| 384 |
- + Builder: Add support for docker builder with native API as top level command |
|
| 385 |
- + Runtime: Add go version to debug infos |
|
| 386 |
- + Builder: Implement ENV within docker builder |
|
| 387 |
- + Registry: Add docker search top level command in order to search a repository |
|
| 388 |
- + Images: output graph of images to dot (graphviz) |
|
| 389 |
- + Documentation: new introduction and high-level overview |
|
| 390 |
- + Documentation: Add the documentation for docker builder |
|
| 391 |
- + Website: new high-level overview |
|
| 392 |
- - Makefile: Swap "go get" for "go get -d", especially to compile on go1.1rc |
|
| 393 |
- - Images: fix ByParent function |
|
| 394 |
- - Builder: Check the command existance prior create and add Unit tests for the case |
|
| 395 |
- - Registry: Fix pull for official images with specific tag |
|
| 396 |
- - Registry: Fix issue when login in with a different user and trying to push |
|
| 397 |
- - Documentation: CSS fix for docker documentation to make REST API docs look better. |
|
| 398 |
- - Documentation: Fixed CouchDB example page header mistake |
|
| 399 |
- - Documentation: fixed README formatting |
|
| 400 |
- * Registry: Improve checksum - async calculation |
|
| 401 |
- * Runtime: kernel version - don't show the dash if flavor is empty |
|
| 402 |
- * Documentation: updated www.docker.io website. |
|
| 403 |
- * Builder: use any whitespaces instead of tabs |
|
| 404 |
- * Packaging: packaging ubuntu; issue #510: Use goland-stable PPA package to build docker |
|
| 611 |
+ |
|
| 612 |
+#### Builder |
|
| 613 |
+ |
|
| 614 |
++ Implement the autorun capability within docker builder |
|
| 615 |
++ Add caching to docker builder |
|
| 616 |
++ Add support for docker builder with native API as top level command |
|
| 617 |
++ Implement ENV within docker builder |
|
| 618 |
+- Check the command existance prior create and add Unit tests for the case |
|
| 619 |
+* use any whitespaces instead of tabs |
|
| 620 |
+ |
|
| 621 |
+#### Runtime |
|
| 622 |
+ |
|
| 623 |
++ Add go version to debug infos |
|
| 624 |
+* Kernel version - don`t show the dash if flavor is empty |
|
| 625 |
+ |
|
| 626 |
+#### Registry |
|
| 627 |
+ |
|
| 628 |
++ Add docker search top level command in order to search a repository |
|
| 629 |
+- Fix pull for official images with specific tag |
|
| 630 |
+- Fix issue when login in with a different user and trying to push |
|
| 631 |
+* Improve checksum - async calculation |
|
| 632 |
+ |
|
| 633 |
+#### Images |
|
| 634 |
+ |
|
| 635 |
++ Output graph of images to dot (graphviz) |
|
| 636 |
+- Fix ByParent function |
|
| 637 |
+ |
|
| 638 |
+#### Documentation |
|
| 639 |
+ |
|
| 640 |
++ New introduction and high-level overview |
|
| 641 |
++ Add the documentation for docker builder |
|
| 642 |
+- CSS fix for docker documentation to make REST API docs look better. |
|
| 643 |
+- Fix CouchDB example page header mistake |
|
| 644 |
+- Fix README formatting |
|
| 645 |
+* Update www.docker.io website. |
|
| 646 |
+ |
|
| 647 |
+#### Other |
|
| 648 |
+ |
|
| 649 |
++ Website: new high-level overview |
|
| 650 |
+- Makefile: Swap "go get" for "go get -d", especially to compile on go1.1rc |
|
| 651 |
+* Packaging: packaging ubuntu; issue #510: Use goland-stable PPA package to build docker |
|
| 405 | 652 |
|
| 406 | 653 |
## 0.3.0 (2013-05-06) |
| 407 |
- + Registry: Implement the new registry |
|
| 408 |
- + Documentation: new example: sharing data between 2 couchdb databases |
|
| 409 |
- - Runtime: Fix the command existance check |
|
| 410 |
- - Runtime: strings.Split may return an empty string on no match |
|
| 411 |
- - Runtime: Fix an index out of range crash if cgroup memory is not |
|
| 412 |
- * Documentation: Various improvments |
|
| 413 |
- * Vagrant: Use only one deb line in /etc/apt |
|
| 654 |
+ |
|
| 655 |
+#### Runtime |
|
| 656 |
+ |
|
| 657 |
+- Fix the command existance check |
|
| 658 |
+- strings.Split may return an empty string on no match |
|
| 659 |
+- Fix an index out of range crash if cgroup memory is not |
|
| 660 |
+ |
|
| 661 |
+#### Documentation |
|
| 662 |
+ |
|
| 663 |
+* Various improvments |
|
| 664 |
++ New example: sharing data between 2 couchdb databases |
|
| 665 |
+ |
|
| 666 |
+#### Other |
|
| 667 |
+ |
|
| 668 |
+* Vagrant: Use only one deb line in /etc/apt |
|
| 669 |
++ Registry: Implement the new registry |
|
| 414 | 670 |
|
| 415 | 671 |
## 0.2.2 (2013-05-03) |
| 416 |
- + Support for data volumes ('docker run -v=PATH')
|
|
| 417 |
- + Share data volumes between containers ('docker run -volumes-from')
|
|
| 418 |
- + Improved documentation |
|
| 419 |
- * Upgrade to Go 1.0.3 |
|
| 420 |
- * Various upgrades to the dev environment for contributors |
|
| 672 |
+ |
|
| 673 |
++ Support for data volumes ('docker run -v=PATH')
|
|
| 674 |
++ Share data volumes between containers ('docker run -volumes-from')
|
|
| 675 |
++ Improved documentation |
|
| 676 |
+* Upgrade to Go 1.0.3 |
|
| 677 |
+* Various upgrades to the dev environment for contributors |
|
| 421 | 678 |
|
| 422 | 679 |
## 0.2.1 (2013-05-01) |
| 423 |
- + 'docker commit -run' bundles a layer with default runtime options: command, ports etc. |
|
| 424 |
- * Improve install process on Vagrant |
|
| 425 |
- + New Dockerfile operation: "maintainer" |
|
| 426 |
- + New Dockerfile operation: "expose" |
|
| 427 |
- + New Dockerfile operation: "cmd" |
|
| 428 |
- + Contrib script to build a Debian base layer |
|
| 429 |
- + 'docker -d -r': restart crashed containers at daemon startup |
|
| 430 |
- * Runtime: improve test coverage |
|
| 680 |
+ |
|
| 681 |
++ 'docker commit -run' bundles a layer with default runtime options: command, ports etc. |
|
| 682 |
+* Improve install process on Vagrant |
|
| 683 |
++ New Dockerfile operation: "maintainer" |
|
| 684 |
++ New Dockerfile operation: "expose" |
|
| 685 |
++ New Dockerfile operation: "cmd" |
|
| 686 |
++ Contrib script to build a Debian base layer |
|
| 687 |
++ 'docker -d -r': restart crashed containers at daemon startup |
|
| 688 |
+* Runtime: improve test coverage |
|
| 431 | 689 |
|
| 432 | 690 |
## 0.2.0 (2013-04-23) |
| 433 |
- - Runtime: ghost containers can be killed and waited for |
|
| 434 |
- * Documentation: update install intructions |
|
| 435 |
- - Packaging: fix Vagrantfile |
|
| 436 |
- - Development: automate releasing binaries and ubuntu packages |
|
| 437 |
- + Add a changelog |
|
| 438 |
- - Various bugfixes |
|
| 691 |
+ |
|
| 692 |
+- Runtime: ghost containers can be killed and waited for |
|
| 693 |
+* Documentation: update install intructions |
|
| 694 |
+- Packaging: fix Vagrantfile |
|
| 695 |
+- Development: automate releasing binaries and ubuntu packages |
|
| 696 |
++ Add a changelog |
|
| 697 |
+- Various bugfixes |
|
| 439 | 698 |
|
| 440 | 699 |
## 0.1.8 (2013-04-22) |
| 441 |
- - Dynamically detect cgroup capabilities |
|
| 442 |
- - Issue stability warning on kernels <3.8 |
|
| 443 |
- - 'docker push' buffers on disk instead of memory |
|
| 444 |
- - Fix 'docker diff' for removed files |
|
| 445 |
- - Fix 'docker stop' for ghost containers |
|
| 446 |
- - Fix handling of pidfile |
|
| 447 |
- - Various bugfixes and stability improvements |
|
| 700 |
+ |
|
| 701 |
+- Dynamically detect cgroup capabilities |
|
| 702 |
+- Issue stability warning on kernels <3.8 |
|
| 703 |
+- 'docker push' buffers on disk instead of memory |
|
| 704 |
+- Fix 'docker diff' for removed files |
|
| 705 |
+- Fix 'docker stop' for ghost containers |
|
| 706 |
+- Fix handling of pidfile |
|
| 707 |
+- Various bugfixes and stability improvements |
|
| 448 | 708 |
|
| 449 | 709 |
## 0.1.7 (2013-04-18) |
| 450 |
- - Container ports are available on localhost |
|
| 451 |
- - 'docker ps' shows allocated TCP ports |
|
| 452 |
- - Contributors can run 'make hack' to start a continuous integration VM |
|
| 453 |
- - Streamline ubuntu packaging & uploading |
|
| 454 |
- - Various bugfixes and stability improvements |
|
| 710 |
+ |
|
| 711 |
+- Container ports are available on localhost |
|
| 712 |
+- 'docker ps' shows allocated TCP ports |
|
| 713 |
+- Contributors can run 'make hack' to start a continuous integration VM |
|
| 714 |
+- Streamline ubuntu packaging & uploading |
|
| 715 |
+- Various bugfixes and stability improvements |
|
| 455 | 716 |
|
| 456 | 717 |
## 0.1.6 (2013-04-17) |
| 457 |
- - Record the author an image with 'docker commit -author' |
|
| 718 |
+ |
|
| 719 |
+- Record the author an image with 'docker commit -author' |
|
| 458 | 720 |
|
| 459 | 721 |
## 0.1.5 (2013-04-17) |
| 460 |
- - Disable standalone mode |
|
| 461 |
- - Use a custom DNS resolver with 'docker -d -dns' |
|
| 462 |
- - Detect ghost containers |
|
| 463 |
- - Improve diagnosis of missing system capabilities |
|
| 464 |
- - Allow disabling memory limits at compile time |
|
| 465 |
- - Add debian packaging |
|
| 466 |
- - Documentation: installing on Arch Linux |
|
| 467 |
- - Documentation: running Redis on docker |
|
| 468 |
- - Fixed lxc 0.9 compatibility |
|
| 469 |
- - Automatically load aufs module |
|
| 470 |
- - Various bugfixes and stability improvements |
|
| 722 |
+ |
|
| 723 |
+- Disable standalone mode |
|
| 724 |
+- Use a custom DNS resolver with 'docker -d -dns' |
|
| 725 |
+- Detect ghost containers |
|
| 726 |
+- Improve diagnosis of missing system capabilities |
|
| 727 |
+- Allow disabling memory limits at compile time |
|
| 728 |
+- Add debian packaging |
|
| 729 |
+- Documentation: installing on Arch Linux |
|
| 730 |
+- Documentation: running Redis on docker |
|
| 731 |
+- Fixed lxc 0.9 compatibility |
|
| 732 |
+- Automatically load aufs module |
|
| 733 |
+- Various bugfixes and stability improvements |
|
| 471 | 734 |
|
| 472 | 735 |
## 0.1.4 (2013-04-09) |
| 473 |
- - Full support for TTY emulation |
|
| 474 |
- - Detach from a TTY session with the escape sequence `C-p C-q` |
|
| 475 |
- - Various bugfixes and stability improvements |
|
| 476 |
- - Minor UI improvements |
|
| 477 |
- - Automatically create our own bridge interface 'docker0' |
|
| 736 |
+ |
|
| 737 |
+- Full support for TTY emulation |
|
| 738 |
+- Detach from a TTY session with the escape sequence `C-p C-q` |
|
| 739 |
+- Various bugfixes and stability improvements |
|
| 740 |
+- Minor UI improvements |
|
| 741 |
+- Automatically create our own bridge interface 'docker0' |
|
| 478 | 742 |
|
| 479 | 743 |
## 0.1.3 (2013-04-04) |
| 480 |
- - Choose TCP frontend port with '-p :PORT' |
|
| 481 |
- - Layer format is versioned |
|
| 482 |
- - Major reliability improvements to the process manager |
|
| 483 |
- - Various bugfixes and stability improvements |
|
| 744 |
+ |
|
| 745 |
+- Choose TCP frontend port with '-p :PORT' |
|
| 746 |
+- Layer format is versioned |
|
| 747 |
+- Major reliability improvements to the process manager |
|
| 748 |
+- Various bugfixes and stability improvements |
|
| 484 | 749 |
|
| 485 | 750 |
## 0.1.2 (2013-04-03) |
| 486 |
- - Set container hostname with 'docker run -h' |
|
| 487 |
- - Selective attach at run with 'docker run -a [stdin[,stdout[,stderr]]]' |
|
| 488 |
- - Various bugfixes and stability improvements |
|
| 489 |
- - UI polish |
|
| 490 |
- - Progress bar on push/pull |
|
| 491 |
- - Use XZ compression by default |
|
| 492 |
- - Make IP allocator lazy |
|
| 751 |
+ |
|
| 752 |
+- Set container hostname with 'docker run -h' |
|
| 753 |
+- Selective attach at run with 'docker run -a [stdin[,stdout[,stderr]]]' |
|
| 754 |
+- Various bugfixes and stability improvements |
|
| 755 |
+- UI polish |
|
| 756 |
+- Progress bar on push/pull |
|
| 757 |
+- Use XZ compression by default |
|
| 758 |
+- Make IP allocator lazy |
|
| 493 | 759 |
|
| 494 | 760 |
## 0.1.1 (2013-03-31) |
| 495 |
- - Display shorthand IDs for convenience |
|
| 496 |
- - Stabilize process management |
|
| 497 |
- - Layers can include a commit message |
|
| 498 |
- - Simplified 'docker attach' |
|
| 499 |
- - Fixed support for re-attaching |
|
| 500 |
- - Various bugfixes and stability improvements |
|
| 501 |
- - Auto-download at run |
|
| 502 |
- - Auto-login on push |
|
| 503 |
- - Beefed up documentation |
|
| 761 |
+ |
|
| 762 |
+- Display shorthand IDs for convenience |
|
| 763 |
+- Stabilize process management |
|
| 764 |
+- Layers can include a commit message |
|
| 765 |
+- Simplified 'docker attach' |
|
| 766 |
+- Fixed support for re-attaching |
|
| 767 |
+- Various bugfixes and stability improvements |
|
| 768 |
+- Auto-download at run |
|
| 769 |
+- Auto-login on push |
|
| 770 |
+- Beefed up documentation |
|
| 504 | 771 |
|
| 505 | 772 |
## 0.1.0 (2013-03-23) |
| 506 |
- - First release |
|
| 507 |
- - Implement registry in order to push/pull images |
|
| 508 |
- - TCP port allocation |
|
| 509 |
- - Fix termcaps on Linux |
|
| 510 |
- - Add documentation |
|
| 511 |
- - Add Vagrant support with Vagrantfile |
|
| 512 |
- - Add unit tests |
|
| 513 |
- - Add repository/tags to ease image management |
|
| 514 |
- - Improve the layer implementation |
|
| 773 |
+ |
|
| 774 |
+Initial public release |
|
| 775 |
+ |
|
| 776 |
+- Implement registry in order to push/pull images |
|
| 777 |
+- TCP port allocation |
|
| 778 |
+- Fix termcaps on Linux |
|
| 779 |
+- Add documentation |
|
| 780 |
+- Add Vagrant support with Vagrantfile |
|
| 781 |
+- Add unit tests |
|
| 782 |
+- Add repository/tags to ease image management |
|
| 783 |
+- Improve the layer implementation |
| ... | ... |
@@ -4,24 +4,24 @@ |
| 4 | 4 |
# |
| 5 | 5 |
# # Assemble the full dev environment. This is slow the first time. |
| 6 | 6 |
# docker build -t docker . |
| 7 |
-# # Apparmor messes with privileged mode: disable it |
|
| 8 |
-# /etc/init.d/apparmor stop ; /etc/init.d/apparmor teardown |
|
| 9 | 7 |
# |
| 10 | 8 |
# # Mount your source in an interactive container for quick testing: |
| 11 |
-# docker run -v `pwd`:/go/src/github.com/dotcloud/docker -privileged -lxc-conf=lxc.aa_profile=unconfined -i -t docker bash |
|
| 12 |
-# |
|
| 9 |
+# docker run -v `pwd`:/go/src/github.com/dotcloud/docker -privileged -i -t docker bash |
|
| 13 | 10 |
# |
| 14 | 11 |
# # Run the test suite: |
| 15 |
-# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh test |
|
| 12 |
+# docker run -privileged docker hack/make.sh test |
|
| 16 | 13 |
# |
| 17 | 14 |
# # Publish a release: |
| 18 |
-# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined \ |
|
| 15 |
+# docker run -privileged \ |
|
| 19 | 16 |
# -e AWS_S3_BUCKET=baz \ |
| 20 | 17 |
# -e AWS_ACCESS_KEY=foo \ |
| 21 | 18 |
# -e AWS_SECRET_KEY=bar \ |
| 22 | 19 |
# -e GPG_PASSPHRASE=gloubiboulga \ |
| 23 | 20 |
# docker hack/release.sh |
| 24 | 21 |
# |
| 22 |
+# Note: Apparmor used to mess with privileged mode, but this is no longer |
|
| 23 |
+# the case. Therefore, you don't have to disable it anymore. |
|
| 24 |
+# |
|
| 25 | 25 |
|
| 26 | 26 |
docker-version 0.6.1 |
| 27 | 27 |
from ubuntu:12.04 |
| ... | ... |
@@ -36,7 +36,7 @@ run apt-get install -y -q mercurial |
| 36 | 36 |
run apt-get install -y -q build-essential libsqlite3-dev |
| 37 | 37 |
|
| 38 | 38 |
# Install Go |
| 39 |
-run curl -s https://go.googlecode.com/files/go1.2rc2.src.tar.gz | tar -v -C /usr/local -xz |
|
| 39 |
+run curl -s https://go.googlecode.com/files/go1.2rc3.src.tar.gz | tar -v -C /usr/local -xz |
|
| 40 | 40 |
env PATH /usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin |
| 41 | 41 |
env GOPATH /go:/go/src/github.com/dotcloud/docker/vendor |
| 42 | 42 |
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 |
| ... | ... |
@@ -5,6 +5,7 @@ import ( |
| 5 | 5 |
"encoding/base64" |
| 6 | 6 |
"encoding/json" |
| 7 | 7 |
"fmt" |
| 8 |
+ "github.com/dotcloud/docker/archive" |
|
| 8 | 9 |
"github.com/dotcloud/docker/auth" |
| 9 | 10 |
"github.com/dotcloud/docker/utils" |
| 10 | 11 |
"github.com/gorilla/mux" |
| ... | ... |
@@ -235,6 +236,7 @@ func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Requ |
| 235 | 235 |
} |
| 236 | 236 |
w.Header().Set("Content-Type", "application/json")
|
| 237 | 237 |
wf := utils.NewWriteFlusher(w) |
| 238 |
+ wf.Flush() |
|
| 238 | 239 |
if since != 0 {
|
| 239 | 240 |
// If since, send previous events that happened after the timestamp |
| 240 | 241 |
for _, event := range srv.events {
|
| ... | ... |
@@ -905,7 +907,7 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ |
| 905 | 905 |
return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
|
| 906 | 906 |
} |
| 907 | 907 |
|
| 908 |
- c, err := Tar(root, Bzip2) |
|
| 908 |
+ c, err := archive.Tar(root, archive.Bzip2) |
|
| 909 | 909 |
if err != nil {
|
| 910 | 910 |
return err |
| 911 | 911 |
} |
| ... | ... |
@@ -5,6 +5,7 @@ type APIHistory struct {
|
| 5 | 5 |
Tags []string `json:",omitempty"` |
| 6 | 6 |
Created int64 |
| 7 | 7 |
CreatedBy string `json:",omitempty"` |
| 8 |
+ Size int64 |
|
| 8 | 9 |
} |
| 9 | 10 |
|
| 10 | 11 |
type APIImages struct {
|
| ... | ... |
@@ -77,11 +78,6 @@ type APIContainersOld struct {
|
| 77 | 77 |
SizeRootFs int64 |
| 78 | 78 |
} |
| 79 | 79 |
|
| 80 |
-type APISearch struct {
|
|
| 81 |
- Name string |
|
| 82 |
- Description string |
|
| 83 |
-} |
|
| 84 |
- |
|
| 85 | 80 |
type APIID struct {
|
| 86 | 81 |
ID string `json:"Id"` |
| 87 | 82 |
} |
| ... | ... |
@@ -499,8 +499,7 @@ func TestGetContainersTop(t *testing.T) {
|
| 499 | 499 |
container.WaitTimeout(2 * time.Second) |
| 500 | 500 |
}() |
| 501 | 501 |
|
| 502 |
- hostConfig := &HostConfig{}
|
|
| 503 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 502 |
+ if err := container.Start(); err != nil {
|
|
| 504 | 503 |
t.Fatal(err) |
| 505 | 504 |
} |
| 506 | 505 |
|
| ... | ... |
@@ -704,8 +703,7 @@ func TestPostContainersKill(t *testing.T) {
|
| 704 | 704 |
} |
| 705 | 705 |
defer runtime.Destroy(container) |
| 706 | 706 |
|
| 707 |
- hostConfig := &HostConfig{}
|
|
| 708 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 707 |
+ if err := container.Start(); err != nil {
|
|
| 709 | 708 |
t.Fatal(err) |
| 710 | 709 |
} |
| 711 | 710 |
|
| ... | ... |
@@ -747,8 +745,7 @@ func TestPostContainersRestart(t *testing.T) {
|
| 747 | 747 |
} |
| 748 | 748 |
defer runtime.Destroy(container) |
| 749 | 749 |
|
| 750 |
- hostConfig := &HostConfig{}
|
|
| 751 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 750 |
+ if err := container.Start(); err != nil {
|
|
| 752 | 751 |
t.Fatal(err) |
| 753 | 752 |
} |
| 754 | 753 |
|
| ... | ... |
@@ -855,8 +852,7 @@ func TestPostContainersStop(t *testing.T) {
|
| 855 | 855 |
} |
| 856 | 856 |
defer runtime.Destroy(container) |
| 857 | 857 |
|
| 858 |
- hostConfig := &HostConfig{}
|
|
| 859 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 858 |
+ if err := container.Start(); err != nil {
|
|
| 860 | 859 |
t.Fatal(err) |
| 861 | 860 |
} |
| 862 | 861 |
|
| ... | ... |
@@ -903,8 +899,7 @@ func TestPostContainersWait(t *testing.T) {
|
| 903 | 903 |
} |
| 904 | 904 |
defer runtime.Destroy(container) |
| 905 | 905 |
|
| 906 |
- hostConfig := &HostConfig{}
|
|
| 907 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 906 |
+ if err := container.Start(); err != nil {
|
|
| 908 | 907 |
t.Fatal(err) |
| 909 | 908 |
} |
| 910 | 909 |
|
| ... | ... |
@@ -947,8 +942,7 @@ func TestPostContainersAttach(t *testing.T) {
|
| 947 | 947 |
defer runtime.Destroy(container) |
| 948 | 948 |
|
| 949 | 949 |
// Start the process |
| 950 |
- hostConfig := &HostConfig{}
|
|
| 951 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 950 |
+ if err := container.Start(); err != nil {
|
|
| 952 | 951 |
t.Fatal(err) |
| 953 | 952 |
} |
| 954 | 953 |
|
| ... | ... |
@@ -1037,8 +1031,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
|
| 1037 | 1037 |
defer runtime.Destroy(container) |
| 1038 | 1038 |
|
| 1039 | 1039 |
// Start the process |
| 1040 |
- hostConfig := &HostConfig{}
|
|
| 1041 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 1040 |
+ if err := container.Start(); err != nil {
|
|
| 1042 | 1041 |
t.Fatal(err) |
| 1043 | 1042 |
} |
| 1044 | 1043 |
|
| 1045 | 1044 |
deleted file mode 100644 |
| ... | ... |
@@ -1,302 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "archive/tar" |
|
| 5 |
- "bytes" |
|
| 6 |
- "fmt" |
|
| 7 |
- "github.com/dotcloud/docker/utils" |
|
| 8 |
- "io" |
|
| 9 |
- "io/ioutil" |
|
| 10 |
- "os" |
|
| 11 |
- "os/exec" |
|
| 12 |
- "path" |
|
| 13 |
- "path/filepath" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-type Archive io.Reader |
|
| 17 |
- |
|
| 18 |
-type Compression uint32 |
|
| 19 |
- |
|
| 20 |
-const ( |
|
| 21 |
- Uncompressed Compression = iota |
|
| 22 |
- Bzip2 |
|
| 23 |
- Gzip |
|
| 24 |
- Xz |
|
| 25 |
-) |
|
| 26 |
- |
|
| 27 |
-func DetectCompression(source []byte) Compression {
|
|
| 28 |
- sourceLen := len(source) |
|
| 29 |
- for compression, m := range map[Compression][]byte{
|
|
| 30 |
- Bzip2: {0x42, 0x5A, 0x68},
|
|
| 31 |
- Gzip: {0x1F, 0x8B, 0x08},
|
|
| 32 |
- Xz: {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00},
|
|
| 33 |
- } {
|
|
| 34 |
- fail := false |
|
| 35 |
- if len(m) > sourceLen {
|
|
| 36 |
- utils.Debugf("Len too short")
|
|
| 37 |
- continue |
|
| 38 |
- } |
|
| 39 |
- i := 0 |
|
| 40 |
- for _, b := range m {
|
|
| 41 |
- if b != source[i] {
|
|
| 42 |
- fail = true |
|
| 43 |
- break |
|
| 44 |
- } |
|
| 45 |
- i++ |
|
| 46 |
- } |
|
| 47 |
- if !fail {
|
|
| 48 |
- return compression |
|
| 49 |
- } |
|
| 50 |
- } |
|
| 51 |
- return Uncompressed |
|
| 52 |
-} |
|
| 53 |
- |
|
| 54 |
-func (compression *Compression) Flag() string {
|
|
| 55 |
- switch *compression {
|
|
| 56 |
- case Bzip2: |
|
| 57 |
- return "j" |
|
| 58 |
- case Gzip: |
|
| 59 |
- return "z" |
|
| 60 |
- case Xz: |
|
| 61 |
- return "J" |
|
| 62 |
- } |
|
| 63 |
- return "" |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-func (compression *Compression) Extension() string {
|
|
| 67 |
- switch *compression {
|
|
| 68 |
- case Uncompressed: |
|
| 69 |
- return "tar" |
|
| 70 |
- case Bzip2: |
|
| 71 |
- return "tar.bz2" |
|
| 72 |
- case Gzip: |
|
| 73 |
- return "tar.gz" |
|
| 74 |
- case Xz: |
|
| 75 |
- return "tar.xz" |
|
| 76 |
- } |
|
| 77 |
- return "" |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-// Tar creates an archive from the directory at `path`, and returns it as a |
|
| 81 |
-// stream of bytes. |
|
| 82 |
-func Tar(path string, compression Compression) (io.Reader, error) {
|
|
| 83 |
- return TarFilter(path, compression, nil) |
|
| 84 |
-} |
|
| 85 |
- |
|
| 86 |
-// Tar creates an archive from the directory at `path`, only including files whose relative |
|
| 87 |
-// paths are included in `filter`. If `filter` is nil, then all files are included. |
|
| 88 |
-func TarFilter(path string, compression Compression, filter []string) (io.Reader, error) {
|
|
| 89 |
- args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path}
|
|
| 90 |
- if filter == nil {
|
|
| 91 |
- filter = []string{"."}
|
|
| 92 |
- } |
|
| 93 |
- for _, f := range filter {
|
|
| 94 |
- args = append(args, "-c"+compression.Flag(), f) |
|
| 95 |
- } |
|
| 96 |
- return CmdStream(exec.Command(args[0], args[1:]...)) |
|
| 97 |
-} |
|
| 98 |
- |
|
| 99 |
-// Untar reads a stream of bytes from `archive`, parses it as a tar archive, |
|
| 100 |
-// and unpacks it into the directory at `path`. |
|
| 101 |
-// The archive may be compressed with one of the following algorithms: |
|
| 102 |
-// identity (uncompressed), gzip, bzip2, xz. |
|
| 103 |
-// FIXME: specify behavior when target path exists vs. doesn't exist. |
|
| 104 |
-func Untar(archive io.Reader, path string) error {
|
|
| 105 |
- if archive == nil {
|
|
| 106 |
- return fmt.Errorf("Empty archive")
|
|
| 107 |
- } |
|
| 108 |
- |
|
| 109 |
- buf := make([]byte, 10) |
|
| 110 |
- totalN := 0 |
|
| 111 |
- for totalN < 10 {
|
|
| 112 |
- if n, err := archive.Read(buf[totalN:]); err != nil {
|
|
| 113 |
- if err == io.EOF {
|
|
| 114 |
- return fmt.Errorf("Tarball too short")
|
|
| 115 |
- } |
|
| 116 |
- return err |
|
| 117 |
- } else {
|
|
| 118 |
- totalN += n |
|
| 119 |
- utils.Debugf("[tar autodetect] n: %d", n)
|
|
| 120 |
- } |
|
| 121 |
- } |
|
| 122 |
- compression := DetectCompression(buf) |
|
| 123 |
- |
|
| 124 |
- utils.Debugf("Archive compression detected: %s", compression.Extension())
|
|
| 125 |
- |
|
| 126 |
- cmd := exec.Command("tar", "--numeric-owner", "-f", "-", "-C", path, "-x"+compression.Flag())
|
|
| 127 |
- cmd.Stdin = io.MultiReader(bytes.NewReader(buf), archive) |
|
| 128 |
- // Hardcode locale environment for predictable outcome regardless of host configuration. |
|
| 129 |
- // (see https://github.com/dotcloud/docker/issues/355) |
|
| 130 |
- cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}
|
|
| 131 |
- output, err := cmd.CombinedOutput() |
|
| 132 |
- if err != nil {
|
|
| 133 |
- return fmt.Errorf("%s: %s", err, output)
|
|
| 134 |
- } |
|
| 135 |
- return nil |
|
| 136 |
-} |
|
| 137 |
- |
|
| 138 |
-// TarUntar is a convenience function which calls Tar and Untar, with |
|
| 139 |
-// the output of one piped into the other. If either Tar or Untar fails, |
|
| 140 |
-// TarUntar aborts and returns the error. |
|
| 141 |
-func TarUntar(src string, filter []string, dst string) error {
|
|
| 142 |
- utils.Debugf("TarUntar(%s %s %s)", src, filter, dst)
|
|
| 143 |
- archive, err := TarFilter(src, Uncompressed, filter) |
|
| 144 |
- if err != nil {
|
|
| 145 |
- return err |
|
| 146 |
- } |
|
| 147 |
- return Untar(archive, dst) |
|
| 148 |
-} |
|
| 149 |
- |
|
| 150 |
-// UntarPath is a convenience function which looks for an archive |
|
| 151 |
-// at filesystem path `src`, and unpacks it at `dst`. |
|
| 152 |
-func UntarPath(src, dst string) error {
|
|
| 153 |
- if archive, err := os.Open(src); err != nil {
|
|
| 154 |
- return err |
|
| 155 |
- } else if err := Untar(archive, dst); err != nil {
|
|
| 156 |
- return err |
|
| 157 |
- } |
|
| 158 |
- return nil |
|
| 159 |
-} |
|
| 160 |
- |
|
| 161 |
-// CopyWithTar creates a tar archive of filesystem path `src`, and |
|
| 162 |
-// unpacks it at filesystem path `dst`. |
|
| 163 |
-// The archive is streamed directly with fixed buffering and no |
|
| 164 |
-// intermediary disk IO. |
|
| 165 |
-// |
|
| 166 |
-func CopyWithTar(src, dst string) error {
|
|
| 167 |
- srcSt, err := os.Stat(src) |
|
| 168 |
- if err != nil {
|
|
| 169 |
- return err |
|
| 170 |
- } |
|
| 171 |
- if !srcSt.IsDir() {
|
|
| 172 |
- return CopyFileWithTar(src, dst) |
|
| 173 |
- } |
|
| 174 |
- // Create dst, copy src's content into it |
|
| 175 |
- utils.Debugf("Creating dest directory: %s", dst)
|
|
| 176 |
- if err := os.MkdirAll(dst, 0755); err != nil && !os.IsExist(err) {
|
|
| 177 |
- return err |
|
| 178 |
- } |
|
| 179 |
- utils.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
|
| 180 |
- return TarUntar(src, nil, dst) |
|
| 181 |
-} |
|
| 182 |
- |
|
| 183 |
-// CopyFileWithTar emulates the behavior of the 'cp' command-line |
|
| 184 |
-// for a single file. It copies a regular file from path `src` to |
|
| 185 |
-// path `dst`, and preserves all its metadata. |
|
| 186 |
-// |
|
| 187 |
-// If `dst` ends with a trailing slash '/', the final destination path |
|
| 188 |
-// will be `dst/base(src)`. |
|
| 189 |
-func CopyFileWithTar(src, dst string) error {
|
|
| 190 |
- utils.Debugf("CopyFileWithTar(%s, %s)", src, dst)
|
|
| 191 |
- srcSt, err := os.Stat(src) |
|
| 192 |
- if err != nil {
|
|
| 193 |
- return err |
|
| 194 |
- } |
|
| 195 |
- if srcSt.IsDir() {
|
|
| 196 |
- return fmt.Errorf("Can't copy a directory")
|
|
| 197 |
- } |
|
| 198 |
- // Clean up the trailing / |
|
| 199 |
- if dst[len(dst)-1] == '/' {
|
|
| 200 |
- dst = path.Join(dst, filepath.Base(src)) |
|
| 201 |
- } |
|
| 202 |
- // Create the holding directory if necessary |
|
| 203 |
- if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
|
|
| 204 |
- return err |
|
| 205 |
- } |
|
| 206 |
- buf := new(bytes.Buffer) |
|
| 207 |
- tw := tar.NewWriter(buf) |
|
| 208 |
- hdr, err := tar.FileInfoHeader(srcSt, "") |
|
| 209 |
- if err != nil {
|
|
| 210 |
- return err |
|
| 211 |
- } |
|
| 212 |
- hdr.Name = filepath.Base(dst) |
|
| 213 |
- if err := tw.WriteHeader(hdr); err != nil {
|
|
| 214 |
- return err |
|
| 215 |
- } |
|
| 216 |
- srcF, err := os.Open(src) |
|
| 217 |
- if err != nil {
|
|
| 218 |
- return err |
|
| 219 |
- } |
|
| 220 |
- if _, err := io.Copy(tw, srcF); err != nil {
|
|
| 221 |
- return err |
|
| 222 |
- } |
|
| 223 |
- tw.Close() |
|
| 224 |
- return Untar(buf, filepath.Dir(dst)) |
|
| 225 |
-} |
|
| 226 |
- |
|
| 227 |
-// CmdStream executes a command, and returns its stdout as a stream. |
|
| 228 |
-// If the command fails to run or doesn't complete successfully, an error |
|
| 229 |
-// will be returned, including anything written on stderr. |
|
| 230 |
-func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
|
|
| 231 |
- stdout, err := cmd.StdoutPipe() |
|
| 232 |
- if err != nil {
|
|
| 233 |
- return nil, err |
|
| 234 |
- } |
|
| 235 |
- stderr, err := cmd.StderrPipe() |
|
| 236 |
- if err != nil {
|
|
| 237 |
- return nil, err |
|
| 238 |
- } |
|
| 239 |
- pipeR, pipeW := io.Pipe() |
|
| 240 |
- errChan := make(chan []byte) |
|
| 241 |
- // Collect stderr, we will use it in case of an error |
|
| 242 |
- go func() {
|
|
| 243 |
- errText, e := ioutil.ReadAll(stderr) |
|
| 244 |
- if e != nil {
|
|
| 245 |
- errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")")
|
|
| 246 |
- } |
|
| 247 |
- errChan <- errText |
|
| 248 |
- }() |
|
| 249 |
- // Copy stdout to the returned pipe |
|
| 250 |
- go func() {
|
|
| 251 |
- _, err := io.Copy(pipeW, stdout) |
|
| 252 |
- if err != nil {
|
|
| 253 |
- pipeW.CloseWithError(err) |
|
| 254 |
- } |
|
| 255 |
- errText := <-errChan |
|
| 256 |
- if err := cmd.Wait(); err != nil {
|
|
| 257 |
- pipeW.CloseWithError(fmt.Errorf("%s: %s", err, errText))
|
|
| 258 |
- } else {
|
|
| 259 |
- pipeW.Close() |
|
| 260 |
- } |
|
| 261 |
- }() |
|
| 262 |
- // Run the command and return the pipe |
|
| 263 |
- if err := cmd.Start(); err != nil {
|
|
| 264 |
- return nil, err |
|
| 265 |
- } |
|
| 266 |
- return pipeR, nil |
|
| 267 |
-} |
|
| 268 |
- |
|
| 269 |
-// NewTempArchive reads the content of src into a temporary file, and returns the contents |
|
| 270 |
-// of that file as an archive. The archive can only be read once - as soon as reading completes, |
|
| 271 |
-// the file will be deleted. |
|
| 272 |
-func NewTempArchive(src Archive, dir string) (*TempArchive, error) {
|
|
| 273 |
- f, err := ioutil.TempFile(dir, "") |
|
| 274 |
- if err != nil {
|
|
| 275 |
- return nil, err |
|
| 276 |
- } |
|
| 277 |
- if _, err := io.Copy(f, src); err != nil {
|
|
| 278 |
- return nil, err |
|
| 279 |
- } |
|
| 280 |
- if _, err := f.Seek(0, 0); err != nil {
|
|
| 281 |
- return nil, err |
|
| 282 |
- } |
|
| 283 |
- st, err := f.Stat() |
|
| 284 |
- if err != nil {
|
|
| 285 |
- return nil, err |
|
| 286 |
- } |
|
| 287 |
- size := st.Size() |
|
| 288 |
- return &TempArchive{f, size}, nil
|
|
| 289 |
-} |
|
| 290 |
- |
|
| 291 |
-type TempArchive struct {
|
|
| 292 |
- *os.File |
|
| 293 |
- Size int64 // Pre-computed from Stat().Size() as a convenience |
|
| 294 |
-} |
|
| 295 |
- |
|
| 296 |
-func (archive *TempArchive) Read(data []byte) (int, error) {
|
|
| 297 |
- n, err := archive.File.Read(data) |
|
| 298 |
- if err != nil {
|
|
| 299 |
- os.Remove(archive.File.Name()) |
|
| 300 |
- } |
|
| 301 |
- return n, err |
|
| 302 |
-} |
| 0 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,302 @@ |
| 0 |
+package archive |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "archive/tar" |
|
| 4 |
+ "bytes" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "github.com/dotcloud/docker/utils" |
|
| 7 |
+ "io" |
|
| 8 |
+ "io/ioutil" |
|
| 9 |
+ "os" |
|
| 10 |
+ "os/exec" |
|
| 11 |
+ "path" |
|
| 12 |
+ "path/filepath" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+type Archive io.Reader |
|
| 16 |
+ |
|
| 17 |
+type Compression uint32 |
|
| 18 |
+ |
|
| 19 |
+const ( |
|
| 20 |
+ Uncompressed Compression = iota |
|
| 21 |
+ Bzip2 |
|
| 22 |
+ Gzip |
|
| 23 |
+ Xz |
|
| 24 |
+) |
|
| 25 |
+ |
|
| 26 |
+func DetectCompression(source []byte) Compression {
|
|
| 27 |
+ sourceLen := len(source) |
|
| 28 |
+ for compression, m := range map[Compression][]byte{
|
|
| 29 |
+ Bzip2: {0x42, 0x5A, 0x68},
|
|
| 30 |
+ Gzip: {0x1F, 0x8B, 0x08},
|
|
| 31 |
+ Xz: {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00},
|
|
| 32 |
+ } {
|
|
| 33 |
+ fail := false |
|
| 34 |
+ if len(m) > sourceLen {
|
|
| 35 |
+ utils.Debugf("Len too short")
|
|
| 36 |
+ continue |
|
| 37 |
+ } |
|
| 38 |
+ i := 0 |
|
| 39 |
+ for _, b := range m {
|
|
| 40 |
+ if b != source[i] {
|
|
| 41 |
+ fail = true |
|
| 42 |
+ break |
|
| 43 |
+ } |
|
| 44 |
+ i++ |
|
| 45 |
+ } |
|
| 46 |
+ if !fail {
|
|
| 47 |
+ return compression |
|
| 48 |
+ } |
|
| 49 |
+ } |
|
| 50 |
+ return Uncompressed |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func (compression *Compression) Flag() string {
|
|
| 54 |
+ switch *compression {
|
|
| 55 |
+ case Bzip2: |
|
| 56 |
+ return "j" |
|
| 57 |
+ case Gzip: |
|
| 58 |
+ return "z" |
|
| 59 |
+ case Xz: |
|
| 60 |
+ return "J" |
|
| 61 |
+ } |
|
| 62 |
+ return "" |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+func (compression *Compression) Extension() string {
|
|
| 66 |
+ switch *compression {
|
|
| 67 |
+ case Uncompressed: |
|
| 68 |
+ return "tar" |
|
| 69 |
+ case Bzip2: |
|
| 70 |
+ return "tar.bz2" |
|
| 71 |
+ case Gzip: |
|
| 72 |
+ return "tar.gz" |
|
| 73 |
+ case Xz: |
|
| 74 |
+ return "tar.xz" |
|
| 75 |
+ } |
|
| 76 |
+ return "" |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// Tar creates an archive from the directory at `path`, and returns it as a |
|
| 80 |
+// stream of bytes. |
|
| 81 |
+func Tar(path string, compression Compression) (io.Reader, error) {
|
|
| 82 |
+ return TarFilter(path, compression, nil) |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+// Tar creates an archive from the directory at `path`, only including files whose relative |
|
| 86 |
+// paths are included in `filter`. If `filter` is nil, then all files are included. |
|
| 87 |
+func TarFilter(path string, compression Compression, filter []string) (io.Reader, error) {
|
|
| 88 |
+ args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path}
|
|
| 89 |
+ if filter == nil {
|
|
| 90 |
+ filter = []string{"."}
|
|
| 91 |
+ } |
|
| 92 |
+ for _, f := range filter {
|
|
| 93 |
+ args = append(args, "-c"+compression.Flag(), f) |
|
| 94 |
+ } |
|
| 95 |
+ return CmdStream(exec.Command(args[0], args[1:]...)) |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+// Untar reads a stream of bytes from `archive`, parses it as a tar archive, |
|
| 99 |
+// and unpacks it into the directory at `path`. |
|
| 100 |
+// The archive may be compressed with one of the following algorithms: |
|
| 101 |
+// identity (uncompressed), gzip, bzip2, xz. |
|
| 102 |
+// FIXME: specify behavior when target path exists vs. doesn't exist. |
|
| 103 |
+func Untar(archive io.Reader, path string) error {
|
|
| 104 |
+ if archive == nil {
|
|
| 105 |
+ return fmt.Errorf("Empty archive")
|
|
| 106 |
+ } |
|
| 107 |
+ |
|
| 108 |
+ buf := make([]byte, 10) |
|
| 109 |
+ totalN := 0 |
|
| 110 |
+ for totalN < 10 {
|
|
| 111 |
+ if n, err := archive.Read(buf[totalN:]); err != nil {
|
|
| 112 |
+ if err == io.EOF {
|
|
| 113 |
+ return fmt.Errorf("Tarball too short")
|
|
| 114 |
+ } |
|
| 115 |
+ return err |
|
| 116 |
+ } else {
|
|
| 117 |
+ totalN += n |
|
| 118 |
+ utils.Debugf("[tar autodetect] n: %d", n)
|
|
| 119 |
+ } |
|
| 120 |
+ } |
|
| 121 |
+ compression := DetectCompression(buf) |
|
| 122 |
+ |
|
| 123 |
+ utils.Debugf("Archive compression detected: %s", compression.Extension())
|
|
| 124 |
+ |
|
| 125 |
+ cmd := exec.Command("tar", "--numeric-owner", "-f", "-", "-C", path, "-x"+compression.Flag())
|
|
| 126 |
+ cmd.Stdin = io.MultiReader(bytes.NewReader(buf), archive) |
|
| 127 |
+ // Hardcode locale environment for predictable outcome regardless of host configuration. |
|
| 128 |
+ // (see https://github.com/dotcloud/docker/issues/355) |
|
| 129 |
+ cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}
|
|
| 130 |
+ output, err := cmd.CombinedOutput() |
|
| 131 |
+ if err != nil {
|
|
| 132 |
+ return fmt.Errorf("%s: %s", err, output)
|
|
| 133 |
+ } |
|
| 134 |
+ return nil |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+// TarUntar is a convenience function which calls Tar and Untar, with |
|
| 138 |
+// the output of one piped into the other. If either Tar or Untar fails, |
|
| 139 |
+// TarUntar aborts and returns the error. |
|
| 140 |
+func TarUntar(src string, filter []string, dst string) error {
|
|
| 141 |
+ utils.Debugf("TarUntar(%s %s %s)", src, filter, dst)
|
|
| 142 |
+ archive, err := TarFilter(src, Uncompressed, filter) |
|
| 143 |
+ if err != nil {
|
|
| 144 |
+ return err |
|
| 145 |
+ } |
|
| 146 |
+ return Untar(archive, dst) |
|
| 147 |
+} |
|
| 148 |
+ |
|
| 149 |
+// UntarPath is a convenience function which looks for an archive |
|
| 150 |
+// at filesystem path `src`, and unpacks it at `dst`. |
|
| 151 |
+func UntarPath(src, dst string) error {
|
|
| 152 |
+ if archive, err := os.Open(src); err != nil {
|
|
| 153 |
+ return err |
|
| 154 |
+ } else if err := Untar(archive, dst); err != nil {
|
|
| 155 |
+ return err |
|
| 156 |
+ } |
|
| 157 |
+ return nil |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 160 |
+// CopyWithTar creates a tar archive of filesystem path `src`, and |
|
| 161 |
+// unpacks it at filesystem path `dst`. |
|
| 162 |
+// The archive is streamed directly with fixed buffering and no |
|
| 163 |
+// intermediary disk IO. |
|
| 164 |
+// |
|
| 165 |
+func CopyWithTar(src, dst string) error {
|
|
| 166 |
+ srcSt, err := os.Stat(src) |
|
| 167 |
+ if err != nil {
|
|
| 168 |
+ return err |
|
| 169 |
+ } |
|
| 170 |
+ if !srcSt.IsDir() {
|
|
| 171 |
+ return CopyFileWithTar(src, dst) |
|
| 172 |
+ } |
|
| 173 |
+ // Create dst, copy src's content into it |
|
| 174 |
+ utils.Debugf("Creating dest directory: %s", dst)
|
|
| 175 |
+ if err := os.MkdirAll(dst, 0755); err != nil && !os.IsExist(err) {
|
|
| 176 |
+ return err |
|
| 177 |
+ } |
|
| 178 |
+ utils.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
|
| 179 |
+ return TarUntar(src, nil, dst) |
|
| 180 |
+} |
|
| 181 |
+ |
|
| 182 |
+// CopyFileWithTar emulates the behavior of the 'cp' command-line |
|
| 183 |
+// for a single file. It copies a regular file from path `src` to |
|
| 184 |
+// path `dst`, and preserves all its metadata. |
|
| 185 |
+// |
|
| 186 |
+// If `dst` ends with a trailing slash '/', the final destination path |
|
| 187 |
+// will be `dst/base(src)`. |
|
| 188 |
+func CopyFileWithTar(src, dst string) error {
|
|
| 189 |
+ utils.Debugf("CopyFileWithTar(%s, %s)", src, dst)
|
|
| 190 |
+ srcSt, err := os.Stat(src) |
|
| 191 |
+ if err != nil {
|
|
| 192 |
+ return err |
|
| 193 |
+ } |
|
| 194 |
+ if srcSt.IsDir() {
|
|
| 195 |
+ return fmt.Errorf("Can't copy a directory")
|
|
| 196 |
+ } |
|
| 197 |
+ // Clean up the trailing / |
|
| 198 |
+ if dst[len(dst)-1] == '/' {
|
|
| 199 |
+ dst = path.Join(dst, filepath.Base(src)) |
|
| 200 |
+ } |
|
| 201 |
+ // Create the holding directory if necessary |
|
| 202 |
+ if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
|
|
| 203 |
+ return err |
|
| 204 |
+ } |
|
| 205 |
+ buf := new(bytes.Buffer) |
|
| 206 |
+ tw := tar.NewWriter(buf) |
|
| 207 |
+ hdr, err := tar.FileInfoHeader(srcSt, "") |
|
| 208 |
+ if err != nil {
|
|
| 209 |
+ return err |
|
| 210 |
+ } |
|
| 211 |
+ hdr.Name = filepath.Base(dst) |
|
| 212 |
+ if err := tw.WriteHeader(hdr); err != nil {
|
|
| 213 |
+ return err |
|
| 214 |
+ } |
|
| 215 |
+ srcF, err := os.Open(src) |
|
| 216 |
+ if err != nil {
|
|
| 217 |
+ return err |
|
| 218 |
+ } |
|
| 219 |
+ if _, err := io.Copy(tw, srcF); err != nil {
|
|
| 220 |
+ return err |
|
| 221 |
+ } |
|
| 222 |
+ tw.Close() |
|
| 223 |
+ return Untar(buf, filepath.Dir(dst)) |
|
| 224 |
+} |
|
| 225 |
+ |
|
| 226 |
+// CmdStream executes a command, and returns its stdout as a stream. |
|
| 227 |
+// If the command fails to run or doesn't complete successfully, an error |
|
| 228 |
+// will be returned, including anything written on stderr. |
|
| 229 |
+func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
|
|
| 230 |
+ stdout, err := cmd.StdoutPipe() |
|
| 231 |
+ if err != nil {
|
|
| 232 |
+ return nil, err |
|
| 233 |
+ } |
|
| 234 |
+ stderr, err := cmd.StderrPipe() |
|
| 235 |
+ if err != nil {
|
|
| 236 |
+ return nil, err |
|
| 237 |
+ } |
|
| 238 |
+ pipeR, pipeW := io.Pipe() |
|
| 239 |
+ errChan := make(chan []byte) |
|
| 240 |
+ // Collect stderr, we will use it in case of an error |
|
| 241 |
+ go func() {
|
|
| 242 |
+ errText, e := ioutil.ReadAll(stderr) |
|
| 243 |
+ if e != nil {
|
|
| 244 |
+ errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")")
|
|
| 245 |
+ } |
|
| 246 |
+ errChan <- errText |
|
| 247 |
+ }() |
|
| 248 |
+ // Copy stdout to the returned pipe |
|
| 249 |
+ go func() {
|
|
| 250 |
+ _, err := io.Copy(pipeW, stdout) |
|
| 251 |
+ if err != nil {
|
|
| 252 |
+ pipeW.CloseWithError(err) |
|
| 253 |
+ } |
|
| 254 |
+ errText := <-errChan |
|
| 255 |
+ if err := cmd.Wait(); err != nil {
|
|
| 256 |
+ pipeW.CloseWithError(fmt.Errorf("%s: %s", err, errText))
|
|
| 257 |
+ } else {
|
|
| 258 |
+ pipeW.Close() |
|
| 259 |
+ } |
|
| 260 |
+ }() |
|
| 261 |
+ // Run the command and return the pipe |
|
| 262 |
+ if err := cmd.Start(); err != nil {
|
|
| 263 |
+ return nil, err |
|
| 264 |
+ } |
|
| 265 |
+ return pipeR, nil |
|
| 266 |
+} |
|
| 267 |
+ |
|
| 268 |
+// NewTempArchive reads the content of src into a temporary file, and returns the contents |
|
| 269 |
+// of that file as an archive. The archive can only be read once - as soon as reading completes, |
|
| 270 |
+// the file will be deleted. |
|
| 271 |
+func NewTempArchive(src Archive, dir string) (*TempArchive, error) {
|
|
| 272 |
+ f, err := ioutil.TempFile(dir, "") |
|
| 273 |
+ if err != nil {
|
|
| 274 |
+ return nil, err |
|
| 275 |
+ } |
|
| 276 |
+ if _, err := io.Copy(f, src); err != nil {
|
|
| 277 |
+ return nil, err |
|
| 278 |
+ } |
|
| 279 |
+ if _, err := f.Seek(0, 0); err != nil {
|
|
| 280 |
+ return nil, err |
|
| 281 |
+ } |
|
| 282 |
+ st, err := f.Stat() |
|
| 283 |
+ if err != nil {
|
|
| 284 |
+ return nil, err |
|
| 285 |
+ } |
|
| 286 |
+ size := st.Size() |
|
| 287 |
+ return &TempArchive{f, size}, nil
|
|
| 288 |
+} |
|
| 289 |
+ |
|
| 290 |
+type TempArchive struct {
|
|
| 291 |
+ *os.File |
|
| 292 |
+ Size int64 // Pre-computed from Stat().Size() as a convenience |
|
| 293 |
+} |
|
| 294 |
+ |
|
| 295 |
+func (archive *TempArchive) Read(data []byte) (int, error) {
|
|
| 296 |
+ n, err := archive.File.Read(data) |
|
| 297 |
+ if err != nil {
|
|
| 298 |
+ os.Remove(archive.File.Name()) |
|
| 299 |
+ } |
|
| 300 |
+ return n, err |
|
| 301 |
+} |
| 0 | 302 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,118 @@ |
| 0 |
+package archive |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "io" |
|
| 6 |
+ "io/ioutil" |
|
| 7 |
+ "os" |
|
| 8 |
+ "os/exec" |
|
| 9 |
+ "path" |
|
| 10 |
+ "testing" |
|
| 11 |
+ "time" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+func TestCmdStreamLargeStderr(t *testing.T) {
|
|
| 15 |
+ cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
|
|
| 16 |
+ out, err := CmdStream(cmd) |
|
| 17 |
+ if err != nil {
|
|
| 18 |
+ t.Fatalf("Failed to start command: %s", err)
|
|
| 19 |
+ } |
|
| 20 |
+ errCh := make(chan error) |
|
| 21 |
+ go func() {
|
|
| 22 |
+ _, err := io.Copy(ioutil.Discard, out) |
|
| 23 |
+ errCh <- err |
|
| 24 |
+ }() |
|
| 25 |
+ select {
|
|
| 26 |
+ case err := <-errCh: |
|
| 27 |
+ if err != nil {
|
|
| 28 |
+ t.Fatalf("Command should not have failed (err=%.100s...)", err)
|
|
| 29 |
+ } |
|
| 30 |
+ case <-time.After(5 * time.Second): |
|
| 31 |
+ t.Fatalf("Command did not complete in 5 seconds; probable deadlock")
|
|
| 32 |
+ } |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+func TestCmdStreamBad(t *testing.T) {
|
|
| 36 |
+ badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
|
|
| 37 |
+ out, err := CmdStream(badCmd) |
|
| 38 |
+ if err != nil {
|
|
| 39 |
+ t.Fatalf("Failed to start command: %s", err)
|
|
| 40 |
+ } |
|
| 41 |
+ if output, err := ioutil.ReadAll(out); err == nil {
|
|
| 42 |
+ t.Fatalf("Command should have failed")
|
|
| 43 |
+ } else if err.Error() != "exit status 1: error couldn't reverse the phase pulser\n" {
|
|
| 44 |
+ t.Fatalf("Wrong error value (%s)", err)
|
|
| 45 |
+ } else if s := string(output); s != "hello\n" {
|
|
| 46 |
+ t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output)
|
|
| 47 |
+ } |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+func TestCmdStreamGood(t *testing.T) {
|
|
| 51 |
+ cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0")
|
|
| 52 |
+ out, err := CmdStream(cmd) |
|
| 53 |
+ if err != nil {
|
|
| 54 |
+ t.Fatal(err) |
|
| 55 |
+ } |
|
| 56 |
+ if output, err := ioutil.ReadAll(out); err != nil {
|
|
| 57 |
+ t.Fatalf("Command should not have failed (err=%s)", err)
|
|
| 58 |
+ } else if s := string(output); s != "hello\n" {
|
|
| 59 |
+ t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output)
|
|
| 60 |
+ } |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func tarUntar(t *testing.T, origin string, compression Compression) error {
|
|
| 64 |
+ archive, err := Tar(origin, compression) |
|
| 65 |
+ if err != nil {
|
|
| 66 |
+ t.Fatal(err) |
|
| 67 |
+ } |
|
| 68 |
+ |
|
| 69 |
+ buf := make([]byte, 10) |
|
| 70 |
+ if _, err := archive.Read(buf); err != nil {
|
|
| 71 |
+ return err |
|
| 72 |
+ } |
|
| 73 |
+ archive = io.MultiReader(bytes.NewReader(buf), archive) |
|
| 74 |
+ |
|
| 75 |
+ detectedCompression := DetectCompression(buf) |
|
| 76 |
+ if detectedCompression.Extension() != compression.Extension() {
|
|
| 77 |
+ return fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
|
|
| 78 |
+ } |
|
| 79 |
+ |
|
| 80 |
+ tmp, err := ioutil.TempDir("", "docker-test-untar")
|
|
| 81 |
+ if err != nil {
|
|
| 82 |
+ return err |
|
| 83 |
+ } |
|
| 84 |
+ defer os.RemoveAll(tmp) |
|
| 85 |
+ if err := Untar(archive, tmp); err != nil {
|
|
| 86 |
+ return err |
|
| 87 |
+ } |
|
| 88 |
+ if _, err := os.Stat(tmp); err != nil {
|
|
| 89 |
+ return err |
|
| 90 |
+ } |
|
| 91 |
+ return nil |
|
| 92 |
+} |
|
| 93 |
+ |
|
| 94 |
+func TestTarUntar(t *testing.T) {
|
|
| 95 |
+ origin, err := ioutil.TempDir("", "docker-test-untar-origin")
|
|
| 96 |
+ if err != nil {
|
|
| 97 |
+ t.Fatal(err) |
|
| 98 |
+ } |
|
| 99 |
+ defer os.RemoveAll(origin) |
|
| 100 |
+ if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
|
|
| 101 |
+ t.Fatal(err) |
|
| 102 |
+ } |
|
| 103 |
+ if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
|
|
| 104 |
+ t.Fatal(err) |
|
| 105 |
+ } |
|
| 106 |
+ |
|
| 107 |
+ for _, c := range []Compression{
|
|
| 108 |
+ Uncompressed, |
|
| 109 |
+ Gzip, |
|
| 110 |
+ Bzip2, |
|
| 111 |
+ Xz, |
|
| 112 |
+ } {
|
|
| 113 |
+ if err := tarUntar(t, origin, c); err != nil {
|
|
| 114 |
+ t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
|
|
| 115 |
+ } |
|
| 116 |
+ } |
|
| 117 |
+} |
| 0 | 118 |
deleted file mode 100644 |
| ... | ... |
@@ -1,118 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "fmt" |
|
| 6 |
- "io" |
|
| 7 |
- "io/ioutil" |
|
| 8 |
- "os" |
|
| 9 |
- "os/exec" |
|
| 10 |
- "path" |
|
| 11 |
- "testing" |
|
| 12 |
- "time" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-func TestCmdStreamLargeStderr(t *testing.T) {
|
|
| 16 |
- cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
|
|
| 17 |
- out, err := CmdStream(cmd) |
|
| 18 |
- if err != nil {
|
|
| 19 |
- t.Fatalf("Failed to start command: %s", err)
|
|
| 20 |
- } |
|
| 21 |
- errCh := make(chan error) |
|
| 22 |
- go func() {
|
|
| 23 |
- _, err := io.Copy(ioutil.Discard, out) |
|
| 24 |
- errCh <- err |
|
| 25 |
- }() |
|
| 26 |
- select {
|
|
| 27 |
- case err := <-errCh: |
|
| 28 |
- if err != nil {
|
|
| 29 |
- t.Fatalf("Command should not have failed (err=%.100s...)", err)
|
|
| 30 |
- } |
|
| 31 |
- case <-time.After(5 * time.Second): |
|
| 32 |
- t.Fatalf("Command did not complete in 5 seconds; probable deadlock")
|
|
| 33 |
- } |
|
| 34 |
-} |
|
| 35 |
- |
|
| 36 |
-func TestCmdStreamBad(t *testing.T) {
|
|
| 37 |
- badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
|
|
| 38 |
- out, err := CmdStream(badCmd) |
|
| 39 |
- if err != nil {
|
|
| 40 |
- t.Fatalf("Failed to start command: %s", err)
|
|
| 41 |
- } |
|
| 42 |
- if output, err := ioutil.ReadAll(out); err == nil {
|
|
| 43 |
- t.Fatalf("Command should have failed")
|
|
| 44 |
- } else if err.Error() != "exit status 1: error couldn't reverse the phase pulser\n" {
|
|
| 45 |
- t.Fatalf("Wrong error value (%s)", err)
|
|
| 46 |
- } else if s := string(output); s != "hello\n" {
|
|
| 47 |
- t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output)
|
|
| 48 |
- } |
|
| 49 |
-} |
|
| 50 |
- |
|
| 51 |
-func TestCmdStreamGood(t *testing.T) {
|
|
| 52 |
- cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0")
|
|
| 53 |
- out, err := CmdStream(cmd) |
|
| 54 |
- if err != nil {
|
|
| 55 |
- t.Fatal(err) |
|
| 56 |
- } |
|
| 57 |
- if output, err := ioutil.ReadAll(out); err != nil {
|
|
| 58 |
- t.Fatalf("Command should not have failed (err=%s)", err)
|
|
| 59 |
- } else if s := string(output); s != "hello\n" {
|
|
| 60 |
- t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output)
|
|
| 61 |
- } |
|
| 62 |
-} |
|
| 63 |
- |
|
| 64 |
-func tarUntar(t *testing.T, origin string, compression Compression) error {
|
|
| 65 |
- archive, err := Tar(origin, compression) |
|
| 66 |
- if err != nil {
|
|
| 67 |
- t.Fatal(err) |
|
| 68 |
- } |
|
| 69 |
- |
|
| 70 |
- buf := make([]byte, 10) |
|
| 71 |
- if _, err := archive.Read(buf); err != nil {
|
|
| 72 |
- return err |
|
| 73 |
- } |
|
| 74 |
- archive = io.MultiReader(bytes.NewReader(buf), archive) |
|
| 75 |
- |
|
| 76 |
- detectedCompression := DetectCompression(buf) |
|
| 77 |
- if detectedCompression.Extension() != compression.Extension() {
|
|
| 78 |
- return fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
|
|
| 79 |
- } |
|
| 80 |
- |
|
| 81 |
- tmp, err := ioutil.TempDir("", "docker-test-untar")
|
|
| 82 |
- if err != nil {
|
|
| 83 |
- return err |
|
| 84 |
- } |
|
| 85 |
- defer os.RemoveAll(tmp) |
|
| 86 |
- if err := Untar(archive, tmp); err != nil {
|
|
| 87 |
- return err |
|
| 88 |
- } |
|
| 89 |
- if _, err := os.Stat(tmp); err != nil {
|
|
| 90 |
- return err |
|
| 91 |
- } |
|
| 92 |
- return nil |
|
| 93 |
-} |
|
| 94 |
- |
|
| 95 |
-func TestTarUntar(t *testing.T) {
|
|
| 96 |
- origin, err := ioutil.TempDir("", "docker-test-untar-origin")
|
|
| 97 |
- if err != nil {
|
|
| 98 |
- t.Fatal(err) |
|
| 99 |
- } |
|
| 100 |
- defer os.RemoveAll(origin) |
|
| 101 |
- if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
|
|
| 102 |
- t.Fatal(err) |
|
| 103 |
- } |
|
| 104 |
- if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
|
|
| 105 |
- t.Fatal(err) |
|
| 106 |
- } |
|
| 107 |
- |
|
| 108 |
- for _, c := range []Compression{
|
|
| 109 |
- Uncompressed, |
|
| 110 |
- Gzip, |
|
| 111 |
- Bzip2, |
|
| 112 |
- Xz, |
|
| 113 |
- } {
|
|
| 114 |
- if err := tarUntar(t, origin, c); err != nil {
|
|
| 115 |
- t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
|
|
| 116 |
- } |
|
| 117 |
- } |
|
| 118 |
-} |
| ... | ... |
@@ -3,6 +3,7 @@ package docker |
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/json" |
| 5 | 5 |
"fmt" |
| 6 |
+ "github.com/dotcloud/docker/archive" |
|
| 6 | 7 |
"github.com/dotcloud/docker/utils" |
| 7 | 8 |
"io" |
| 8 | 9 |
"io/ioutil" |
| ... | ... |
@@ -64,6 +65,9 @@ func (b *buildFile) CmdFrom(name string) error {
|
| 64 | 64 |
} |
| 65 | 65 |
b.image = image.ID |
| 66 | 66 |
b.config = &Config{}
|
| 67 |
+ if image.Config != nil {
|
|
| 68 |
+ b.config = image.Config |
|
| 69 |
+ } |
|
| 67 | 70 |
if b.config.Env == nil || len(b.config.Env) == 0 {
|
| 68 | 71 |
b.config.Env = append(b.config.Env, "HOME=/", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") |
| 69 | 72 |
} |
| ... | ... |
@@ -291,17 +295,17 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error {
|
| 291 | 291 |
return fmt.Errorf("%s: no such file or directory", orig)
|
| 292 | 292 |
} |
| 293 | 293 |
if fi.IsDir() {
|
| 294 |
- if err := CopyWithTar(origPath, destPath); err != nil {
|
|
| 294 |
+ if err := archive.CopyWithTar(origPath, destPath); err != nil {
|
|
| 295 | 295 |
return err |
| 296 | 296 |
} |
| 297 | 297 |
// First try to unpack the source as an archive |
| 298 |
- } else if err := UntarPath(origPath, destPath); err != nil {
|
|
| 298 |
+ } else if err := archive.UntarPath(origPath, destPath); err != nil {
|
|
| 299 | 299 |
utils.Debugf("Couldn't untar %s to %s: %s", origPath, destPath, err)
|
| 300 | 300 |
// If that fails, just copy it as a regular file |
| 301 | 301 |
if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
|
| 302 | 302 |
return err |
| 303 | 303 |
} |
| 304 |
- if err := CopyWithTar(origPath, destPath); err != nil {
|
|
| 304 |
+ if err := archive.CopyWithTar(origPath, destPath); err != nil {
|
|
| 305 | 305 |
return err |
| 306 | 306 |
} |
| 307 | 307 |
} |
| ... | ... |
@@ -387,8 +391,7 @@ func (b *buildFile) run() (string, error) {
|
| 387 | 387 |
} |
| 388 | 388 |
|
| 389 | 389 |
//start the container |
| 390 |
- hostConfig := &HostConfig{}
|
|
| 391 |
- if err := c.Start(hostConfig); err != nil {
|
|
| 390 |
+ if err := c.Start(); err != nil {
|
|
| 392 | 391 |
return "", err |
| 393 | 392 |
} |
| 394 | 393 |
|
| ... | ... |
@@ -473,7 +476,7 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
|
| 473 | 473 |
if err != nil {
|
| 474 | 474 |
return "", err |
| 475 | 475 |
} |
| 476 |
- if err := Untar(context, name); err != nil {
|
|
| 476 |
+ if err := archive.Untar(context, name); err != nil {
|
|
| 477 | 477 |
return "", err |
| 478 | 478 |
} |
| 479 | 479 |
defer os.RemoveAll(name) |
| ... | ... |
@@ -2,6 +2,7 @@ package docker |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
+ "github.com/dotcloud/docker/archive" |
|
| 5 | 6 |
"io/ioutil" |
| 6 | 7 |
"net" |
| 7 | 8 |
"net/http" |
| ... | ... |
@@ -12,7 +13,7 @@ import ( |
| 12 | 12 |
|
| 13 | 13 |
// mkTestContext generates a build context from the contents of the provided dockerfile. |
| 14 | 14 |
// This context is suitable for use as an argument to BuildFile.Build() |
| 15 |
-func mkTestContext(dockerfile string, files [][2]string, t *testing.T) Archive {
|
|
| 15 |
+func mkTestContext(dockerfile string, files [][2]string, t *testing.T) archive.Archive {
|
|
| 16 | 16 |
context, err := mkBuildContext(dockerfile, files) |
| 17 | 17 |
if err != nil {
|
| 18 | 18 |
t.Fatal(err) |
| ... | ... |
@@ -541,3 +542,39 @@ func TestBuildADDFileNotFound(t *testing.T) {
|
| 541 | 541 |
t.Fail() |
| 542 | 542 |
} |
| 543 | 543 |
} |
| 544 |
+ |
|
| 545 |
+func TestBuildInheritance(t *testing.T) {
|
|
| 546 |
+ runtime, err := newTestRuntime("")
|
|
| 547 |
+ if err != nil {
|
|
| 548 |
+ t.Fatal(err) |
|
| 549 |
+ } |
|
| 550 |
+ defer nuke(runtime) |
|
| 551 |
+ |
|
| 552 |
+ srv := &Server{
|
|
| 553 |
+ runtime: runtime, |
|
| 554 |
+ pullingPool: make(map[string]struct{}),
|
|
| 555 |
+ pushingPool: make(map[string]struct{}),
|
|
| 556 |
+ } |
|
| 557 |
+ |
|
| 558 |
+ img := buildImage(testContextTemplate{`
|
|
| 559 |
+ from {IMAGE}
|
|
| 560 |
+ expose 4243 |
|
| 561 |
+ `, |
|
| 562 |
+ nil, nil}, t, srv, true) |
|
| 563 |
+ |
|
| 564 |
+ img2 := buildImage(testContextTemplate{fmt.Sprintf(`
|
|
| 565 |
+ from %s |
|
| 566 |
+ entrypoint ["/bin/echo"] |
|
| 567 |
+ `, img.ID), |
|
| 568 |
+ nil, nil}, t, srv, true) |
|
| 569 |
+ |
|
| 570 |
+ // from child |
|
| 571 |
+ if img2.Config.Entrypoint[0] != "/bin/echo" {
|
|
| 572 |
+ t.Fail() |
|
| 573 |
+ } |
|
| 574 |
+ |
|
| 575 |
+ // from parent |
|
| 576 |
+ if img.Config.PortSpecs[0] != "4243" {
|
|
| 577 |
+ t.Fail() |
|
| 578 |
+ } |
|
| 579 |
+} |
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"errors" |
| 10 | 10 |
"flag" |
| 11 | 11 |
"fmt" |
| 12 |
+ "github.com/dotcloud/docker/archive" |
|
| 12 | 13 |
"github.com/dotcloud/docker/auth" |
| 13 | 14 |
"github.com/dotcloud/docker/registry" |
| 14 | 15 |
"github.com/dotcloud/docker/term" |
| ... | ... |
@@ -137,7 +138,7 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
|
| 137 | 137 |
|
| 138 | 138 |
// mkBuildContext returns an archive of an empty context with the contents |
| 139 | 139 |
// of `dockerfile` at the path ./Dockerfile |
| 140 |
-func mkBuildContext(dockerfile string, files [][2]string) (Archive, error) {
|
|
| 140 |
+func mkBuildContext(dockerfile string, files [][2]string) (archive.Archive, error) {
|
|
| 141 | 141 |
buf := new(bytes.Buffer) |
| 142 | 142 |
tw := tar.NewWriter(buf) |
| 143 | 143 |
files = append(files, [2]string{"Dockerfile", dockerfile})
|
| ... | ... |
@@ -175,7 +176,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
| 175 | 175 |
} |
| 176 | 176 |
|
| 177 | 177 |
var ( |
| 178 |
- context Archive |
|
| 178 |
+ context archive.Archive |
|
| 179 | 179 |
isRemote bool |
| 180 | 180 |
err error |
| 181 | 181 |
) |
| ... | ... |
@@ -194,7 +195,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
| 194 | 194 |
if _, err := os.Stat(cmd.Arg(0)); err != nil {
|
| 195 | 195 |
return err |
| 196 | 196 |
} |
| 197 |
- context, err = Tar(cmd.Arg(0), Uncompressed) |
|
| 197 |
+ context, err = archive.Tar(cmd.Arg(0), archive.Uncompressed) |
|
| 198 | 198 |
} |
| 199 | 199 |
var body io.Reader |
| 200 | 200 |
// Setup an upload progress bar |
| ... | ... |
@@ -491,7 +492,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
| 491 | 491 |
} |
| 492 | 492 |
|
| 493 | 493 |
func (cli *DockerCli) CmdStop(args ...string) error {
|
| 494 |
- cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container")
|
|
| 494 |
+ cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container (Send SIGTERM, and then SIGKILL after grace period)")
|
|
| 495 | 495 |
nSeconds := cmd.Int("t", 10, "Number of seconds to wait for the container to stop before killing it.")
|
| 496 | 496 |
if err := cmd.Parse(args); err != nil {
|
| 497 | 497 |
return nil |
| ... | ... |
@@ -654,7 +655,11 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
| 654 | 654 |
if err != nil {
|
| 655 | 655 |
obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
|
| 656 | 656 |
if err != nil {
|
| 657 |
- fmt.Fprintf(cli.err, "No such image or container: %s\n", name) |
|
| 657 |
+ if strings.Contains(err.Error(), "No such") {
|
|
| 658 |
+ fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name) |
|
| 659 |
+ } else {
|
|
| 660 |
+ fmt.Fprintf(cli.err, "%s", err) |
|
| 661 |
+ } |
|
| 658 | 662 |
status = 1 |
| 659 | 663 |
continue |
| 660 | 664 |
} |
| ... | ... |
@@ -667,9 +672,11 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
| 667 | 667 |
} |
| 668 | 668 |
indented.WriteString(",")
|
| 669 | 669 |
} |
| 670 |
- // Remove trailling ',' |
|
| 671 |
- indented.Truncate(indented.Len() - 1) |
|
| 672 | 670 |
|
| 671 |
+ if indented.Len() > 0 {
|
|
| 672 |
+ // Remove trailing ',' |
|
| 673 |
+ indented.Truncate(indented.Len() - 1) |
|
| 674 |
+ } |
|
| 673 | 675 |
fmt.Fprintf(cli.out, "[") |
| 674 | 676 |
if _, err := io.Copy(cli.out, indented); err != nil {
|
| 675 | 677 |
return err |
| ... | ... |
@@ -682,7 +689,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
| 682 | 682 |
} |
| 683 | 683 |
|
| 684 | 684 |
func (cli *DockerCli) CmdTop(args ...string) error {
|
| 685 |
- cmd := Subcmd("top", "CONTAINER", "Lookup the running processes of a container")
|
|
| 685 |
+ cmd := Subcmd("top", "CONTAINER [ps OPTIONS]", "Lookup the running processes of a container")
|
|
| 686 | 686 |
if err := cmd.Parse(args); err != nil {
|
| 687 | 687 |
return nil |
| 688 | 688 |
} |
| ... | ... |
@@ -788,7 +795,10 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
|
| 788 | 788 |
} |
| 789 | 789 |
|
| 790 | 790 |
func (cli *DockerCli) CmdHistory(args ...string) error {
|
| 791 |
- cmd := Subcmd("history", "IMAGE", "Show the history of an image")
|
|
| 791 |
+ cmd := Subcmd("history", "[OPTIONS] IMAGE", "Show the history of an image")
|
|
| 792 |
+ quiet := cmd.Bool("q", false, "only show numeric IDs")
|
|
| 793 |
+ noTrunc := cmd.Bool("notrunc", false, "Don't truncate output")
|
|
| 794 |
+ |
|
| 792 | 795 |
if err := cmd.Parse(args); err != nil {
|
| 793 | 796 |
return nil |
| 794 | 797 |
} |
| ... | ... |
@@ -807,14 +817,35 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
|
| 807 | 807 |
if err != nil {
|
| 808 | 808 |
return err |
| 809 | 809 |
} |
| 810 |
+ |
|
| 810 | 811 |
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) |
| 811 |
- fmt.Fprintln(w, "ID\tCREATED\tCREATED BY") |
|
| 812 |
+ if !*quiet {
|
|
| 813 |
+ fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE") |
|
| 814 |
+ } |
|
| 812 | 815 |
|
| 813 | 816 |
for _, out := range outs {
|
| 814 |
- if out.Tags != nil {
|
|
| 815 |
- out.ID = out.Tags[0] |
|
| 817 |
+ if !*quiet {
|
|
| 818 |
+ if *noTrunc {
|
|
| 819 |
+ fmt.Fprintf(w, "%s\t", out.ID) |
|
| 820 |
+ } else {
|
|
| 821 |
+ fmt.Fprintf(w, "%s\t", utils.TruncateID(out.ID)) |
|
| 822 |
+ } |
|
| 823 |
+ |
|
| 824 |
+ fmt.Fprintf(w, "%s ago\t", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) |
|
| 825 |
+ |
|
| 826 |
+ if *noTrunc {
|
|
| 827 |
+ fmt.Fprintf(w, "%s\t", out.CreatedBy) |
|
| 828 |
+ } else {
|
|
| 829 |
+ fmt.Fprintf(w, "%s\t", utils.Trunc(out.CreatedBy, 45)) |
|
| 830 |
+ } |
|
| 831 |
+ fmt.Fprintf(w, "%s\n", utils.HumanSize(out.Size)) |
|
| 832 |
+ } else {
|
|
| 833 |
+ if *noTrunc {
|
|
| 834 |
+ fmt.Fprintln(w, out.ID) |
|
| 835 |
+ } else {
|
|
| 836 |
+ fmt.Fprintln(w, utils.TruncateID(out.ID)) |
|
| 837 |
+ } |
|
| 816 | 838 |
} |
| 817 |
- fmt.Fprintf(w, "%s \t%s ago\t%s\n", out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy) |
|
| 818 | 839 |
} |
| 819 | 840 |
w.Flush() |
| 820 | 841 |
return nil |
| ... | ... |
@@ -852,7 +883,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
| 852 | 852 |
|
| 853 | 853 |
// 'docker kill NAME' kills a running container |
| 854 | 854 |
func (cli *DockerCli) CmdKill(args ...string) error {
|
| 855 |
- cmd := Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container")
|
|
| 855 |
+ cmd := Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container (send SIGKILL)")
|
|
| 856 | 856 |
if err := cmd.Parse(args); err != nil {
|
| 857 | 857 |
return nil |
| 858 | 858 |
} |
| ... | ... |
@@ -873,7 +904,7 @@ func (cli *DockerCli) CmdKill(args ...string) error {
|
| 873 | 873 |
} |
| 874 | 874 |
|
| 875 | 875 |
func (cli *DockerCli) CmdImport(args ...string) error {
|
| 876 |
- cmd := Subcmd("import", "URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball(.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).")
|
|
| 876 |
+ cmd := Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create a new filesystem image from the contents of a tarball(.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).")
|
|
| 877 | 877 |
|
| 878 | 878 |
if err := cmd.Parse(args); err != nil {
|
| 879 | 879 |
return nil |
| ... | ... |
@@ -882,7 +913,8 @@ func (cli *DockerCli) CmdImport(args ...string) error {
|
| 882 | 882 |
cmd.Usage() |
| 883 | 883 |
return nil |
| 884 | 884 |
} |
| 885 |
- src, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2) |
|
| 885 |
+ src := cmd.Arg(0) |
|
| 886 |
+ repository, tag := utils.ParseRepositoryTag(cmd.Arg(1)) |
|
| 886 | 887 |
v := url.Values{}
|
| 887 | 888 |
v.Set("repo", repository)
|
| 888 | 889 |
v.Set("tag", tag)
|
| ... | ... |
@@ -1062,7 +1094,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
| 1062 | 1062 |
|
| 1063 | 1063 |
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) |
| 1064 | 1064 |
if !*quiet {
|
| 1065 |
- fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED\tSIZE") |
|
| 1065 |
+ fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tSIZE") |
|
| 1066 | 1066 |
} |
| 1067 | 1067 |
|
| 1068 | 1068 |
for _, out := range outs {
|
| ... | ... |
@@ -1155,7 +1187,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
| 1155 | 1155 |
} |
| 1156 | 1156 |
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) |
| 1157 | 1157 |
if !*quiet {
|
| 1158 |
- fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES") |
|
| 1158 |
+ fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES") |
|
| 1159 | 1159 |
if *size {
|
| 1160 | 1160 |
fmt.Fprintln(w, "\tSIZE") |
| 1161 | 1161 |
} else {
|
| ... | ... |
@@ -1199,14 +1231,16 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
| 1199 | 1199 |
} |
| 1200 | 1200 |
|
| 1201 | 1201 |
func (cli *DockerCli) CmdCommit(args ...string) error {
|
| 1202 |
- cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes")
|
|
| 1202 |
+ cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes")
|
|
| 1203 | 1203 |
flComment := cmd.String("m", "", "Commit message")
|
| 1204 | 1204 |
flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
|
| 1205 | 1205 |
flConfig := cmd.String("run", "", "Config automatically applied when the image is run. "+`(ex: {"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')`)
|
| 1206 | 1206 |
if err := cmd.Parse(args); err != nil {
|
| 1207 | 1207 |
return nil |
| 1208 | 1208 |
} |
| 1209 |
- name, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2) |
|
| 1209 |
+ name := cmd.Arg(0) |
|
| 1210 |
+ repository, tag := utils.ParseRepositoryTag(cmd.Arg(1)) |
|
| 1211 |
+ |
|
| 1210 | 1212 |
if name == "" {
|
| 1211 | 1213 |
cmd.Usage() |
| 1212 | 1214 |
return nil |
| ... | ... |
@@ -1316,8 +1350,18 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
| 1316 | 1316 |
return nil |
| 1317 | 1317 |
} |
| 1318 | 1318 |
name := cmd.Arg(0) |
| 1319 |
+ body, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
|
|
| 1320 |
+ if err != nil {
|
|
| 1321 |
+ return err |
|
| 1322 |
+ } |
|
| 1319 | 1323 |
|
| 1320 |
- if err := cli.hijack("POST", "/containers/"+name+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err, nil); err != nil {
|
|
| 1324 |
+ container := &Container{}
|
|
| 1325 |
+ err = json.Unmarshal(body, container) |
|
| 1326 |
+ if err != nil {
|
|
| 1327 |
+ return err |
|
| 1328 |
+ } |
|
| 1329 |
+ |
|
| 1330 |
+ if err := cli.hijack("POST", "/containers/"+name+"/attach?logs=1&stdout=1&stderr=1", container.Config.Tty, nil, cli.out, cli.err, nil); err != nil {
|
|
| 1321 | 1331 |
return err |
| 1322 | 1332 |
} |
| 1323 | 1333 |
return nil |
| ... | ... |
@@ -1379,8 +1423,10 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
| 1379 | 1379 |
} |
| 1380 | 1380 |
|
| 1381 | 1381 |
func (cli *DockerCli) CmdSearch(args ...string) error {
|
| 1382 |
- cmd := Subcmd("search", "NAME", "Search the docker index for images")
|
|
| 1382 |
+ cmd := Subcmd("search", "TERM", "Search the docker index for images")
|
|
| 1383 | 1383 |
noTrunc := cmd.Bool("notrunc", false, "Don't truncate output")
|
| 1384 |
+ trusted := cmd.Bool("trusted", false, "Only show trusted builds")
|
|
| 1385 |
+ stars := cmd.Int("stars", 0, "Only displays with at least xxx stars")
|
|
| 1384 | 1386 |
if err := cmd.Parse(args); err != nil {
|
| 1385 | 1387 |
return nil |
| 1386 | 1388 |
} |
| ... | ... |
@@ -1396,27 +1442,32 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
|
| 1396 | 1396 |
return err |
| 1397 | 1397 |
} |
| 1398 | 1398 |
|
| 1399 |
- outs := []APISearch{}
|
|
| 1399 |
+ outs := []registry.SearchResult{}
|
|
| 1400 | 1400 |
err = json.Unmarshal(body, &outs) |
| 1401 | 1401 |
if err != nil {
|
| 1402 | 1402 |
return err |
| 1403 | 1403 |
} |
| 1404 |
- fmt.Fprintf(cli.out, "Found %d results matching your query (\"%s\")\n", len(outs), cmd.Arg(0)) |
|
| 1405 |
- w := tabwriter.NewWriter(cli.out, 33, 1, 3, ' ', 0) |
|
| 1406 |
- fmt.Fprintf(w, "NAME\tDESCRIPTION\n") |
|
| 1407 |
- _, width := cli.getTtySize() |
|
| 1408 |
- if width == 0 {
|
|
| 1409 |
- width = 45 |
|
| 1410 |
- } else {
|
|
| 1411 |
- width = width - 33 //remote the first column |
|
| 1412 |
- } |
|
| 1404 |
+ w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0) |
|
| 1405 |
+ fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tTRUSTED\n") |
|
| 1413 | 1406 |
for _, out := range outs {
|
| 1407 |
+ if (*trusted && !out.IsTrusted) || (*stars > out.StarCount) {
|
|
| 1408 |
+ continue |
|
| 1409 |
+ } |
|
| 1414 | 1410 |
desc := strings.Replace(out.Description, "\n", " ", -1) |
| 1415 | 1411 |
desc = strings.Replace(desc, "\r", " ", -1) |
| 1416 |
- if !*noTrunc && len(desc) > width {
|
|
| 1417 |
- desc = utils.Trunc(desc, width-3) + "..." |
|
| 1412 |
+ if !*noTrunc && len(desc) > 45 {
|
|
| 1413 |
+ desc = utils.Trunc(desc, 42) + "..." |
|
| 1414 |
+ } |
|
| 1415 |
+ fmt.Fprintf(w, "%s\t%s\t%d\t", out.Name, desc, out.StarCount) |
|
| 1416 |
+ if out.IsOfficial {
|
|
| 1417 |
+ fmt.Fprint(w, "[OK]") |
|
| 1418 |
+ |
|
| 1418 | 1419 |
} |
| 1419 |
- fmt.Fprintf(w, "%s\t%s\n", out.Name, desc) |
|
| 1420 |
+ fmt.Fprint(w, "\t") |
|
| 1421 |
+ if out.IsTrusted {
|
|
| 1422 |
+ fmt.Fprint(w, "[OK]") |
|
| 1423 |
+ } |
|
| 1424 |
+ fmt.Fprint(w, "\n") |
|
| 1420 | 1425 |
} |
| 1421 | 1426 |
w.Flush() |
| 1422 | 1427 |
return nil |
| ... | ... |
@@ -1484,7 +1535,7 @@ func (opts PathOpts) Set(val string) error {
|
| 1484 | 1484 |
} |
| 1485 | 1485 |
|
| 1486 | 1486 |
func (cli *DockerCli) CmdTag(args ...string) error {
|
| 1487 |
- cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
|
|
| 1487 |
+ cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY[:TAG]", "Tag an image into a repository")
|
|
| 1488 | 1488 |
force := cmd.Bool("f", false, "Force")
|
| 1489 | 1489 |
if err := cmd.Parse(args); err != nil {
|
| 1490 | 1490 |
return nil |
| ... | ... |
@@ -1495,10 +1546,10 @@ func (cli *DockerCli) CmdTag(args ...string) error {
|
| 1495 | 1495 |
} |
| 1496 | 1496 |
|
| 1497 | 1497 |
v := url.Values{}
|
| 1498 |
- v.Set("repo", cmd.Arg(1))
|
|
| 1499 |
- if cmd.NArg() == 3 {
|
|
| 1500 |
- v.Set("tag", cmd.Arg(2))
|
|
| 1501 |
- } |
|
| 1498 |
+ repository, tag := utils.ParseRepositoryTag(cmd.Arg(1)) |
|
| 1499 |
+ |
|
| 1500 |
+ v.Set("repo", repository)
|
|
| 1501 |
+ v.Set("tag", tag)
|
|
| 1502 | 1502 |
|
| 1503 | 1503 |
if *force {
|
| 1504 | 1504 |
v.Set("force", "1")
|
| ... | ... |
@@ -1749,7 +1800,7 @@ func (cli *DockerCli) CmdCp(args ...string) error {
|
| 1749 | 1749 |
|
| 1750 | 1750 |
if statusCode == 200 {
|
| 1751 | 1751 |
r := bytes.NewReader(data) |
| 1752 |
- if err := Untar(r, copyData.HostPath); err != nil {
|
|
| 1752 |
+ if err := archive.Untar(r, copyData.HostPath); err != nil {
|
|
| 1753 | 1753 |
return err |
| 1754 | 1754 |
} |
| 1755 | 1755 |
} |
| ... | ... |
@@ -645,3 +645,57 @@ func TestRunAutoRemove(t *testing.T) {
|
| 645 | 645 |
t.Fatalf("failed to remove container automatically: container %s still exists", temporaryContainerID)
|
| 646 | 646 |
} |
| 647 | 647 |
} |
| 648 |
+ |
|
| 649 |
+func TestCmdLogs(t *testing.T) {
|
|
| 650 |
+ cli := NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr) |
|
| 651 |
+ defer cleanup(globalRuntime) |
|
| 652 |
+ |
|
| 653 |
+ if err := cli.CmdRun(unitTestImageID, "sh", "-c", "ls -l"); err != nil {
|
|
| 654 |
+ t.Fatal(err) |
|
| 655 |
+ } |
|
| 656 |
+ if err := cli.CmdRun("-t", unitTestImageID, "sh", "-c", "ls -l"); err != nil {
|
|
| 657 |
+ t.Fatal(err) |
|
| 658 |
+ } |
|
| 659 |
+ |
|
| 660 |
+ if err := cli.CmdLogs(globalRuntime.List()[0].ID); err != nil {
|
|
| 661 |
+ t.Fatal(err) |
|
| 662 |
+ } |
|
| 663 |
+} |
|
| 664 |
+ |
|
| 665 |
+// Expected behaviour: using / as a bind mount source should throw an error |
|
| 666 |
+func TestRunErrorBindMountRootSource(t *testing.T) {
|
|
| 667 |
+ |
|
| 668 |
+ cli := NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr) |
|
| 669 |
+ defer cleanup(globalRuntime) |
|
| 670 |
+ |
|
| 671 |
+ c := make(chan struct{})
|
|
| 672 |
+ go func() {
|
|
| 673 |
+ defer close(c) |
|
| 674 |
+ if err := cli.CmdRun("-v", "/:/tmp", unitTestImageID, "echo 'should fail'"); err == nil {
|
|
| 675 |
+ t.Fatal("should have failed to run when using / as a source for the bind mount")
|
|
| 676 |
+ } |
|
| 677 |
+ }() |
|
| 678 |
+ |
|
| 679 |
+ setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
|
|
| 680 |
+ <-c |
|
| 681 |
+ }) |
|
| 682 |
+} |
|
| 683 |
+ |
|
| 684 |
+// Expected behaviour: error out when attempting to bind mount non-existing source paths |
|
| 685 |
+func TestRunErrorBindNonExistingSource(t *testing.T) {
|
|
| 686 |
+ |
|
| 687 |
+ cli := NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr) |
|
| 688 |
+ defer cleanup(globalRuntime) |
|
| 689 |
+ |
|
| 690 |
+ c := make(chan struct{})
|
|
| 691 |
+ go func() {
|
|
| 692 |
+ defer close(c) |
|
| 693 |
+ if err := cli.CmdRun("-v", "/i/dont/exist:/tmp", unitTestImageID, "echo 'should fail'"); err == nil {
|
|
| 694 |
+ t.Fatal("should have failed to run when using /i/dont/exist as a source for the bind mount")
|
|
| 695 |
+ } |
|
| 696 |
+ }() |
|
| 697 |
+ |
|
| 698 |
+ setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
|
|
| 699 |
+ <-c |
|
| 700 |
+ }) |
|
| 701 |
+} |
| ... | ... |
@@ -2,11 +2,13 @@ package docker |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"net" |
| 5 |
+ "github.com/dotcloud/docker/engine" |
|
| 5 | 6 |
) |
| 6 | 7 |
|
| 8 |
+// FIXME: separate runtime configuration from http api configuration |
|
| 7 | 9 |
type DaemonConfig struct {
|
| 8 | 10 |
Pidfile string |
| 9 |
- GraphPath string |
|
| 11 |
+ Root string |
|
| 10 | 12 |
ProtoAddresses []string |
| 11 | 13 |
AutoRestart bool |
| 12 | 14 |
EnableCors bool |
| ... | ... |
@@ -16,3 +18,26 @@ type DaemonConfig struct {
|
| 16 | 16 |
DefaultIp net.IP |
| 17 | 17 |
InterContainerCommunication bool |
| 18 | 18 |
} |
| 19 |
+ |
|
| 20 |
+// ConfigFromJob creates and returns a new DaemonConfig object |
|
| 21 |
+// by parsing the contents of a job's environment. |
|
| 22 |
+func ConfigFromJob(job *engine.Job) *DaemonConfig {
|
|
| 23 |
+ var config DaemonConfig |
|
| 24 |
+ config.Pidfile = job.Getenv("Pidfile")
|
|
| 25 |
+ config.Root = job.Getenv("Root")
|
|
| 26 |
+ config.AutoRestart = job.GetenvBool("AutoRestart")
|
|
| 27 |
+ config.EnableCors = job.GetenvBool("EnableCors")
|
|
| 28 |
+ if dns := job.Getenv("Dns"); dns != "" {
|
|
| 29 |
+ config.Dns = []string{dns}
|
|
| 30 |
+ } |
|
| 31 |
+ config.EnableIptables = job.GetenvBool("EnableIptables")
|
|
| 32 |
+ if br := job.Getenv("BridgeIface"); br != "" {
|
|
| 33 |
+ config.BridgeIface = br |
|
| 34 |
+ } else {
|
|
| 35 |
+ config.BridgeIface = DefaultNetworkBridge |
|
| 36 |
+ } |
|
| 37 |
+ config.ProtoAddresses = job.GetenvList("ProtoAddresses")
|
|
| 38 |
+ config.DefaultIp = net.ParseIP(job.Getenv("DefaultIp"))
|
|
| 39 |
+ config.InterContainerCommunication = job.GetenvBool("InterContainerCommunication")
|
|
| 40 |
+ return &config |
|
| 41 |
+} |
| ... | ... |
@@ -6,6 +6,7 @@ import ( |
| 6 | 6 |
"errors" |
| 7 | 7 |
"flag" |
| 8 | 8 |
"fmt" |
| 9 |
+ "github.com/dotcloud/docker/archive" |
|
| 9 | 10 |
"github.com/dotcloud/docker/term" |
| 10 | 11 |
"github.com/dotcloud/docker/utils" |
| 11 | 12 |
"github.com/kr/pty" |
| ... | ... |
@@ -59,11 +60,15 @@ type Container struct {
|
| 59 | 59 |
Volumes map[string]string |
| 60 | 60 |
// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk. |
| 61 | 61 |
// Easier than migrating older container configs :) |
| 62 |
- VolumesRW map[string]bool |
|
| 62 |
+ VolumesRW map[string]bool |
|
| 63 |
+ hostConfig *HostConfig |
|
| 63 | 64 |
|
| 64 | 65 |
activeLinks map[string]*Link |
| 65 | 66 |
} |
| 66 | 67 |
|
| 68 |
+// Note: the Config structure should hold only portable information about the container. |
|
| 69 |
+// Here, "portable" means "independent from the host we are running on". |
|
| 70 |
+// Non-portable information *should* appear in HostConfig. |
|
| 67 | 71 |
type Config struct {
|
| 68 | 72 |
Hostname string |
| 69 | 73 |
Domainname string |
| ... | ... |
@@ -88,15 +93,16 @@ type Config struct {
|
| 88 | 88 |
WorkingDir string |
| 89 | 89 |
Entrypoint []string |
| 90 | 90 |
NetworkDisabled bool |
| 91 |
- Privileged bool |
|
| 92 | 91 |
} |
| 93 | 92 |
|
| 94 | 93 |
type HostConfig struct {
|
| 95 | 94 |
Binds []string |
| 96 | 95 |
ContainerIDFile string |
| 97 | 96 |
LxcConf []KeyValuePair |
| 97 |
+ Privileged bool |
|
| 98 | 98 |
PortBindings map[Port][]PortBinding |
| 99 | 99 |
Links []string |
| 100 |
+ PublishAllPorts bool |
|
| 100 | 101 |
} |
| 101 | 102 |
|
| 102 | 103 |
type BindMap struct {
|
| ... | ... |
@@ -168,6 +174,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, |
| 168 | 168 |
flAutoRemove := cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)")
|
| 169 | 169 |
cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)")
|
| 170 | 170 |
cmd.String("name", "", "Assign a name to the container")
|
| 171 |
+ flPublishAll := cmd.Bool("P", false, "Publish all exposed ports to the host interfaces")
|
|
| 171 | 172 |
|
| 172 | 173 |
if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit {
|
| 173 | 174 |
//fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n") |
| ... | ... |
@@ -226,12 +233,27 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, |
| 226 | 226 |
} |
| 227 | 227 |
} |
| 228 | 228 |
|
| 229 |
+ envs := []string{}
|
|
| 230 |
+ |
|
| 231 |
+ for _, env := range flEnv {
|
|
| 232 |
+ arr := strings.Split(env, "=") |
|
| 233 |
+ if len(arr) > 1 {
|
|
| 234 |
+ envs = append(envs, env) |
|
| 235 |
+ } else {
|
|
| 236 |
+ v := os.Getenv(env) |
|
| 237 |
+ envs = append(envs, env+"="+v) |
|
| 238 |
+ } |
|
| 239 |
+ } |
|
| 240 |
+ |
|
| 229 | 241 |
var binds []string |
| 230 | 242 |
|
| 231 | 243 |
// add any bind targets to the list of container volumes |
| 232 | 244 |
for bind := range flVolumes {
|
| 233 | 245 |
arr := strings.Split(bind, ":") |
| 234 | 246 |
if len(arr) > 1 {
|
| 247 |
+ if arr[0] == "/" {
|
|
| 248 |
+ return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
|
|
| 249 |
+ } |
|
| 235 | 250 |
dstDir := arr[1] |
| 236 | 251 |
flVolumes[dstDir] = struct{}{}
|
| 237 | 252 |
binds = append(binds, bind) |
| ... | ... |
@@ -285,7 +307,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, |
| 285 | 285 |
} |
| 286 | 286 |
|
| 287 | 287 |
config := &Config{
|
| 288 |
- Hostname: *flHostname, |
|
| 288 |
+ Hostname: hostname, |
|
| 289 | 289 |
Domainname: domainname, |
| 290 | 290 |
PortSpecs: nil, // Deprecated |
| 291 | 291 |
ExposedPorts: ports, |
| ... | ... |
@@ -298,14 +320,13 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, |
| 298 | 298 |
AttachStdin: flAttach.Get("stdin"),
|
| 299 | 299 |
AttachStdout: flAttach.Get("stdout"),
|
| 300 | 300 |
AttachStderr: flAttach.Get("stderr"),
|
| 301 |
- Env: flEnv, |
|
| 301 |
+ Env: envs, |
|
| 302 | 302 |
Cmd: runCmd, |
| 303 | 303 |
Dns: flDns, |
| 304 | 304 |
Image: image, |
| 305 | 305 |
Volumes: flVolumes, |
| 306 | 306 |
VolumesFrom: strings.Join(flVolumesFrom, ","), |
| 307 | 307 |
Entrypoint: entrypoint, |
| 308 |
- Privileged: *flPrivileged, |
|
| 309 | 308 |
WorkingDir: *flWorkingDir, |
| 310 | 309 |
} |
| 311 | 310 |
|
| ... | ... |
@@ -313,8 +334,10 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, |
| 313 | 313 |
Binds: binds, |
| 314 | 314 |
ContainerIDFile: *flContainerIDFile, |
| 315 | 315 |
LxcConf: lxcConf, |
| 316 |
+ Privileged: *flPrivileged, |
|
| 316 | 317 |
PortBindings: portBindings, |
| 317 | 318 |
Links: flLinks, |
| 319 |
+ PublishAllPorts: *flPublishAll, |
|
| 318 | 320 |
} |
| 319 | 321 |
|
| 320 | 322 |
if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
|
| ... | ... |
@@ -367,11 +390,22 @@ func (settings *NetworkSettings) PortMappingAPI() []APIPort {
|
| 367 | 367 |
|
| 368 | 368 |
// Inject the io.Reader at the given path. Note: do not close the reader |
| 369 | 369 |
func (container *Container) Inject(file io.Reader, pth string) error {
|
| 370 |
+ // Return error if path exists |
|
| 371 |
+ if _, err := os.Stat(path.Join(container.rwPath(), pth)); err == nil {
|
|
| 372 |
+ // Since err is nil, the path could be stat'd and it exists |
|
| 373 |
+ return fmt.Errorf("%s exists", pth)
|
|
| 374 |
+ } else if ! os.IsNotExist(err) {
|
|
| 375 |
+ // Expect err might be that the file doesn't exist, so |
|
| 376 |
+ // if it's some other error, return that. |
|
| 377 |
+ |
|
| 378 |
+ return err |
|
| 379 |
+ } |
|
| 380 |
+ |
|
| 370 | 381 |
// Make sure the directory exists |
| 371 | 382 |
if err := os.MkdirAll(path.Join(container.rwPath(), path.Dir(pth)), 0755); err != nil {
|
| 372 | 383 |
return err |
| 373 | 384 |
} |
| 374 |
- // FIXME: Handle permissions/already existing dest |
|
| 385 |
+ |
|
| 375 | 386 |
dest, err := os.Create(path.Join(container.rwPath(), pth)) |
| 376 | 387 |
if err != nil {
|
| 377 | 388 |
return err |
| ... | ... |
@@ -400,7 +434,7 @@ func (container *Container) FromDisk() error {
|
| 400 | 400 |
if err := json.Unmarshal(data, container); err != nil && !strings.Contains(err.Error(), "docker.PortMapping") {
|
| 401 | 401 |
return err |
| 402 | 402 |
} |
| 403 |
- return nil |
|
| 403 |
+ return container.readHostConfig() |
|
| 404 | 404 |
} |
| 405 | 405 |
|
| 406 | 406 |
func (container *Container) ToDisk() (err error) {
|
| ... | ... |
@@ -408,44 +442,53 @@ func (container *Container) ToDisk() (err error) {
|
| 408 | 408 |
if err != nil {
|
| 409 | 409 |
return |
| 410 | 410 |
} |
| 411 |
- return ioutil.WriteFile(container.jsonPath(), data, 0666) |
|
| 411 |
+ err = ioutil.WriteFile(container.jsonPath(), data, 0666) |
|
| 412 |
+ if err != nil {
|
|
| 413 |
+ return |
|
| 414 |
+ } |
|
| 415 |
+ return container.writeHostConfig() |
|
| 412 | 416 |
} |
| 413 | 417 |
|
| 414 |
-func (container *Container) ReadHostConfig() (*HostConfig, error) {
|
|
| 418 |
+func (container *Container) readHostConfig() error {
|
|
| 419 |
+ container.hostConfig = &HostConfig{}
|
|
| 420 |
+ // If the hostconfig file does not exist, do not read it. |
|
| 421 |
+ // (We still have to initialize container.hostConfig, |
|
| 422 |
+ // but that's OK, since we just did that above.) |
|
| 423 |
+ _, err := os.Stat(container.hostConfigPath()) |
|
| 424 |
+ if os.IsNotExist(err) {
|
|
| 425 |
+ return nil |
|
| 426 |
+ } |
|
| 415 | 427 |
data, err := ioutil.ReadFile(container.hostConfigPath()) |
| 416 | 428 |
if err != nil {
|
| 417 |
- return &HostConfig{}, err
|
|
| 418 |
- } |
|
| 419 |
- hostConfig := &HostConfig{}
|
|
| 420 |
- if err := json.Unmarshal(data, hostConfig); err != nil {
|
|
| 421 |
- return &HostConfig{}, err
|
|
| 429 |
+ return err |
|
| 422 | 430 |
} |
| 423 |
- return hostConfig, nil |
|
| 431 |
+ return json.Unmarshal(data, container.hostConfig) |
|
| 424 | 432 |
} |
| 425 | 433 |
|
| 426 |
-func (container *Container) SaveHostConfig(hostConfig *HostConfig) (err error) {
|
|
| 427 |
- data, err := json.Marshal(hostConfig) |
|
| 434 |
+func (container *Container) writeHostConfig() (err error) {
|
|
| 435 |
+ data, err := json.Marshal(container.hostConfig) |
|
| 428 | 436 |
if err != nil {
|
| 429 | 437 |
return |
| 430 | 438 |
} |
| 431 | 439 |
return ioutil.WriteFile(container.hostConfigPath(), data, 0666) |
| 432 | 440 |
} |
| 433 | 441 |
|
| 434 |
-func (container *Container) generateLXCConfig(hostConfig *HostConfig) error {
|
|
| 435 |
- fo, err := os.Create(container.lxcConfigPath()) |
|
| 442 |
+func (container *Container) generateEnvConfig(env []string) error {
|
|
| 443 |
+ data, err := json.Marshal(env) |
|
| 436 | 444 |
if err != nil {
|
| 437 | 445 |
return err |
| 438 | 446 |
} |
| 439 |
- defer fo.Close() |
|
| 440 |
- if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
|
|
| 447 |
+ ioutil.WriteFile(container.EnvConfigPath(), data, 0600) |
|
| 448 |
+ return nil |
|
| 449 |
+} |
|
| 450 |
+ |
|
| 451 |
+func (container *Container) generateLXCConfig() error {
|
|
| 452 |
+ fo, err := os.Create(container.lxcConfigPath()) |
|
| 453 |
+ if err != nil {
|
|
| 441 | 454 |
return err |
| 442 | 455 |
} |
| 443 |
- if hostConfig != nil {
|
|
| 444 |
- if err := LxcHostConfigTemplateCompiled.Execute(fo, hostConfig); err != nil {
|
|
| 445 |
- return err |
|
| 446 |
- } |
|
| 447 |
- } |
|
| 448 |
- return nil |
|
| 456 |
+ defer fo.Close() |
|
| 457 |
+ return LxcTemplateCompiled.Execute(fo, container) |
|
| 449 | 458 |
} |
| 450 | 459 |
|
| 451 | 460 |
func (container *Container) startPty() error {
|
| ... | ... |
@@ -640,7 +683,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s |
| 640 | 640 |
}) |
| 641 | 641 |
} |
| 642 | 642 |
|
| 643 |
-func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
|
| 643 |
+func (container *Container) Start() (err error) {
|
|
| 644 | 644 |
container.State.Lock() |
| 645 | 645 |
defer container.State.Unlock() |
| 646 | 646 |
defer func() {
|
| ... | ... |
@@ -649,10 +692,6 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 649 | 649 |
} |
| 650 | 650 |
}() |
| 651 | 651 |
|
| 652 |
- if hostConfig == nil { // in docker start of docker restart we want to reuse previous HostConfigFile
|
|
| 653 |
- hostConfig, _ = container.ReadHostConfig() |
|
| 654 |
- } |
|
| 655 |
- |
|
| 656 | 652 |
if container.State.Running {
|
| 657 | 653 |
return fmt.Errorf("The container %s is already running.", container.ID)
|
| 658 | 654 |
} |
| ... | ... |
@@ -662,7 +701,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 662 | 662 |
if container.runtime.networkManager.disabled {
|
| 663 | 663 |
container.Config.NetworkDisabled = true |
| 664 | 664 |
} else {
|
| 665 |
- if err := container.allocateNetwork(hostConfig); err != nil {
|
|
| 665 |
+ if err := container.allocateNetwork(); err != nil {
|
|
| 666 | 666 |
return err |
| 667 | 667 |
} |
| 668 | 668 |
} |
| ... | ... |
@@ -686,7 +725,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 686 | 686 |
// Define illegal container destinations |
| 687 | 687 |
illegalDsts := []string{"/", "."}
|
| 688 | 688 |
|
| 689 |
- for _, bind := range hostConfig.Binds {
|
|
| 689 |
+ for _, bind := range container.hostConfig.Binds {
|
|
| 690 | 690 |
// FIXME: factorize bind parsing in parseBind |
| 691 | 691 |
var src, dst, mode string |
| 692 | 692 |
arr := strings.Split(bind, ":") |
| ... | ... |
@@ -796,7 +835,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 796 | 796 |
} |
| 797 | 797 |
if len(srcList) == 0 {
|
| 798 | 798 |
// If the source volume is empty copy files from the root into the volume |
| 799 |
- if err := CopyWithTar(rootVolPath, srcPath); err != nil {
|
|
| 799 |
+ if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
|
|
| 800 | 800 |
return err |
| 801 | 801 |
} |
| 802 | 802 |
|
| ... | ... |
@@ -820,7 +859,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 820 | 820 |
} |
| 821 | 821 |
} |
| 822 | 822 |
|
| 823 |
- if err := container.generateLXCConfig(hostConfig); err != nil {
|
|
| 823 |
+ if err := container.generateLXCConfig(); err != nil {
|
|
| 824 | 824 |
return err |
| 825 | 825 |
} |
| 826 | 826 |
|
| ... | ... |
@@ -841,17 +880,17 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 841 | 841 |
params = append(params, "-u", container.Config.User) |
| 842 | 842 |
} |
| 843 | 843 |
|
| 844 |
- if container.Config.Tty {
|
|
| 845 |
- params = append(params, "-e", "TERM=xterm") |
|
| 844 |
+ // Setup environment |
|
| 845 |
+ env := []string{
|
|
| 846 |
+ "HOME=/", |
|
| 847 |
+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", |
|
| 848 |
+ "container=lxc", |
|
| 849 |
+ "HOSTNAME=" + container.Config.Hostname, |
|
| 846 | 850 |
} |
| 847 | 851 |
|
| 848 |
- // Setup environment |
|
| 849 |
- params = append(params, |
|
| 850 |
- "-e", "HOME=/", |
|
| 851 |
- "-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", |
|
| 852 |
- "-e", "container=lxc", |
|
| 853 |
- "-e", "HOSTNAME="+container.Config.Hostname, |
|
| 854 |
- ) |
|
| 852 |
+ if container.Config.Tty {
|
|
| 853 |
+ env = append(env, "TERM=xterm") |
|
| 854 |
+ } |
|
| 855 | 855 |
|
| 856 | 856 |
// Init any links between the parent and children |
| 857 | 857 |
runtime := container.runtime |
| ... | ... |
@@ -887,11 +926,19 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 887 | 887 |
} |
| 888 | 888 |
|
| 889 | 889 |
for _, envVar := range link.ToEnv() {
|
| 890 |
- params = append(params, "-e", envVar) |
|
| 890 |
+ env = append(env, envVar) |
|
| 891 | 891 |
} |
| 892 | 892 |
} |
| 893 | 893 |
} |
| 894 | 894 |
|
| 895 |
+ for _, elem := range container.Config.Env {
|
|
| 896 |
+ env = append(env, elem) |
|
| 897 |
+ } |
|
| 898 |
+ |
|
| 899 |
+ if err := container.generateEnvConfig(env); err != nil {
|
|
| 900 |
+ return err |
|
| 901 |
+ } |
|
| 902 |
+ |
|
| 895 | 903 |
if container.Config.WorkingDir != "" {
|
| 896 | 904 |
workingDir := path.Clean(container.Config.WorkingDir) |
| 897 | 905 |
utils.Debugf("[working dir] working dir is %s", workingDir)
|
| ... | ... |
@@ -905,16 +952,15 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 905 | 905 |
) |
| 906 | 906 |
} |
| 907 | 907 |
|
| 908 |
- for _, elem := range container.Config.Env {
|
|
| 909 |
- params = append(params, "-e", elem) |
|
| 910 |
- } |
|
| 911 |
- |
|
| 912 | 908 |
// Program |
| 913 | 909 |
params = append(params, "--", container.Path) |
| 914 | 910 |
params = append(params, container.Args...) |
| 915 | 911 |
|
| 916 |
- container.cmd = exec.Command("lxc-start", params...)
|
|
| 917 |
- |
|
| 912 |
+ var lxcStart string = "lxc-start" |
|
| 913 |
+ if container.hostConfig.Privileged && container.runtime.capabilities.AppArmor {
|
|
| 914 |
+ lxcStart = path.Join(container.runtime.config.Root, "lxc-start-unconfined") |
|
| 915 |
+ } |
|
| 916 |
+ container.cmd = exec.Command(lxcStart, params...) |
|
| 918 | 917 |
// Setup logging of stdout and stderr to disk |
| 919 | 918 |
if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
|
| 920 | 919 |
return err |
| ... | ... |
@@ -941,8 +987,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 941 | 941 |
container.waitLock = make(chan struct{})
|
| 942 | 942 |
|
| 943 | 943 |
container.ToDisk() |
| 944 |
- container.SaveHostConfig(hostConfig) |
|
| 945 |
- go container.monitor(hostConfig) |
|
| 944 |
+ go container.monitor() |
|
| 946 | 945 |
|
| 947 | 946 |
defer utils.Debugf("Container running: %v", container.State.Running)
|
| 948 | 947 |
// We wait for the container to be fully running. |
| ... | ... |
@@ -979,7 +1024,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 979 | 979 |
} |
| 980 | 980 |
|
| 981 | 981 |
func (container *Container) Run() error {
|
| 982 |
- if err := container.Start(&HostConfig{}); err != nil {
|
|
| 982 |
+ if err := container.Start(); err != nil {
|
|
| 983 | 983 |
return err |
| 984 | 984 |
} |
| 985 | 985 |
container.Wait() |
| ... | ... |
@@ -992,8 +1037,7 @@ func (container *Container) Output() (output []byte, err error) {
|
| 992 | 992 |
return nil, err |
| 993 | 993 |
} |
| 994 | 994 |
defer pipe.Close() |
| 995 |
- hostConfig := &HostConfig{}
|
|
| 996 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 995 |
+ if err := container.Start(); err != nil {
|
|
| 997 | 996 |
return nil, err |
| 998 | 997 |
} |
| 999 | 998 |
output, err = ioutil.ReadAll(pipe) |
| ... | ... |
@@ -1025,19 +1069,14 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) {
|
| 1025 | 1025 |
return utils.NewBufReader(reader), nil |
| 1026 | 1026 |
} |
| 1027 | 1027 |
|
| 1028 |
-func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
|
|
| 1028 |
+func (container *Container) allocateNetwork() error {
|
|
| 1029 | 1029 |
if container.Config.NetworkDisabled {
|
| 1030 | 1030 |
return nil |
| 1031 | 1031 |
} |
| 1032 | 1032 |
|
| 1033 | 1033 |
var iface *NetworkInterface |
| 1034 | 1034 |
var err error |
| 1035 |
- if !container.State.Ghost {
|
|
| 1036 |
- iface, err = container.runtime.networkManager.Allocate() |
|
| 1037 |
- if err != nil {
|
|
| 1038 |
- return err |
|
| 1039 |
- } |
|
| 1040 |
- } else {
|
|
| 1035 |
+ if container.State.Ghost {
|
|
| 1041 | 1036 |
manager := container.runtime.networkManager |
| 1042 | 1037 |
if manager.disabled {
|
| 1043 | 1038 |
iface = &NetworkInterface{disabled: true}
|
| ... | ... |
@@ -1047,18 +1086,30 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
|
| 1047 | 1047 |
Gateway: manager.bridgeNetwork.IP, |
| 1048 | 1048 |
manager: manager, |
| 1049 | 1049 |
} |
| 1050 |
- ipNum := ipToInt(iface.IPNet.IP) |
|
| 1051 |
- manager.ipAllocator.inUse[ipNum] = struct{}{}
|
|
| 1050 |
+ if iface !=nil && iface.IPNet.IP != nil {
|
|
| 1051 |
+ ipNum := ipToInt(iface.IPNet.IP) |
|
| 1052 |
+ manager.ipAllocator.inUse[ipNum] = struct{}{}
|
|
| 1053 |
+ } else {
|
|
| 1054 |
+ iface, err = container.runtime.networkManager.Allocate() |
|
| 1055 |
+ if err != nil {
|
|
| 1056 |
+ return err |
|
| 1057 |
+ } |
|
| 1058 |
+ } |
|
| 1059 |
+ } |
|
| 1060 |
+ } else {
|
|
| 1061 |
+ iface, err = container.runtime.networkManager.Allocate() |
|
| 1062 |
+ if err != nil {
|
|
| 1063 |
+ return err |
|
| 1052 | 1064 |
} |
| 1053 | 1065 |
} |
| 1054 | 1066 |
|
| 1055 | 1067 |
if container.Config.PortSpecs != nil {
|
| 1056 | 1068 |
utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", "))
|
| 1057 |
- if err := migratePortMappings(container.Config, hostConfig); err != nil {
|
|
| 1069 |
+ if err := migratePortMappings(container.Config, container.hostConfig); err != nil {
|
|
| 1058 | 1070 |
return err |
| 1059 | 1071 |
} |
| 1060 | 1072 |
container.Config.PortSpecs = nil |
| 1061 |
- if err := container.SaveHostConfig(hostConfig); err != nil {
|
|
| 1073 |
+ if err := container.writeHostConfig(); err != nil {
|
|
| 1062 | 1074 |
return err |
| 1063 | 1075 |
} |
| 1064 | 1076 |
} |
| ... | ... |
@@ -1070,8 +1121,8 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
|
| 1070 | 1070 |
if container.Config.ExposedPorts != nil {
|
| 1071 | 1071 |
portSpecs = container.Config.ExposedPorts |
| 1072 | 1072 |
} |
| 1073 |
- if hostConfig.PortBindings != nil {
|
|
| 1074 |
- bindings = hostConfig.PortBindings |
|
| 1073 |
+ if container.hostConfig.PortBindings != nil {
|
|
| 1074 |
+ bindings = container.hostConfig.PortBindings |
|
| 1075 | 1075 |
} |
| 1076 | 1076 |
} else {
|
| 1077 | 1077 |
if container.NetworkSettings.Ports != nil {
|
| ... | ... |
@@ -1086,6 +1137,9 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
|
| 1086 | 1086 |
|
| 1087 | 1087 |
for port := range portSpecs {
|
| 1088 | 1088 |
binding := bindings[port] |
| 1089 |
+ if container.hostConfig.PublishAllPorts && len(binding) == 0 {
|
|
| 1090 |
+ binding = append(binding, PortBinding{})
|
|
| 1091 |
+ } |
|
| 1089 | 1092 |
for i := 0; i < len(binding); i++ {
|
| 1090 | 1093 |
b := binding[i] |
| 1091 | 1094 |
nat, err := iface.AllocatePort(port, b) |
| ... | ... |
@@ -1098,7 +1152,7 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
|
| 1098 | 1098 |
} |
| 1099 | 1099 |
bindings[port] = binding |
| 1100 | 1100 |
} |
| 1101 |
- container.SaveHostConfig(hostConfig) |
|
| 1101 |
+ container.writeHostConfig() |
|
| 1102 | 1102 |
|
| 1103 | 1103 |
container.NetworkSettings.Ports = bindings |
| 1104 | 1104 |
container.network = iface |
| ... | ... |
@@ -1134,7 +1188,7 @@ func (container *Container) waitLxc() error {
|
| 1134 | 1134 |
} |
| 1135 | 1135 |
} |
| 1136 | 1136 |
|
| 1137 |
-func (container *Container) monitor(hostConfig *HostConfig) {
|
|
| 1137 |
+func (container *Container) monitor() {
|
|
| 1138 | 1138 |
// Wait for the program to exit |
| 1139 | 1139 |
|
| 1140 | 1140 |
// If the command does not exist, try to wait via lxc |
| ... | ... |
@@ -1291,11 +1345,7 @@ func (container *Container) Restart(seconds int) error {
|
| 1291 | 1291 |
if err := container.Stop(seconds); err != nil {
|
| 1292 | 1292 |
return err |
| 1293 | 1293 |
} |
| 1294 |
- hostConfig := &HostConfig{}
|
|
| 1295 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 1296 |
- return err |
|
| 1297 |
- } |
|
| 1298 |
- return nil |
|
| 1294 |
+ return container.Start() |
|
| 1299 | 1295 |
} |
| 1300 | 1296 |
|
| 1301 | 1297 |
// Wait blocks until the container stops running, then returns its exit code. |
| ... | ... |
@@ -1312,23 +1362,23 @@ func (container *Container) Resize(h, w int) error {
|
| 1312 | 1312 |
return term.SetWinsize(pty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
| 1313 | 1313 |
} |
| 1314 | 1314 |
|
| 1315 |
-func (container *Container) ExportRw() (Archive, error) {
|
|
| 1316 |
- return Tar(container.rwPath(), Uncompressed) |
|
| 1315 |
+func (container *Container) ExportRw() (archive.Archive, error) {
|
|
| 1316 |
+ return archive.Tar(container.rwPath(), archive.Uncompressed) |
|
| 1317 | 1317 |
} |
| 1318 | 1318 |
|
| 1319 | 1319 |
func (container *Container) RwChecksum() (string, error) {
|
| 1320 |
- rwData, err := Tar(container.rwPath(), Xz) |
|
| 1320 |
+ rwData, err := archive.Tar(container.rwPath(), archive.Xz) |
|
| 1321 | 1321 |
if err != nil {
|
| 1322 | 1322 |
return "", err |
| 1323 | 1323 |
} |
| 1324 | 1324 |
return utils.HashData(rwData) |
| 1325 | 1325 |
} |
| 1326 | 1326 |
|
| 1327 |
-func (container *Container) Export() (Archive, error) {
|
|
| 1327 |
+func (container *Container) Export() (archive.Archive, error) {
|
|
| 1328 | 1328 |
if err := container.EnsureMounted(); err != nil {
|
| 1329 | 1329 |
return nil, err |
| 1330 | 1330 |
} |
| 1331 |
- return Tar(container.RootfsPath(), Uncompressed) |
|
| 1331 |
+ return archive.Tar(container.RootfsPath(), archive.Uncompressed) |
|
| 1332 | 1332 |
} |
| 1333 | 1333 |
|
| 1334 | 1334 |
func (container *Container) WaitTimeout(timeout time.Duration) error {
|
| ... | ... |
@@ -1416,6 +1466,10 @@ func (container *Container) jsonPath() string {
|
| 1416 | 1416 |
return path.Join(container.root, "config.json") |
| 1417 | 1417 |
} |
| 1418 | 1418 |
|
| 1419 |
+func (container *Container) EnvConfigPath() string {
|
|
| 1420 |
+ return path.Join(container.root, "config.env") |
|
| 1421 |
+} |
|
| 1422 |
+ |
|
| 1419 | 1423 |
func (container *Container) lxcConfigPath() string {
|
| 1420 | 1424 |
return path.Join(container.root, "config.lxc") |
| 1421 | 1425 |
} |
| ... | ... |
@@ -1459,7 +1513,7 @@ func (container *Container) GetSize() (int64, int64) {
|
| 1459 | 1459 |
return sizeRw, sizeRootfs |
| 1460 | 1460 |
} |
| 1461 | 1461 |
|
| 1462 |
-func (container *Container) Copy(resource string) (Archive, error) {
|
|
| 1462 |
+func (container *Container) Copy(resource string) (archive.Archive, error) {
|
|
| 1463 | 1463 |
if err := container.EnsureMounted(); err != nil {
|
| 1464 | 1464 |
return nil, err |
| 1465 | 1465 |
} |
| ... | ... |
@@ -1477,7 +1531,7 @@ func (container *Container) Copy(resource string) (Archive, error) {
|
| 1477 | 1477 |
filter = []string{path.Base(basePath)}
|
| 1478 | 1478 |
basePath = path.Dir(basePath) |
| 1479 | 1479 |
} |
| 1480 |
- return TarFilter(basePath, Uncompressed, filter) |
|
| 1480 |
+ return archive.TarFilter(basePath, archive.Uncompressed, filter) |
|
| 1481 | 1481 |
} |
| 1482 | 1482 |
|
| 1483 | 1483 |
// Returns true if the container exposes a certain port |
| ... | ... |
@@ -40,7 +40,7 @@ func TestIDFormat(t *testing.T) {
|
| 40 | 40 |
func TestMultipleAttachRestart(t *testing.T) {
|
| 41 | 41 |
runtime := mkRuntime(t) |
| 42 | 42 |
defer nuke(runtime) |
| 43 |
- container, hostConfig, _ := mkContainer( |
|
| 43 |
+ container, _ := mkContainer( |
|
| 44 | 44 |
runtime, |
| 45 | 45 |
[]string{"_", "/bin/sh", "-c", "i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"},
|
| 46 | 46 |
t, |
| ... | ... |
@@ -61,7 +61,7 @@ func TestMultipleAttachRestart(t *testing.T) {
|
| 61 | 61 |
if err != nil {
|
| 62 | 62 |
t.Fatal(err) |
| 63 | 63 |
} |
| 64 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 64 |
+ if err := container.Start(); err != nil {
|
|
| 65 | 65 |
t.Fatal(err) |
| 66 | 66 |
} |
| 67 | 67 |
l1, err := bufio.NewReader(stdout1).ReadString('\n')
|
| ... | ... |
@@ -102,7 +102,7 @@ func TestMultipleAttachRestart(t *testing.T) {
|
| 102 | 102 |
if err != nil {
|
| 103 | 103 |
t.Fatal(err) |
| 104 | 104 |
} |
| 105 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 105 |
+ if err := container.Start(); err != nil {
|
|
| 106 | 106 |
t.Fatal(err) |
| 107 | 107 |
} |
| 108 | 108 |
|
| ... | ... |
@@ -136,7 +136,7 @@ func TestDiff(t *testing.T) {
|
| 136 | 136 |
runtime := mkRuntime(t) |
| 137 | 137 |
defer nuke(runtime) |
| 138 | 138 |
// Create a container and remove a file |
| 139 |
- container1, _, _ := mkContainer(runtime, []string{"_", "/bin/rm", "/etc/passwd"}, t)
|
|
| 139 |
+ container1, _ := mkContainer(runtime, []string{"_", "/bin/rm", "/etc/passwd"}, t)
|
|
| 140 | 140 |
defer runtime.Destroy(container1) |
| 141 | 141 |
|
| 142 | 142 |
// The changelog should be empty and not fail before run. See #1705 |
| ... | ... |
@@ -178,7 +178,7 @@ func TestDiff(t *testing.T) {
|
| 178 | 178 |
} |
| 179 | 179 |
|
| 180 | 180 |
// Create a new container from the commited image |
| 181 |
- container2, _, _ := mkContainer(runtime, []string{img.ID, "cat", "/etc/passwd"}, t)
|
|
| 181 |
+ container2, _ := mkContainer(runtime, []string{img.ID, "cat", "/etc/passwd"}, t)
|
|
| 182 | 182 |
defer runtime.Destroy(container2) |
| 183 | 183 |
|
| 184 | 184 |
if err := container2.Run(); err != nil {
|
| ... | ... |
@@ -197,7 +197,7 @@ func TestDiff(t *testing.T) {
|
| 197 | 197 |
} |
| 198 | 198 |
|
| 199 | 199 |
// Create a new container |
| 200 |
- container3, _, _ := mkContainer(runtime, []string{"_", "rm", "/bin/httpd"}, t)
|
|
| 200 |
+ container3, _ := mkContainer(runtime, []string{"_", "rm", "/bin/httpd"}, t)
|
|
| 201 | 201 |
defer runtime.Destroy(container3) |
| 202 | 202 |
|
| 203 | 203 |
if err := container3.Run(); err != nil {
|
| ... | ... |
@@ -223,7 +223,7 @@ func TestDiff(t *testing.T) {
|
| 223 | 223 |
func TestCommitAutoRun(t *testing.T) {
|
| 224 | 224 |
runtime := mkRuntime(t) |
| 225 | 225 |
defer nuke(runtime) |
| 226 |
- container1, _, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t)
|
|
| 226 |
+ container1, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t)
|
|
| 227 | 227 |
defer runtime.Destroy(container1) |
| 228 | 228 |
|
| 229 | 229 |
if container1.State.Running {
|
| ... | ... |
@@ -246,7 +246,7 @@ func TestCommitAutoRun(t *testing.T) {
|
| 246 | 246 |
} |
| 247 | 247 |
|
| 248 | 248 |
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world |
| 249 |
- container2, hostConfig, _ := mkContainer(runtime, []string{img.ID}, t)
|
|
| 249 |
+ container2, _ := mkContainer(runtime, []string{img.ID}, t)
|
|
| 250 | 250 |
defer runtime.Destroy(container2) |
| 251 | 251 |
stdout, err := container2.StdoutPipe() |
| 252 | 252 |
if err != nil {
|
| ... | ... |
@@ -256,7 +256,7 @@ func TestCommitAutoRun(t *testing.T) {
|
| 256 | 256 |
if err != nil {
|
| 257 | 257 |
t.Fatal(err) |
| 258 | 258 |
} |
| 259 |
- if err := container2.Start(hostConfig); err != nil {
|
|
| 259 |
+ if err := container2.Start(); err != nil {
|
|
| 260 | 260 |
t.Fatal(err) |
| 261 | 261 |
} |
| 262 | 262 |
container2.Wait() |
| ... | ... |
@@ -283,7 +283,7 @@ func TestCommitRun(t *testing.T) {
|
| 283 | 283 |
runtime := mkRuntime(t) |
| 284 | 284 |
defer nuke(runtime) |
| 285 | 285 |
|
| 286 |
- container1, hostConfig, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t)
|
|
| 286 |
+ container1, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t)
|
|
| 287 | 287 |
defer runtime.Destroy(container1) |
| 288 | 288 |
|
| 289 | 289 |
if container1.State.Running {
|
| ... | ... |
@@ -306,7 +306,7 @@ func TestCommitRun(t *testing.T) {
|
| 306 | 306 |
} |
| 307 | 307 |
|
| 308 | 308 |
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world |
| 309 |
- container2, hostConfig, _ := mkContainer(runtime, []string{img.ID, "cat", "/world"}, t)
|
|
| 309 |
+ container2, _ := mkContainer(runtime, []string{img.ID, "cat", "/world"}, t)
|
|
| 310 | 310 |
defer runtime.Destroy(container2) |
| 311 | 311 |
stdout, err := container2.StdoutPipe() |
| 312 | 312 |
if err != nil {
|
| ... | ... |
@@ -316,7 +316,7 @@ func TestCommitRun(t *testing.T) {
|
| 316 | 316 |
if err != nil {
|
| 317 | 317 |
t.Fatal(err) |
| 318 | 318 |
} |
| 319 |
- if err := container2.Start(hostConfig); err != nil {
|
|
| 319 |
+ if err := container2.Start(); err != nil {
|
|
| 320 | 320 |
t.Fatal(err) |
| 321 | 321 |
} |
| 322 | 322 |
container2.Wait() |
| ... | ... |
@@ -342,7 +342,7 @@ func TestCommitRun(t *testing.T) {
|
| 342 | 342 |
func TestStart(t *testing.T) {
|
| 343 | 343 |
runtime := mkRuntime(t) |
| 344 | 344 |
defer nuke(runtime) |
| 345 |
- container, hostConfig, _ := mkContainer(runtime, []string{"-m", "33554432", "-c", "1000", "-i", "_", "/bin/cat"}, t)
|
|
| 345 |
+ container, _ := mkContainer(runtime, []string{"-m", "33554432", "-c", "1000", "-i", "_", "/bin/cat"}, t)
|
|
| 346 | 346 |
defer runtime.Destroy(container) |
| 347 | 347 |
|
| 348 | 348 |
cStdin, err := container.StdinPipe() |
| ... | ... |
@@ -350,7 +350,7 @@ func TestStart(t *testing.T) {
|
| 350 | 350 |
t.Fatal(err) |
| 351 | 351 |
} |
| 352 | 352 |
|
| 353 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 353 |
+ if err := container.Start(); err != nil {
|
|
| 354 | 354 |
t.Fatal(err) |
| 355 | 355 |
} |
| 356 | 356 |
|
| ... | ... |
@@ -360,7 +360,7 @@ func TestStart(t *testing.T) {
|
| 360 | 360 |
if !container.State.Running {
|
| 361 | 361 |
t.Errorf("Container should be running")
|
| 362 | 362 |
} |
| 363 |
- if err := container.Start(hostConfig); err == nil {
|
|
| 363 |
+ if err := container.Start(); err == nil {
|
|
| 364 | 364 |
t.Fatalf("A running container should be able to be started")
|
| 365 | 365 |
} |
| 366 | 366 |
|
| ... | ... |
@@ -372,7 +372,7 @@ func TestStart(t *testing.T) {
|
| 372 | 372 |
func TestRun(t *testing.T) {
|
| 373 | 373 |
runtime := mkRuntime(t) |
| 374 | 374 |
defer nuke(runtime) |
| 375 |
- container, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
|
|
| 375 |
+ container, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
|
|
| 376 | 376 |
defer runtime.Destroy(container) |
| 377 | 377 |
|
| 378 | 378 |
if container.State.Running {
|
| ... | ... |
@@ -452,7 +452,7 @@ func TestKillDifferentUser(t *testing.T) {
|
| 452 | 452 |
if container.State.Running {
|
| 453 | 453 |
t.Errorf("Container shouldn't be running")
|
| 454 | 454 |
} |
| 455 |
- if err := container.Start(&HostConfig{}); err != nil {
|
|
| 455 |
+ if err := container.Start(); err != nil {
|
|
| 456 | 456 |
t.Fatal(err) |
| 457 | 457 |
} |
| 458 | 458 |
|
| ... | ... |
@@ -501,7 +501,8 @@ func TestCreateVolume(t *testing.T) {
|
| 501 | 501 |
t.Fatal(err) |
| 502 | 502 |
} |
| 503 | 503 |
defer runtime.Destroy(c) |
| 504 |
- if err := c.Start(hc); err != nil {
|
|
| 504 |
+ c.hostConfig = hc |
|
| 505 |
+ if err := c.Start(); err != nil {
|
|
| 505 | 506 |
t.Fatal(err) |
| 506 | 507 |
} |
| 507 | 508 |
c.WaitTimeout(500 * time.Millisecond) |
| ... | ... |
@@ -525,8 +526,7 @@ func TestKill(t *testing.T) {
|
| 525 | 525 |
if container.State.Running {
|
| 526 | 526 |
t.Errorf("Container shouldn't be running")
|
| 527 | 527 |
} |
| 528 |
- hostConfig := &HostConfig{}
|
|
| 529 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 528 |
+ if err := container.Start(); err != nil {
|
|
| 530 | 529 |
t.Fatal(err) |
| 531 | 530 |
} |
| 532 | 531 |
|
| ... | ... |
@@ -642,8 +642,7 @@ func TestRestartStdin(t *testing.T) {
|
| 642 | 642 |
if err != nil {
|
| 643 | 643 |
t.Fatal(err) |
| 644 | 644 |
} |
| 645 |
- hostConfig := &HostConfig{}
|
|
| 646 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 645 |
+ if err := container.Start(); err != nil {
|
|
| 647 | 646 |
t.Fatal(err) |
| 648 | 647 |
} |
| 649 | 648 |
if _, err := io.WriteString(stdin, "hello world"); err != nil {
|
| ... | ... |
@@ -673,7 +672,7 @@ func TestRestartStdin(t *testing.T) {
|
| 673 | 673 |
if err != nil {
|
| 674 | 674 |
t.Fatal(err) |
| 675 | 675 |
} |
| 676 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 676 |
+ if err := container.Start(); err != nil {
|
|
| 677 | 677 |
t.Fatal(err) |
| 678 | 678 |
} |
| 679 | 679 |
if _, err := io.WriteString(stdin, "hello world #2"); err != nil {
|
| ... | ... |
@@ -850,11 +849,10 @@ func TestMultipleContainers(t *testing.T) {
|
| 850 | 850 |
defer runtime.Destroy(container2) |
| 851 | 851 |
|
| 852 | 852 |
// Start both containers |
| 853 |
- hostConfig := &HostConfig{}
|
|
| 854 |
- if err := container1.Start(hostConfig); err != nil {
|
|
| 853 |
+ if err := container1.Start(); err != nil {
|
|
| 855 | 854 |
t.Fatal(err) |
| 856 | 855 |
} |
| 857 |
- if err := container2.Start(hostConfig); err != nil {
|
|
| 856 |
+ if err := container2.Start(); err != nil {
|
|
| 858 | 857 |
t.Fatal(err) |
| 859 | 858 |
} |
| 860 | 859 |
|
| ... | ... |
@@ -904,8 +902,7 @@ func TestStdin(t *testing.T) {
|
| 904 | 904 |
if err != nil {
|
| 905 | 905 |
t.Fatal(err) |
| 906 | 906 |
} |
| 907 |
- hostConfig := &HostConfig{}
|
|
| 908 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 907 |
+ if err := container.Start(); err != nil {
|
|
| 909 | 908 |
t.Fatal(err) |
| 910 | 909 |
} |
| 911 | 910 |
defer stdin.Close() |
| ... | ... |
@@ -950,8 +947,7 @@ func TestTty(t *testing.T) {
|
| 950 | 950 |
if err != nil {
|
| 951 | 951 |
t.Fatal(err) |
| 952 | 952 |
} |
| 953 |
- hostConfig := &HostConfig{}
|
|
| 954 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 953 |
+ if err := container.Start(); err != nil {
|
|
| 955 | 954 |
t.Fatal(err) |
| 956 | 955 |
} |
| 957 | 956 |
defer stdin.Close() |
| ... | ... |
@@ -973,14 +969,15 @@ func TestTty(t *testing.T) {
|
| 973 | 973 |
} |
| 974 | 974 |
|
| 975 | 975 |
func TestEnv(t *testing.T) {
|
| 976 |
+ os.Setenv("TRUE", "false")
|
|
| 977 |
+ os.Setenv("TRICKY", "tri\ncky\n")
|
|
| 976 | 978 |
runtime := mkRuntime(t) |
| 977 | 979 |
defer nuke(runtime) |
| 978 |
- container, _, err := runtime.Create(&Config{
|
|
| 979 |
- Image: GetTestImage(runtime).ID, |
|
| 980 |
- Cmd: []string{"env"},
|
|
| 981 |
- }, |
|
| 982 |
- "", |
|
| 983 |
- ) |
|
| 980 |
+ config, _, _, err := ParseRun([]string{"-e=FALSE=true", "-e=TRUE", "-e=TRICKY", GetTestImage(runtime).ID, "env"}, nil)
|
|
| 981 |
+ if err != nil {
|
|
| 982 |
+ t.Fatal(err) |
|
| 983 |
+ } |
|
| 984 |
+ container, _, err := runtime.Create(config, "") |
|
| 984 | 985 |
if err != nil {
|
| 985 | 986 |
t.Fatal(err) |
| 986 | 987 |
} |
| ... | ... |
@@ -991,8 +988,7 @@ func TestEnv(t *testing.T) {
|
| 991 | 991 |
t.Fatal(err) |
| 992 | 992 |
} |
| 993 | 993 |
defer stdout.Close() |
| 994 |
- hostConfig := &HostConfig{}
|
|
| 995 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 994 |
+ if err := container.Start(); err != nil {
|
|
| 996 | 995 |
t.Fatal(err) |
| 997 | 996 |
} |
| 998 | 997 |
container.Wait() |
| ... | ... |
@@ -1010,6 +1006,11 @@ func TestEnv(t *testing.T) {
|
| 1010 | 1010 |
"HOME=/", |
| 1011 | 1011 |
"container=lxc", |
| 1012 | 1012 |
"HOSTNAME=" + container.ShortID(), |
| 1013 |
+ "FALSE=true", |
|
| 1014 |
+ "TRUE=false", |
|
| 1015 |
+ "TRICKY=tri", |
|
| 1016 |
+ "cky", |
|
| 1017 |
+ "", |
|
| 1013 | 1018 |
} |
| 1014 | 1019 |
sort.Strings(goodEnv) |
| 1015 | 1020 |
if len(goodEnv) != len(actualEnv) {
|
| ... | ... |
@@ -1115,7 +1116,7 @@ func TestLXCConfig(t *testing.T) {
|
| 1115 | 1115 |
t.Fatal(err) |
| 1116 | 1116 |
} |
| 1117 | 1117 |
defer runtime.Destroy(container) |
| 1118 |
- container.generateLXCConfig(nil) |
|
| 1118 |
+ container.generateLXCConfig() |
|
| 1119 | 1119 |
grepFile(t, container.lxcConfigPath(), "lxc.utsname = foobar") |
| 1120 | 1120 |
grepFile(t, container.lxcConfigPath(), |
| 1121 | 1121 |
fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
|
| ... | ... |
@@ -1138,7 +1139,7 @@ func TestCustomLxcConfig(t *testing.T) {
|
| 1138 | 1138 |
t.Fatal(err) |
| 1139 | 1139 |
} |
| 1140 | 1140 |
defer runtime.Destroy(container) |
| 1141 |
- hostConfig := &HostConfig{LxcConf: []KeyValuePair{
|
|
| 1141 |
+ container.hostConfig = &HostConfig{LxcConf: []KeyValuePair{
|
|
| 1142 | 1142 |
{
|
| 1143 | 1143 |
Key: "lxc.utsname", |
| 1144 | 1144 |
Value: "docker", |
| ... | ... |
@@ -1149,7 +1150,7 @@ func TestCustomLxcConfig(t *testing.T) {
|
| 1149 | 1149 |
}, |
| 1150 | 1150 |
}} |
| 1151 | 1151 |
|
| 1152 |
- container.generateLXCConfig(hostConfig) |
|
| 1152 |
+ container.generateLXCConfig() |
|
| 1153 | 1153 |
grepFile(t, container.lxcConfigPath(), "lxc.utsname = docker") |
| 1154 | 1154 |
grepFile(t, container.lxcConfigPath(), "lxc.cgroup.cpuset.cpus = 0,1") |
| 1155 | 1155 |
} |
| ... | ... |
@@ -1202,8 +1203,7 @@ func BenchmarkRunParallel(b *testing.B) {
|
| 1202 | 1202 |
return |
| 1203 | 1203 |
} |
| 1204 | 1204 |
defer runtime.Destroy(container) |
| 1205 |
- hostConfig := &HostConfig{}
|
|
| 1206 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 1205 |
+ if err := container.Start(); err != nil {
|
|
| 1207 | 1206 |
complete <- err |
| 1208 | 1207 |
return |
| 1209 | 1208 |
} |
| ... | ... |
@@ -1247,7 +1247,7 @@ func TestCopyVolumeUidGid(t *testing.T) {
|
| 1247 | 1247 |
defer nuke(r) |
| 1248 | 1248 |
|
| 1249 | 1249 |
// Add directory not owned by root |
| 1250 |
- container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello && touch /hello/test.txt && chown daemon.daemon /hello"}, t)
|
|
| 1250 |
+ container1, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello && touch /hello/test.txt && chown daemon.daemon /hello"}, t)
|
|
| 1251 | 1251 |
defer r.Destroy(container1) |
| 1252 | 1252 |
|
| 1253 | 1253 |
if container1.State.Running {
|
| ... | ... |
@@ -1284,7 +1284,7 @@ func TestCopyVolumeContent(t *testing.T) {
|
| 1284 | 1284 |
defer nuke(r) |
| 1285 | 1285 |
|
| 1286 | 1286 |
// Put some content in a directory of a container and commit it |
| 1287 |
- container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello/local && echo hello > /hello/local/world"}, t)
|
|
| 1287 |
+ container1, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello/local && echo hello > /hello/local/world"}, t)
|
|
| 1288 | 1288 |
defer r.Destroy(container1) |
| 1289 | 1289 |
|
| 1290 | 1290 |
if container1.State.Running {
|
| ... | ... |
@@ -1521,9 +1521,9 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
|
| 1521 | 1521 |
if err != nil {
|
| 1522 | 1522 |
t.Fatal(err) |
| 1523 | 1523 |
} |
| 1524 |
- |
|
| 1525 | 1524 |
defer runtime.Destroy(c) |
| 1526 |
- if err := c.Start(hc); err != nil {
|
|
| 1525 |
+ c.hostConfig = hc |
|
| 1526 |
+ if err := c.Start(); err != nil {
|
|
| 1527 | 1527 |
t.Fatal(err) |
| 1528 | 1528 |
} |
| 1529 | 1529 |
c.WaitTimeout(500 * time.Millisecond) |
| ... | ... |
@@ -1652,3 +1652,31 @@ func TestMultipleVolumesFrom(t *testing.T) {
|
| 1652 | 1652 |
t.Fail() |
| 1653 | 1653 |
} |
| 1654 | 1654 |
} |
| 1655 |
+ |
|
| 1656 |
+func TestRestartGhost(t *testing.T) {
|
|
| 1657 |
+ runtime := mkRuntime(t) |
|
| 1658 |
+ defer nuke(runtime) |
|
| 1659 |
+ |
|
| 1660 |
+ container, _, err := runtime.Create( |
|
| 1661 |
+ &Config{
|
|
| 1662 |
+ Image: GetTestImage(runtime).ID, |
|
| 1663 |
+ Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"},
|
|
| 1664 |
+ Volumes: map[string]struct{}{"/test": {}},
|
|
| 1665 |
+ }, |
|
| 1666 |
+ "", |
|
| 1667 |
+ ) |
|
| 1668 |
+ |
|
| 1669 |
+ if err != nil {
|
|
| 1670 |
+ t.Fatal(err) |
|
| 1671 |
+ } |
|
| 1672 |
+ if err := container.Kill(); err != nil {
|
|
| 1673 |
+ t.Fatal(err) |
|
| 1674 |
+ } |
|
| 1675 |
+ |
|
| 1676 |
+ container.State.Ghost = true |
|
| 1677 |
+ _, err = container.Output() |
|
| 1678 |
+ |
|
| 1679 |
+ if err != nil {
|
|
| 1680 |
+ t.Fatal(err) |
|
| 1681 |
+ } |
|
| 1682 |
+} |
| 433 | 433 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,11 @@ |
| 0 |
+Desktop Integration |
|
| 1 |
+=================== |
|
| 2 |
+ |
|
| 3 |
+The ./contrib/desktop-integration contains examples of typical dockerized |
|
| 4 |
+desktop applications. |
|
| 5 |
+ |
|
| 6 |
+Examples |
|
| 7 |
+======== |
|
| 8 |
+ |
|
| 9 |
+* Data container: ./data/Dockerfile creates a data image sharing /data volume |
|
| 10 |
+* Firefox: ./firefox/Dockerfile shows a way to dockerize a common multimedia application |
| 0 | 11 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,38 @@ |
| 0 |
+# VERSION: 0.1 |
|
| 1 |
+# DESCRIPTION: Create data image sharing /data volume |
|
| 2 |
+# AUTHOR: Daniel Mizyrycki <daniel@dotcloud.com> |
|
| 3 |
+# COMMENTS: |
|
| 4 |
+# This image is used as base for all data containers. |
|
| 5 |
+# /data volume is owned by sysadmin. |
|
| 6 |
+# USAGE: |
|
| 7 |
+# # Download data Dockerfile |
|
| 8 |
+# wget http://raw.github.com/dotcloud/docker/master/contrib/desktop-integration/data/Dockerfile |
|
| 9 |
+# |
|
| 10 |
+# # Build data image |
|
| 11 |
+# docker build -t data -rm . |
|
| 12 |
+# |
|
| 13 |
+# # Create a data container. (eg: firefox-data) |
|
| 14 |
+# docker run -name firefox-data data true |
|
| 15 |
+# |
|
| 16 |
+# # List data from it |
|
| 17 |
+# docker run -volumes-from firefox-data busybox ls -al /data |
|
| 18 |
+ |
|
| 19 |
+docker-version 0.6.5 |
|
| 20 |
+ |
|
| 21 |
+# Smallest base image, just to launch a container |
|
| 22 |
+from busybox |
|
| 23 |
+maintainer Daniel Mizyrycki <daniel@docker.com> |
|
| 24 |
+ |
|
| 25 |
+# Create a regular user |
|
| 26 |
+run echo 'sysadmin:x:1000:1000::/data:/bin/sh' >> /etc/passwd |
|
| 27 |
+run echo 'sysadmin:x:1000:' >> /etc/group |
|
| 28 |
+ |
|
| 29 |
+# Create directory for that user |
|
| 30 |
+run mkdir /data |
|
| 31 |
+run chown sysadmin.sysadmin /data |
|
| 32 |
+ |
|
| 33 |
+# Add content to /data. This will keep sysadmin ownership |
|
| 34 |
+run touch /data/init_volume |
|
| 35 |
+ |
|
| 36 |
+# Create /data volume |
|
| 37 |
+VOLUME /data |
| 0 | 38 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,49 @@ |
| 0 |
+# VERSION: 0.7 |
|
| 1 |
+# DESCRIPTION: Create firefox container with its dependencies |
|
| 2 |
+# AUTHOR: Daniel Mizyrycki <daniel@dotcloud.com> |
|
| 3 |
+# COMMENTS: |
|
| 4 |
+# This file describes how to build a Firefox container with all |
|
| 5 |
+# dependencies installed. It uses native X11 unix socket and alsa |
|
| 6 |
+# sound devices. Tested on Debian 7.2 |
|
| 7 |
+# USAGE: |
|
| 8 |
+# # Download Firefox Dockerfile |
|
| 9 |
+# wget http://raw.github.com/dotcloud/docker/master/contrib/desktop-integration/firefox/Dockerfile |
|
| 10 |
+# |
|
| 11 |
+# # Build firefox image |
|
| 12 |
+# docker build -t firefox -rm . |
|
| 13 |
+# |
|
| 14 |
+# # Run stateful data-on-host firefox. For ephemeral, remove -v /data/firefox:/data |
|
| 15 |
+# docker run -v /data/firefox:/data -v /tmp/.X11-unix:/tmp/.X11-unix \ |
|
| 16 |
+# -v /dev/snd:/dev/snd -lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \ |
|
| 17 |
+# -e DISPLAY=unix$DISPLAY firefox |
|
| 18 |
+# |
|
| 19 |
+# # To run stateful dockerized data containers |
|
| 20 |
+# docker run -volumes-from firefox-data -v /tmp/.X11-unix:/tmp/.X11-unix \ |
|
| 21 |
+# -v /dev/snd:/dev/snd -lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \ |
|
| 22 |
+# -e DISPLAY=unix$DISPLAY firefox |
|
| 23 |
+ |
|
| 24 |
+docker-version 0.6.5 |
|
| 25 |
+ |
|
| 26 |
+# Base docker image |
|
| 27 |
+from tianon/debian:wheezy |
|
| 28 |
+maintainer Daniel Mizyrycki <daniel@docker.com> |
|
| 29 |
+ |
|
| 30 |
+# Install firefox dependencies |
|
| 31 |
+run echo "deb http://ftp.debian.org/debian/ wheezy main contrib" > /etc/apt/sources.list |
|
| 32 |
+run apt-get update |
|
| 33 |
+run DEBIAN_FRONTEND=noninteractive apt-get install -y libXrender1 libasound2 \ |
|
| 34 |
+ libdbus-glib-1-2 libgtk2.0-0 libpango1.0-0 libxt6 wget bzip2 sudo |
|
| 35 |
+ |
|
| 36 |
+# Install Firefox |
|
| 37 |
+run mkdir /application |
|
| 38 |
+run cd /application; wget -O - \ |
|
| 39 |
+ http://ftp.mozilla.org/pub/mozilla.org/firefox/releases/25.0/linux-x86_64/en-US/firefox-25.0.tar.bz2 | tar jx |
|
| 40 |
+ |
|
| 41 |
+# create sysadmin account |
|
| 42 |
+run useradd -m -d /data -p saIVpsc0EVTwA sysadmin |
|
| 43 |
+run sed -Ei 's/sudo:x:27:/sudo:x:27:sysadmin/' /etc/group |
|
| 44 |
+run sed -Ei 's/(\%sudo\s+ALL=\(ALL\:ALL\) )ALL/\1 NOPASSWD:ALL/' /etc/sudoers |
|
| 45 |
+ |
|
| 46 |
+# Autorun firefox. -no-remote is necessary to create a new container, as firefox |
|
| 47 |
+# appears to communicate with itself through X11. |
|
| 48 |
+cmd ["/bin/sh", "-c", "/usr/bin/sudo -u sysadmin -H -E /application/firefox/firefox -no-remote"] |
| ... | ... |
@@ -6,43 +6,52 @@ |
| 6 | 6 |
# Required-Stop: $syslog $remote_fs |
| 7 | 7 |
# Default-Start: 2 3 4 5 |
| 8 | 8 |
# Default-Stop: 0 1 6 |
| 9 |
-# Short-Description: Linux container runtime |
|
| 10 |
-# Description: Linux container runtime |
|
| 9 |
+# Short-Description: Create lightweight, portable, self-sufficient containers. |
|
| 10 |
+# Description: |
|
| 11 |
+# Docker is an open-source project to easily create lightweight, portable, |
|
| 12 |
+# self-sufficient containers from any application. The same container that a |
|
| 13 |
+# developer builds and tests on a laptop can run at scale, in production, on |
|
| 14 |
+# VMs, bare metal, OpenStack clusters, public clouds and more. |
|
| 11 | 15 |
### END INIT INFO |
| 12 | 16 |
|
| 13 |
-DOCKER=/usr/bin/docker |
|
| 14 |
-DOCKER_PIDFILE=/var/run/docker.pid |
|
| 17 |
+BASE=$(basename $0) |
|
| 18 |
+ |
|
| 19 |
+DOCKER=/usr/bin/$BASE |
|
| 20 |
+DOCKER_PIDFILE=/var/run/$BASE.pid |
|
| 15 | 21 |
DOCKER_OPTS= |
| 16 | 22 |
|
| 17 | 23 |
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin |
| 18 | 24 |
|
| 19 |
-# Check lxc-docker is present |
|
| 20 |
-[ -x $DOCKER ] || (log_failure_msg "docker not present"; exit 1) |
|
| 21 |
- |
|
| 22 | 25 |
# Get lsb functions |
| 23 | 26 |
. /lib/lsb/init-functions |
| 24 | 27 |
|
| 25 |
-if [ -f /etc/default/lxc ]; then |
|
| 26 |
- . /etc/default/lxc |
|
| 28 |
+if [ -f /etc/default/$BASE ]; then |
|
| 29 |
+ . /etc/default/$BASE |
|
| 27 | 30 |
fi |
| 28 | 31 |
|
| 29 | 32 |
if [ "$1" = start ] && which initctl >/dev/null && initctl version | grep -q upstart; then |
| 30 | 33 |
exit 1 |
| 31 | 34 |
fi |
| 32 | 35 |
|
| 33 |
-check_root_id () |
|
| 34 |
-{
|
|
| 35 |
- if [ "$(id -u)" != "0" ]; then |
|
| 36 |
- log_failure_msg "Docker must be run as root"; exit 1 |
|
| 36 |
+# Check docker is present |
|
| 37 |
+if [ ! -x $DOCKER ]; then |
|
| 38 |
+ log_failure_msg "$DOCKER not present or not executable" |
|
| 39 |
+ exit 1 |
|
| 40 |
+fi |
|
| 41 |
+ |
|
| 42 |
+fail_unless_root() {
|
|
| 43 |
+ if [ "$(id -u)" != '0' ]; then |
|
| 44 |
+ log_failure_msg "Docker must be run as root" |
|
| 45 |
+ exit 1 |
|
| 37 | 46 |
fi |
| 38 | 47 |
} |
| 39 | 48 |
|
| 40 | 49 |
case "$1" in |
| 41 | 50 |
start) |
| 42 |
- check_root_id || exit 1 |
|
| 43 |
- log_begin_msg "Starting Docker" |
|
| 51 |
+ fail_unless_root |
|
| 52 |
+ log_begin_msg "Starting Docker: $BASE" |
|
| 44 | 53 |
mount | grep cgroup >/dev/null || mount -t cgroup none /sys/fs/cgroup 2>/dev/null |
| 45 |
- start-stop-daemon --start --background $NO_CLOSE \ |
|
| 54 |
+ start-stop-daemon --start --background \ |
|
| 46 | 55 |
--exec "$DOCKER" \ |
| 47 | 56 |
--pidfile "$DOCKER_PIDFILE" \ |
| 48 | 57 |
-- -d -p "$DOCKER_PIDFILE" \ |
| ... | ... |
@@ -51,15 +60,15 @@ case "$1" in |
| 51 | 51 |
;; |
| 52 | 52 |
|
| 53 | 53 |
stop) |
| 54 |
- check_root_id || exit 1 |
|
| 55 |
- log_begin_msg "Stopping Docker" |
|
| 54 |
+ fail_unless_root |
|
| 55 |
+ log_begin_msg "Stopping Docker: $BASE" |
|
| 56 | 56 |
start-stop-daemon --stop \ |
| 57 | 57 |
--pidfile "$DOCKER_PIDFILE" |
| 58 | 58 |
log_end_msg $? |
| 59 | 59 |
;; |
| 60 | 60 |
|
| 61 | 61 |
restart) |
| 62 |
- check_root_id || exit 1 |
|
| 62 |
+ fail_unless_root |
|
| 63 | 63 |
docker_pid=`cat "$DOCKER_PIDFILE" 2>/dev/null` |
| 64 | 64 |
[ -n "$docker_pid" ] \ |
| 65 | 65 |
&& ps -p $docker_pid > /dev/null 2>&1 \ |
| ... | ... |
@@ -68,7 +77,7 @@ case "$1" in |
| 68 | 68 |
;; |
| 69 | 69 |
|
| 70 | 70 |
force-reload) |
| 71 |
- check_root_id || exit 1 |
|
| 71 |
+ fail_unless_root |
|
| 72 | 72 |
$0 restart |
| 73 | 73 |
;; |
| 74 | 74 |
|
| ... | ... |
@@ -6,14 +6,10 @@ import ( |
| 6 | 6 |
"github.com/dotcloud/docker" |
| 7 | 7 |
"github.com/dotcloud/docker/sysinit" |
| 8 | 8 |
"github.com/dotcloud/docker/utils" |
| 9 |
- "io/ioutil" |
|
| 9 |
+ "github.com/dotcloud/docker/engine" |
|
| 10 | 10 |
"log" |
| 11 |
- "net" |
|
| 12 | 11 |
"os" |
| 13 |
- "os/signal" |
|
| 14 |
- "strconv" |
|
| 15 | 12 |
"strings" |
| 16 |
- "syscall" |
|
| 17 | 13 |
) |
| 18 | 14 |
|
| 19 | 15 |
var ( |
| ... | ... |
@@ -34,7 +30,7 @@ func main() {
|
| 34 | 34 |
flAutoRestart := flag.Bool("r", true, "Restart previously running containers")
|
| 35 | 35 |
bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge. Use 'none' to disable container networking")
|
| 36 | 36 |
pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
|
| 37 |
- flGraphPath := flag.String("g", "/var/lib/docker", "Path to graph storage base dir.")
|
|
| 37 |
+ flRoot := flag.String("g", "/var/lib/docker", "Path to use as the root of the docker runtime.")
|
|
| 38 | 38 |
flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
|
| 39 | 39 |
flDns := flag.String("dns", "", "Set custom dns servers")
|
| 40 | 40 |
flHosts := utils.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)}
|
| ... | ... |
@@ -61,10 +57,6 @@ func main() {
|
| 61 | 61 |
} |
| 62 | 62 |
} |
| 63 | 63 |
|
| 64 |
- bridge := docker.DefaultNetworkBridge |
|
| 65 |
- if *bridgeName != "" {
|
|
| 66 |
- bridge = *bridgeName |
|
| 67 |
- } |
|
| 68 | 64 |
if *flDebug {
|
| 69 | 65 |
os.Setenv("DEBUG", "1")
|
| 70 | 66 |
} |
| ... | ... |
@@ -75,26 +67,22 @@ func main() {
|
| 75 | 75 |
flag.Usage() |
| 76 | 76 |
return |
| 77 | 77 |
} |
| 78 |
- var dns []string |
|
| 79 |
- if *flDns != "" {
|
|
| 80 |
- dns = []string{*flDns}
|
|
| 81 |
- } |
|
| 82 |
- |
|
| 83 |
- ip := net.ParseIP(*flDefaultIp) |
|
| 84 |
- |
|
| 85 |
- config := &docker.DaemonConfig{
|
|
| 86 |
- Pidfile: *pidfile, |
|
| 87 |
- GraphPath: *flGraphPath, |
|
| 88 |
- AutoRestart: *flAutoRestart, |
|
| 89 |
- EnableCors: *flEnableCors, |
|
| 90 |
- Dns: dns, |
|
| 91 |
- EnableIptables: *flEnableIptables, |
|
| 92 |
- BridgeIface: bridge, |
|
| 93 |
- ProtoAddresses: flHosts, |
|
| 94 |
- DefaultIp: ip, |
|
| 95 |
- InterContainerCommunication: *flInterContainerComm, |
|
| 78 |
+ eng, err := engine.New(*flRoot) |
|
| 79 |
+ if err != nil {
|
|
| 80 |
+ log.Fatal(err) |
|
| 96 | 81 |
} |
| 97 |
- if err := daemon(config); err != nil {
|
|
| 82 |
+ job := eng.Job("serveapi")
|
|
| 83 |
+ job.Setenv("Pidfile", *pidfile)
|
|
| 84 |
+ job.Setenv("Root", *flRoot)
|
|
| 85 |
+ job.SetenvBool("AutoRestart", *flAutoRestart)
|
|
| 86 |
+ job.SetenvBool("EnableCors", *flEnableCors)
|
|
| 87 |
+ job.Setenv("Dns", *flDns)
|
|
| 88 |
+ job.SetenvBool("EnableIptables", *flEnableIptables)
|
|
| 89 |
+ job.Setenv("BridgeIface", *bridgeName)
|
|
| 90 |
+ job.SetenvList("ProtoAddresses", flHosts)
|
|
| 91 |
+ job.Setenv("DefaultIp", *flDefaultIp)
|
|
| 92 |
+ job.SetenvBool("InterContainerCommunication", *flInterContainerComm)
|
|
| 93 |
+ if err := job.Run(); err != nil {
|
|
| 98 | 94 |
log.Fatal(err) |
| 99 | 95 |
} |
| 100 | 96 |
} else {
|
| ... | ... |
@@ -114,79 +102,3 @@ func main() {
|
| 114 | 114 |
func showVersion() {
|
| 115 | 115 |
fmt.Printf("Docker version %s, build %s\n", VERSION, GITCOMMIT)
|
| 116 | 116 |
} |
| 117 |
- |
|
| 118 |
-func createPidFile(pidfile string) error {
|
|
| 119 |
- if pidString, err := ioutil.ReadFile(pidfile); err == nil {
|
|
| 120 |
- pid, err := strconv.Atoi(string(pidString)) |
|
| 121 |
- if err == nil {
|
|
| 122 |
- if _, err := os.Stat(fmt.Sprintf("/proc/%d/", pid)); err == nil {
|
|
| 123 |
- return fmt.Errorf("pid file found, ensure docker is not running or delete %s", pidfile)
|
|
| 124 |
- } |
|
| 125 |
- } |
|
| 126 |
- } |
|
| 127 |
- |
|
| 128 |
- file, err := os.Create(pidfile) |
|
| 129 |
- if err != nil {
|
|
| 130 |
- return err |
|
| 131 |
- } |
|
| 132 |
- |
|
| 133 |
- defer file.Close() |
|
| 134 |
- |
|
| 135 |
- _, err = fmt.Fprintf(file, "%d", os.Getpid()) |
|
| 136 |
- return err |
|
| 137 |
-} |
|
| 138 |
- |
|
| 139 |
-func removePidFile(pidfile string) {
|
|
| 140 |
- if err := os.Remove(pidfile); err != nil {
|
|
| 141 |
- log.Printf("Error removing %s: %s", pidfile, err)
|
|
| 142 |
- } |
|
| 143 |
-} |
|
| 144 |
- |
|
| 145 |
-func daemon(config *docker.DaemonConfig) error {
|
|
| 146 |
- if err := createPidFile(config.Pidfile); err != nil {
|
|
| 147 |
- log.Fatal(err) |
|
| 148 |
- } |
|
| 149 |
- defer removePidFile(config.Pidfile) |
|
| 150 |
- |
|
| 151 |
- server, err := docker.NewServer(config) |
|
| 152 |
- if err != nil {
|
|
| 153 |
- return err |
|
| 154 |
- } |
|
| 155 |
- defer server.Close() |
|
| 156 |
- |
|
| 157 |
- c := make(chan os.Signal, 1) |
|
| 158 |
- signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) |
|
| 159 |
- go func() {
|
|
| 160 |
- sig := <-c |
|
| 161 |
- log.Printf("Received signal '%v', exiting\n", sig)
|
|
| 162 |
- server.Close() |
|
| 163 |
- removePidFile(config.Pidfile) |
|
| 164 |
- os.Exit(0) |
|
| 165 |
- }() |
|
| 166 |
- |
|
| 167 |
- chErrors := make(chan error, len(config.ProtoAddresses)) |
|
| 168 |
- for _, protoAddr := range config.ProtoAddresses {
|
|
| 169 |
- protoAddrParts := strings.SplitN(protoAddr, "://", 2) |
|
| 170 |
- if protoAddrParts[0] == "unix" {
|
|
| 171 |
- syscall.Unlink(protoAddrParts[1]) |
|
| 172 |
- } else if protoAddrParts[0] == "tcp" {
|
|
| 173 |
- if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") {
|
|
| 174 |
- log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
|
| 175 |
- } |
|
| 176 |
- } else {
|
|
| 177 |
- server.Close() |
|
| 178 |
- removePidFile(config.Pidfile) |
|
| 179 |
- log.Fatal("Invalid protocol format.")
|
|
| 180 |
- } |
|
| 181 |
- go func() {
|
|
| 182 |
- chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true) |
|
| 183 |
- }() |
|
| 184 |
- } |
|
| 185 |
- for i := 0; i < len(config.ProtoAddresses); i += 1 {
|
|
| 186 |
- err := <-chErrors |
|
| 187 |
- if err != nil {
|
|
| 188 |
- return err |
|
| 189 |
- } |
|
| 190 |
- } |
|
| 191 |
- return nil |
|
| 192 |
-} |
| ... | ... |
@@ -943,36 +943,36 @@ Build an image from Dockerfile via stdin |
| 943 | 943 |
|
| 944 | 944 |
.. http:post:: /build |
| 945 | 945 |
|
| 946 |
- Build an image from Dockerfile via stdin |
|
| 946 |
+ Build an image from Dockerfile via stdin |
|
| 947 | 947 |
|
| 948 |
- **Example request**: |
|
| 948 |
+ **Example request**: |
|
| 949 | 949 |
|
| 950 |
- .. sourcecode:: http |
|
| 950 |
+ .. sourcecode:: http |
|
| 951 | 951 |
|
| 952 |
- POST /build HTTP/1.1 |
|
| 952 |
+ POST /build HTTP/1.1 |
|
| 953 | 953 |
|
| 954 |
- {{ STREAM }}
|
|
| 954 |
+ {{ STREAM }}
|
|
| 955 | 955 |
|
| 956 |
- **Example response**: |
|
| 956 |
+ **Example response**: |
|
| 957 | 957 |
|
| 958 |
- .. sourcecode:: http |
|
| 958 |
+ .. sourcecode:: http |
|
| 959 | 959 |
|
| 960 |
- HTTP/1.1 200 OK |
|
| 960 |
+ HTTP/1.1 200 OK |
|
| 961 | 961 |
|
| 962 |
- {{ STREAM }}
|
|
| 962 |
+ {{ STREAM }}
|
|
| 963 | 963 |
|
| 964 | 964 |
|
| 965 |
- The stream must be a tar archive compressed with one of the following algorithms: |
|
| 966 |
- identity (no compression), gzip, bzip2, xz. The archive must include a file called |
|
| 967 |
- `Dockerfile` at its root. It may include any number of other files, which will be |
|
| 968 |
- accessible in the build context (See the ADD build command). |
|
| 965 |
+ The stream must be a tar archive compressed with one of the following algorithms: |
|
| 966 |
+ identity (no compression), gzip, bzip2, xz. The archive must include a file called |
|
| 967 |
+ `Dockerfile` at its root. It may include any number of other files, which will be |
|
| 968 |
+ accessible in the build context (See the ADD build command). |
|
| 969 | 969 |
|
| 970 |
- The Content-type header should be set to "application/tar". |
|
| 970 |
+ The Content-type header should be set to "application/tar". |
|
| 971 | 971 |
|
| 972 |
- :query t: repository name (and optionally a tag) to be applied to the resulting image in case of success |
|
| 973 |
- :query q: suppress verbose build output |
|
| 972 |
+ :query t: repository name (and optionally a tag) to be applied to the resulting image in case of success |
|
| 973 |
+ :query q: suppress verbose build output |
|
| 974 | 974 |
:query nocache: do not use the cache when building the image |
| 975 |
- :statuscode 200: no error |
|
| 975 |
+ :statuscode 200: no error |
|
| 976 | 976 |
:statuscode 500: server error |
| 977 | 977 |
|
| 978 | 978 |
|
| ... | ... |
@@ -1069,8 +1069,8 @@ Show the docker version information |
| 1069 | 1069 |
"GoVersion":"go1.0.3" |
| 1070 | 1070 |
} |
| 1071 | 1071 |
|
| 1072 |
- :statuscode 200: no error |
|
| 1073 |
- :statuscode 500: server error |
|
| 1072 |
+ :statuscode 200: no error |
|
| 1073 |
+ :statuscode 500: server error |
|
| 1074 | 1074 |
|
| 1075 | 1075 |
|
| 1076 | 1076 |
Create a new image from a container's changes |
| ... | ... |
@@ -995,36 +995,36 @@ Build an image from Dockerfile via stdin |
| 995 | 995 |
|
| 996 | 996 |
.. http:post:: /build |
| 997 | 997 |
|
| 998 |
- Build an image from Dockerfile via stdin |
|
| 998 |
+ Build an image from Dockerfile via stdin |
|
| 999 | 999 |
|
| 1000 |
- **Example request**: |
|
| 1001 |
- |
|
| 1002 |
- .. sourcecode:: http |
|
| 1000 |
+ **Example request**: |
|
| 1003 | 1001 |
|
| 1004 |
- POST /build HTTP/1.1 |
|
| 1002 |
+ .. sourcecode:: http |
|
| 1005 | 1003 |
|
| 1006 |
- {{ STREAM }}
|
|
| 1004 |
+ POST /build HTTP/1.1 |
|
| 1007 | 1005 |
|
| 1008 |
- **Example response**: |
|
| 1006 |
+ {{ STREAM }}
|
|
| 1009 | 1007 |
|
| 1010 |
- .. sourcecode:: http |
|
| 1008 |
+ **Example response**: |
|
| 1011 | 1009 |
|
| 1012 |
- HTTP/1.1 200 OK |
|
| 1010 |
+ .. sourcecode:: http |
|
| 1013 | 1011 |
|
| 1014 |
- {{ STREAM }}
|
|
| 1012 |
+ HTTP/1.1 200 OK |
|
| 1015 | 1013 |
|
| 1014 |
+ {{ STREAM }}
|
|
| 1016 | 1015 |
|
| 1017 |
- The stream must be a tar archive compressed with one of the following algorithms: |
|
| 1018 |
- identity (no compression), gzip, bzip2, xz. The archive must include a file called |
|
| 1019 |
- `Dockerfile` at its root. It may include any number of other files, which will be |
|
| 1020 |
- accessible in the build context (See the ADD build command). |
|
| 1021 | 1016 |
|
| 1022 |
- The Content-type header should be set to "application/tar". |
|
| 1017 |
+ The stream must be a tar archive compressed with one of the following algorithms: |
|
| 1018 |
+ identity (no compression), gzip, bzip2, xz. The archive must include a file called |
|
| 1019 |
+ `Dockerfile` at its root. It may include any number of other files, which will be |
|
| 1020 |
+ accessible in the build context (See the ADD build command). |
|
| 1021 |
+ |
|
| 1022 |
+ The Content-type header should be set to "application/tar". |
|
| 1023 | 1023 |
|
| 1024 |
- :query t: repository name (and optionally a tag) to be applied to the resulting image in case of success |
|
| 1025 |
- :query q: suppress verbose build output |
|
| 1024 |
+ :query t: repository name (and optionally a tag) to be applied to the resulting image in case of success |
|
| 1025 |
+ :query q: suppress verbose build output |
|
| 1026 | 1026 |
:query nocache: do not use the cache when building the image |
| 1027 |
- :statuscode 200: no error |
|
| 1027 |
+ :statuscode 200: no error |
|
| 1028 | 1028 |
:statuscode 500: server error |
| 1029 | 1029 |
|
| 1030 | 1030 |
|
| ... | ... |
@@ -41,7 +41,7 @@ To stop a container, use ``docker stop`` |
| 41 | 41 |
To kill the container, use ``docker kill`` |
| 42 | 42 |
|
| 43 | 43 |
.. _cli_attach_examples: |
| 44 |
- |
|
| 44 |
+ |
|
| 45 | 45 |
Examples: |
| 46 | 46 |
~~~~~~~~~ |
| 47 | 47 |
|
| ... | ... |
@@ -55,8 +55,8 @@ Examples: |
| 55 | 55 |
Mem: 373572k total, 355560k used, 18012k free, 27872k buffers |
| 56 | 56 |
Swap: 786428k total, 0k used, 786428k free, 221740k cached |
| 57 | 57 |
|
| 58 |
- PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND |
|
| 59 |
- 1 root 20 0 17200 1116 912 R 0 0.3 0:00.03 top |
|
| 58 |
+ PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND |
|
| 59 |
+ 1 root 20 0 17200 1116 912 R 0 0.3 0:00.03 top |
|
| 60 | 60 |
|
| 61 | 61 |
top - 02:05:55 up 3:05, 0 users, load average: 0.01, 0.02, 0.05 |
| 62 | 62 |
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie |
| ... | ... |
@@ -64,8 +64,8 @@ Examples: |
| 64 | 64 |
Mem: 373572k total, 355244k used, 18328k free, 27872k buffers |
| 65 | 65 |
Swap: 786428k total, 0k used, 786428k free, 221776k cached |
| 66 | 66 |
|
| 67 |
- PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND |
|
| 68 |
- 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top |
|
| 67 |
+ PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND |
|
| 68 |
+ 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top |
|
| 69 | 69 |
|
| 70 | 70 |
|
| 71 | 71 |
top - 02:05:58 up 3:06, 0 users, load average: 0.01, 0.02, 0.05 |
| ... | ... |
@@ -74,9 +74,9 @@ Examples: |
| 74 | 74 |
Mem: 373572k total, 355780k used, 17792k free, 27880k buffers |
| 75 | 75 |
Swap: 786428k total, 0k used, 786428k free, 221776k cached |
| 76 | 76 |
|
| 77 |
- PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND |
|
| 78 |
- 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top |
|
| 79 |
- ^C$ |
|
| 77 |
+ PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND |
|
| 78 |
+ 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top |
|
| 79 |
+ ^C$ |
|
| 80 | 80 |
$ sudo docker stop $ID |
| 81 | 81 |
|
| 82 | 82 |
.. _cli_build: |
| ... | ... |
@@ -133,7 +133,6 @@ to the ``docker`` daemon. ``ADD`` doesn't work when running in this |
| 133 | 133 |
mode because the absence of the context provides no source files to |
| 134 | 134 |
copy to the container. |
| 135 | 135 |
|
| 136 |
- |
|
| 137 | 136 |
.. code-block:: bash |
| 138 | 137 |
|
| 139 | 138 |
sudo docker build github.com/creack/docker-firefox |
| ... | ... |
@@ -151,16 +150,35 @@ by using the ``git://`` schema. |
| 151 | 151 |
|
| 152 | 152 |
:: |
| 153 | 153 |
|
| 154 |
- Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY [TAG]] |
|
| 154 |
+ Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] |
|
| 155 | 155 |
|
| 156 | 156 |
Create a new image from a container's changes |
| 157 | 157 |
|
| 158 | 158 |
-m="": Commit message |
| 159 | 159 |
-author="": Author (eg. "John Hannibal Smith <hannibal@a-team.com>" |
| 160 |
- -run="": Configuration to be applied when the image is launched with `docker run`. |
|
| 160 |
+ -run="": Configuration to be applied when the image is launched with `docker run`. |
|
| 161 | 161 |
(ex: '{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')
|
| 162 | 162 |
|
| 163 |
-Full -run example (multiline is ok within a single quote ``'``) |
|
| 163 |
+Simple commit of an existing container |
|
| 164 |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
| 165 |
+ |
|
| 166 |
+.. code-block:: bash |
|
| 167 |
+ |
|
| 168 |
+ $ docker ps |
|
| 169 |
+ ID IMAGE COMMAND CREATED STATUS PORTS |
|
| 170 |
+ c3f279d17e0a ubuntu:12.04 /bin/bash 7 days ago Up 25 hours |
|
| 171 |
+ 197387f1b436 ubuntu:12.04 /bin/bash 7 days ago Up 25 hours |
|
| 172 |
+ $ docker commit c3f279d17e0a SvenDowideit/testimage:version3 |
|
| 173 |
+ f5283438590d |
|
| 174 |
+ $ docker images | head |
|
| 175 |
+ REPOSITORY TAG ID CREATED SIZE |
|
| 176 |
+ SvenDowideit/testimage version3 f5283438590d 16 seconds ago 204.2 MB (virtual 335.7 MB) |
|
| 177 |
+ S |
|
| 178 |
+ |
|
| 179 |
+Full -run example |
|
| 180 |
+................. |
|
| 181 |
+ |
|
| 182 |
+(multiline is ok within a single quote ``'``) |
|
| 164 | 183 |
|
| 165 | 184 |
:: |
| 166 | 185 |
|
| ... | ... |
@@ -239,7 +257,7 @@ Shell 1: Listening for events |
| 239 | 239 |
............................. |
| 240 | 240 |
|
| 241 | 241 |
.. code-block:: bash |
| 242 |
- |
|
| 242 |
+ |
|
| 243 | 243 |
$ sudo docker events |
| 244 | 244 |
|
| 245 | 245 |
Shell 2: Start and Stop a Container |
| ... | ... |
@@ -282,6 +300,9 @@ Shell 1: (Again .. now showing events) |
| 282 | 282 |
|
| 283 | 283 |
Show the history of an image |
| 284 | 284 |
|
| 285 |
+ -notrunc=false: Don't truncate output |
|
| 286 |
+ -q=false: only show numeric IDs |
|
| 287 |
+ |
|
| 285 | 288 |
.. _cli_images: |
| 286 | 289 |
|
| 287 | 290 |
``images`` |
| ... | ... |
@@ -314,7 +335,7 @@ Displaying images visually |
| 314 | 314 |
|
| 315 | 315 |
:: |
| 316 | 316 |
|
| 317 |
- Usage: docker import URL|- [REPOSITORY [TAG]] |
|
| 317 |
+ Usage: docker import URL|- [REPOSITORY[:TAG]] |
|
| 318 | 318 |
|
| 319 | 319 |
Create a new filesystem image from the contents of a tarball |
| 320 | 320 |
|
| ... | ... |
@@ -330,14 +351,16 @@ Examples |
| 330 | 330 |
Import from a remote location |
| 331 | 331 |
............................. |
| 332 | 332 |
|
| 333 |
-``$ sudo docker import http://example.com/exampleimage.tgz exampleimagerepo`` |
|
| 333 |
+This will create a new untagged image. |
|
| 334 |
+ |
|
| 335 |
+``$ sudo docker import http://example.com/exampleimage.tgz`` |
|
| 334 | 336 |
|
| 335 | 337 |
Import from a local file |
| 336 | 338 |
........................ |
| 337 | 339 |
|
| 338 | 340 |
Import to docker via pipe and standard in |
| 339 | 341 |
|
| 340 |
-``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal`` |
|
| 342 |
+``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal:new`` |
|
| 341 | 343 |
|
| 342 | 344 |
Import from a local directory |
| 343 | 345 |
............................. |
| ... | ... |
@@ -401,7 +424,9 @@ Insert file from github |
| 401 | 401 |
|
| 402 | 402 |
Usage: docker kill CONTAINER [CONTAINER...] |
| 403 | 403 |
|
| 404 |
- Kill a running container |
|
| 404 |
+ Kill a running container (Send SIGKILL) |
|
| 405 |
+ |
|
| 406 |
+The main process inside the container will be sent SIGKILL. |
|
| 405 | 407 |
|
| 406 | 408 |
.. _cli_login: |
| 407 | 409 |
|
| ... | ... |
@@ -510,7 +535,7 @@ Insert file from github |
| 510 | 510 |
|
| 511 | 511 |
Remove one or more containers |
| 512 | 512 |
-link="": Remove the link instead of the actual container |
| 513 |
- |
|
| 513 |
+ |
|
| 514 | 514 |
|
| 515 | 515 |
Examples: |
| 516 | 516 |
~~~~~~~~~ |
| ... | ... |
@@ -579,6 +604,7 @@ network communication. |
| 579 | 579 |
-expose=[]: Expose a port from the container without publishing it to your host |
| 580 | 580 |
-link="": Add link to another container (name:alias) |
| 581 | 581 |
-name="": Assign the specified name to the container. If no name is specific docker will generate a random name |
| 582 |
+ -P=false: Publish all exposed ports to the host interfaces |
|
| 582 | 583 |
|
| 583 | 584 |
Examples |
| 584 | 585 |
-------- |
| ... | ... |
@@ -615,58 +641,51 @@ use-cases, like running Docker within Docker. |
| 615 | 615 |
|
| 616 | 616 |
docker run -w /path/to/dir/ -i -t ubuntu pwd |
| 617 | 617 |
|
| 618 |
-The ``-w`` lets the command being executed inside directory given, |
|
| 619 |
-here /path/to/dir/. If the path does not exists it is created inside the |
|
| 618 |
+The ``-w`` lets the command being executed inside directory given, |
|
| 619 |
+here /path/to/dir/. If the path does not exists it is created inside the |
|
| 620 | 620 |
container. |
| 621 | 621 |
|
| 622 | 622 |
.. code-block:: bash |
| 623 | 623 |
|
| 624 | 624 |
docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd |
| 625 | 625 |
|
| 626 |
-The ``-v`` flag mounts the current working directory into the container. |
|
| 627 |
-The ``-w`` lets the command being executed inside the current |
|
| 626 |
+The ``-v`` flag mounts the current working directory into the container. |
|
| 627 |
+The ``-w`` lets the command being executed inside the current |
|
| 628 | 628 |
working directory, by changing into the directory to the value |
| 629 | 629 |
returned by ``pwd``. So this combination executes the command |
| 630 | 630 |
using the container, but inside the current working directory. |
| 631 | 631 |
|
| 632 | 632 |
.. code-block:: bash |
| 633 | 633 |
|
| 634 |
- docker run -p 127.0.0.0::80 ubuntu bash |
|
| 635 |
- |
|
| 636 |
-This the ``-p`` flag now allows you to bind a port to a specific |
|
| 637 |
-interface of the host machine. In this example port ``80`` of the |
|
| 638 |
-container will have a dynamically allocated port bound to 127.0.0.1 |
|
| 639 |
-of the host. |
|
| 640 |
- |
|
| 641 |
-.. code-block:: bash |
|
| 642 |
- |
|
| 643 |
- docker run -p 127.0.0.1:80:80 ubuntu bash |
|
| 634 |
+ docker run -p 127.0.0.1:80:8080 ubuntu bash |
|
| 644 | 635 |
|
| 645 |
-This will bind port ``80`` of the container to port ``80`` on 127.0.0.1 of your |
|
| 646 |
-host machine. |
|
| 636 |
+This binds port ``8080`` of the container to port ``80`` on 127.0.0.1 of the |
|
| 637 |
+host machine. :ref:`port_redirection` explains in detail how to manipulate ports |
|
| 638 |
+in Docker. |
|
| 647 | 639 |
|
| 648 | 640 |
.. code-block:: bash |
| 649 | 641 |
|
| 650 | 642 |
docker run -expose 80 ubuntu bash |
| 651 | 643 |
|
| 652 |
-This will expose port ``80`` of the container for use within a link |
|
| 653 |
-without publishing the port to the host system's interfaces. |
|
| 644 |
+This exposes port ``80`` of the container for use within a link without |
|
| 645 |
+publishing the port to the host system's interfaces. :ref:`port_redirection` |
|
| 646 |
+explains in detail how to manipulate ports in Docker. |
|
| 654 | 647 |
|
| 655 | 648 |
.. code-block:: bash |
| 656 | 649 |
|
| 657 | 650 |
docker run -name console -t -i ubuntu bash |
| 658 | 651 |
|
| 659 |
-This will create and run a new container with the container name |
|
| 652 |
+This will create and run a new container with the container name |
|
| 660 | 653 |
being ``console``. |
| 661 | 654 |
|
| 662 | 655 |
.. code-block:: bash |
| 663 | 656 |
|
| 664 | 657 |
docker run -link /redis:redis -name console ubuntu bash |
| 665 | 658 |
|
| 666 |
-The ``-link`` flag will link the container named ``/redis`` into the |
|
| 659 |
+The ``-link`` flag will link the container named ``/redis`` into the |
|
| 667 | 660 |
newly created container with the alias ``redis``. The new container |
| 668 | 661 |
can access the network and environment of the redis container via |
| 669 |
-environment variables. The ``-name`` flag will assign the name ``console`` |
|
| 662 |
+environment variables. The ``-name`` flag will assign the name ``console`` |
|
| 670 | 663 |
to the newly created container. |
| 671 | 664 |
|
| 672 | 665 |
.. _cli_search: |
| ... | ... |
@@ -678,8 +697,11 @@ to the newly created container. |
| 678 | 678 |
|
| 679 | 679 |
Usage: docker search TERM |
| 680 | 680 |
|
| 681 |
- Searches for the TERM parameter on the Docker index and prints out |
|
| 682 |
- a list of repositories that match. |
|
| 681 |
+ Search the docker index for images |
|
| 682 |
+ |
|
| 683 |
+ -notrunc=false: Don't truncate output |
|
| 684 |
+ -stars=0: Only displays with at least xxx stars |
|
| 685 |
+ -trusted=false: Only show trusted builds |
|
| 683 | 686 |
|
| 684 | 687 |
.. _cli_start: |
| 685 | 688 |
|
| ... | ... |
@@ -704,10 +726,12 @@ to the newly created container. |
| 704 | 704 |
|
| 705 | 705 |
Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] |
| 706 | 706 |
|
| 707 |
- Stop a running container |
|
| 707 |
+ Stop a running container (Send SIGTERM, and then SIGKILL after grace period) |
|
| 708 | 708 |
|
| 709 | 709 |
-t=10: Number of seconds to wait for the container to stop before killing it. |
| 710 | 710 |
|
| 711 |
+The main process inside the container will receive SIGTERM, and after a grace period, SIGKILL |
|
| 712 |
+ |
|
| 711 | 713 |
.. _cli_tag: |
| 712 | 714 |
|
| 713 | 715 |
``tag`` |
| ... | ... |
@@ -715,7 +739,7 @@ to the newly created container. |
| 715 | 715 |
|
| 716 | 716 |
:: |
| 717 | 717 |
|
| 718 |
- Usage: docker tag [OPTIONS] IMAGE REPOSITORY [TAG] |
|
| 718 |
+ Usage: docker tag [OPTIONS] IMAGE REPOSITORY[:TAG] |
|
| 719 | 719 |
|
| 720 | 720 |
Tag an image into a repository |
| 721 | 721 |
|
| ... | ... |
@@ -728,7 +752,7 @@ to the newly created container. |
| 728 | 728 |
|
| 729 | 729 |
:: |
| 730 | 730 |
|
| 731 |
- Usage: docker top CONTAINER |
|
| 731 |
+ Usage: docker top CONTAINER [ps OPTIONS] |
|
| 732 | 732 |
|
| 733 | 733 |
Lookup the running processes of a container |
| 734 | 734 |
|
| ... | ... |
@@ -750,6 +774,3 @@ Show the version of the docker client, daemon, and latest released version. |
| 750 | 750 |
Usage: docker wait [OPTIONS] NAME |
| 751 | 751 |
|
| 752 | 752 |
Block until a container stops, then print its exit code. |
| 753 |
- |
|
| 754 |
- |
|
| 755 |
- |
| ... | ... |
@@ -56,7 +56,7 @@ To create the Docker binary, run this command: |
| 56 | 56 |
|
| 57 | 57 |
.. code-block:: bash |
| 58 | 58 |
|
| 59 |
- sudo docker run -lxc-conf=lxc.aa_profile=unconfined -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh binary |
|
| 59 |
+ sudo docker run -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh binary |
|
| 60 | 60 |
|
| 61 | 61 |
This will create the Docker binary in ``./bundles/<version>-dev/binary/`` |
| 62 | 62 |
|
| ... | ... |
@@ -64,19 +64,14 @@ This will create the Docker binary in ``./bundles/<version>-dev/binary/`` |
| 64 | 64 |
Step 5: Run the Tests |
| 65 | 65 |
--------------------- |
| 66 | 66 |
|
| 67 |
-To run the Docker test cases you first need to disable `AppArmor <https://wiki.ubuntu.com/AppArmor>`_ using the following commands |
|
| 68 |
- |
|
| 69 |
-.. code-block:: bash |
|
| 70 |
- |
|
| 71 |
- sudo /etc/init.d/apparmor stop |
|
| 72 |
- sudo /etc/init.d/apparmor teardown |
|
| 73 |
- |
|
| 74 | 67 |
To execute the test cases, run this command: |
| 75 | 68 |
|
| 76 | 69 |
.. code-block:: bash |
| 77 | 70 |
|
| 78 |
- sudo docker run -lxc-conf=lxc.aa_profile=unconfined -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh test |
|
| 71 |
+ sudo docker run -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh test |
|
| 72 |
+ |
|
| 79 | 73 |
|
| 74 |
+Note: if you're running the tests in vagrant, you need to specify a dns entry in the command: `-dns 8.8.8.8` |
|
| 80 | 75 |
|
| 81 | 76 |
If the test are successful then the tail of the output should look something like this |
| 82 | 77 |
|
| ... | ... |
@@ -10,7 +10,7 @@ CouchDB Service |
| 10 | 10 |
.. include:: example_header.inc |
| 11 | 11 |
|
| 12 | 12 |
Here's an example of using data volumes to share the same data between |
| 13 |
-2 CouchDB containers. This could be used for hot upgrades, testing |
|
| 13 |
+two CouchDB containers. This could be used for hot upgrades, testing |
|
| 14 | 14 |
different versions of CouchDB on the same data, etc. |
| 15 | 15 |
|
| 16 | 16 |
Create first database |
| ... | ... |
@@ -25,8 +25,8 @@ Note that we're marking ``/var/lib/couchdb`` as a data volume. |
| 25 | 25 |
Add data to the first database |
| 26 | 26 |
------------------------------ |
| 27 | 27 |
|
| 28 |
-We're assuming your docker host is reachable at `localhost`. If not, |
|
| 29 |
-replace `localhost` with the public IP of your docker host. |
|
| 28 |
+We're assuming your Docker host is reachable at ``localhost``. If not, |
|
| 29 |
+replace ``localhost`` with the public IP of your Docker host. |
|
| 30 | 30 |
|
| 31 | 31 |
.. code-block:: bash |
| 32 | 32 |
|
| ... | ... |
@@ -37,7 +37,7 @@ replace `localhost` with the public IP of your docker host. |
| 37 | 37 |
Create second database |
| 38 | 38 |
---------------------- |
| 39 | 39 |
|
| 40 |
-This time, we're requesting shared access to $COUCH1's volumes. |
|
| 40 |
+This time, we're requesting shared access to ``$COUCH1``'s volumes. |
|
| 41 | 41 |
|
| 42 | 42 |
.. code-block:: bash |
| 43 | 43 |
|
| ... | ... |
@@ -52,5 +52,5 @@ Browse data on the second database |
| 52 | 52 |
URL="http://$HOST:$(sudo docker port $COUCH2 5984)/_utils/" |
| 53 | 53 |
echo "Navigate to $URL in your browser. You should see the same data as in the first database"'!' |
| 54 | 54 |
|
| 55 |
-Congratulations, you are running 2 Couchdb containers, completely |
|
| 55 |
+Congratulations, you are now running two Couchdb containers, completely |
|
| 56 | 56 |
isolated from each other *except* for their data. |
| ... | ... |
@@ -12,16 +12,16 @@ Hello World |
| 12 | 12 |
Running the Examples |
| 13 | 13 |
==================== |
| 14 | 14 |
|
| 15 |
-All the examples assume your machine is running the docker daemon. To |
|
| 16 |
-run the docker daemon in the background, simply type: |
|
| 15 |
+All the examples assume your machine is running the ``docker`` daemon. To |
|
| 16 |
+run the ``docker`` daemon in the background, simply type: |
|
| 17 | 17 |
|
| 18 | 18 |
.. code-block:: bash |
| 19 | 19 |
|
| 20 | 20 |
sudo docker -d & |
| 21 | 21 |
|
| 22 |
-Now you can run docker in client mode: by default all commands will be |
|
| 22 |
+Now you can run Docker in client mode: by default all commands will be |
|
| 23 | 23 |
forwarded to the ``docker`` daemon via a protected Unix socket, so you |
| 24 |
-must run as root. |
|
| 24 |
+must run as the ``root`` or via the ``sudo`` command. |
|
| 25 | 25 |
|
| 26 | 26 |
.. code-block:: bash |
| 27 | 27 |
|
| ... | ... |
@@ -38,23 +38,24 @@ Hello World |
| 38 | 38 |
|
| 39 | 39 |
This is the most basic example available for using Docker. |
| 40 | 40 |
|
| 41 |
-Download the base image (named "ubuntu"): |
|
| 41 |
+Download the base image which is named ``ubuntu``: |
|
| 42 | 42 |
|
| 43 | 43 |
.. code-block:: bash |
| 44 | 44 |
|
| 45 | 45 |
# Download an ubuntu image |
| 46 | 46 |
sudo docker pull ubuntu |
| 47 | 47 |
|
| 48 |
-Alternatively to the *ubuntu* image, you can select *busybox*, a bare |
|
| 48 |
+Alternatively to the ``ubuntu`` image, you can select ``busybox``, a bare |
|
| 49 | 49 |
minimal Linux system. The images are retrieved from the Docker |
| 50 | 50 |
repository. |
| 51 | 51 |
|
| 52 | 52 |
|
| 53 | 53 |
.. code-block:: bash |
| 54 | 54 |
|
| 55 |
- #run a simple echo command, that will echo hello world back to the console over standard out. |
|
| 56 | 55 |
sudo docker run ubuntu /bin/echo hello world |
| 57 | 56 |
|
| 57 |
+This command will run a simple ``echo`` command, that will echo ``hello world`` back to the console over standard out. |
|
| 58 |
+ |
|
| 58 | 59 |
**Explanation:** |
| 59 | 60 |
|
| 60 | 61 |
- **"sudo"** execute the following commands as user *root* |
| ... | ... |
@@ -100,9 +101,9 @@ we stop it. |
| 100 | 100 |
CONTAINER_ID=$(sudo docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done") |
| 101 | 101 |
|
| 102 | 102 |
We are going to run a simple hello world daemon in a new container |
| 103 |
-made from the *ubuntu* image. |
|
| 103 |
+made from the ``ubuntu`` image. |
|
| 104 | 104 |
|
| 105 |
-- **"docker run -d "** run a command in a new container. We pass "-d" |
|
| 105 |
+- **"sudo docker run -d "** run a command in a new container. We pass "-d" |
|
| 106 | 106 |
so it runs as a daemon. |
| 107 | 107 |
- **"ubuntu"** is the image we want to run the command inside of. |
| 108 | 108 |
- **"/bin/sh -c"** is the command we want to run in the container |
| ... | ... |
@@ -10,7 +10,7 @@ Examples |
| 10 | 10 |
|
| 11 | 11 |
Here are some examples of how to use Docker to create running |
| 12 | 12 |
processes, starting from a very simple *Hello World* and progressing |
| 13 |
-to more substantial services like you might find in production. |
|
| 13 |
+to more substantial services like those which you might find in production. |
|
| 14 | 14 |
|
| 15 | 15 |
.. toctree:: |
| 16 | 16 |
:maxdepth: 1 |
| ... | ... |
@@ -9,12 +9,14 @@ Linking Redis |
| 9 | 9 |
|
| 10 | 10 |
.. include:: example_header.inc |
| 11 | 11 |
|
| 12 |
-Building a redis container to link as a child of our web application. |
|
| 12 |
+Building a Redis container to link as a child of our web application. |
|
| 13 | 13 |
|
| 14 |
-Building the redis container |
|
| 14 |
+Building the Redis container |
|
| 15 | 15 |
---------------------------- |
| 16 | 16 |
|
| 17 |
-Lets build a redis image with the following Dockerfile. |
|
| 17 |
+Lets build a Redis image with the following Dockerfile. |
|
| 18 |
+ |
|
| 19 |
+First checkout the Redis source code. |
|
| 18 | 20 |
|
| 19 | 21 |
.. code-block:: bash |
| 20 | 22 |
|
| ... | ... |
@@ -22,7 +24,10 @@ Lets build a redis image with the following Dockerfile. |
| 22 | 22 |
cd redis |
| 23 | 23 |
git checkout 2.6 |
| 24 | 24 |
|
| 25 |
- # Save this Dockerfile to the root of the redis repository. |
|
| 25 |
+ |
|
| 26 |
+Now let's create a Dockerfile in the root of the Redis repository. |
|
| 27 |
+ |
|
| 28 |
+.. code-block:: bash |
|
| 26 | 29 |
|
| 27 | 30 |
# Build redis from source |
| 28 | 31 |
# Make sure you have the redis source code checked out in |
| ... | ... |
@@ -51,37 +56,37 @@ Lets build a redis image with the following Dockerfile. |
| 51 | 51 |
|
| 52 | 52 |
|
| 53 | 53 |
We need to ``EXPOSE`` the default port of 6379 so that our link knows what ports |
| 54 |
-to connect to our redis container on. If you do not expose any ports for the |
|
| 54 |
+to connect to our Redis container on. If you do not expose any ports for the |
|
| 55 | 55 |
image then docker will not be able to establish the link between containers. |
| 56 | 56 |
|
| 57 | 57 |
|
| 58 |
-Run the redis container |
|
| 58 |
+Run the Redis container |
|
| 59 | 59 |
----------------------- |
| 60 | 60 |
|
| 61 | 61 |
.. code-block:: bash |
| 62 |
- |
|
| 63 |
- docker run -d -e PASSWORD=docker -name redis redis-2.6 --requirepass docker |
|
| 64 |
- |
|
| 65 |
-This will run our redis container wit the password docker |
|
| 62 |
+ |
|
| 63 |
+ sudo docker run -d -e PASSWORD=docker -name redis redis-2.6 --requirepass docker |
|
| 64 |
+ |
|
| 65 |
+This will run our Redis container with the password docker |
|
| 66 | 66 |
to secure our service. By specifying the ``-name`` flag on run |
| 67 |
-we will assign the name ``redis`` to this container. If we do not specify a name for |
|
| 67 |
+we will assign the name ``redis`` to this container. If we do not specify a name for |
|
| 68 | 68 |
our container via the ``-name`` flag docker will automatically generate a name for us. |
| 69 | 69 |
We can issue all the commands that you would expect; start, stop, attach, using the name for our container. |
| 70 | 70 |
The name also allows us to link other containers into this one. |
| 71 | 71 |
|
| 72 |
-Linking redis as a child |
|
| 72 |
+Linking Redis as a child |
|
| 73 | 73 |
------------------------ |
| 74 | 74 |
|
| 75 |
-Next we can start a new web application that has a dependency on redis and apply a link |
|
| 76 |
-to connect both containers. If you noticed when running our redis server we did not use |
|
| 77 |
-the ``-p`` flag to publish the redis port to the host system. Redis exposed port 6379 via the Dockerfile |
|
| 75 |
+Next we can start a new web application that has a dependency on Redis and apply a link |
|
| 76 |
+to connect both containers. If you noticed when running our Redis server we did not use |
|
| 77 |
+the ``-p`` flag to publish the Redis port to the host system. Redis exposed port 6379 via the Dockerfile |
|
| 78 | 78 |
and this is all we need to establish a link. |
| 79 | 79 |
|
| 80 |
-Now lets start our web application with a link into redis. |
|
| 80 |
+Now let's start our web application with a link into Redis. |
|
| 81 | 81 |
|
| 82 | 82 |
.. code-block:: bash |
| 83 |
- |
|
| 84 |
- docker run -t -i -link redis:db -name webapp ubuntu bash |
|
| 83 |
+ |
|
| 84 |
+ sudo docker run -t -i -link redis:db -name webapp ubuntu bash |
|
| 85 | 85 |
|
| 86 | 86 |
root@4c01db0b339c:/# env |
| 87 | 87 |
|
| ... | ... |
@@ -95,7 +100,7 @@ Now lets start our web application with a link into redis. |
| 95 | 95 |
DB_PORT_6379_TCP_PORT=6379 |
| 96 | 96 |
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |
| 97 | 97 |
PWD=/ |
| 98 |
- DB_ENV_PASSWORD=dockerpass |
|
| 98 |
+ DB_ENV_PASSWORD=docker |
|
| 99 | 99 |
SHLVL=1 |
| 100 | 100 |
HOME=/ |
| 101 | 101 |
container=lxc |
| ... | ... |
@@ -105,7 +110,7 @@ Now lets start our web application with a link into redis. |
| 105 | 105 |
|
| 106 | 106 |
When we inspect the environment of the linked container we can see a few extra environment |
| 107 | 107 |
variables have been added. When you specified ``-link redis:db`` you are telling docker |
| 108 |
-to link the container named ``redis`` into this new container with the alias ``db``. |
|
| 108 |
+to link the container named ``redis`` into this new container with the alias ``db``. |
|
| 109 | 109 |
Environment variables are prefixed with the alias so that the parent container can access |
| 110 | 110 |
network and environment information from the containers that are linked into it. |
| 111 | 111 |
|
| ... | ... |
@@ -124,9 +129,9 @@ network and environment information from the containers that are linked into it. |
| 124 | 124 |
DB_PORT_6379_TCP_PORT=6379 |
| 125 | 125 |
|
| 126 | 126 |
# Get environment variables of the container |
| 127 |
- DB_ENV_PASSWORD=dockerpass |
|
| 127 |
+ DB_ENV_PASSWORD=docker |
|
| 128 | 128 |
|
| 129 | 129 |
|
| 130 | 130 |
Accessing the network information along with the environment of the child container allows |
| 131 |
-us to easily connect to the redis service on the specific ip and port and use the password |
|
| 131 |
+us to easily connect to the Redis service on the specific IP and port and use the password |
|
| 132 | 132 |
specified in the environment. |
| ... | ... |
@@ -10,8 +10,8 @@ Building an Image with MongoDB |
| 10 | 10 |
.. include:: example_header.inc |
| 11 | 11 |
|
| 12 | 12 |
The goal of this example is to show how you can build your own |
| 13 |
-docker images with MongoDB preinstalled. We will do that by |
|
| 14 |
-constructing a Dockerfile that downloads a base image, adds an |
|
| 13 |
+Docker images with MongoDB pre-installed. We will do that by |
|
| 14 |
+constructing a ``Dockerfile`` that downloads a base image, adds an |
|
| 15 | 15 |
apt source and installs the database software on Ubuntu. |
| 16 | 16 |
|
| 17 | 17 |
Creating a ``Dockerfile`` |
| ... | ... |
@@ -41,7 +41,7 @@ Since we want to be running the latest version of MongoDB we'll need to add the |
| 41 | 41 |
RUN echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | tee /etc/apt/sources.list.d/10gen.list |
| 42 | 42 |
|
| 43 | 43 |
Then, we don't want Ubuntu to complain about init not being available so we'll |
| 44 |
-divert /sbin/initctl to /bin/true so it thinks everything is working. |
|
| 44 |
+divert ``/sbin/initctl`` to ``/bin/true`` so it thinks everything is working. |
|
| 45 | 45 |
|
| 46 | 46 |
.. code-block:: bash |
| 47 | 47 |
|
| ... | ... |
@@ -65,8 +65,8 @@ run without needing to provide a special configuration file) |
| 65 | 65 |
# Create the MongoDB data directory |
| 66 | 66 |
RUN mkdir -p /data/db |
| 67 | 67 |
|
| 68 |
-Finally, we'll expose the standard port that MongoDB runs on (27107) as well as |
|
| 69 |
-define an ENTRYPOINT for the container. |
|
| 68 |
+Finally, we'll expose the standard port that MongoDB runs on, 27107, as well as |
|
| 69 |
+define an ``ENTRYPOINT`` instruction for the container. |
|
| 70 | 70 |
|
| 71 | 71 |
.. code-block:: bash |
| 72 | 72 |
|
| ... | ... |
@@ -78,7 +78,7 @@ run all of the commands. |
| 78 | 78 |
|
| 79 | 79 |
.. code-block:: bash |
| 80 | 80 |
|
| 81 |
- docker build -t <yourname>/mongodb . |
|
| 81 |
+ sudo docker build -t <yourname>/mongodb . |
|
| 82 | 82 |
|
| 83 | 83 |
Now you should be able to run ``mongod`` as a daemon and be able to connect on |
| 84 | 84 |
the local port! |
| ... | ... |
@@ -86,13 +86,13 @@ the local port! |
| 86 | 86 |
.. code-block:: bash |
| 87 | 87 |
|
| 88 | 88 |
# Regular style |
| 89 |
- MONGO_ID=$(docker run -d <yourname>/mongodb) |
|
| 89 |
+ MONGO_ID=$(sudo docker run -d <yourname>/mongodb) |
|
| 90 | 90 |
|
| 91 | 91 |
# Lean and mean |
| 92 |
- MONGO_ID=$(docker run -d <yourname>/mongodb --noprealloc --smallfiles) |
|
| 92 |
+ MONGO_ID=$(sudo docker run -d <yourname>/mongodb --noprealloc --smallfiles) |
|
| 93 | 93 |
|
| 94 | 94 |
# Check the logs out |
| 95 |
- docker logs $MONGO_ID |
|
| 95 |
+ sudo docker logs $MONGO_ID |
|
| 96 | 96 |
|
| 97 | 97 |
# Connect and play around |
| 98 | 98 |
mongo --port <port you get from `docker ps`> |
| ... | ... |
@@ -10,7 +10,7 @@ Node.js Web App |
| 10 | 10 |
.. include:: example_header.inc |
| 11 | 11 |
|
| 12 | 12 |
The goal of this example is to show you how you can build your own |
| 13 |
-docker images from a parent image using a ``Dockerfile`` . We will do |
|
| 13 |
+Docker images from a parent image using a ``Dockerfile`` . We will do |
|
| 14 | 14 |
that by making a simple Node.js hello world web application running on |
| 15 | 15 |
CentOS. You can get the full source code at |
| 16 | 16 |
https://github.com/gasi/docker-node-hello. |
| ... | ... |
@@ -55,7 +55,7 @@ Then, create an ``index.js`` file that defines a web app using the |
| 55 | 55 |
|
| 56 | 56 |
|
| 57 | 57 |
In the next steps, we’ll look at how you can run this app inside a CentOS |
| 58 |
-container using docker. First, you’ll need to build a docker image of your app. |
|
| 58 |
+container using Docker. First, you’ll need to build a Docker image of your app. |
|
| 59 | 59 |
|
| 60 | 60 |
Creating a ``Dockerfile`` |
| 61 | 61 |
+++++++++++++++++++++++++ |
| ... | ... |
@@ -67,8 +67,8 @@ Create an empty file called ``Dockerfile``: |
| 67 | 67 |
touch Dockerfile |
| 68 | 68 |
|
| 69 | 69 |
Open the ``Dockerfile`` in your favorite text editor and add the following line |
| 70 |
-that defines the version of docker the image requires to build |
|
| 71 |
-(this example uses docker 0.3.4): |
|
| 70 |
+that defines the version of Docker the image requires to build |
|
| 71 |
+(this example uses Docker 0.3.4): |
|
| 72 | 72 |
|
| 73 | 73 |
.. code-block:: bash |
| 74 | 74 |
|
| ... | ... |
@@ -76,7 +76,7 @@ that defines the version of docker the image requires to build |
| 76 | 76 |
|
| 77 | 77 |
Next, define the parent image you want to use to build your own image on top of. |
| 78 | 78 |
Here, we’ll use `CentOS <https://index.docker.io/_/centos/>`_ (tag: ``6.4``) |
| 79 |
-available on the `docker index`_: |
|
| 79 |
+available on the `Docker index`_: |
|
| 80 | 80 |
|
| 81 | 81 |
.. code-block:: bash |
| 82 | 82 |
|
| ... | ... |
@@ -95,23 +95,23 @@ To install the right package for CentOS, we’ll use the instructions from the |
| 95 | 95 |
# Install Node.js and npm |
| 96 | 96 |
RUN yum install -y npm |
| 97 | 97 |
|
| 98 |
-To bundle your app’s source code inside the docker image, use the ``ADD`` |
|
| 99 |
-command: |
|
| 98 |
+To bundle your app’s source code inside the Docker image, use the ``ADD`` |
|
| 99 |
+instruction: |
|
| 100 | 100 |
|
| 101 | 101 |
.. code-block:: bash |
| 102 | 102 |
|
| 103 | 103 |
# Bundle app source |
| 104 | 104 |
ADD . /src |
| 105 | 105 |
|
| 106 |
-Install your app dependencies using npm: |
|
| 106 |
+Install your app dependencies using the ``npm`` binary: |
|
| 107 | 107 |
|
| 108 | 108 |
.. code-block:: bash |
| 109 | 109 |
|
| 110 | 110 |
# Install app dependencies |
| 111 | 111 |
RUN cd /src; npm install |
| 112 | 112 |
|
| 113 |
-Your app binds to port ``8080`` so you’ll use the ``EXPOSE`` command |
|
| 114 |
-to have it mapped by the docker daemon: |
|
| 113 |
+Your app binds to port ``8080`` so you’ll use the ``EXPOSE`` instruction |
|
| 114 |
+to have it mapped by the ``docker`` daemon: |
|
| 115 | 115 |
|
| 116 | 116 |
.. code-block:: bash |
| 117 | 117 |
|
| ... | ... |
@@ -152,7 +152,7 @@ Building your image |
| 152 | 152 |
+++++++++++++++++++ |
| 153 | 153 |
|
| 154 | 154 |
Go to the directory that has your ``Dockerfile`` and run the following |
| 155 |
-command to build a docker image. The ``-t`` flag let’s you tag your |
|
| 155 |
+command to build a Docker image. The ``-t`` flag let’s you tag your |
|
| 156 | 156 |
image so it’s easier to find later using the ``docker images`` |
| 157 | 157 |
command: |
| 158 | 158 |
|
| ... | ... |
@@ -160,7 +160,7 @@ command: |
| 160 | 160 |
|
| 161 | 161 |
sudo docker build -t <your username>/centos-node-hello . |
| 162 | 162 |
|
| 163 |
-Your image will now be listed by docker: |
|
| 163 |
+Your image will now be listed by Docker: |
|
| 164 | 164 |
|
| 165 | 165 |
.. code-block:: bash |
| 166 | 166 |
|
| ... | ... |
@@ -199,17 +199,17 @@ Print the output of your app: |
| 199 | 199 |
Test |
| 200 | 200 |
++++ |
| 201 | 201 |
|
| 202 |
-To test your app, get the the port of your app that docker mapped: |
|
| 202 |
+To test your app, get the the port of your app that Docker mapped: |
|
| 203 | 203 |
|
| 204 | 204 |
.. code-block:: bash |
| 205 | 205 |
|
| 206 |
- docker ps |
|
| 206 |
+ sudo docker ps |
|
| 207 | 207 |
|
| 208 | 208 |
> # Example |
| 209 | 209 |
> ID IMAGE COMMAND ... PORTS |
| 210 | 210 |
> ecce33b30ebf gasi/centos-node-hello:latest node /src/index.js 49160->8080 |
| 211 | 211 |
|
| 212 |
-In the example above, docker mapped the ``8080`` port of the container to |
|
| 212 |
+In the example above, Docker mapped the ``8080`` port of the container to |
|
| 213 | 213 |
``49160``. |
| 214 | 214 |
|
| 215 | 215 |
Now you can call your app using ``curl`` (install if needed via: |
| ... | ... |
@@ -229,7 +229,7 @@ Now you can call your app using ``curl`` (install if needed via: |
| 229 | 229 |
> Hello World |
| 230 | 230 |
|
| 231 | 231 |
We hope this tutorial helped you get up and running with Node.js and |
| 232 |
-CentOS on docker. You can get the full source code at |
|
| 232 |
+CentOS on Docker. You can get the full source code at |
|
| 233 | 233 |
https://github.com/gasi/docker-node-hello. |
| 234 | 234 |
|
| 235 | 235 |
Continue to :ref:`running_redis_service`. |
| ... | ... |
@@ -13,7 +13,7 @@ PostgreSQL Service |
| 13 | 13 |
|
| 14 | 14 |
.. note:: |
| 15 | 15 |
|
| 16 |
- As of version 0.5.2, docker requires root privileges to run. |
|
| 16 |
+ As of version 0.5.2, Docker requires root privileges to run. |
|
| 17 | 17 |
You have to either manually adjust your system configuration (permissions on |
| 18 | 18 |
/var/run/docker.sock or sudo config), or prefix `docker` with `sudo`. Check |
| 19 | 19 |
`this thread`_ for details. |
| ... | ... |
@@ -24,8 +24,7 @@ PostgreSQL Service |
| 24 | 24 |
Installing PostgreSQL on Docker |
| 25 | 25 |
------------------------------- |
| 26 | 26 |
|
| 27 |
-For clarity I won't be showing commands output. |
|
| 28 |
- |
|
| 27 |
+For clarity I won't be showing command output. |
|
| 29 | 28 |
|
| 30 | 29 |
Run an interactive shell in Docker container. |
| 31 | 30 |
|
| ... | ... |
@@ -62,7 +61,7 @@ Finally, install PostgreSQL 9.2 |
| 62 | 62 |
|
| 63 | 63 |
Now, create a PostgreSQL superuser role that can create databases and |
| 64 | 64 |
other roles. Following Vagrant's convention the role will be named |
| 65 |
-`docker` with `docker` password assigned to it. |
|
| 65 |
+``docker`` with ``docker`` password assigned to it. |
|
| 66 | 66 |
|
| 67 | 67 |
.. code-block:: bash |
| 68 | 68 |
|
| ... | ... |
@@ -108,7 +107,7 @@ Bash prompt; you can also locate it using ``docker ps -a``. |
| 108 | 108 |
|
| 109 | 109 |
.. code-block:: bash |
| 110 | 110 |
|
| 111 |
- docker commit <container_id> <your username>/postgresql |
|
| 111 |
+ sudo docker commit <container_id> <your username>/postgresql |
|
| 112 | 112 |
|
| 113 | 113 |
Finally, run PostgreSQL server via ``docker``. |
| 114 | 114 |
|
| ... | ... |
@@ -10,9 +10,9 @@ Python Web App |
| 10 | 10 |
.. include:: example_header.inc |
| 11 | 11 |
|
| 12 | 12 |
The goal of this example is to show you how you can author your own |
| 13 |
-docker images using a parent image, making changes to it, and then |
|
| 13 |
+Docker images using a parent image, making changes to it, and then |
|
| 14 | 14 |
saving the results as a new image. We will do that by making a simple |
| 15 |
-hello flask web application image. |
|
| 15 |
+hello Flask web application image. |
|
| 16 | 16 |
|
| 17 | 17 |
**Steps:** |
| 18 | 18 |
|
| ... | ... |
@@ -20,22 +20,22 @@ hello flask web application image. |
| 20 | 20 |
|
| 21 | 21 |
sudo docker pull shykes/pybuilder |
| 22 | 22 |
|
| 23 |
-We are downloading the "shykes/pybuilder" docker image |
|
| 23 |
+We are downloading the ``shykes/pybuilder`` Docker image |
|
| 24 | 24 |
|
| 25 | 25 |
.. code-block:: bash |
| 26 | 26 |
|
| 27 | 27 |
URL=http://github.com/shykes/helloflask/archive/master.tar.gz |
| 28 | 28 |
|
| 29 |
-We set a URL variable that points to a tarball of a simple helloflask web app |
|
| 29 |
+We set a ``URL`` variable that points to a tarball of a simple helloflask web app |
|
| 30 | 30 |
|
| 31 | 31 |
.. code-block:: bash |
| 32 | 32 |
|
| 33 | 33 |
BUILD_JOB=$(sudo docker run -d -t shykes/pybuilder:latest /usr/local/bin/buildapp $URL) |
| 34 | 34 |
|
| 35 |
-Inside of the "shykes/pybuilder" image there is a command called |
|
| 36 |
-buildapp, we are running that command and passing the $URL variable |
|
| 35 |
+Inside of the ``shykes/pybuilder`` image there is a command called |
|
| 36 |
+``buildapp``, we are running that command and passing the ``$URL`` variable |
|
| 37 | 37 |
from step 2 to it, and running the whole thing inside of a new |
| 38 |
-container. BUILD_JOB will be set with the new container_id. |
|
| 38 |
+container. The ``BUILD_JOB`` environment variable will be set with the new container ID. |
|
| 39 | 39 |
|
| 40 | 40 |
.. code-block:: bash |
| 41 | 41 |
|
| ... | ... |
@@ -43,13 +43,13 @@ container. BUILD_JOB will be set with the new container_id. |
| 43 | 43 |
[...] |
| 44 | 44 |
|
| 45 | 45 |
While this container is running, we can attach to the new container to |
| 46 |
-see what is going on. Ctrl-C to disconnect. |
|
| 46 |
+see what is going on. You can use Ctrl-C to disconnect. |
|
| 47 | 47 |
|
| 48 | 48 |
.. code-block:: bash |
| 49 | 49 |
|
| 50 | 50 |
sudo docker ps -a |
| 51 |
- |
|
| 52 |
-List all docker containers. If this container has already finished |
|
| 51 |
+ |
|
| 52 |
+List all Docker containers. If this container has already finished |
|
| 53 | 53 |
running, it will still be listed here. |
| 54 | 54 |
|
| 55 | 55 |
.. code-block:: bash |
| ... | ... |
@@ -57,8 +57,8 @@ running, it will still be listed here. |
| 57 | 57 |
BUILD_IMG=$(sudo docker commit $BUILD_JOB _/builds/github.com/shykes/helloflask/master) |
| 58 | 58 |
|
| 59 | 59 |
Save the changes we just made in the container to a new image called |
| 60 |
-``_/builds/github.com/hykes/helloflask/master`` and save the image id in |
|
| 61 |
-the BUILD_IMG variable name. |
|
| 60 |
+``_/builds/github.com/hykes/helloflask/master`` and save the image ID in |
|
| 61 |
+the ``BUILD_IMG`` variable name. |
|
| 62 | 62 |
|
| 63 | 63 |
.. code-block:: bash |
| 64 | 64 |
|
| ... | ... |
@@ -72,24 +72,24 @@ the BUILD_IMG variable name. |
| 72 | 72 |
- **/usr/local/bin/runapp** is the command which starts the web app. |
| 73 | 73 |
|
| 74 | 74 |
Use the new image we just created and create a new container with |
| 75 |
-network port 5000, and return the container id and store in the |
|
| 76 |
-WEB_WORKER variable. |
|
| 75 |
+network port 5000, and return the container ID and store in the |
|
| 76 |
+``WEB_WORKER`` variable. |
|
| 77 | 77 |
|
| 78 | 78 |
.. code-block:: bash |
| 79 | 79 |
|
| 80 | 80 |
sudo docker logs $WEB_WORKER |
| 81 | 81 |
* Running on http://0.0.0.0:5000/ |
| 82 | 82 |
|
| 83 |
-View the logs for the new container using the WEB_WORKER variable, and |
|
| 84 |
-if everything worked as planned you should see the line "Running on |
|
| 85 |
-http://0.0.0.0:5000/" in the log output. |
|
| 83 |
+View the logs for the new container using the ``WEB_WORKER`` variable, and |
|
| 84 |
+if everything worked as planned you should see the line ``Running on |
|
| 85 |
+http://0.0.0.0:5000/`` in the log output. |
|
| 86 | 86 |
|
| 87 | 87 |
.. code-block:: bash |
| 88 | 88 |
|
| 89 | 89 |
WEB_PORT=$(sudo docker port $WEB_WORKER 5000) |
| 90 | 90 |
|
| 91 | 91 |
Look up the public-facing port which is NAT-ed. Find the private port |
| 92 |
-used by the container and store it inside of the WEB_PORT variable. |
|
| 92 |
+used by the container and store it inside of the ``WEB_PORT`` variable. |
|
| 93 | 93 |
|
| 94 | 94 |
.. code-block:: bash |
| 95 | 95 |
|
| ... | ... |
@@ -97,8 +97,8 @@ used by the container and store it inside of the WEB_PORT variable. |
| 97 | 97 |
curl http://127.0.0.1:$WEB_PORT |
| 98 | 98 |
Hello world! |
| 99 | 99 |
|
| 100 |
-Access the web app using curl. If everything worked as planned you |
|
| 101 |
-should see the line "Hello world!" inside of your console. |
|
| 100 |
+Access the web app using the ``curl`` binary. If everything worked as planned you |
|
| 101 |
+should see the line ``Hello world!`` inside of your console. |
|
| 102 | 102 |
|
| 103 | 103 |
**Video:** |
| 104 | 104 |
|
| ... | ... |
@@ -9,7 +9,7 @@ Redis Service |
| 9 | 9 |
|
| 10 | 10 |
.. include:: example_header.inc |
| 11 | 11 |
|
| 12 |
-Very simple, no frills, redis service. |
|
| 12 |
+Very simple, no frills, Redis service. |
|
| 13 | 13 |
|
| 14 | 14 |
Open a docker container |
| 15 | 15 |
----------------------- |
| ... | ... |
@@ -35,13 +35,13 @@ Snapshot the installation |
| 35 | 35 |
|
| 36 | 36 |
.. code-block:: bash |
| 37 | 37 |
|
| 38 |
- docker ps -a # grab the container id (this will be the first one in the list) |
|
| 39 |
- docker commit <container_id> <your username>/redis |
|
| 38 |
+ sudo docker ps -a # grab the container id (this will be the first one in the list) |
|
| 39 |
+ sudo docker commit <container_id> <your username>/redis |
|
| 40 | 40 |
|
| 41 | 41 |
Run the service |
| 42 | 42 |
--------------- |
| 43 | 43 |
|
| 44 |
-Running the service with `-d` runs the container in detached mode, leaving the |
|
| 44 |
+Running the service with ``-d`` runs the container in detached mode, leaving the |
|
| 45 | 45 |
container running in the background. Use your snapshot. |
| 46 | 46 |
|
| 47 | 47 |
.. code-block:: bash |
| ... | ... |
@@ -51,7 +51,7 @@ container running in the background. Use your snapshot. |
| 51 | 51 |
Test 1 |
| 52 | 52 |
++++++ |
| 53 | 53 |
|
| 54 |
-Connect to the container with the redis-cli. |
|
| 54 |
+Connect to the container with the ``redis-cli`` binary. |
|
| 55 | 55 |
|
| 56 | 56 |
.. code-block:: bash |
| 57 | 57 |
|
| ... | ... |
@@ -67,7 +67,7 @@ Connect to the container with the redis-cli. |
| 67 | 67 |
Test 2 |
| 68 | 68 |
++++++ |
| 69 | 69 |
|
| 70 |
-Connect to the host os with the redis-cli. |
|
| 70 |
+Connect to the host os with the ``redis-cli`` binary. |
|
| 71 | 71 |
|
| 72 | 72 |
.. code-block:: bash |
| 73 | 73 |
|
| ... | ... |
@@ -107,7 +107,7 @@ Create a ``supervisord`` configuration file |
| 107 | 107 |
+++++++++++++++++++++++++++++++++++++++++++ |
| 108 | 108 |
|
| 109 | 109 |
Create an empty file called ``supervisord.conf``. Make sure it's at the same |
| 110 |
-level as your ``Dockerfile``: |
|
| 110 |
+directory level as your ``Dockerfile``: |
|
| 111 | 111 |
|
| 112 | 112 |
.. code-block:: bash |
| 113 | 113 |
|
| ... | ... |
@@ -12,14 +12,14 @@ SSH Daemon Service |
| 12 | 12 |
|
| 13 | 13 |
**Video:** |
| 14 | 14 |
|
| 15 |
-I've create a little screencast to show how to create a sshd service |
|
| 15 |
+I've create a little screencast to show how to create a SSHd service |
|
| 16 | 16 |
and connect to it. It is something like 11 minutes and not entirely |
| 17 | 17 |
smooth, but gives you a good idea. |
| 18 | 18 |
|
| 19 | 19 |
.. note:: |
| 20 |
- This screencast was created before ``docker`` version 0.5.2, so the |
|
| 20 |
+ This screencast was created before Docker version 0.5.2, so the |
|
| 21 | 21 |
daemon is unprotected and available via a TCP port. When you run |
| 22 |
- through the same steps in a newer version of ``docker``, you will |
|
| 22 |
+ through the same steps in a newer version of Docker, you will |
|
| 23 | 23 |
need to add ``sudo`` in front of each ``docker`` command in order |
| 24 | 24 |
to reach the daemon over its protected Unix socket. |
| 25 | 25 |
|
| ... | ... |
@@ -29,13 +29,14 @@ smooth, but gives you a good idea. |
| 29 | 29 |
<iframe width="800" height="400" src="http://ascii.io/a/2637/raw" frameborder="0"></iframe> |
| 30 | 30 |
</div> |
| 31 | 31 |
|
| 32 |
-You can also get this sshd container by using |
|
| 33 |
-:: |
|
| 32 |
+You can also get this sshd container by using: |
|
| 33 |
+ |
|
| 34 |
+.. code-block:: bash |
|
| 34 | 35 |
|
| 35 | 36 |
sudo docker pull dhrp/sshd |
| 36 | 37 |
|
| 37 | 38 |
|
| 38 |
-The password is 'screencast' |
|
| 39 |
+The password is ``screencast``. |
|
| 39 | 40 |
|
| 40 | 41 |
**Video's Transcription:** |
| 41 | 42 |
|
| ... | ... |
@@ -162,8 +162,8 @@ Verify it worked |
| 162 | 162 |
Docker and UFW |
| 163 | 163 |
^^^^^^^^^^^^^^ |
| 164 | 164 |
|
| 165 |
-Docker uses a bridge to manage containers networking, by default UFW |
|
| 166 |
-drop all `forwarding`, a first step is to enable forwarding: |
|
| 165 |
+Docker uses a bridge to manage container networking. By default, UFW |
|
| 166 |
+drops all `forwarding`, thus a first step is to enable UFW forwarding: |
|
| 167 | 167 |
|
| 168 | 168 |
.. code-block:: bash |
| 169 | 169 |
|
| ... | ... |
@@ -9,7 +9,7 @@ Using Vagrant (Mac, Linux) |
| 9 | 9 |
|
| 10 | 10 |
This guide will setup a new virtualbox virtual machine with docker |
| 11 | 11 |
installed on your computer. This works on most operating systems, |
| 12 |
-including MacOX, Windows, Linux, FreeBSD and others. If you can |
|
| 12 |
+including MacOSX, Windows, Linux, FreeBSD and others. If you can |
|
| 13 | 13 |
install these and have at least 400MB RAM to spare you should be good. |
| 14 | 14 |
|
| 15 | 15 |
Install Vagrant and Virtualbox |
| ... | ... |
@@ -208,6 +208,10 @@ configuration / Device configuration) |
| 208 | 208 |
|
| 209 | 209 |
.. image:: images/win/hp_bios_vm.JPG |
| 210 | 210 |
|
| 211 |
+On some machines the BIOS menu can only be accessed before startup. |
|
| 212 |
+To access BIOS in this scenario you should restart your computer and |
|
| 213 |
+press ESC/Enter when prompted to access the boot and BIOS controls. Typically |
|
| 214 |
+the option to allow virtualization is contained within the BIOS/Security menu. |
|
| 211 | 215 |
|
| 212 | 216 |
Docker is not installed |
| 213 | 217 |
``````````````````````` |
| ... | ... |
@@ -138,22 +138,19 @@ Listing all running containers |
| 138 | 138 |
|
| 139 | 139 |
sudo docker ps |
| 140 | 140 |
|
| 141 |
-Expose a service on a TCP port |
|
| 141 |
+Bind a service on a TCP port |
|
| 142 | 142 |
------------------------------ |
| 143 | 143 |
|
| 144 | 144 |
.. code-block:: bash |
| 145 | 145 |
|
| 146 |
- # Expose port 4444 of this container, and tell netcat to listen on it |
|
| 146 |
+ # Bind port 4444 of this container, and tell netcat to listen on it |
|
| 147 | 147 |
JOB=$(sudo docker run -d -p 4444 ubuntu:12.10 /bin/nc -l 4444) |
| 148 | 148 |
|
| 149 | 149 |
# Which public port is NATed to my container? |
| 150 |
- PORT=$(sudo docker port $JOB 4444) |
|
| 150 |
+ PORT=$(sudo docker port $JOB 4444 | awk -F: '{ print $2 }')
|
|
| 151 | 151 |
|
| 152 |
- # Connect to the public port via the host's public address |
|
| 153 |
- # Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work. |
|
| 154 |
- # Replace *eth0* according to your local interface name. |
|
| 155 |
- IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }')
|
|
| 156 |
- echo hello world | nc $IP $PORT |
|
| 152 |
+ # Connect to the public port |
|
| 153 |
+ echo hello world | nc 127.0.0.1 $PORT |
|
| 157 | 154 |
|
| 158 | 155 |
# Verify that the network connection worked |
| 159 | 156 |
echo "Daemon received: $(sudo docker logs $JOB)" |
| ... | ... |
@@ -183,4 +180,3 @@ You now have a image state from which you can create new instances. |
| 183 | 183 |
|
| 184 | 184 |
Read more about :ref:`working_with_the_repository` or continue to the |
| 185 | 185 |
complete :ref:`cli` |
| 186 |
- |
| ... | ... |
@@ -174,10 +174,10 @@ override the default specified in CMD. |
| 174 | 174 |
|
| 175 | 175 |
``EXPOSE <port> [<port>...]`` |
| 176 | 176 |
|
| 177 |
-The ``EXPOSE`` instruction sets ports to be publicly exposed when |
|
| 178 |
-running the image. This is functionally equivalent to running ``docker |
|
| 179 |
-commit -run '{"PortSpecs": ["<port>", "<port2>"]}'`` outside the
|
|
| 180 |
-builder. Take a look at :ref:`port_redirection` for more information. |
|
| 177 |
+The ``EXPOSE`` instruction exposes ports for use within links. This is |
|
| 178 |
+functionally equivalent to running ``docker commit -run '{"PortSpecs":
|
|
| 179 |
+["<port>", "<port2>"]}'`` outside the builder. Refer to |
|
| 180 |
+:ref:`port_redirection` for detailed information. |
|
| 181 | 181 |
|
| 182 | 182 |
3.6 ENV |
| 183 | 183 |
------- |
| ... | ... |
@@ -208,6 +208,9 @@ a remote file URL. |
| 208 | 208 |
``<dest>`` is the path at which the source will be copied in the |
| 209 | 209 |
destination container. |
| 210 | 210 |
|
| 211 |
+All new files and directories are created with mode 0755, uid and gid |
|
| 212 |
+0. |
|
| 213 |
+ |
|
| 211 | 214 |
The copy obeys the following rules: |
| 212 | 215 |
|
| 213 | 216 |
* If ``<src>`` is a URL and ``<dest>`` does not end with a trailing slash, |
| ... | ... |
@@ -220,8 +223,9 @@ The copy obeys the following rules: |
| 220 | 220 |
(``http://example.com`` will not work). |
| 221 | 221 |
* If ``<src>`` is a directory, the entire directory is copied, |
| 222 | 222 |
including filesystem metadata. |
| 223 |
-* If ``<src>`` is a tar archive in a recognized compression format |
|
| 224 |
- (identity, gzip, bzip2 or xz), it is unpacked as a directory. |
|
| 223 |
+* If ``<src>`` is a *local* tar archive in a recognized compression |
|
| 224 |
+ format (identity, gzip, bzip2 or xz) then it is unpacked as a |
|
| 225 |
+ directory. Resources from *remote* URLs are **not** decompressed. |
|
| 225 | 226 |
|
| 226 | 227 |
When a directory is copied or unpacked, it has the same behavior as |
| 227 | 228 |
``tar -x``: the result is the union of |
| ... | ... |
@@ -229,7 +233,7 @@ The copy obeys the following rules: |
| 229 | 229 |
1. whatever existed at the destination path and |
| 230 | 230 |
2. the contents of the source tree, |
| 231 | 231 |
|
| 232 |
- with conflicts resolved in favor of 2) on a file-by-file basis. |
|
| 232 |
+ with conflicts resolved in favor of "2." on a file-by-file basis. |
|
| 233 | 233 |
|
| 234 | 234 |
* If ``<src>`` is any other kind of file, it is copied individually |
| 235 | 235 |
along with its metadata. In this case, if ``<dest>`` ends with a |
| ... | ... |
@@ -237,10 +241,9 @@ The copy obeys the following rules: |
| 237 | 237 |
contents of ``<src>`` will be written at ``<dest>/base(<src>)``. |
| 238 | 238 |
* If ``<dest>`` does not end with a trailing slash, it will be |
| 239 | 239 |
considered a regular file and the contents of ``<src>`` will be |
| 240 |
- written at ``<dst>``. |
|
| 240 |
+ written at ``<dest>``. |
|
| 241 | 241 |
* If ``<dest>`` doesn't exist, it is created along with all missing |
| 242 |
- directories in its path. All new files and directories are created |
|
| 243 |
- with mode 0755, uid and gid 0. |
|
| 242 |
+ directories in its path. |
|
| 244 | 243 |
|
| 245 | 244 |
.. _entrypoint_def: |
| 246 | 245 |
|
| ... | ... |
@@ -8,29 +8,136 @@ |
| 8 | 8 |
Port redirection |
| 9 | 9 |
================ |
| 10 | 10 |
|
| 11 |
-Docker can redirect public TCP and UDP ports to your container, so it can be |
|
| 12 |
-reached over the network. Port redirection is done on ``docker run`` |
|
| 13 |
-using the -p flag. |
|
| 11 |
+Interacting with a service is commonly done through a connection to a |
|
| 12 |
+port. When this service runs inside a container, one can connect to |
|
| 13 |
+the port after finding the IP address of the container as follows: |
|
| 14 | 14 |
|
| 15 |
-A port redirect is specified as *PUBLIC:PRIVATE*, where TCP port |
|
| 16 |
-*PUBLIC* will be redirected to TCP port *PRIVATE*. As a special case, |
|
| 17 |
-the public port can be omitted, in which case a random public port |
|
| 18 |
-will be allocated. |
|
| 15 |
+.. code-block:: bash |
|
| 16 |
+ |
|
| 17 |
+ # Find IP address of container with ID <container_id> |
|
| 18 |
+ docker inspect <container_id> | grep IPAddress | cut -d '"' -f 4 |
|
| 19 |
+ |
|
| 20 |
+However, this IP address is local to the host system and the container |
|
| 21 |
+port is not reachable by the outside world. Furthermore, even if the |
|
| 22 |
+port is used locally, e.g. by another container, this method is |
|
| 23 |
+tedious as the IP address of the container changes every time it |
|
| 24 |
+starts. |
|
| 25 |
+ |
|
| 26 |
+Docker addresses these two problems and give a simple and robust way |
|
| 27 |
+to access services running inside containers. |
|
| 28 |
+ |
|
| 29 |
+To allow non-local clients to reach the service running inside the |
|
| 30 |
+container, Docker provide ways to bind the container port to an |
|
| 31 |
+interface of the host system. To simplify communication between |
|
| 32 |
+containers, Docker provides the linking mechanism. |
|
| 33 |
+ |
|
| 34 |
+Binding a port to an host interface |
|
| 35 |
+----------------------------------- |
|
| 36 |
+ |
|
| 37 |
+To bind a port of the container to a specific interface of the host |
|
| 38 |
+system, use the ``-p`` parameter of the ``docker run`` command: |
|
| 19 | 39 |
|
| 20 | 40 |
.. code-block:: bash |
| 21 | 41 |
|
| 22 |
- # A random PUBLIC port is redirected to PRIVATE port 80 on the container |
|
| 23 |
- sudo docker run -p 80 <image> <cmd> |
|
| 42 |
+ # General syntax |
|
| 43 |
+ docker run -p [([<host_interface>:[host_port]])|(<host_port>):]<container_port>[/udp] <image> <cmd> |
|
| 44 |
+ |
|
| 45 |
+When no host interface is provided, the port is bound to all available |
|
| 46 |
+interfaces of the host machine (aka INADDR_ANY, or 0.0.0.0).When no host port is |
|
| 47 |
+provided, one is dynamically allocated. The possible combinations of options for |
|
| 48 |
+TCP port are the following: |
|
| 49 |
+ |
|
| 50 |
+.. code-block:: bash |
|
| 24 | 51 |
|
| 25 |
- # PUBLIC port 80 is redirected to PRIVATE port 80 |
|
| 26 |
- sudo docker run -p 80:80 <image> <cmd> |
|
| 52 |
+ # Bind TCP port 8080 of the container to TCP port 80 on 127.0.0.1 of the host machine. |
|
| 53 |
+ docker run -p 127.0.0.1:80:8080 <image> <cmd> |
|
| 27 | 54 |
|
| 28 |
-To redirect a UDP port the redirection must be expressed as *PUBLIC:PRIVATE/udp*: |
|
| 55 |
+ # Bind TCP port 8080 of the container to a dynamically allocated TCP port on 127.0.0.1 of the host machine. |
|
| 56 |
+ docker run -p 127.0.0.1::8080 <image> <cmd> |
|
| 57 |
+ |
|
| 58 |
+ # Bind TCP port 8080 of the container to TCP port 80 on all available interfaces of the host machine. |
|
| 59 |
+ docker run -p 80:8080 <image> <cmd> |
|
| 60 |
+ |
|
| 61 |
+ # Bind TCP port 8080 of the container to a dynamically allocated TCP port on all available interfaces of the host machine. |
|
| 62 |
+ docker run -p 8080 <image> <cmd> |
|
| 63 |
+ |
|
| 64 |
+UDP ports can also be bound by adding a trailing ``/udp``. All the |
|
| 65 |
+combinations described for TCP work. Here is only one example: |
|
| 29 | 66 |
|
| 30 | 67 |
.. code-block:: bash |
| 31 | 68 |
|
| 32 |
- # PUBLIC port 5300 is redirected to the PRIVATE port 53 using UDP |
|
| 33 |
- sudo docker run -p 5300:53/udp <image> <cmd> |
|
| 69 |
+ # Bind UDP port 5353 of the container to UDP port 53 on 127.0.0.1 of the host machine. |
|
| 70 |
+ docker run -p 127.0.0.1:53:5353/udp <image> <cmd> |
|
| 71 |
+ |
|
| 72 |
+The command ``docker port`` lists the interface and port on the host |
|
| 73 |
+machine bound to a given container port. It is useful when using |
|
| 74 |
+dynamically allocated ports: |
|
| 75 |
+ |
|
| 76 |
+.. code-block:: bash |
|
| 77 |
+ |
|
| 78 |
+ # Bind to a dynamically allocated port |
|
| 79 |
+ docker run -p 127.0.0.1::8080 -name dyn-bound <image> <cmd> |
|
| 80 |
+ |
|
| 81 |
+ # Lookup the actual port |
|
| 82 |
+ docker port dyn-bound 8080 |
|
| 83 |
+ 127.0.0.1:49160 |
|
| 84 |
+ |
|
| 85 |
+ |
|
| 86 |
+Linking a container |
|
| 87 |
+------------------- |
|
| 88 |
+ |
|
| 89 |
+Communication between two containers can also be established in a |
|
| 90 |
+docker-specific way called linking. |
|
| 91 |
+ |
|
| 92 |
+To briefly present the concept of linking, let us consider two |
|
| 93 |
+containers: ``server``, containing the service, and ``client``, |
|
| 94 |
+accessing the service. Once ``server`` is running, ``client`` is |
|
| 95 |
+started and links to server. Linking sets environment variables in |
|
| 96 |
+``client`` giving it some information about ``server``. In this sense, |
|
| 97 |
+linking is a method of service discovery. |
|
| 98 |
+ |
|
| 99 |
+Let us now get back to our topic of interest; communication between |
|
| 100 |
+the two containers. We mentioned that the tricky part about this |
|
| 101 |
+communication was that the IP address of ``server`` was not |
|
| 102 |
+fixed. Therefore, some of the environment variables are going to be |
|
| 103 |
+used to inform ``client`` about this IP address. This process called |
|
| 104 |
+exposure, is possible because ``client`` is started after ``server`` |
|
| 105 |
+has been started. |
|
| 106 |
+ |
|
| 107 |
+Here is a full example. On ``server``, the port of interest is |
|
| 108 |
+exposed. The exposure is done either through the ``-expose`` parameter |
|
| 109 |
+to the ``docker run`` command, or the ``EXPOSE`` build command in a |
|
| 110 |
+Dockerfile: |
|
| 111 |
+ |
|
| 112 |
+.. code-block:: bash |
|
| 113 |
+ |
|
| 114 |
+ # Expose port 80 |
|
| 115 |
+ docker run -expose 80 -name server <image> <cmd> |
|
| 116 |
+ |
|
| 117 |
+The ``client`` then links to the ``server``: |
|
| 118 |
+ |
|
| 119 |
+.. code-block:: bash |
|
| 120 |
+ |
|
| 121 |
+ # Link |
|
| 122 |
+ docker run -name client -link server:linked-server <image> <cmd> |
|
| 123 |
+ |
|
| 124 |
+``client`` locally refers to ``server`` as ``linked-server``. The |
|
| 125 |
+following environment variables, among others, are available on |
|
| 126 |
+``client``: |
|
| 127 |
+ |
|
| 128 |
+.. code-block:: bash |
|
| 129 |
+ |
|
| 130 |
+ # The default protocol, ip, and port of the service running in the container |
|
| 131 |
+ LINKED-SERVER_PORT=tcp://172.17.0.8:80 |
|
| 132 |
+ |
|
| 133 |
+ # A specific protocol, ip, and port of various services |
|
| 134 |
+ LINKED-SERVER_PORT_80_TCP=tcp://172.17.0.8:80 |
|
| 135 |
+ LINKED-SERVER_PORT_80_TCP_PROTO=tcp |
|
| 136 |
+ LINKED-SERVER_PORT_80_TCP_ADDR=172.17.0.8 |
|
| 137 |
+ LINKED-SERVER_PORT_80_TCP_PORT=80 |
|
| 138 |
+ |
|
| 139 |
+This tells ``client`` that a service is running on port 80 of |
|
| 140 |
+``server`` and that ``server`` is accessible at the IP address |
|
| 141 |
+172.17.0.8 |
|
| 34 | 142 |
|
| 35 |
-Default port redirects can be built into a container with the |
|
| 36 |
-``EXPOSE`` build command. |
|
| 143 |
+Note: Using the ``-p`` parameter also exposes the port.. |
| 37 | 144 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,73 @@ |
| 0 |
+:title: Working with Volumes |
|
| 1 |
+:description: How to create and share volumes |
|
| 2 |
+:keywords: Examples, Usage, volume, docker, documentation, examples |
|
| 3 |
+ |
|
| 4 |
+.. _volume_def: |
|
| 5 |
+ |
|
| 6 |
+Data Volume |
|
| 7 |
+=========== |
|
| 8 |
+ |
|
| 9 |
+.. versionadded:: v0.3.0 |
|
| 10 |
+ Data volumes have been available since version 1 of the |
|
| 11 |
+ :doc:`../api/docker_remote_api` |
|
| 12 |
+ |
|
| 13 |
+A *data volume* is a specially-designated directory within one or more |
|
| 14 |
+containers that bypasses the :ref:`ufs_def` to provide several useful |
|
| 15 |
+features for persistant or shared data: |
|
| 16 |
+ |
|
| 17 |
+* **Data volumes can be shared and reused between containers.** This |
|
| 18 |
+ is the feature that makes data volumes so powerful. You can use it |
|
| 19 |
+ for anything from hot database upgrades to custom backup or |
|
| 20 |
+ replication tools. See the example below. |
|
| 21 |
+* **Changes to a data volume are made directly**, without the overhead |
|
| 22 |
+ of a copy-on-write mechanism. This is good for very large files. |
|
| 23 |
+* **Changes to a data volume will not be included at the next commit** |
|
| 24 |
+ because they are not recorded as regular filesystem changes in the |
|
| 25 |
+ top layer of the :ref:`ufs_def` |
|
| 26 |
+ |
|
| 27 |
+Each container can have zero or more data volumes. |
|
| 28 |
+ |
|
| 29 |
+Getting Started |
|
| 30 |
+............... |
|
| 31 |
+ |
|
| 32 |
+ |
|
| 33 |
+ |
|
| 34 |
+Using data volumes is as simple as adding a new flag: ``-v``. The parameter ``-v`` can be used more than once in order to create more volumes within the new container. The example below shows the instruction to create a container with two new volumes:: |
|
| 35 |
+ |
|
| 36 |
+ docker run -v /var/volume1 -v /var/volume2 shykes/couchdb |
|
| 37 |
+ |
|
| 38 |
+For a Dockerfile, the VOLUME instruction will add one or more new volumes to any container created from the image:: |
|
| 39 |
+ |
|
| 40 |
+ VOLUME ["/var/volume1", "/var/volume2"] |
|
| 41 |
+ |
|
| 42 |
+ |
|
| 43 |
+Create a new container using existing volumes from an existing container: |
|
| 44 |
+--------------------------------------------------------------------------- |
|
| 45 |
+ |
|
| 46 |
+ |
|
| 47 |
+The command below creates a new container which is runnning as daemon ``-d`` and with one volume ``/var/lib/couchdb``:: |
|
| 48 |
+ |
|
| 49 |
+ COUCH1=$(sudo docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03) |
|
| 50 |
+ |
|
| 51 |
+From the container id of that previous container ``$COUCH1`` it's possible to create new container sharing the same volume using the parameter ``-volumes-from container_id``:: |
|
| 52 |
+ |
|
| 53 |
+ COUCH2=$(sudo docker run -d -volumes-from $COUCH1 shykes/couchdb:2013-05-03) |
|
| 54 |
+ |
|
| 55 |
+Now, the second container has the all the information from the first volume. |
|
| 56 |
+ |
|
| 57 |
+ |
|
| 58 |
+Create a new container which mounts a host directory into it: |
|
| 59 |
+------------------------------------------------------------- |
|
| 60 |
+ |
|
| 61 |
+ -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. |
|
| 62 |
+ If "host-dir" is missing, then docker creates a new volume. |
|
| 63 |
+ |
|
| 64 |
+ This is not available for a Dockerfile due the portability and sharing purpose of it. The [host-dir] volumes is something 100% host dependent and will break on any other machine. |
|
| 65 |
+ |
|
| 66 |
+For example:: |
|
| 67 |
+ |
|
| 68 |
+ sudo docker run -v /var/logs:/var/host_logs:ro shykes/couchdb:2013-05-03 |
|
| 69 |
+ |
|
| 70 |
+The command above mounts the host directory ``/var/logs`` into the container with read only permissions as ``/var/host_logs``. |
|
| 71 |
+ |
|
| 72 |
+.. versionadded:: v0.5.0 |
| 0 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,82 @@ |
| 0 |
+package engine |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "os" |
|
| 5 |
+ "log" |
|
| 6 |
+ "runtime" |
|
| 7 |
+ "github.com/dotcloud/docker/utils" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+ |
|
| 11 |
+type Handler func(*Job) string |
|
| 12 |
+ |
|
| 13 |
+var globalHandlers map[string]Handler |
|
| 14 |
+ |
|
| 15 |
+func Register(name string, handler Handler) error {
|
|
| 16 |
+ if globalHandlers == nil {
|
|
| 17 |
+ globalHandlers = make(map[string]Handler) |
|
| 18 |
+ } |
|
| 19 |
+ globalHandlers[name] = handler |
|
| 20 |
+ return nil |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// The Engine is the core of Docker. |
|
| 24 |
+// It acts as a store for *containers*, and allows manipulation of these |
|
| 25 |
+// containers by executing *jobs*. |
|
| 26 |
+type Engine struct {
|
|
| 27 |
+ root string |
|
| 28 |
+ handlers map[string]Handler |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+// New initializes a new engine managing the directory specified at `root`. |
|
| 32 |
+// `root` is used to store containers and any other state private to the engine. |
|
| 33 |
+// Changing the contents of the root without executing a job will cause unspecified |
|
| 34 |
+// behavior. |
|
| 35 |
+func New(root string) (*Engine, error) {
|
|
| 36 |
+ // Check for unsupported architectures |
|
| 37 |
+ if runtime.GOARCH != "amd64" {
|
|
| 38 |
+ return nil, fmt.Errorf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
|
|
| 39 |
+ } |
|
| 40 |
+ // Check for unsupported kernel versions |
|
| 41 |
+ // FIXME: it would be cleaner to not test for specific versions, but rather |
|
| 42 |
+ // test for specific functionalities. |
|
| 43 |
+ // Unfortunately we can't test for the feature "does not cause a kernel panic" |
|
| 44 |
+ // without actually causing a kernel panic, so we need this workaround until |
|
| 45 |
+ // the circumstances of pre-3.8 crashes are clearer. |
|
| 46 |
+ // For details see http://github.com/dotcloud/docker/issues/407 |
|
| 47 |
+ if k, err := utils.GetKernelVersion(); err != nil {
|
|
| 48 |
+ log.Printf("WARNING: %s\n", err)
|
|
| 49 |
+ } else {
|
|
| 50 |
+ if utils.CompareKernelVersion(k, &utils.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 {
|
|
| 51 |
+ log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String())
|
|
| 52 |
+ } |
|
| 53 |
+ } |
|
| 54 |
+ if err := os.MkdirAll(root, 0700); err != nil && !os.IsExist(err) {
|
|
| 55 |
+ return nil, err |
|
| 56 |
+ } |
|
| 57 |
+ eng := &Engine{
|
|
| 58 |
+ root: root, |
|
| 59 |
+ handlers: globalHandlers, |
|
| 60 |
+ } |
|
| 61 |
+ return eng, nil |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 64 |
+// Job creates a new job which can later be executed. |
|
| 65 |
+// This function mimics `Command` from the standard os/exec package. |
|
| 66 |
+func (eng *Engine) Job(name string, args ...string) *Job {
|
|
| 67 |
+ job := &Job{
|
|
| 68 |
+ eng: eng, |
|
| 69 |
+ Name: name, |
|
| 70 |
+ Args: args, |
|
| 71 |
+ Stdin: os.Stdin, |
|
| 72 |
+ Stdout: os.Stdout, |
|
| 73 |
+ Stderr: os.Stderr, |
|
| 74 |
+ } |
|
| 75 |
+ handler, exists := eng.handlers[name] |
|
| 76 |
+ if exists {
|
|
| 77 |
+ job.handler = handler |
|
| 78 |
+ } |
|
| 79 |
+ return job |
|
| 80 |
+} |
|
| 81 |
+ |
| 0 | 82 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,29 @@ |
| 0 |
+package engine |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+func TestNewJob(t *testing.T) {
|
|
| 7 |
+ job := mkJob(t, "dummy", "--level=awesome") |
|
| 8 |
+ if job.Name != "dummy" {
|
|
| 9 |
+ t.Fatalf("Wrong job name: %s", job.Name)
|
|
| 10 |
+ } |
|
| 11 |
+ if len(job.Args) != 1 {
|
|
| 12 |
+ t.Fatalf("Wrong number of job arguments: %d", len(job.Args))
|
|
| 13 |
+ } |
|
| 14 |
+ if job.Args[0] != "--level=awesome" {
|
|
| 15 |
+ t.Fatalf("Wrong job arguments: %s", job.Args[0])
|
|
| 16 |
+ } |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+func TestSetenv(t *testing.T) {
|
|
| 20 |
+ job := mkJob(t, "dummy") |
|
| 21 |
+ job.Setenv("foo", "bar")
|
|
| 22 |
+ if val := job.Getenv("foo"); val != "bar" {
|
|
| 23 |
+ t.Fatalf("Getenv returns incorrect value: %s", val)
|
|
| 24 |
+ } |
|
| 25 |
+ if val := job.Getenv("nonexistent"); val != "" {
|
|
| 26 |
+ t.Fatalf("Getenv returns incorrect value: %s", val)
|
|
| 27 |
+ } |
|
| 28 |
+} |
| 0 | 29 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,42 @@ |
| 0 |
+package engine |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ "runtime" |
|
| 5 |
+ "strings" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "io/ioutil" |
|
| 8 |
+ "github.com/dotcloud/docker/utils" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+var globalTestID string |
|
| 12 |
+ |
|
| 13 |
+func init() {
|
|
| 14 |
+ Register("dummy", func(job *Job) string { return ""; })
|
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+func mkEngine(t *testing.T) *Engine {
|
|
| 18 |
+ // Use the caller function name as a prefix. |
|
| 19 |
+ // This helps trace temp directories back to their test. |
|
| 20 |
+ pc, _, _, _ := runtime.Caller(1) |
|
| 21 |
+ callerLongName := runtime.FuncForPC(pc).Name() |
|
| 22 |
+ parts := strings.Split(callerLongName, ".") |
|
| 23 |
+ callerShortName := parts[len(parts)-1] |
|
| 24 |
+ if globalTestID == "" {
|
|
| 25 |
+ globalTestID = utils.RandomString()[:4] |
|
| 26 |
+ } |
|
| 27 |
+ prefix := fmt.Sprintf("docker-test%s-%s-", globalTestID, callerShortName)
|
|
| 28 |
+ root, err := ioutil.TempDir("", prefix)
|
|
| 29 |
+ if err != nil {
|
|
| 30 |
+ t.Fatal(err) |
|
| 31 |
+ } |
|
| 32 |
+ eng, err := New(root) |
|
| 33 |
+ if err != nil {
|
|
| 34 |
+ t.Fatal(err) |
|
| 35 |
+ } |
|
| 36 |
+ return eng |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func mkJob(t *testing.T, name string, args ...string) *Job {
|
|
| 40 |
+ return mkEngine(t).Job(name, args...) |
|
| 41 |
+} |
| 0 | 42 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,113 @@ |
| 0 |
+package engine |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "io" |
|
| 4 |
+ "strings" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "encoding/json" |
|
| 7 |
+ "github.com/dotcloud/docker/utils" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// A job is the fundamental unit of work in the docker engine. |
|
| 11 |
+// Everything docker can do should eventually be exposed as a job. |
|
| 12 |
+// For example: execute a process in a container, create a new container, |
|
| 13 |
+// download an archive from the internet, serve the http api, etc. |
|
| 14 |
+// |
|
| 15 |
+// The job API is designed after unix processes: a job has a name, arguments, |
|
| 16 |
+// environment variables, standard streams for input, output and error, and |
|
| 17 |
+// an exit status which can indicate success (0) or error (anything else). |
|
| 18 |
+// |
|
| 19 |
+// One slight variation is that jobs report their status as a string. The |
|
| 20 |
+// string "0" indicates success, and any other strings indicates an error. |
|
| 21 |
+// This allows for richer error reporting. |
|
| 22 |
+// |
|
| 23 |
+type Job struct {
|
|
| 24 |
+ eng *Engine |
|
| 25 |
+ Name string |
|
| 26 |
+ Args []string |
|
| 27 |
+ env []string |
|
| 28 |
+ Stdin io.ReadCloser |
|
| 29 |
+ Stdout io.WriteCloser |
|
| 30 |
+ Stderr io.WriteCloser |
|
| 31 |
+ handler func(*Job) string |
|
| 32 |
+ status string |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+// Run executes the job and blocks until the job completes. |
|
| 36 |
+// If the job returns a failure status, an error is returned |
|
| 37 |
+// which includes the status. |
|
| 38 |
+func (job *Job) Run() error {
|
|
| 39 |
+ randId := utils.RandomString()[:4] |
|
| 40 |
+ fmt.Printf("Job #%s: %s\n", randId, job)
|
|
| 41 |
+ defer fmt.Printf("Job #%s: %s = '%s'", randId, job, job.status)
|
|
| 42 |
+ if job.handler == nil {
|
|
| 43 |
+ job.status = "command not found" |
|
| 44 |
+ } else {
|
|
| 45 |
+ job.status = job.handler(job) |
|
| 46 |
+ } |
|
| 47 |
+ if job.status != "0" {
|
|
| 48 |
+ return fmt.Errorf("%s: %s", job.Name, job.status)
|
|
| 49 |
+ } |
|
| 50 |
+ return nil |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+// String returns a human-readable description of `job` |
|
| 54 |
+func (job *Job) String() string {
|
|
| 55 |
+ return strings.Join(append([]string{job.Name}, job.Args...), " ")
|
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func (job *Job) Getenv(key string) (value string) {
|
|
| 59 |
+ for _, kv := range job.env {
|
|
| 60 |
+ if strings.Index(kv, "=") == -1 {
|
|
| 61 |
+ continue |
|
| 62 |
+ } |
|
| 63 |
+ parts := strings.SplitN(kv, "=", 2) |
|
| 64 |
+ if parts[0] != key {
|
|
| 65 |
+ continue |
|
| 66 |
+ } |
|
| 67 |
+ if len(parts) < 2 {
|
|
| 68 |
+ value = "" |
|
| 69 |
+ } else {
|
|
| 70 |
+ value = parts[1] |
|
| 71 |
+ } |
|
| 72 |
+ } |
|
| 73 |
+ return |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+func (job *Job) GetenvBool(key string) (value bool) {
|
|
| 77 |
+ s := strings.ToLower(strings.Trim(job.Getenv(key), " \t")) |
|
| 78 |
+ if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
|
|
| 79 |
+ return false |
|
| 80 |
+ } |
|
| 81 |
+ return true |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+func (job *Job) SetenvBool(key string, value bool) {
|
|
| 85 |
+ if value {
|
|
| 86 |
+ job.Setenv(key, "1") |
|
| 87 |
+ } else {
|
|
| 88 |
+ job.Setenv(key, "0") |
|
| 89 |
+ } |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+func (job *Job) GetenvList(key string) []string {
|
|
| 93 |
+ sval := job.Getenv(key) |
|
| 94 |
+ l := make([]string, 0, 1) |
|
| 95 |
+ if err := json.Unmarshal([]byte(sval), &l); err != nil {
|
|
| 96 |
+ l = append(l, sval) |
|
| 97 |
+ } |
|
| 98 |
+ return l |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 101 |
+func (job *Job) SetenvList(key string, value []string) error {
|
|
| 102 |
+ sval, err := json.Marshal(value) |
|
| 103 |
+ if err != nil {
|
|
| 104 |
+ return err |
|
| 105 |
+ } |
|
| 106 |
+ job.Setenv(key, string(sval)) |
|
| 107 |
+ return nil |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+func (job *Job) Setenv(key, value string) {
|
|
| 111 |
+ job.env = append(job.env, key + "=" + value) |
|
| 112 |
+} |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"database/sql" |
| 5 | 5 |
"fmt" |
| 6 | 6 |
"path" |
| 7 |
+ "sync" |
|
| 7 | 8 |
) |
| 8 | 9 |
|
| 9 | 10 |
const ( |
| ... | ... |
@@ -47,6 +48,7 @@ type WalkFunc func(fullPath string, entity *Entity) error |
| 47 | 47 |
// Graph database for storing entities and their relationships |
| 48 | 48 |
type Database struct {
|
| 49 | 49 |
conn *sql.DB |
| 50 |
+ mux sync.Mutex |
|
| 50 | 51 |
} |
| 51 | 52 |
|
| 52 | 53 |
// Create a new graph database initialized with a root entity |
| ... | ... |
@@ -54,7 +56,7 @@ func NewDatabase(conn *sql.DB, init bool) (*Database, error) {
|
| 54 | 54 |
if conn == nil {
|
| 55 | 55 |
return nil, fmt.Errorf("Database connection cannot be nil")
|
| 56 | 56 |
} |
| 57 |
- db := &Database{conn}
|
|
| 57 |
+ db := &Database{conn: conn}
|
|
| 58 | 58 |
|
| 59 | 59 |
if init {
|
| 60 | 60 |
if _, err := conn.Exec(createEntityTable); err != nil {
|
| ... | ... |
@@ -99,7 +101,9 @@ func (db *Database) Close() error {
|
| 99 | 99 |
|
| 100 | 100 |
// Set the entity id for a given path |
| 101 | 101 |
func (db *Database) Set(fullPath, id string) (*Entity, error) {
|
| 102 |
- // FIXME: is rollback implicit when closing the connection? |
|
| 102 |
+ db.mux.Lock() |
|
| 103 |
+ defer db.mux.Unlock() |
|
| 104 |
+ |
|
| 103 | 105 |
rollback := func() {
|
| 104 | 106 |
db.conn.Exec("ROLLBACK")
|
| 105 | 107 |
} |
| ... | ... |
@@ -256,6 +260,9 @@ func (db *Database) RefPaths(id string) Edges {
|
| 256 | 256 |
|
| 257 | 257 |
// Delete the reference to an entity at a given path |
| 258 | 258 |
func (db *Database) Delete(name string) error {
|
| 259 |
+ db.mux.Lock() |
|
| 260 |
+ defer db.mux.Unlock() |
|
| 261 |
+ |
|
| 259 | 262 |
if name == "/" {
|
| 260 | 263 |
return fmt.Errorf("Cannot delete root entity")
|
| 261 | 264 |
} |
| ... | ... |
@@ -276,6 +283,9 @@ func (db *Database) Delete(name string) error {
|
| 276 | 276 |
// Walk the graph to make sure all references to the entity |
| 277 | 277 |
// are removed and return the number of references removed |
| 278 | 278 |
func (db *Database) Purge(id string) (int, error) {
|
| 279 |
+ db.mux.Lock() |
|
| 280 |
+ defer db.mux.Unlock() |
|
| 281 |
+ |
|
| 279 | 282 |
rollback := func() {
|
| 280 | 283 |
db.conn.Exec("ROLLBACK")
|
| 281 | 284 |
} |
| ... | ... |
@@ -310,6 +320,9 @@ func (db *Database) Purge(id string) (int, error) {
|
| 310 | 310 |
|
| 311 | 311 |
// Rename an edge for a given path |
| 312 | 312 |
func (db *Database) Rename(currentName, newName string) error {
|
| 313 |
+ db.mux.Lock() |
|
| 314 |
+ defer db.mux.Unlock() |
|
| 315 |
+ |
|
| 313 | 316 |
parentPath, name := splitPath(currentName) |
| 314 | 317 |
newParentPath, newEdgeName := splitPath(newName) |
| 315 | 318 |
|
| ... | ... |
@@ -3,6 +3,7 @@ package gograph |
| 3 | 3 |
import ( |
| 4 | 4 |
_ "code.google.com/p/gosqlite/sqlite3" |
| 5 | 5 |
"database/sql" |
| 6 |
+ "fmt" |
|
| 6 | 7 |
"os" |
| 7 | 8 |
"path" |
| 8 | 9 |
"strconv" |
| ... | ... |
@@ -501,3 +502,39 @@ func TestGetNameWithTrailingSlash(t *testing.T) {
|
| 501 | 501 |
t.Fatalf("Entity should not be nil")
|
| 502 | 502 |
} |
| 503 | 503 |
} |
| 504 |
+ |
|
| 505 |
+func TestConcurrentWrites(t *testing.T) {
|
|
| 506 |
+ db, dbpath := newTestDb(t) |
|
| 507 |
+ defer destroyTestDb(dbpath) |
|
| 508 |
+ |
|
| 509 |
+ errs := make(chan error, 2) |
|
| 510 |
+ |
|
| 511 |
+ save := func(name string, id string) {
|
|
| 512 |
+ if _, err := db.Set(fmt.Sprintf("/%s", name), id); err != nil {
|
|
| 513 |
+ errs <- err |
|
| 514 |
+ } |
|
| 515 |
+ errs <- nil |
|
| 516 |
+ } |
|
| 517 |
+ purge := func(id string) {
|
|
| 518 |
+ if _, err := db.Purge(id); err != nil {
|
|
| 519 |
+ errs <- err |
|
| 520 |
+ } |
|
| 521 |
+ errs <- nil |
|
| 522 |
+ } |
|
| 523 |
+ |
|
| 524 |
+ save("/1", "1")
|
|
| 525 |
+ |
|
| 526 |
+ go purge("1")
|
|
| 527 |
+ go save("/2", "2")
|
|
| 528 |
+ |
|
| 529 |
+ any := false |
|
| 530 |
+ for i := 0; i < 2; i++ {
|
|
| 531 |
+ if err := <-errs; err != nil {
|
|
| 532 |
+ any = true |
|
| 533 |
+ t.Log(err) |
|
| 534 |
+ } |
|
| 535 |
+ } |
|
| 536 |
+ if any {
|
|
| 537 |
+ t.Fatal() |
|
| 538 |
+ } |
|
| 539 |
+} |
| ... | ... |
@@ -2,6 +2,7 @@ package docker |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
+ "github.com/dotcloud/docker/archive" |
|
| 5 | 6 |
"github.com/dotcloud/docker/utils" |
| 6 | 7 |
"io" |
| 7 | 8 |
"io/ioutil" |
| ... | ... |
@@ -94,7 +95,7 @@ func (graph *Graph) Get(name string) (*Image, error) {
|
| 94 | 94 |
} |
| 95 | 95 |
|
| 96 | 96 |
// Create creates a new image and registers it in the graph. |
| 97 |
-func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) {
|
|
| 97 |
+func (graph *Graph) Create(layerData archive.Archive, container *Container, comment, author string, config *Config) (*Image, error) {
|
|
| 98 | 98 |
img := &Image{
|
| 99 | 99 |
ID: GenerateID(), |
| 100 | 100 |
Comment: comment, |
| ... | ... |
@@ -117,7 +118,7 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut |
| 117 | 117 |
|
| 118 | 118 |
// Register imports a pre-existing image into the graph. |
| 119 | 119 |
// FIXME: pass img as first argument |
| 120 |
-func (graph *Graph) Register(jsonData []byte, layerData Archive, img *Image) error {
|
|
| 120 |
+func (graph *Graph) Register(jsonData []byte, layerData archive.Archive, img *Image) error {
|
|
| 121 | 121 |
if err := ValidateID(img.ID); err != nil {
|
| 122 | 122 |
return err |
| 123 | 123 |
} |
| ... | ... |
@@ -146,7 +147,7 @@ func (graph *Graph) Register(jsonData []byte, layerData Archive, img *Image) err |
| 146 | 146 |
// The archive is stored on disk and will be automatically deleted as soon as has been read. |
| 147 | 147 |
// If output is not nil, a human-readable progress bar will be written to it. |
| 148 | 148 |
// FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives? |
| 149 |
-func (graph *Graph) TempLayerArchive(id string, compression Compression, sf *utils.StreamFormatter, output io.Writer) (*TempArchive, error) {
|
|
| 149 |
+func (graph *Graph) TempLayerArchive(id string, compression archive.Compression, sf *utils.StreamFormatter, output io.Writer) (*archive.TempArchive, error) {
|
|
| 150 | 150 |
image, err := graph.Get(id) |
| 151 | 151 |
if err != nil {
|
| 152 | 152 |
return nil, err |
| ... | ... |
@@ -155,11 +156,11 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, sf *uti |
| 155 | 155 |
if err != nil {
|
| 156 | 156 |
return nil, err |
| 157 | 157 |
} |
| 158 |
- archive, err := image.TarLayer(compression) |
|
| 158 |
+ a, err := image.TarLayer(compression) |
|
| 159 | 159 |
if err != nil {
|
| 160 | 160 |
return nil, err |
| 161 | 161 |
} |
| 162 |
- return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp.Root)
|
|
| 162 |
+ return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp.Root)
|
|
| 163 | 163 |
} |
| 164 | 164 |
|
| 165 | 165 |
// Mktemp creates a temporary sub-directory inside the graph's filesystem. |
| ... | ... |
@@ -201,6 +202,7 @@ func (graph *Graph) getDockerInitLayer() (string, error) {
|
| 201 | 201 |
"/proc": "dir", |
| 202 | 202 |
"/sys": "dir", |
| 203 | 203 |
"/.dockerinit": "file", |
| 204 |
+ "/.dockerenv": "file", |
|
| 204 | 205 |
"/etc/resolv.conf": "file", |
| 205 | 206 |
"/etc/hosts": "file", |
| 206 | 207 |
"/etc/hostname": "file", |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"archive/tar" |
| 5 | 5 |
"bytes" |
| 6 | 6 |
"errors" |
| 7 |
+ "github.com/dotcloud/docker/archive" |
|
| 7 | 8 |
"github.com/dotcloud/docker/utils" |
| 8 | 9 |
"io" |
| 9 | 10 |
"io/ioutil" |
| ... | ... |
@@ -301,7 +302,7 @@ func tempGraph(t *testing.T) *Graph {
|
| 301 | 301 |
return graph |
| 302 | 302 |
} |
| 303 | 303 |
|
| 304 |
-func testArchive(t *testing.T) Archive {
|
|
| 304 |
+func testArchive(t *testing.T) archive.Archive {
|
|
| 305 | 305 |
archive, err := fakeTar() |
| 306 | 306 |
if err != nil {
|
| 307 | 307 |
t.Fatal(err) |
| ... | ... |
@@ -9,7 +9,8 @@ to keep it up-to-date. |
| 9 | 9 |
### 1. Pull from master and create a release branch |
| 10 | 10 |
|
| 11 | 11 |
```bash |
| 12 |
-git checkout master |
|
| 12 |
+export VERSION=vXXX |
|
| 13 |
+git checkout release |
|
| 13 | 14 |
git pull |
| 14 | 15 |
git checkout -b bump_$VERSION |
| 15 | 16 |
``` |
| ... | ... |
@@ -55,7 +56,9 @@ EXAMPLES: |
| 55 | 55 |
|
| 56 | 56 |
### 4. Run all tests |
| 57 | 57 |
|
| 58 |
-FIXME |
|
| 58 |
+```bash |
|
| 59 |
+docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh test |
|
| 60 |
+``` |
|
| 59 | 61 |
|
| 60 | 62 |
### 5. Test the docs |
| 61 | 63 |
|
| ... | ... |
@@ -66,21 +69,17 @@ the docs are in ``docs/README.md`` |
| 66 | 66 |
### 6. Commit and create a pull request to the "release" branch |
| 67 | 67 |
|
| 68 | 68 |
```bash |
| 69 |
-git add CHANGELOG.md |
|
| 69 |
+git add VERSION CHANGELOG.md |
|
| 70 | 70 |
git commit -m "Bump version to $VERSION" |
| 71 | 71 |
git push origin bump_$VERSION |
| 72 | 72 |
``` |
| 73 | 73 |
|
| 74 | 74 |
### 7. Get 2 other maintainers to validate the pull request |
| 75 | 75 |
|
| 76 |
-### 8. Merge the pull request and apply tags |
|
| 76 |
+### 8. Apply tag |
|
| 77 | 77 |
|
| 78 | 78 |
```bash |
| 79 |
-git checkout release |
|
| 80 |
-git merge bump_$VERSION |
|
| 81 | 79 |
git tag -a v$VERSION # Don't forget the v! |
| 82 |
-git tag -f -a latest |
|
| 83 |
-git push |
|
| 84 | 80 |
git push --tags |
| 85 | 81 |
``` |
| 86 | 82 |
|
| ... | ... |
@@ -90,28 +89,35 @@ should see the updated docs 5-10 minutes after the merge. The docs |
| 90 | 90 |
will appear on http://docs.docker.io/. For more information about |
| 91 | 91 |
documentation releases, see ``docs/README.md`` |
| 92 | 92 |
|
| 93 |
-### 9. Publish binaries |
|
| 93 |
+### 9. Go to github to merge the bump_$VERSION into release |
|
| 94 |
+ |
|
| 95 |
+### 10. Publish binaries |
|
| 94 | 96 |
|
| 95 | 97 |
To run this you will need access to the release credentials. |
| 96 | 98 |
Get them from [the infrastructure maintainers]( |
| 97 | 99 |
https://github.com/dotcloud/docker/blob/master/hack/infrastructure/MAINTAINERS). |
| 98 | 100 |
|
| 99 | 101 |
```bash |
| 102 |
+git checkout release |
|
| 103 |
+git fetch |
|
| 104 |
+git reset --hard origin/release |
|
| 100 | 105 |
docker build -t docker . |
| 101 | 106 |
docker run \ |
| 102 |
- -e AWS_S3_BUCKET=get-nightly.docker.io \ |
|
| 103 |
- -e AWS_ACCESS_KEY=$(cat ~/.aws/access_key) \ |
|
| 104 |
- -e AWS_SECRET_KEY=$(cat ~/.aws/secret_key) \ |
|
| 105 |
- -e GPG_PASSPHRASE=supersecretsesame \ |
|
| 106 |
- docker |
|
| 107 |
- hack/release.sh |
|
| 107 |
+ -e AWS_S3_BUCKET=test.docker.io \ |
|
| 108 |
+ -e AWS_ACCESS_KEY=$(cat ~/.aws/access_key) \ |
|
| 109 |
+ -e AWS_SECRET_KEY=$(cat ~/.aws/secret_key) \ |
|
| 110 |
+ -e GPG_PASSPHRASE=supersecretsesame \ |
|
| 111 |
+ -privileged -lxc-conf=lxc.aa_profile=unconfined \ |
|
| 112 |
+ -t -i \ |
|
| 113 |
+ docker \ |
|
| 114 |
+ hack/release.sh |
|
| 108 | 115 |
``` |
| 109 | 116 |
|
| 110 | 117 |
It will build and upload the binaries on the specified bucket (you should |
| 111 |
-use get-nightly.docker.io for general testing, and once everything is fine, |
|
| 118 |
+use test.docker.io for general testing, and once everything is fine, |
|
| 112 | 119 |
switch to get.docker.io). |
| 113 | 120 |
|
| 114 | 121 |
|
| 115 |
-### 10. Rejoice! |
|
| 122 |
+### 11. Rejoice! |
|
| 116 | 123 |
|
| 117 | 124 |
Congratulations! You're done. |
| ... | ... |
@@ -21,6 +21,14 @@ mountpoint -q $CGROUP || |
| 21 | 21 |
exit 1 |
| 22 | 22 |
} |
| 23 | 23 |
|
| 24 |
+if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security |
|
| 25 |
+then |
|
| 26 |
+ mount -t securityfs none /sys/kernel/security || {
|
|
| 27 |
+ echo "Could not mount /sys/kernel/security." |
|
| 28 |
+ echo "AppArmor detection and -privileged mode might break." |
|
| 29 |
+ } |
|
| 30 |
+fi |
|
| 31 |
+ |
|
| 24 | 32 |
# Mount the cgroup hierarchies exactly as they are in the parent system. |
| 25 | 33 |
for SUBSYS in $(cut -d: -f2 /proc/1/cgroup) |
| 26 | 34 |
do |
| ... | ... |
@@ -17,15 +17,12 @@ PORT_GITHUB = 8011 # Buildbot github hook port |
| 17 | 17 |
PORT_MASTER = 9989 # Port where buildbot master listen buildworkers |
| 18 | 18 |
TEST_USER = 'buildbot' # Credential to authenticate build triggers |
| 19 | 19 |
TEST_PWD = 'docker' # Credential to authenticate build triggers |
| 20 |
-BUILDER_NAME = 'docker' |
|
| 21 | 20 |
GITHUB_DOCKER = 'github.com/dotcloud/docker' |
| 22 | 21 |
BUILDBOT_PATH = '/data/buildbot' |
| 23 | 22 |
DOCKER_PATH = '/go/src/github.com/dotcloud/docker' |
| 24 | 23 |
DOCKER_CI_PATH = '/docker-ci' |
| 25 |
-BUILDER_PATH = '/data/buildbot/slave/{0}/build'.format(BUILDER_NAME)
|
|
| 26 |
-PULL_REQUEST_PATH = '/data/buildbot/slave/pullrequest/build' |
|
| 27 | 24 |
|
| 28 |
-# Credentials set by setup.sh and Vagrantfile |
|
| 25 |
+# Credentials set by setup.sh from deployment.py |
|
| 29 | 26 |
BUILDBOT_PWD = '' |
| 30 | 27 |
IRC_PWD = '' |
| 31 | 28 |
IRC_CHANNEL = '' |
| ... | ... |
@@ -45,34 +42,35 @@ c['slavePortnum'] = PORT_MASTER |
| 45 | 45 |
|
| 46 | 46 |
|
| 47 | 47 |
# Schedulers |
| 48 |
-c['schedulers'] = [ForceScheduler(name='trigger', builderNames=[BUILDER_NAME, |
|
| 48 |
+c['schedulers'] = [ForceScheduler(name='trigger', builderNames=['docker', |
|
| 49 | 49 |
'index','registry','coverage','nightlyrelease'])] |
| 50 |
-c['schedulers'] += [SingleBranchScheduler(name="all", |
|
| 51 |
- change_filter=filter.ChangeFilter(branch='master'), treeStableTimer=None, |
|
| 52 |
- builderNames=[BUILDER_NAME])] |
|
| 50 |
+c['schedulers'] += [SingleBranchScheduler(name="all", treeStableTimer=None, |
|
| 51 |
+ change_filter=filter.ChangeFilter(branch='master', |
|
| 52 |
+ repository='https://github.com/dotcloud/docker'), builderNames=['docker'])] |
|
| 53 | 53 |
c['schedulers'] += [SingleBranchScheduler(name='pullrequest', |
| 54 | 54 |
change_filter=filter.ChangeFilter(category='github_pullrequest'), treeStableTimer=None, |
| 55 | 55 |
builderNames=['pullrequest'])] |
| 56 |
-c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['nightlyrelease'], |
|
| 57 |
- hour=7, minute=00)] |
|
| 56 |
+c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['nightlyrelease', |
|
| 57 |
+ 'coverage'], hour=7, minute=00)] |
|
| 58 | 58 |
c['schedulers'] += [Nightly(name='every4hrs', branch=None, builderNames=['registry','index'], |
| 59 | 59 |
hour=range(0,24,4), minute=15)] |
| 60 | 60 |
|
| 61 | 61 |
# Builders |
| 62 | 62 |
# Docker commit test |
| 63 |
+test_cmd = ('docker run -privileged mzdaniel/test_docker hack/dind'
|
|
| 64 |
+ ' test_docker.sh %(src::revision)s') |
|
| 63 | 65 |
factory = BuildFactory() |
| 64 | 66 |
factory.addStep(ShellCommand(description='Docker', logEnviron=False, |
| 65 |
- usePTY=True, command=['sh', '-c', Interpolate( |
|
| 66 |
- '{0}/docker-test/test_docker.sh %(src::revision)s'.format(DOCKER_CI_PATH))]))
|
|
| 67 |
+ usePTY=True, command=["sh", "-c", Interpolate(test_cmd)])) |
|
| 67 | 68 |
c['builders'] = [BuilderConfig(name='docker',slavenames=['buildworker'], |
| 68 | 69 |
factory=factory)] |
| 69 | 70 |
|
| 70 | 71 |
# Docker pull request test |
| 72 |
+test_cmd = ('docker run -privileged mzdaniel/test_docker hack/dind'
|
|
| 73 |
+ ' test_docker.sh %(src::revision)s %(src::repository)s %(src::branch)s') |
|
| 71 | 74 |
factory = BuildFactory() |
| 72 | 75 |
factory.addStep(ShellCommand(description='pull_request', logEnviron=False, |
| 73 |
- usePTY=True, command=['sh', '-c', Interpolate( |
|
| 74 |
- '{0}/docker-test/test_docker.sh %(src::revision)s %(src::repository)s'
|
|
| 75 |
- ' %(src::branch)s'.format(DOCKER_CI_PATH))])) |
|
| 76 |
+ usePTY=True, command=["sh", "-c", Interpolate(test_cmd)])) |
|
| 76 | 77 |
c['builders'] += [BuilderConfig(name='pullrequest',slavenames=['buildworker'], |
| 77 | 78 |
factory=factory)] |
| 78 | 79 |
|
| ... | ... |
@@ -97,17 +95,16 @@ c['builders'] += [BuilderConfig(name='registry',slavenames=['buildworker'], |
| 97 | 97 |
factory = BuildFactory() |
| 98 | 98 |
factory.addStep(ShellCommand(description='index', logEnviron=False, |
| 99 | 99 |
command='. {0}/master/credentials.cfg; '
|
| 100 |
- '{1}/testing/functionaltests/test_index.py'.format(BUILDBOT_PATH,
|
|
| 101 |
- DOCKER_PATH), usePTY=True)) |
|
| 100 |
+ '/docker-ci/functionaltests/test_index.py'.format(BUILDBOT_PATH), |
|
| 101 |
+ usePTY=True)) |
|
| 102 | 102 |
c['builders'] += [BuilderConfig(name='index',slavenames=['buildworker'], |
| 103 | 103 |
factory=factory)] |
| 104 | 104 |
|
| 105 | 105 |
# Docker nightly release |
| 106 |
-nightlyrelease_cmd = ('docker run -i -t -privileged -lxc-conf=lxc.aa_profile=unconfined'
|
|
| 107 |
- ' -e AWS_S3_BUCKET=test.docker.io dockerbuilder') |
|
| 108 | 106 |
factory = BuildFactory() |
| 109 |
-factory.addStep(ShellCommand(description='NightlyRelease',logEnviron=False,usePTY=True, |
|
| 110 |
- command=nightlyrelease_cmd)) |
|
| 107 |
+factory.addStep(ShellCommand(description='NightlyRelease', logEnviron=False, |
|
| 108 |
+ usePTY=True, command='docker run -privileged' |
|
| 109 |
+ ' -e AWS_S3_BUCKET=test.docker.io dockerbuilder')) |
|
| 111 | 110 |
c['builders'] += [BuilderConfig(name='nightlyrelease',slavenames=['buildworker'], |
| 112 | 111 |
factory=factory)] |
| 113 | 112 |
|
| ... | ... |
@@ -135,16 +135,17 @@ sudo('curl -s https://phantomjs.googlecode.com/files/'
|
| 135 | 135 |
'phantomjs-1.9.1-linux-x86_64.tar.bz2 | tar jx -C /usr/bin' |
| 136 | 136 |
' --strip-components=2 phantomjs-1.9.1-linux-x86_64/bin/phantomjs') |
| 137 | 137 |
|
| 138 |
-#### FIXME. Temporarily install docker with proper apparmor handling |
|
| 139 |
-sudo('stop docker')
|
|
| 140 |
-sudo('wget -q -O /usr/bin/docker http://test.docker.io/test/docker')
|
|
| 141 |
-sudo('start docker')
|
|
| 138 |
+# Preventively reboot docker-ci daily |
|
| 139 |
+sudo('ln -s /sbin/reboot /etc/cron.daily')
|
|
| 142 | 140 |
|
| 143 | 141 |
# Build docker-ci containers |
| 144 | 142 |
sudo('cd {}; docker build -t docker .'.format(DOCKER_PATH))
|
| 145 | 143 |
sudo('cd {}/nightlyrelease; docker build -t dockerbuilder .'.format(
|
| 146 | 144 |
DOCKER_CI_PATH)) |
| 147 | 145 |
|
| 146 |
+# Download docker-ci testing container |
|
| 147 |
+sudo('docker pull mzdaniel/test_docker')
|
|
| 148 |
+ |
|
| 148 | 149 |
# Setup buildbot |
| 149 | 150 |
sudo('mkdir /data')
|
| 150 | 151 |
sudo('{0}/setup.sh root {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10}'
|
| 151 | 152 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,30 @@ |
| 0 |
+# VERSION: 0.3 |
|
| 1 |
+# DOCKER-VERSION 0.6.3 |
|
| 2 |
+# AUTHOR: Daniel Mizyrycki <daniel@dotcloud.com> |
|
| 3 |
+# DESCRIPTION: Testing docker PRs and commits on top of master using |
|
| 4 |
+# REFERENCES: This code reuses the excellent implementation of |
|
| 5 |
+# Docker in Docker made by Jerome Petazzoni. |
|
| 6 |
+# https://github.com/jpetazzo/dind |
|
| 7 |
+# COMMENTS: |
|
| 8 |
+# This Dockerfile adapts /Dockerfile to enable docker PRs and commits testing |
|
| 9 |
+# Optional arguments: |
|
| 10 |
+# [commit] (default: 'HEAD') |
|
| 11 |
+# [repo] (default: 'http://github.com/dotcloud/docker') |
|
| 12 |
+# [branch] (default: 'master') |
|
| 13 |
+# TO_BUILD: docker build -t test_docker . |
|
| 14 |
+# TO_RUN: docker run -privileged test_docker hack/dind test_docker.sh [commit] [repo] [branch] |
|
| 15 |
+ |
|
| 16 |
+from docker |
|
| 17 |
+maintainer Daniel Mizyrycki <daniel@dotcloud.com> |
|
| 18 |
+ |
|
| 19 |
+# Setup go environment. Extracted from /Dockerfile |
|
| 20 |
+env CGO_ENABLED 0 |
|
| 21 |
+env GOROOT /goroot |
|
| 22 |
+env PATH $PATH:/goroot/bin |
|
| 23 |
+env GOPATH /go:/go/src/github.com/dotcloud/docker/vendor |
|
| 24 |
+volume /var/lib/docker |
|
| 25 |
+workdir /go/src/github.com/dotcloud/docker |
|
| 26 |
+ |
|
| 27 |
+# Add test_docker.sh |
|
| 28 |
+add test_docker.sh /usr/bin/test_docker.sh |
|
| 29 |
+run chmod +x /usr/bin/test_docker.sh |
| ... | ... |
@@ -5,17 +5,11 @@ COMMIT=${1-HEAD}
|
| 5 | 5 |
REPO=${2-http://github.com/dotcloud/docker}
|
| 6 | 6 |
BRANCH=${3-master}
|
| 7 | 7 |
|
| 8 |
-# Generate a random string of $1 characters |
|
| 9 |
-function random {
|
|
| 10 |
- cat /dev/urandom | tr -cd 'a-f0-9' | head -c $1 |
|
| 11 |
-} |
|
| 12 |
- |
|
| 13 | 8 |
# Compute test paths |
| 14 |
-BASE_PATH=`pwd`/test_docker_$(random 12) |
|
| 15 |
-DOCKER_PATH=$BASE_PATH/go/src/github.com/dotcloud/docker |
|
| 16 |
-export GOPATH=$BASE_PATH/go:$DOCKER_PATH/vendor |
|
| 9 |
+DOCKER_PATH=/go/src/github.com/dotcloud/docker |
|
| 17 | 10 |
|
| 18 | 11 |
# Fetch latest master |
| 12 |
+rm -rf /go |
|
| 19 | 13 |
mkdir -p $DOCKER_PATH |
| 20 | 14 |
cd $DOCKER_PATH |
| 21 | 15 |
git init . |
| ... | ... |
@@ -23,12 +17,21 @@ git fetch -q http://github.com/dotcloud/docker master |
| 23 | 23 |
git reset --hard FETCH_HEAD |
| 24 | 24 |
|
| 25 | 25 |
# Merge commit |
| 26 |
+#echo FIXME. Temporarily skip TestPrivilegedCanMount until DinD works reliable on AWS |
|
| 27 |
+git pull -q https://github.com/mzdaniel/docker.git dind-aws || exit 1 |
|
| 28 |
+ |
|
| 29 |
+# Merge commit in top of master |
|
| 26 | 30 |
git fetch -q "$REPO" "$BRANCH" |
| 27 | 31 |
git merge --no-edit $COMMIT || exit 1 |
| 28 | 32 |
|
| 29 | 33 |
# Test commit |
| 30 | 34 |
go test -v; exit_status=$? |
| 31 | 35 |
|
| 36 |
+# Display load if test fails |
|
| 37 |
+if [ $exit_status -eq 1 ] ; then |
|
| 38 |
+ uptime; echo; free |
|
| 39 |
+fi |
|
| 40 |
+ |
|
| 32 | 41 |
# Cleanup testing directory |
| 33 | 42 |
rm -rf $BASE_PATH |
| 34 | 43 |
|
| ... | ... |
@@ -11,7 +11,7 @@ |
| 11 | 11 |
# "GPG_PASSPHRASE='Test_docker_GPG_passphrase_signature' |
| 12 | 12 |
# "INDEX_AUTH='Encripted_index_authentication' } |
| 13 | 13 |
# TO_BUILD: docker build -t dockerbuilder . |
| 14 |
-# TO_RELEASE: docker run -i -t -privileged -lxc-conf="lxc.aa_profile = unconfined" -e AWS_S3_BUCKET="test.docker.io" dockerbuilder |
|
| 14 |
+# TO_RELEASE: docker run -i -t -privileged -e AWS_S3_BUCKET="test.docker.io" dockerbuilder |
|
| 15 | 15 |
|
| 16 | 16 |
from docker |
| 17 | 17 |
maintainer Daniel Mizyrycki <daniel@dotcloud.com> |
| ... | ... |
@@ -23,9 +23,6 @@ run apt-get update; apt-get install -y -q wget python2.7 |
| 23 | 23 |
# Add production docker binary |
| 24 | 24 |
run wget -q -O /usr/bin/docker http://get.docker.io/builds/Linux/x86_64/docker-latest; chmod +x /usr/bin/docker |
| 25 | 25 |
|
| 26 |
-#### FIXME. Temporarily install docker with proper apparmor handling |
|
| 27 |
-run wget -q -O /usr/bin/docker http://test.docker.io/test/docker; chmod +x /usr/bin/docker |
|
| 28 |
- |
|
| 29 | 26 |
# Add proto docker builder |
| 30 | 27 |
add ./dockerbuild /usr/bin/dockerbuild |
| 31 | 28 |
run chmod +x /usr/bin/dockerbuild |
| ... | ... |
@@ -13,10 +13,6 @@ cd / |
| 13 | 13 |
git clone -q http://github.com/dotcloud/docker /go/src/github.com/dotcloud/docker |
| 14 | 14 |
cd /go/src/github.com/dotcloud/docker |
| 15 | 15 |
|
| 16 |
-echo FIXME. Temporarily add Jerome changeset with proper apparmor handling |
|
| 17 |
-git fetch http://github.com/jpetazzo/docker escape-apparmor-confinement:escape-apparmor-confinement |
|
| 18 |
-git rebase --onto master master escape-apparmor-confinement |
|
| 19 |
- |
|
| 20 | 16 |
# Launch docker daemon using dind inside the container |
| 21 | 17 |
./hack/dind /usr/bin/docker -d & |
| 22 | 18 |
sleep 5 |
| ... | ... |
@@ -28,7 +24,17 @@ date > timestamp |
| 28 | 28 |
docker build -t docker . |
| 29 | 29 |
|
| 30 | 30 |
# Run Docker unittests binary and Ubuntu package |
| 31 |
-docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh || exit 1 |
|
| 31 |
+docker run -privileged docker hack/make.sh |
|
| 32 |
+exit_status=$? |
|
| 33 |
+ |
|
| 34 |
+# Display load if test fails |
|
| 35 |
+if [ $exit_status -eq 1 ] ; then |
|
| 36 |
+ uptime; echo; free |
|
| 37 |
+ exit 1 |
|
| 38 |
+fi |
|
| 39 |
+ |
|
| 40 |
+# Commit binary and ubuntu bundles for release |
|
| 41 |
+docker commit -run '{"Env": ["PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"], "WorkingDir": "/go/src/github.com/dotcloud/docker"}' $(docker ps -l -q) release
|
|
| 32 | 42 |
|
| 33 | 43 |
# Turn debug off to load credentials from the environment |
| 34 | 44 |
set +x |
| ... | ... |
@@ -36,7 +42,6 @@ eval $(cat /root/release_credentials.json | python -c ' |
| 36 | 36 |
import sys,json,base64; |
| 37 | 37 |
d=json.loads(base64.b64decode(sys.stdin.read())); |
| 38 | 38 |
exec("""for k in d: print "export {0}=\\"{1}\\"".format(k,d[k])""")')
|
| 39 |
-echo '{"https://index.docker.io/v1/":{"auth":"'$INDEX_AUTH'","email":"engineering@dotcloud.com"}}' > /.dockercfg
|
|
| 40 | 39 |
set -x |
| 41 | 40 |
|
| 42 | 41 |
# Push docker nightly |
| ... | ... |
@@ -5,6 +5,7 @@ import ( |
| 5 | 5 |
"encoding/hex" |
| 6 | 6 |
"encoding/json" |
| 7 | 7 |
"fmt" |
| 8 |
+ "github.com/dotcloud/docker/archive" |
|
| 8 | 9 |
"github.com/dotcloud/docker/utils" |
| 9 | 10 |
"io" |
| 10 | 11 |
"io/ioutil" |
| ... | ... |
@@ -72,7 +73,7 @@ func LoadImage(root string) (*Image, error) {
|
| 72 | 72 |
return img, nil |
| 73 | 73 |
} |
| 74 | 74 |
|
| 75 |
-func StoreImage(img *Image, jsonData []byte, layerData Archive, root string) error {
|
|
| 75 |
+func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root string) error {
|
|
| 76 | 76 |
// Check that root doesn't already exist |
| 77 | 77 |
if _, err := os.Stat(root); err == nil {
|
| 78 | 78 |
return fmt.Errorf("Image %s already exists", img.ID)
|
| ... | ... |
@@ -89,7 +90,7 @@ func StoreImage(img *Image, jsonData []byte, layerData Archive, root string) err |
| 89 | 89 |
if layerData != nil {
|
| 90 | 90 |
start := time.Now() |
| 91 | 91 |
utils.Debugf("Start untar layer")
|
| 92 |
- if err := Untar(layerData, layer); err != nil {
|
|
| 92 |
+ if err := archive.Untar(layerData, layer); err != nil {
|
|
| 93 | 93 |
return err |
| 94 | 94 |
} |
| 95 | 95 |
utils.Debugf("Untar time: %vs", time.Now().Sub(start).Seconds())
|
| ... | ... |
@@ -162,12 +163,12 @@ func MountAUFS(ro []string, rw string, target string) error {
|
| 162 | 162 |
} |
| 163 | 163 |
|
| 164 | 164 |
// TarLayer returns a tar archive of the image's filesystem layer. |
| 165 |
-func (image *Image) TarLayer(compression Compression) (Archive, error) {
|
|
| 165 |
+func (image *Image) TarLayer(compression archive.Compression) (archive.Archive, error) {
|
|
| 166 | 166 |
layerPath, err := image.layer() |
| 167 | 167 |
if err != nil {
|
| 168 | 168 |
return nil, err |
| 169 | 169 |
} |
| 170 |
- return Tar(layerPath, compression) |
|
| 170 |
+ return archive.Tar(layerPath, compression) |
|
| 171 | 171 |
} |
| 172 | 172 |
|
| 173 | 173 |
func (image *Image) Mount(root, rw string) error {
|
| ... | ... |
@@ -11,7 +11,6 @@ lxc.utsname = {{.Config.Hostname}}
|
| 11 | 11 |
{{else}}
|
| 12 | 12 |
lxc.utsname = {{.Id}}
|
| 13 | 13 |
{{end}}
|
| 14 |
-#lxc.aa_profile = unconfined |
|
| 15 | 14 |
|
| 16 | 15 |
{{if .Config.NetworkDisabled}}
|
| 17 | 16 |
# network is disabled (-n=false) |
| ... | ... |
@@ -46,7 +45,7 @@ lxc.console = none |
| 46 | 46 |
# no controlling tty at all |
| 47 | 47 |
lxc.tty = 1 |
| 48 | 48 |
|
| 49 |
-{{if .Config.Privileged}}
|
|
| 49 |
+{{if (getHostConfig .).Privileged}}
|
|
| 50 | 50 |
lxc.cgroup.devices.allow = a |
| 51 | 51 |
{{else}}
|
| 52 | 52 |
# no implicit access to devices |
| ... | ... |
@@ -66,7 +65,7 @@ lxc.cgroup.devices.allow = c 4:1 rwm |
| 66 | 66 |
lxc.cgroup.devices.allow = c 1:9 rwm |
| 67 | 67 |
lxc.cgroup.devices.allow = c 1:8 rwm |
| 68 | 68 |
|
| 69 |
-# /dev/pts/* - pts namespaces are "coming soon" |
|
| 69 |
+# /dev/pts/ - pts namespaces are "coming soon" |
|
| 70 | 70 |
lxc.cgroup.devices.allow = c 136:* rwm |
| 71 | 71 |
lxc.cgroup.devices.allow = c 5:2 rwm |
| 72 | 72 |
|
| ... | ... |
@@ -97,6 +96,9 @@ lxc.mount.entry = shm {{$ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec
|
| 97 | 97 |
# Inject dockerinit |
| 98 | 98 |
lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/.dockerinit none bind,ro 0 0
|
| 99 | 99 |
|
| 100 |
+# Inject env |
|
| 101 |
+lxc.mount.entry = {{.EnvConfigPath}} {{$ROOTFS}}/.dockerenv none bind,ro 0 0
|
|
| 102 |
+ |
|
| 100 | 103 |
# In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container |
| 101 | 104 |
lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
|
| 102 | 105 |
{{if .Volumes}}
|
| ... | ... |
@@ -106,8 +108,13 @@ lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,{{ if ind
|
| 106 | 106 |
{{end}}
|
| 107 | 107 |
{{end}}
|
| 108 | 108 |
|
| 109 |
-{{if .Config.Privileged}}
|
|
| 109 |
+{{if (getHostConfig .).Privileged}}
|
|
| 110 | 110 |
# retain all capabilities; no lxc.cap.drop line |
| 111 |
+{{if (getCapabilities .).AppArmor}}
|
|
| 112 |
+lxc.aa_profile = unconfined |
|
| 113 |
+{{else}}
|
|
| 114 |
+#lxc.aa_profile = unconfined |
|
| 115 |
+{{end}}
|
|
| 111 | 116 |
{{else}}
|
| 112 | 117 |
# drop linux capabilities (apply mainly to the user root in the container) |
| 113 | 118 |
# (Note: 'lxc.cap.keep' is coming soon and should replace this under the |
| ... | ... |
@@ -127,18 +134,15 @@ lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
|
| 127 | 127 |
{{if .Config.CpuShares}}
|
| 128 | 128 |
lxc.cgroup.cpu.shares = {{.Config.CpuShares}}
|
| 129 | 129 |
{{end}}
|
| 130 |
-` |
|
| 131 | 130 |
|
| 132 |
-const LxcHostConfigTemplate = ` |
|
| 133 |
-{{if .LxcConf}}
|
|
| 134 |
-{{range $pair := .LxcConf}}
|
|
| 131 |
+{{if (getHostConfig .).LxcConf}}
|
|
| 132 |
+{{range $pair := (getHostConfig .).LxcConf}}
|
|
| 135 | 133 |
{{$pair.Key}} = {{$pair.Value}}
|
| 136 | 134 |
{{end}}
|
| 137 | 135 |
{{end}}
|
| 138 | 136 |
` |
| 139 | 137 |
|
| 140 | 138 |
var LxcTemplateCompiled *template.Template |
| 141 |
-var LxcHostConfigTemplateCompiled *template.Template |
|
| 142 | 139 |
|
| 143 | 140 |
func getMemorySwap(config *Config) int64 {
|
| 144 | 141 |
// By default, MemorySwap is set to twice the size of RAM. |
| ... | ... |
@@ -149,17 +153,23 @@ func getMemorySwap(config *Config) int64 {
|
| 149 | 149 |
return config.Memory * 2 |
| 150 | 150 |
} |
| 151 | 151 |
|
| 152 |
+func getHostConfig(container *Container) *HostConfig {
|
|
| 153 |
+ return container.hostConfig |
|
| 154 |
+} |
|
| 155 |
+ |
|
| 156 |
+func getCapabilities(container *Container) *Capabilities {
|
|
| 157 |
+ return container.runtime.capabilities |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 152 | 160 |
func init() {
|
| 153 | 161 |
var err error |
| 154 | 162 |
funcMap := template.FuncMap{
|
| 155 |
- "getMemorySwap": getMemorySwap, |
|
| 163 |
+ "getMemorySwap": getMemorySwap, |
|
| 164 |
+ "getHostConfig": getHostConfig, |
|
| 165 |
+ "getCapabilities": getCapabilities, |
|
| 156 | 166 |
} |
| 157 | 167 |
LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
|
| 158 | 168 |
if err != nil {
|
| 159 | 169 |
panic(err) |
| 160 | 170 |
} |
| 161 |
- LxcHostConfigTemplateCompiled, err = template.New("lxc-hostconfig").Funcs(funcMap).Parse(LxcHostConfigTemplate)
|
|
| 162 |
- if err != nil {
|
|
| 163 |
- panic(err) |
|
| 164 |
- } |
|
| 165 | 171 |
} |
| ... | ... |
@@ -11,8 +11,8 @@ type NameChecker interface {
|
| 11 | 11 |
} |
| 12 | 12 |
|
| 13 | 13 |
var ( |
| 14 |
- colors = [...]string{"white", "silver", "gray", "black", "blue", "green", "cyan", "yellow", "gold", "orange", "brown", "red", "violet", "pink", "magenta", "purple"}
|
|
| 15 |
- animals = [...]string{"ant", "bird", "cat", "chicken", "cow", "dog", "fish", "fox", "horse", "lion", "monkey", "pig", "sheep", "tiger", "whale", "wolf"}
|
|
| 14 |
+ colors = [...]string{"white", "silver", "gray", "black", "blue", "green", "cyan", "yellow", "gold", "orange", "brown", "red", "violet", "pink", "magenta", "purple", "maroon", "crimson", "plum", "fuchsia", "lavender", "slate", "navy", "azure", "aqua", "olive", "teal", "lime", "beige", "tan", "sienna"}
|
|
| 15 |
+ animals = [...]string{"ant", "bear", "bird", "cat", "chicken", "cow", "deer", "dog", "donkey", "duck", "fish", "fox", "frog", "horse", "kangaroo", "koala", "lemur", "lion", "lizard", "monkey", "octopus", "pig", "shark", "sheep", "sloth", "spider", "squirrel", "tiger", "toad", "weasel", "whale", "wolf"}
|
|
| 16 | 16 |
) |
| 17 | 17 |
|
| 18 | 18 |
func GenerateRandomName(checker NameChecker) (string, error) {
|
| 19 | 19 |
deleted file mode 100644 |
| ... | ... |
@@ -1,548 +0,0 @@ |
| 1 |
-package netlink |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/binary" |
|
| 5 |
- "fmt" |
|
| 6 |
- "net" |
|
| 7 |
- "syscall" |
|
| 8 |
- "unsafe" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-var nextSeqNr int |
|
| 12 |
- |
|
| 13 |
-func nativeEndian() binary.ByteOrder {
|
|
| 14 |
- var x uint32 = 0x01020304 |
|
| 15 |
- if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
|
| 16 |
- return binary.BigEndian |
|
| 17 |
- } |
|
| 18 |
- return binary.LittleEndian |
|
| 19 |
-} |
|
| 20 |
- |
|
| 21 |
-func getSeq() int {
|
|
| 22 |
- nextSeqNr = nextSeqNr + 1 |
|
| 23 |
- return nextSeqNr |
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 |
-func getIpFamily(ip net.IP) int {
|
|
| 27 |
- if len(ip) <= net.IPv4len {
|
|
| 28 |
- return syscall.AF_INET |
|
| 29 |
- } |
|
| 30 |
- if ip.To4() != nil {
|
|
| 31 |
- return syscall.AF_INET |
|
| 32 |
- } |
|
| 33 |
- return syscall.AF_INET6 |
|
| 34 |
-} |
|
| 35 |
- |
|
| 36 |
-type NetlinkRequestData interface {
|
|
| 37 |
- ToWireFormat() []byte |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-type IfInfomsg struct {
|
|
| 41 |
- syscall.IfInfomsg |
|
| 42 |
-} |
|
| 43 |
- |
|
| 44 |
-func newIfInfomsg(family int) *IfInfomsg {
|
|
| 45 |
- msg := &IfInfomsg{}
|
|
| 46 |
- msg.Family = uint8(family) |
|
| 47 |
- msg.Type = uint16(0) |
|
| 48 |
- msg.Index = int32(0) |
|
| 49 |
- msg.Flags = uint32(0) |
|
| 50 |
- msg.Change = uint32(0) |
|
| 51 |
- |
|
| 52 |
- return msg |
|
| 53 |
-} |
|
| 54 |
- |
|
| 55 |
-func (msg *IfInfomsg) ToWireFormat() []byte {
|
|
| 56 |
- native := nativeEndian() |
|
| 57 |
- |
|
| 58 |
- len := syscall.SizeofIfInfomsg |
|
| 59 |
- b := make([]byte, len) |
|
| 60 |
- b[0] = msg.Family |
|
| 61 |
- b[1] = 0 |
|
| 62 |
- native.PutUint16(b[2:4], msg.Type) |
|
| 63 |
- native.PutUint32(b[4:8], uint32(msg.Index)) |
|
| 64 |
- native.PutUint32(b[8:12], msg.Flags) |
|
| 65 |
- native.PutUint32(b[12:16], msg.Change) |
|
| 66 |
- return b |
|
| 67 |
-} |
|
| 68 |
- |
|
| 69 |
-type IfAddrmsg struct {
|
|
| 70 |
- syscall.IfAddrmsg |
|
| 71 |
-} |
|
| 72 |
- |
|
| 73 |
-func newIfAddrmsg(family int) *IfAddrmsg {
|
|
| 74 |
- msg := &IfAddrmsg{}
|
|
| 75 |
- msg.Family = uint8(family) |
|
| 76 |
- msg.Prefixlen = uint8(0) |
|
| 77 |
- msg.Flags = uint8(0) |
|
| 78 |
- msg.Scope = uint8(0) |
|
| 79 |
- msg.Index = uint32(0) |
|
| 80 |
- |
|
| 81 |
- return msg |
|
| 82 |
-} |
|
| 83 |
- |
|
| 84 |
-func (msg *IfAddrmsg) ToWireFormat() []byte {
|
|
| 85 |
- native := nativeEndian() |
|
| 86 |
- |
|
| 87 |
- len := syscall.SizeofIfAddrmsg |
|
| 88 |
- b := make([]byte, len) |
|
| 89 |
- b[0] = msg.Family |
|
| 90 |
- b[1] = msg.Prefixlen |
|
| 91 |
- b[2] = msg.Flags |
|
| 92 |
- b[3] = msg.Scope |
|
| 93 |
- native.PutUint32(b[4:8], msg.Index) |
|
| 94 |
- return b |
|
| 95 |
-} |
|
| 96 |
- |
|
| 97 |
-type RtMsg struct {
|
|
| 98 |
- syscall.RtMsg |
|
| 99 |
-} |
|
| 100 |
- |
|
| 101 |
-func newRtMsg(family int) *RtMsg {
|
|
| 102 |
- msg := &RtMsg{}
|
|
| 103 |
- msg.Family = uint8(family) |
|
| 104 |
- msg.Table = syscall.RT_TABLE_MAIN |
|
| 105 |
- msg.Scope = syscall.RT_SCOPE_UNIVERSE |
|
| 106 |
- msg.Protocol = syscall.RTPROT_BOOT |
|
| 107 |
- msg.Type = syscall.RTN_UNICAST |
|
| 108 |
- |
|
| 109 |
- return msg |
|
| 110 |
-} |
|
| 111 |
- |
|
| 112 |
-func (msg *RtMsg) ToWireFormat() []byte {
|
|
| 113 |
- native := nativeEndian() |
|
| 114 |
- |
|
| 115 |
- len := syscall.SizeofRtMsg |
|
| 116 |
- b := make([]byte, len) |
|
| 117 |
- b[0] = msg.Family |
|
| 118 |
- b[1] = msg.Dst_len |
|
| 119 |
- b[2] = msg.Src_len |
|
| 120 |
- b[3] = msg.Tos |
|
| 121 |
- b[4] = msg.Table |
|
| 122 |
- b[5] = msg.Protocol |
|
| 123 |
- b[6] = msg.Scope |
|
| 124 |
- b[7] = msg.Type |
|
| 125 |
- native.PutUint32(b[8:12], msg.Flags) |
|
| 126 |
- return b |
|
| 127 |
-} |
|
| 128 |
- |
|
| 129 |
-func rtaAlignOf(attrlen int) int {
|
|
| 130 |
- return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1) |
|
| 131 |
-} |
|
| 132 |
- |
|
| 133 |
-type RtAttr struct {
|
|
| 134 |
- syscall.RtAttr |
|
| 135 |
- Data []byte |
|
| 136 |
-} |
|
| 137 |
- |
|
| 138 |
-func newRtAttr(attrType int, data []byte) *RtAttr {
|
|
| 139 |
- attr := &RtAttr{}
|
|
| 140 |
- attr.Type = uint16(attrType) |
|
| 141 |
- attr.Data = data |
|
| 142 |
- |
|
| 143 |
- return attr |
|
| 144 |
-} |
|
| 145 |
- |
|
| 146 |
-func (attr *RtAttr) ToWireFormat() []byte {
|
|
| 147 |
- native := nativeEndian() |
|
| 148 |
- |
|
| 149 |
- len := syscall.SizeofRtAttr + len(attr.Data) |
|
| 150 |
- b := make([]byte, rtaAlignOf(len)) |
|
| 151 |
- native.PutUint16(b[0:2], uint16(len)) |
|
| 152 |
- native.PutUint16(b[2:4], attr.Type) |
|
| 153 |
- for i, d := range attr.Data {
|
|
| 154 |
- b[4+i] = d |
|
| 155 |
- } |
|
| 156 |
- |
|
| 157 |
- return b |
|
| 158 |
-} |
|
| 159 |
- |
|
| 160 |
-type NetlinkRequest struct {
|
|
| 161 |
- syscall.NlMsghdr |
|
| 162 |
- Data []NetlinkRequestData |
|
| 163 |
-} |
|
| 164 |
- |
|
| 165 |
-func (rr *NetlinkRequest) ToWireFormat() []byte {
|
|
| 166 |
- native := nativeEndian() |
|
| 167 |
- |
|
| 168 |
- length := rr.Len |
|
| 169 |
- dataBytes := make([][]byte, len(rr.Data)) |
|
| 170 |
- for i, data := range rr.Data {
|
|
| 171 |
- dataBytes[i] = data.ToWireFormat() |
|
| 172 |
- length = length + uint32(len(dataBytes[i])) |
|
| 173 |
- } |
|
| 174 |
- b := make([]byte, length) |
|
| 175 |
- native.PutUint32(b[0:4], length) |
|
| 176 |
- native.PutUint16(b[4:6], rr.Type) |
|
| 177 |
- native.PutUint16(b[6:8], rr.Flags) |
|
| 178 |
- native.PutUint32(b[8:12], rr.Seq) |
|
| 179 |
- native.PutUint32(b[12:16], rr.Pid) |
|
| 180 |
- |
|
| 181 |
- i := 16 |
|
| 182 |
- for _, data := range dataBytes {
|
|
| 183 |
- for _, dataByte := range data {
|
|
| 184 |
- b[i] = dataByte |
|
| 185 |
- i = i + 1 |
|
| 186 |
- } |
|
| 187 |
- } |
|
| 188 |
- return b |
|
| 189 |
-} |
|
| 190 |
- |
|
| 191 |
-func (rr *NetlinkRequest) AddData(data NetlinkRequestData) {
|
|
| 192 |
- rr.Data = append(rr.Data, data) |
|
| 193 |
-} |
|
| 194 |
- |
|
| 195 |
-func newNetlinkRequest(proto, flags int) *NetlinkRequest {
|
|
| 196 |
- rr := &NetlinkRequest{}
|
|
| 197 |
- rr.Len = uint32(syscall.NLMSG_HDRLEN) |
|
| 198 |
- rr.Type = uint16(proto) |
|
| 199 |
- rr.Flags = syscall.NLM_F_REQUEST | uint16(flags) |
|
| 200 |
- rr.Seq = uint32(getSeq()) |
|
| 201 |
- return rr |
|
| 202 |
-} |
|
| 203 |
- |
|
| 204 |
-type NetlinkSocket struct {
|
|
| 205 |
- fd int |
|
| 206 |
- lsa syscall.SockaddrNetlink |
|
| 207 |
-} |
|
| 208 |
- |
|
| 209 |
-func getNetlinkSocket() (*NetlinkSocket, error) {
|
|
| 210 |
- fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE) |
|
| 211 |
- if err != nil {
|
|
| 212 |
- return nil, err |
|
| 213 |
- } |
|
| 214 |
- s := &NetlinkSocket{
|
|
| 215 |
- fd: fd, |
|
| 216 |
- } |
|
| 217 |
- s.lsa.Family = syscall.AF_NETLINK |
|
| 218 |
- if err := syscall.Bind(fd, &s.lsa); err != nil {
|
|
| 219 |
- syscall.Close(fd) |
|
| 220 |
- return nil, err |
|
| 221 |
- } |
|
| 222 |
- |
|
| 223 |
- return s, nil |
|
| 224 |
-} |
|
| 225 |
- |
|
| 226 |
-func (s *NetlinkSocket) Close() {
|
|
| 227 |
- syscall.Close(s.fd) |
|
| 228 |
-} |
|
| 229 |
- |
|
| 230 |
-func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
|
|
| 231 |
- if err := syscall.Sendto(s.fd, request.ToWireFormat(), 0, &s.lsa); err != nil {
|
|
| 232 |
- return err |
|
| 233 |
- } |
|
| 234 |
- return nil |
|
| 235 |
-} |
|
| 236 |
- |
|
| 237 |
-func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) {
|
|
| 238 |
- rb := make([]byte, syscall.Getpagesize()) |
|
| 239 |
- nr, _, err := syscall.Recvfrom(s.fd, rb, 0) |
|
| 240 |
- if err != nil {
|
|
| 241 |
- return nil, err |
|
| 242 |
- } |
|
| 243 |
- if nr < syscall.NLMSG_HDRLEN {
|
|
| 244 |
- return nil, fmt.Errorf("Got short response from netlink")
|
|
| 245 |
- } |
|
| 246 |
- rb = rb[:nr] |
|
| 247 |
- return syscall.ParseNetlinkMessage(rb) |
|
| 248 |
-} |
|
| 249 |
- |
|
| 250 |
-func (s *NetlinkSocket) GetPid() (uint32, error) {
|
|
| 251 |
- lsa, err := syscall.Getsockname(s.fd) |
|
| 252 |
- if err != nil {
|
|
| 253 |
- return 0, err |
|
| 254 |
- } |
|
| 255 |
- switch v := lsa.(type) {
|
|
| 256 |
- case *syscall.SockaddrNetlink: |
|
| 257 |
- return v.Pid, nil |
|
| 258 |
- } |
|
| 259 |
- return 0, fmt.Errorf("Wrong socket type")
|
|
| 260 |
-} |
|
| 261 |
- |
|
| 262 |
-func (s *NetlinkSocket) HandleAck(seq uint32) error {
|
|
| 263 |
- native := nativeEndian() |
|
| 264 |
- |
|
| 265 |
- pid, err := s.GetPid() |
|
| 266 |
- if err != nil {
|
|
| 267 |
- return err |
|
| 268 |
- } |
|
| 269 |
- |
|
| 270 |
-done: |
|
| 271 |
- for {
|
|
| 272 |
- msgs, err := s.Recieve() |
|
| 273 |
- if err != nil {
|
|
| 274 |
- return err |
|
| 275 |
- } |
|
| 276 |
- for _, m := range msgs {
|
|
| 277 |
- if m.Header.Seq != seq {
|
|
| 278 |
- return fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, seq)
|
|
| 279 |
- } |
|
| 280 |
- if m.Header.Pid != pid {
|
|
| 281 |
- return fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
|
|
| 282 |
- } |
|
| 283 |
- if m.Header.Type == syscall.NLMSG_DONE {
|
|
| 284 |
- break done |
|
| 285 |
- } |
|
| 286 |
- if m.Header.Type == syscall.NLMSG_ERROR {
|
|
| 287 |
- error := int32(native.Uint32(m.Data[0:4])) |
|
| 288 |
- if error == 0 {
|
|
| 289 |
- break done |
|
| 290 |
- } |
|
| 291 |
- return syscall.Errno(-error) |
|
| 292 |
- } |
|
| 293 |
- } |
|
| 294 |
- } |
|
| 295 |
- |
|
| 296 |
- return nil |
|
| 297 |
-} |
|
| 298 |
- |
|
| 299 |
-// Add a new default gateway. Identical to: |
|
| 300 |
-// ip route add default via $ip |
|
| 301 |
-func AddDefaultGw(ip net.IP) error {
|
|
| 302 |
- s, err := getNetlinkSocket() |
|
| 303 |
- if err != nil {
|
|
| 304 |
- return err |
|
| 305 |
- } |
|
| 306 |
- defer s.Close() |
|
| 307 |
- |
|
| 308 |
- family := getIpFamily(ip) |
|
| 309 |
- |
|
| 310 |
- wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) |
|
| 311 |
- |
|
| 312 |
- msg := newRtMsg(family) |
|
| 313 |
- wb.AddData(msg) |
|
| 314 |
- |
|
| 315 |
- var ipData []byte |
|
| 316 |
- if family == syscall.AF_INET {
|
|
| 317 |
- ipData = ip.To4() |
|
| 318 |
- } else {
|
|
| 319 |
- ipData = ip.To16() |
|
| 320 |
- } |
|
| 321 |
- |
|
| 322 |
- gateway := newRtAttr(syscall.RTA_GATEWAY, ipData) |
|
| 323 |
- |
|
| 324 |
- wb.AddData(gateway) |
|
| 325 |
- |
|
| 326 |
- if err := s.Send(wb); err != nil {
|
|
| 327 |
- return err |
|
| 328 |
- } |
|
| 329 |
- |
|
| 330 |
- return s.HandleAck(wb.Seq) |
|
| 331 |
- |
|
| 332 |
-} |
|
| 333 |
- |
|
| 334 |
-// Bring up a particular network interface |
|
| 335 |
-func NetworkLinkUp(iface *net.Interface) error {
|
|
| 336 |
- s, err := getNetlinkSocket() |
|
| 337 |
- if err != nil {
|
|
| 338 |
- return err |
|
| 339 |
- } |
|
| 340 |
- defer s.Close() |
|
| 341 |
- |
|
| 342 |
- wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) |
|
| 343 |
- |
|
| 344 |
- msg := newIfInfomsg(syscall.AF_UNSPEC) |
|
| 345 |
- msg.Change = syscall.IFF_UP |
|
| 346 |
- msg.Flags = syscall.IFF_UP |
|
| 347 |
- msg.Index = int32(iface.Index) |
|
| 348 |
- wb.AddData(msg) |
|
| 349 |
- |
|
| 350 |
- if err := s.Send(wb); err != nil {
|
|
| 351 |
- return err |
|
| 352 |
- } |
|
| 353 |
- |
|
| 354 |
- return s.HandleAck(wb.Seq) |
|
| 355 |
-} |
|
| 356 |
- |
|
| 357 |
-// Add an Ip address to an interface. This is identical to: |
|
| 358 |
-// ip addr add $ip/$ipNet dev $iface |
|
| 359 |
-func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
|
| 360 |
- s, err := getNetlinkSocket() |
|
| 361 |
- if err != nil {
|
|
| 362 |
- return err |
|
| 363 |
- } |
|
| 364 |
- defer s.Close() |
|
| 365 |
- |
|
| 366 |
- family := getIpFamily(ip) |
|
| 367 |
- |
|
| 368 |
- wb := newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) |
|
| 369 |
- |
|
| 370 |
- msg := newIfAddrmsg(family) |
|
| 371 |
- msg.Index = uint32(iface.Index) |
|
| 372 |
- prefixLen, _ := ipNet.Mask.Size() |
|
| 373 |
- msg.Prefixlen = uint8(prefixLen) |
|
| 374 |
- wb.AddData(msg) |
|
| 375 |
- |
|
| 376 |
- var ipData []byte |
|
| 377 |
- if family == syscall.AF_INET {
|
|
| 378 |
- ipData = ip.To4() |
|
| 379 |
- } else {
|
|
| 380 |
- ipData = ip.To16() |
|
| 381 |
- } |
|
| 382 |
- |
|
| 383 |
- localData := newRtAttr(syscall.IFA_LOCAL, ipData) |
|
| 384 |
- wb.AddData(localData) |
|
| 385 |
- |
|
| 386 |
- addrData := newRtAttr(syscall.IFA_ADDRESS, ipData) |
|
| 387 |
- wb.AddData(addrData) |
|
| 388 |
- |
|
| 389 |
- if err := s.Send(wb); err != nil {
|
|
| 390 |
- return err |
|
| 391 |
- } |
|
| 392 |
- |
|
| 393 |
- return s.HandleAck(wb.Seq) |
|
| 394 |
-} |
|
| 395 |
- |
|
| 396 |
-func zeroTerminated(s string) []byte {
|
|
| 397 |
- bytes := make([]byte, len(s)+1) |
|
| 398 |
- for i := 0; i < len(s); i++ {
|
|
| 399 |
- bytes[i] = s[i] |
|
| 400 |
- } |
|
| 401 |
- bytes[len(s)] = 0 |
|
| 402 |
- return bytes |
|
| 403 |
-} |
|
| 404 |
- |
|
| 405 |
-func nonZeroTerminated(s string) []byte {
|
|
| 406 |
- bytes := make([]byte, len(s)) |
|
| 407 |
- for i := 0; i < len(s); i++ {
|
|
| 408 |
- bytes[i] = s[i] |
|
| 409 |
- } |
|
| 410 |
- return bytes |
|
| 411 |
-} |
|
| 412 |
- |
|
| 413 |
-// Add a new network link of a specified type. This is identical to |
|
| 414 |
-// running: ip add link $name type $linkType |
|
| 415 |
-func NetworkLinkAdd(name string, linkType string) error {
|
|
| 416 |
- s, err := getNetlinkSocket() |
|
| 417 |
- if err != nil {
|
|
| 418 |
- return err |
|
| 419 |
- } |
|
| 420 |
- defer s.Close() |
|
| 421 |
- |
|
| 422 |
- wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) |
|
| 423 |
- |
|
| 424 |
- msg := newIfInfomsg(syscall.AF_UNSPEC) |
|
| 425 |
- wb.AddData(msg) |
|
| 426 |
- |
|
| 427 |
- nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name)) |
|
| 428 |
- wb.AddData(nameData) |
|
| 429 |
- |
|
| 430 |
- IFLA_INFO_KIND := 1 |
|
| 431 |
- |
|
| 432 |
- kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType)) |
|
| 433 |
- |
|
| 434 |
- infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat()) |
|
| 435 |
- wb.AddData(infoData) |
|
| 436 |
- |
|
| 437 |
- if err := s.Send(wb); err != nil {
|
|
| 438 |
- return err |
|
| 439 |
- } |
|
| 440 |
- |
|
| 441 |
- return s.HandleAck(wb.Seq) |
|
| 442 |
-} |
|
| 443 |
- |
|
| 444 |
-// Returns an array of IPNet for all the currently routed subnets on ipv4 |
|
| 445 |
-// This is similar to the first column of "ip route" output |
|
| 446 |
-func NetworkGetRoutes() ([]*net.IPNet, error) {
|
|
| 447 |
- native := nativeEndian() |
|
| 448 |
- |
|
| 449 |
- s, err := getNetlinkSocket() |
|
| 450 |
- if err != nil {
|
|
| 451 |
- return nil, err |
|
| 452 |
- } |
|
| 453 |
- defer s.Close() |
|
| 454 |
- |
|
| 455 |
- wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) |
|
| 456 |
- |
|
| 457 |
- msg := newIfInfomsg(syscall.AF_UNSPEC) |
|
| 458 |
- wb.AddData(msg) |
|
| 459 |
- |
|
| 460 |
- if err := s.Send(wb); err != nil {
|
|
| 461 |
- return nil, err |
|
| 462 |
- } |
|
| 463 |
- |
|
| 464 |
- pid, err := s.GetPid() |
|
| 465 |
- if err != nil {
|
|
| 466 |
- return nil, err |
|
| 467 |
- } |
|
| 468 |
- |
|
| 469 |
- res := make([]*net.IPNet, 0) |
|
| 470 |
- |
|
| 471 |
-done: |
|
| 472 |
- for {
|
|
| 473 |
- msgs, err := s.Recieve() |
|
| 474 |
- if err != nil {
|
|
| 475 |
- return nil, err |
|
| 476 |
- } |
|
| 477 |
- for _, m := range msgs {
|
|
| 478 |
- if m.Header.Seq != wb.Seq {
|
|
| 479 |
- return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
|
|
| 480 |
- } |
|
| 481 |
- if m.Header.Pid != pid {
|
|
| 482 |
- return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
|
|
| 483 |
- } |
|
| 484 |
- if m.Header.Type == syscall.NLMSG_DONE {
|
|
| 485 |
- break done |
|
| 486 |
- } |
|
| 487 |
- if m.Header.Type == syscall.NLMSG_ERROR {
|
|
| 488 |
- error := int32(native.Uint32(m.Data[0:4])) |
|
| 489 |
- if error == 0 {
|
|
| 490 |
- break done |
|
| 491 |
- } |
|
| 492 |
- return nil, syscall.Errno(-error) |
|
| 493 |
- } |
|
| 494 |
- if m.Header.Type != syscall.RTM_NEWROUTE {
|
|
| 495 |
- continue |
|
| 496 |
- } |
|
| 497 |
- |
|
| 498 |
- var iface *net.Interface = nil |
|
| 499 |
- var ipNet *net.IPNet = nil |
|
| 500 |
- |
|
| 501 |
- msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0])) |
|
| 502 |
- |
|
| 503 |
- if msg.Flags&syscall.RTM_F_CLONED != 0 {
|
|
| 504 |
- // Ignore cloned routes |
|
| 505 |
- continue |
|
| 506 |
- } |
|
| 507 |
- |
|
| 508 |
- if msg.Table != syscall.RT_TABLE_MAIN {
|
|
| 509 |
- // Ignore non-main tables |
|
| 510 |
- continue |
|
| 511 |
- } |
|
| 512 |
- |
|
| 513 |
- if msg.Family != syscall.AF_INET {
|
|
| 514 |
- // Ignore non-ipv4 routes |
|
| 515 |
- continue |
|
| 516 |
- } |
|
| 517 |
- |
|
| 518 |
- if msg.Dst_len == 0 {
|
|
| 519 |
- // Ignore default routes |
|
| 520 |
- continue |
|
| 521 |
- } |
|
| 522 |
- |
|
| 523 |
- attrs, err := syscall.ParseNetlinkRouteAttr(&m) |
|
| 524 |
- if err != nil {
|
|
| 525 |
- return nil, err |
|
| 526 |
- } |
|
| 527 |
- for _, attr := range attrs {
|
|
| 528 |
- switch attr.Attr.Type {
|
|
| 529 |
- case syscall.RTA_DST: |
|
| 530 |
- ip := attr.Value |
|
| 531 |
- ipNet = &net.IPNet{
|
|
| 532 |
- IP: ip, |
|
| 533 |
- Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)), |
|
| 534 |
- } |
|
| 535 |
- case syscall.RTA_OIF: |
|
| 536 |
- index := int(native.Uint32(attr.Value[0:4])) |
|
| 537 |
- iface, _ = net.InterfaceByIndex(index) |
|
| 538 |
- _ = iface |
|
| 539 |
- } |
|
| 540 |
- } |
|
| 541 |
- if ipNet != nil {
|
|
| 542 |
- res = append(res, ipNet) |
|
| 543 |
- } |
|
| 544 |
- } |
|
| 545 |
- } |
|
| 546 |
- |
|
| 547 |
- return res, nil |
|
| 548 |
-} |
| 549 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,29 @@ |
| 0 |
+package netlink |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "net" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+func NetworkGetRoutes() ([]*net.IPNet, error) {
|
|
| 8 |
+ return nil, fmt.Errorf("Not implemented")
|
|
| 9 |
+} |
|
| 10 |
+ |
|
| 11 |
+ |
|
| 12 |
+func NetworkLinkAdd(name string, linkType string) error {
|
|
| 13 |
+ return fmt.Errorf("Not implemented")
|
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+func NetworkLinkUp(iface *net.Interface) error {
|
|
| 17 |
+ return fmt.Errorf("Not implemented")
|
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+ |
|
| 21 |
+func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
|
| 22 |
+ return fmt.Errorf("Not implemented")
|
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+func AddDefaultGw(ip net.IP) error {
|
|
| 26 |
+ return fmt.Errorf("Not implemented")
|
|
| 27 |
+ |
|
| 28 |
+} |
| 0 | 29 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,548 @@ |
| 0 |
+package netlink |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/binary" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "net" |
|
| 6 |
+ "syscall" |
|
| 7 |
+ "unsafe" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+var nextSeqNr int |
|
| 11 |
+ |
|
| 12 |
+func nativeEndian() binary.ByteOrder {
|
|
| 13 |
+ var x uint32 = 0x01020304 |
|
| 14 |
+ if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
|
| 15 |
+ return binary.BigEndian |
|
| 16 |
+ } |
|
| 17 |
+ return binary.LittleEndian |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func getSeq() int {
|
|
| 21 |
+ nextSeqNr = nextSeqNr + 1 |
|
| 22 |
+ return nextSeqNr |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+func getIpFamily(ip net.IP) int {
|
|
| 26 |
+ if len(ip) <= net.IPv4len {
|
|
| 27 |
+ return syscall.AF_INET |
|
| 28 |
+ } |
|
| 29 |
+ if ip.To4() != nil {
|
|
| 30 |
+ return syscall.AF_INET |
|
| 31 |
+ } |
|
| 32 |
+ return syscall.AF_INET6 |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+type NetlinkRequestData interface {
|
|
| 36 |
+ ToWireFormat() []byte |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+type IfInfomsg struct {
|
|
| 40 |
+ syscall.IfInfomsg |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+func newIfInfomsg(family int) *IfInfomsg {
|
|
| 44 |
+ msg := &IfInfomsg{}
|
|
| 45 |
+ msg.Family = uint8(family) |
|
| 46 |
+ msg.Type = uint16(0) |
|
| 47 |
+ msg.Index = int32(0) |
|
| 48 |
+ msg.Flags = uint32(0) |
|
| 49 |
+ msg.Change = uint32(0) |
|
| 50 |
+ |
|
| 51 |
+ return msg |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+func (msg *IfInfomsg) ToWireFormat() []byte {
|
|
| 55 |
+ native := nativeEndian() |
|
| 56 |
+ |
|
| 57 |
+ len := syscall.SizeofIfInfomsg |
|
| 58 |
+ b := make([]byte, len) |
|
| 59 |
+ b[0] = msg.Family |
|
| 60 |
+ b[1] = 0 |
|
| 61 |
+ native.PutUint16(b[2:4], msg.Type) |
|
| 62 |
+ native.PutUint32(b[4:8], uint32(msg.Index)) |
|
| 63 |
+ native.PutUint32(b[8:12], msg.Flags) |
|
| 64 |
+ native.PutUint32(b[12:16], msg.Change) |
|
| 65 |
+ return b |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+type IfAddrmsg struct {
|
|
| 69 |
+ syscall.IfAddrmsg |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+func newIfAddrmsg(family int) *IfAddrmsg {
|
|
| 73 |
+ msg := &IfAddrmsg{}
|
|
| 74 |
+ msg.Family = uint8(family) |
|
| 75 |
+ msg.Prefixlen = uint8(0) |
|
| 76 |
+ msg.Flags = uint8(0) |
|
| 77 |
+ msg.Scope = uint8(0) |
|
| 78 |
+ msg.Index = uint32(0) |
|
| 79 |
+ |
|
| 80 |
+ return msg |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+func (msg *IfAddrmsg) ToWireFormat() []byte {
|
|
| 84 |
+ native := nativeEndian() |
|
| 85 |
+ |
|
| 86 |
+ len := syscall.SizeofIfAddrmsg |
|
| 87 |
+ b := make([]byte, len) |
|
| 88 |
+ b[0] = msg.Family |
|
| 89 |
+ b[1] = msg.Prefixlen |
|
| 90 |
+ b[2] = msg.Flags |
|
| 91 |
+ b[3] = msg.Scope |
|
| 92 |
+ native.PutUint32(b[4:8], msg.Index) |
|
| 93 |
+ return b |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+type RtMsg struct {
|
|
| 97 |
+ syscall.RtMsg |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+func newRtMsg(family int) *RtMsg {
|
|
| 101 |
+ msg := &RtMsg{}
|
|
| 102 |
+ msg.Family = uint8(family) |
|
| 103 |
+ msg.Table = syscall.RT_TABLE_MAIN |
|
| 104 |
+ msg.Scope = syscall.RT_SCOPE_UNIVERSE |
|
| 105 |
+ msg.Protocol = syscall.RTPROT_BOOT |
|
| 106 |
+ msg.Type = syscall.RTN_UNICAST |
|
| 107 |
+ |
|
| 108 |
+ return msg |
|
| 109 |
+} |
|
| 110 |
+ |
|
| 111 |
+func (msg *RtMsg) ToWireFormat() []byte {
|
|
| 112 |
+ native := nativeEndian() |
|
| 113 |
+ |
|
| 114 |
+ len := syscall.SizeofRtMsg |
|
| 115 |
+ b := make([]byte, len) |
|
| 116 |
+ b[0] = msg.Family |
|
| 117 |
+ b[1] = msg.Dst_len |
|
| 118 |
+ b[2] = msg.Src_len |
|
| 119 |
+ b[3] = msg.Tos |
|
| 120 |
+ b[4] = msg.Table |
|
| 121 |
+ b[5] = msg.Protocol |
|
| 122 |
+ b[6] = msg.Scope |
|
| 123 |
+ b[7] = msg.Type |
|
| 124 |
+ native.PutUint32(b[8:12], msg.Flags) |
|
| 125 |
+ return b |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+func rtaAlignOf(attrlen int) int {
|
|
| 129 |
+ return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1) |
|
| 130 |
+} |
|
| 131 |
+ |
|
| 132 |
+type RtAttr struct {
|
|
| 133 |
+ syscall.RtAttr |
|
| 134 |
+ Data []byte |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+func newRtAttr(attrType int, data []byte) *RtAttr {
|
|
| 138 |
+ attr := &RtAttr{}
|
|
| 139 |
+ attr.Type = uint16(attrType) |
|
| 140 |
+ attr.Data = data |
|
| 141 |
+ |
|
| 142 |
+ return attr |
|
| 143 |
+} |
|
| 144 |
+ |
|
| 145 |
+func (attr *RtAttr) ToWireFormat() []byte {
|
|
| 146 |
+ native := nativeEndian() |
|
| 147 |
+ |
|
| 148 |
+ len := syscall.SizeofRtAttr + len(attr.Data) |
|
| 149 |
+ b := make([]byte, rtaAlignOf(len)) |
|
| 150 |
+ native.PutUint16(b[0:2], uint16(len)) |
|
| 151 |
+ native.PutUint16(b[2:4], attr.Type) |
|
| 152 |
+ for i, d := range attr.Data {
|
|
| 153 |
+ b[4+i] = d |
|
| 154 |
+ } |
|
| 155 |
+ |
|
| 156 |
+ return b |
|
| 157 |
+} |
|
| 158 |
+ |
|
| 159 |
+type NetlinkRequest struct {
|
|
| 160 |
+ syscall.NlMsghdr |
|
| 161 |
+ Data []NetlinkRequestData |
|
| 162 |
+} |
|
| 163 |
+ |
|
| 164 |
+func (rr *NetlinkRequest) ToWireFormat() []byte {
|
|
| 165 |
+ native := nativeEndian() |
|
| 166 |
+ |
|
| 167 |
+ length := rr.Len |
|
| 168 |
+ dataBytes := make([][]byte, len(rr.Data)) |
|
| 169 |
+ for i, data := range rr.Data {
|
|
| 170 |
+ dataBytes[i] = data.ToWireFormat() |
|
| 171 |
+ length = length + uint32(len(dataBytes[i])) |
|
| 172 |
+ } |
|
| 173 |
+ b := make([]byte, length) |
|
| 174 |
+ native.PutUint32(b[0:4], length) |
|
| 175 |
+ native.PutUint16(b[4:6], rr.Type) |
|
| 176 |
+ native.PutUint16(b[6:8], rr.Flags) |
|
| 177 |
+ native.PutUint32(b[8:12], rr.Seq) |
|
| 178 |
+ native.PutUint32(b[12:16], rr.Pid) |
|
| 179 |
+ |
|
| 180 |
+ i := 16 |
|
| 181 |
+ for _, data := range dataBytes {
|
|
| 182 |
+ for _, dataByte := range data {
|
|
| 183 |
+ b[i] = dataByte |
|
| 184 |
+ i = i + 1 |
|
| 185 |
+ } |
|
| 186 |
+ } |
|
| 187 |
+ return b |
|
| 188 |
+} |
|
| 189 |
+ |
|
| 190 |
+func (rr *NetlinkRequest) AddData(data NetlinkRequestData) {
|
|
| 191 |
+ rr.Data = append(rr.Data, data) |
|
| 192 |
+} |
|
| 193 |
+ |
|
| 194 |
+func newNetlinkRequest(proto, flags int) *NetlinkRequest {
|
|
| 195 |
+ rr := &NetlinkRequest{}
|
|
| 196 |
+ rr.Len = uint32(syscall.NLMSG_HDRLEN) |
|
| 197 |
+ rr.Type = uint16(proto) |
|
| 198 |
+ rr.Flags = syscall.NLM_F_REQUEST | uint16(flags) |
|
| 199 |
+ rr.Seq = uint32(getSeq()) |
|
| 200 |
+ return rr |
|
| 201 |
+} |
|
| 202 |
+ |
|
| 203 |
+type NetlinkSocket struct {
|
|
| 204 |
+ fd int |
|
| 205 |
+ lsa syscall.SockaddrNetlink |
|
| 206 |
+} |
|
| 207 |
+ |
|
| 208 |
+func getNetlinkSocket() (*NetlinkSocket, error) {
|
|
| 209 |
+ fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE) |
|
| 210 |
+ if err != nil {
|
|
| 211 |
+ return nil, err |
|
| 212 |
+ } |
|
| 213 |
+ s := &NetlinkSocket{
|
|
| 214 |
+ fd: fd, |
|
| 215 |
+ } |
|
| 216 |
+ s.lsa.Family = syscall.AF_NETLINK |
|
| 217 |
+ if err := syscall.Bind(fd, &s.lsa); err != nil {
|
|
| 218 |
+ syscall.Close(fd) |
|
| 219 |
+ return nil, err |
|
| 220 |
+ } |
|
| 221 |
+ |
|
| 222 |
+ return s, nil |
|
| 223 |
+} |
|
| 224 |
+ |
|
| 225 |
+func (s *NetlinkSocket) Close() {
|
|
| 226 |
+ syscall.Close(s.fd) |
|
| 227 |
+} |
|
| 228 |
+ |
|
| 229 |
+func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
|
|
| 230 |
+ if err := syscall.Sendto(s.fd, request.ToWireFormat(), 0, &s.lsa); err != nil {
|
|
| 231 |
+ return err |
|
| 232 |
+ } |
|
| 233 |
+ return nil |
|
| 234 |
+} |
|
| 235 |
+ |
|
| 236 |
+func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) {
|
|
| 237 |
+ rb := make([]byte, syscall.Getpagesize()) |
|
| 238 |
+ nr, _, err := syscall.Recvfrom(s.fd, rb, 0) |
|
| 239 |
+ if err != nil {
|
|
| 240 |
+ return nil, err |
|
| 241 |
+ } |
|
| 242 |
+ if nr < syscall.NLMSG_HDRLEN {
|
|
| 243 |
+ return nil, fmt.Errorf("Got short response from netlink")
|
|
| 244 |
+ } |
|
| 245 |
+ rb = rb[:nr] |
|
| 246 |
+ return syscall.ParseNetlinkMessage(rb) |
|
| 247 |
+} |
|
| 248 |
+ |
|
| 249 |
+func (s *NetlinkSocket) GetPid() (uint32, error) {
|
|
| 250 |
+ lsa, err := syscall.Getsockname(s.fd) |
|
| 251 |
+ if err != nil {
|
|
| 252 |
+ return 0, err |
|
| 253 |
+ } |
|
| 254 |
+ switch v := lsa.(type) {
|
|
| 255 |
+ case *syscall.SockaddrNetlink: |
|
| 256 |
+ return v.Pid, nil |
|
| 257 |
+ } |
|
| 258 |
+ return 0, fmt.Errorf("Wrong socket type")
|
|
| 259 |
+} |
|
| 260 |
+ |
|
| 261 |
+func (s *NetlinkSocket) HandleAck(seq uint32) error {
|
|
| 262 |
+ native := nativeEndian() |
|
| 263 |
+ |
|
| 264 |
+ pid, err := s.GetPid() |
|
| 265 |
+ if err != nil {
|
|
| 266 |
+ return err |
|
| 267 |
+ } |
|
| 268 |
+ |
|
| 269 |
+done: |
|
| 270 |
+ for {
|
|
| 271 |
+ msgs, err := s.Recieve() |
|
| 272 |
+ if err != nil {
|
|
| 273 |
+ return err |
|
| 274 |
+ } |
|
| 275 |
+ for _, m := range msgs {
|
|
| 276 |
+ if m.Header.Seq != seq {
|
|
| 277 |
+ return fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, seq)
|
|
| 278 |
+ } |
|
| 279 |
+ if m.Header.Pid != pid {
|
|
| 280 |
+ return fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
|
|
| 281 |
+ } |
|
| 282 |
+ if m.Header.Type == syscall.NLMSG_DONE {
|
|
| 283 |
+ break done |
|
| 284 |
+ } |
|
| 285 |
+ if m.Header.Type == syscall.NLMSG_ERROR {
|
|
| 286 |
+ error := int32(native.Uint32(m.Data[0:4])) |
|
| 287 |
+ if error == 0 {
|
|
| 288 |
+ break done |
|
| 289 |
+ } |
|
| 290 |
+ return syscall.Errno(-error) |
|
| 291 |
+ } |
|
| 292 |
+ } |
|
| 293 |
+ } |
|
| 294 |
+ |
|
| 295 |
+ return nil |
|
| 296 |
+} |
|
| 297 |
+ |
|
| 298 |
+// Add a new default gateway. Identical to: |
|
| 299 |
+// ip route add default via $ip |
|
| 300 |
+func AddDefaultGw(ip net.IP) error {
|
|
| 301 |
+ s, err := getNetlinkSocket() |
|
| 302 |
+ if err != nil {
|
|
| 303 |
+ return err |
|
| 304 |
+ } |
|
| 305 |
+ defer s.Close() |
|
| 306 |
+ |
|
| 307 |
+ family := getIpFamily(ip) |
|
| 308 |
+ |
|
| 309 |
+ wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) |
|
| 310 |
+ |
|
| 311 |
+ msg := newRtMsg(family) |
|
| 312 |
+ wb.AddData(msg) |
|
| 313 |
+ |
|
| 314 |
+ var ipData []byte |
|
| 315 |
+ if family == syscall.AF_INET {
|
|
| 316 |
+ ipData = ip.To4() |
|
| 317 |
+ } else {
|
|
| 318 |
+ ipData = ip.To16() |
|
| 319 |
+ } |
|
| 320 |
+ |
|
| 321 |
+ gateway := newRtAttr(syscall.RTA_GATEWAY, ipData) |
|
| 322 |
+ |
|
| 323 |
+ wb.AddData(gateway) |
|
| 324 |
+ |
|
| 325 |
+ if err := s.Send(wb); err != nil {
|
|
| 326 |
+ return err |
|
| 327 |
+ } |
|
| 328 |
+ |
|
| 329 |
+ return s.HandleAck(wb.Seq) |
|
| 330 |
+ |
|
| 331 |
+} |
|
| 332 |
+ |
|
| 333 |
+// Bring up a particular network interface |
|
| 334 |
+func NetworkLinkUp(iface *net.Interface) error {
|
|
| 335 |
+ s, err := getNetlinkSocket() |
|
| 336 |
+ if err != nil {
|
|
| 337 |
+ return err |
|
| 338 |
+ } |
|
| 339 |
+ defer s.Close() |
|
| 340 |
+ |
|
| 341 |
+ wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) |
|
| 342 |
+ |
|
| 343 |
+ msg := newIfInfomsg(syscall.AF_UNSPEC) |
|
| 344 |
+ msg.Change = syscall.IFF_UP |
|
| 345 |
+ msg.Flags = syscall.IFF_UP |
|
| 346 |
+ msg.Index = int32(iface.Index) |
|
| 347 |
+ wb.AddData(msg) |
|
| 348 |
+ |
|
| 349 |
+ if err := s.Send(wb); err != nil {
|
|
| 350 |
+ return err |
|
| 351 |
+ } |
|
| 352 |
+ |
|
| 353 |
+ return s.HandleAck(wb.Seq) |
|
| 354 |
+} |
|
| 355 |
+ |
|
| 356 |
+// Add an Ip address to an interface. This is identical to: |
|
| 357 |
+// ip addr add $ip/$ipNet dev $iface |
|
| 358 |
+func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
|
| 359 |
+ s, err := getNetlinkSocket() |
|
| 360 |
+ if err != nil {
|
|
| 361 |
+ return err |
|
| 362 |
+ } |
|
| 363 |
+ defer s.Close() |
|
| 364 |
+ |
|
| 365 |
+ family := getIpFamily(ip) |
|
| 366 |
+ |
|
| 367 |
+ wb := newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) |
|
| 368 |
+ |
|
| 369 |
+ msg := newIfAddrmsg(family) |
|
| 370 |
+ msg.Index = uint32(iface.Index) |
|
| 371 |
+ prefixLen, _ := ipNet.Mask.Size() |
|
| 372 |
+ msg.Prefixlen = uint8(prefixLen) |
|
| 373 |
+ wb.AddData(msg) |
|
| 374 |
+ |
|
| 375 |
+ var ipData []byte |
|
| 376 |
+ if family == syscall.AF_INET {
|
|
| 377 |
+ ipData = ip.To4() |
|
| 378 |
+ } else {
|
|
| 379 |
+ ipData = ip.To16() |
|
| 380 |
+ } |
|
| 381 |
+ |
|
| 382 |
+ localData := newRtAttr(syscall.IFA_LOCAL, ipData) |
|
| 383 |
+ wb.AddData(localData) |
|
| 384 |
+ |
|
| 385 |
+ addrData := newRtAttr(syscall.IFA_ADDRESS, ipData) |
|
| 386 |
+ wb.AddData(addrData) |
|
| 387 |
+ |
|
| 388 |
+ if err := s.Send(wb); err != nil {
|
|
| 389 |
+ return err |
|
| 390 |
+ } |
|
| 391 |
+ |
|
| 392 |
+ return s.HandleAck(wb.Seq) |
|
| 393 |
+} |
|
| 394 |
+ |
|
| 395 |
+func zeroTerminated(s string) []byte {
|
|
| 396 |
+ bytes := make([]byte, len(s)+1) |
|
| 397 |
+ for i := 0; i < len(s); i++ {
|
|
| 398 |
+ bytes[i] = s[i] |
|
| 399 |
+ } |
|
| 400 |
+ bytes[len(s)] = 0 |
|
| 401 |
+ return bytes |
|
| 402 |
+} |
|
| 403 |
+ |
|
| 404 |
+func nonZeroTerminated(s string) []byte {
|
|
| 405 |
+ bytes := make([]byte, len(s)) |
|
| 406 |
+ for i := 0; i < len(s); i++ {
|
|
| 407 |
+ bytes[i] = s[i] |
|
| 408 |
+ } |
|
| 409 |
+ return bytes |
|
| 410 |
+} |
|
| 411 |
+ |
|
| 412 |
+// Add a new network link of a specified type. This is identical to |
|
| 413 |
+// running: ip add link $name type $linkType |
|
| 414 |
+func NetworkLinkAdd(name string, linkType string) error {
|
|
| 415 |
+ s, err := getNetlinkSocket() |
|
| 416 |
+ if err != nil {
|
|
| 417 |
+ return err |
|
| 418 |
+ } |
|
| 419 |
+ defer s.Close() |
|
| 420 |
+ |
|
| 421 |
+ wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) |
|
| 422 |
+ |
|
| 423 |
+ msg := newIfInfomsg(syscall.AF_UNSPEC) |
|
| 424 |
+ wb.AddData(msg) |
|
| 425 |
+ |
|
| 426 |
+ nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name)) |
|
| 427 |
+ wb.AddData(nameData) |
|
| 428 |
+ |
|
| 429 |
+ IFLA_INFO_KIND := 1 |
|
| 430 |
+ |
|
| 431 |
+ kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType)) |
|
| 432 |
+ |
|
| 433 |
+ infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat()) |
|
| 434 |
+ wb.AddData(infoData) |
|
| 435 |
+ |
|
| 436 |
+ if err := s.Send(wb); err != nil {
|
|
| 437 |
+ return err |
|
| 438 |
+ } |
|
| 439 |
+ |
|
| 440 |
+ return s.HandleAck(wb.Seq) |
|
| 441 |
+} |
|
| 442 |
+ |
|
| 443 |
+// Returns an array of IPNet for all the currently routed subnets on ipv4 |
|
| 444 |
+// This is similar to the first column of "ip route" output |
|
| 445 |
+func NetworkGetRoutes() ([]*net.IPNet, error) {
|
|
| 446 |
+ native := nativeEndian() |
|
| 447 |
+ |
|
| 448 |
+ s, err := getNetlinkSocket() |
|
| 449 |
+ if err != nil {
|
|
| 450 |
+ return nil, err |
|
| 451 |
+ } |
|
| 452 |
+ defer s.Close() |
|
| 453 |
+ |
|
| 454 |
+ wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) |
|
| 455 |
+ |
|
| 456 |
+ msg := newIfInfomsg(syscall.AF_UNSPEC) |
|
| 457 |
+ wb.AddData(msg) |
|
| 458 |
+ |
|
| 459 |
+ if err := s.Send(wb); err != nil {
|
|
| 460 |
+ return nil, err |
|
| 461 |
+ } |
|
| 462 |
+ |
|
| 463 |
+ pid, err := s.GetPid() |
|
| 464 |
+ if err != nil {
|
|
| 465 |
+ return nil, err |
|
| 466 |
+ } |
|
| 467 |
+ |
|
| 468 |
+ res := make([]*net.IPNet, 0) |
|
| 469 |
+ |
|
| 470 |
+done: |
|
| 471 |
+ for {
|
|
| 472 |
+ msgs, err := s.Recieve() |
|
| 473 |
+ if err != nil {
|
|
| 474 |
+ return nil, err |
|
| 475 |
+ } |
|
| 476 |
+ for _, m := range msgs {
|
|
| 477 |
+ if m.Header.Seq != wb.Seq {
|
|
| 478 |
+ return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
|
|
| 479 |
+ } |
|
| 480 |
+ if m.Header.Pid != pid {
|
|
| 481 |
+ return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
|
|
| 482 |
+ } |
|
| 483 |
+ if m.Header.Type == syscall.NLMSG_DONE {
|
|
| 484 |
+ break done |
|
| 485 |
+ } |
|
| 486 |
+ if m.Header.Type == syscall.NLMSG_ERROR {
|
|
| 487 |
+ error := int32(native.Uint32(m.Data[0:4])) |
|
| 488 |
+ if error == 0 {
|
|
| 489 |
+ break done |
|
| 490 |
+ } |
|
| 491 |
+ return nil, syscall.Errno(-error) |
|
| 492 |
+ } |
|
| 493 |
+ if m.Header.Type != syscall.RTM_NEWROUTE {
|
|
| 494 |
+ continue |
|
| 495 |
+ } |
|
| 496 |
+ |
|
| 497 |
+ var iface *net.Interface = nil |
|
| 498 |
+ var ipNet *net.IPNet = nil |
|
| 499 |
+ |
|
| 500 |
+ msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0])) |
|
| 501 |
+ |
|
| 502 |
+ if msg.Flags&syscall.RTM_F_CLONED != 0 {
|
|
| 503 |
+ // Ignore cloned routes |
|
| 504 |
+ continue |
|
| 505 |
+ } |
|
| 506 |
+ |
|
| 507 |
+ if msg.Table != syscall.RT_TABLE_MAIN {
|
|
| 508 |
+ // Ignore non-main tables |
|
| 509 |
+ continue |
|
| 510 |
+ } |
|
| 511 |
+ |
|
| 512 |
+ if msg.Family != syscall.AF_INET {
|
|
| 513 |
+ // Ignore non-ipv4 routes |
|
| 514 |
+ continue |
|
| 515 |
+ } |
|
| 516 |
+ |
|
| 517 |
+ if msg.Dst_len == 0 {
|
|
| 518 |
+ // Ignore default routes |
|
| 519 |
+ continue |
|
| 520 |
+ } |
|
| 521 |
+ |
|
| 522 |
+ attrs, err := syscall.ParseNetlinkRouteAttr(&m) |
|
| 523 |
+ if err != nil {
|
|
| 524 |
+ return nil, err |
|
| 525 |
+ } |
|
| 526 |
+ for _, attr := range attrs {
|
|
| 527 |
+ switch attr.Attr.Type {
|
|
| 528 |
+ case syscall.RTA_DST: |
|
| 529 |
+ ip := attr.Value |
|
| 530 |
+ ipNet = &net.IPNet{
|
|
| 531 |
+ IP: ip, |
|
| 532 |
+ Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)), |
|
| 533 |
+ } |
|
| 534 |
+ case syscall.RTA_OIF: |
|
| 535 |
+ index := int(native.Uint32(attr.Value[0:4])) |
|
| 536 |
+ iface, _ = net.InterfaceByIndex(index) |
|
| 537 |
+ _ = iface |
|
| 538 |
+ } |
|
| 539 |
+ } |
|
| 540 |
+ if ipNet != nil {
|
|
| 541 |
+ res = append(res, ipNet) |
|
| 542 |
+ } |
|
| 543 |
+ } |
|
| 544 |
+ } |
|
| 545 |
+ |
|
| 546 |
+ return res, nil |
|
| 547 |
+} |
| ... | ... |
@@ -76,6 +76,21 @@ func checkRouteOverlaps(networks []*net.IPNet, dockerNetwork *net.IPNet) error {
|
| 76 | 76 |
return nil |
| 77 | 77 |
} |
| 78 | 78 |
|
| 79 |
+func checkNameserverOverlaps(nameservers []string, dockerNetwork *net.IPNet) error {
|
|
| 80 |
+ if len(nameservers) > 0 {
|
|
| 81 |
+ for _, ns := range nameservers {
|
|
| 82 |
+ _, nsNetwork, err := net.ParseCIDR(ns) |
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ return err |
|
| 85 |
+ } |
|
| 86 |
+ if networkOverlaps(dockerNetwork, nsNetwork) {
|
|
| 87 |
+ return fmt.Errorf("%s overlaps nameserver %s", dockerNetwork, nsNetwork)
|
|
| 88 |
+ } |
|
| 89 |
+ } |
|
| 90 |
+ } |
|
| 91 |
+ return nil |
|
| 92 |
+} |
|
| 93 |
+ |
|
| 79 | 94 |
// CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`, |
| 80 | 95 |
// and attempts to configure it with an address which doesn't conflict with any other interface on the host. |
| 81 | 96 |
// If it can't find an address which doesn't conflict, it will return an error. |
| ... | ... |
@@ -100,6 +115,16 @@ func CreateBridgeIface(config *DaemonConfig) error {
|
| 100 | 100 |
"192.168.44.1/24", |
| 101 | 101 |
} |
| 102 | 102 |
|
| 103 |
+ nameservers := []string{}
|
|
| 104 |
+ resolvConf, _ := utils.GetResolvConf() |
|
| 105 |
+ // we don't check for an error here, because we don't really care |
|
| 106 |
+ // if we can't read /etc/resolv.conf. So instead we skip the append |
|
| 107 |
+ // if resolvConf is nil. It either doesn't exist, or we can't read it |
|
| 108 |
+ // for some reason. |
|
| 109 |
+ if resolvConf != nil {
|
|
| 110 |
+ nameservers = append(nameservers, utils.GetNameserversAsCIDR(resolvConf)...) |
|
| 111 |
+ } |
|
| 112 |
+ |
|
| 103 | 113 |
var ifaceAddr string |
| 104 | 114 |
for _, addr := range addrs {
|
| 105 | 115 |
_, dockerNetwork, err := net.ParseCIDR(addr) |
| ... | ... |
@@ -111,8 +136,10 @@ func CreateBridgeIface(config *DaemonConfig) error {
|
| 111 | 111 |
return err |
| 112 | 112 |
} |
| 113 | 113 |
if err := checkRouteOverlaps(routes, dockerNetwork); err == nil {
|
| 114 |
- ifaceAddr = addr |
|
| 115 |
- break |
|
| 114 |
+ if err := checkNameserverOverlaps(nameservers, dockerNetwork); err == nil {
|
|
| 115 |
+ ifaceAddr = addr |
|
| 116 |
+ break |
|
| 117 |
+ } |
|
| 116 | 118 |
} else {
|
| 117 | 119 |
utils.Debugf("%s: %s", addr, err)
|
| 118 | 120 |
} |
| ... | ... |
@@ -295,3 +295,19 @@ func TestCheckRouteOverlaps(t *testing.T) {
|
| 295 | 295 |
t.Fatalf("10.0.2.0/24 and 10.0.2.0 should overlap but it doesn't")
|
| 296 | 296 |
} |
| 297 | 297 |
} |
| 298 |
+ |
|
| 299 |
+func TestCheckNameserverOverlaps(t *testing.T) {
|
|
| 300 |
+ nameservers := []string{"10.0.2.3/32", "192.168.102.1/32"}
|
|
| 301 |
+ |
|
| 302 |
+ _, netX, _ := net.ParseCIDR("10.0.2.3/32")
|
|
| 303 |
+ |
|
| 304 |
+ if err := checkNameserverOverlaps(nameservers, netX); err == nil {
|
|
| 305 |
+ t.Fatalf("%s should overlap 10.0.2.3/32 but doesn't", netX)
|
|
| 306 |
+ } |
|
| 307 |
+ |
|
| 308 |
+ _, netX, _ = net.ParseCIDR("192.168.102.2/32")
|
|
| 309 |
+ |
|
| 310 |
+ if err := checkNameserverOverlaps(nameservers, netX); err != nil {
|
|
| 311 |
+ t.Fatalf("%s should not overlap %v but it does", netX, nameservers)
|
|
| 312 |
+ } |
|
| 313 |
+} |
| ... | ... |
@@ -615,10 +615,18 @@ func (r *Registry) GetAuthConfig(withPasswd bool) *auth.AuthConfig {
|
| 615 | 615 |
} |
| 616 | 616 |
} |
| 617 | 617 |
|
| 618 |
+type SearchResult struct {
|
|
| 619 |
+ StarCount int `json:"star_count"` |
|
| 620 |
+ IsOfficial bool `json:"is_official"` |
|
| 621 |
+ Name string `json:"name"` |
|
| 622 |
+ IsTrusted bool `json:"is_trusted"` |
|
| 623 |
+ Description string `json:"description"` |
|
| 624 |
+} |
|
| 625 |
+ |
|
| 618 | 626 |
type SearchResults struct {
|
| 619 |
- Query string `json:"query"` |
|
| 620 |
- NumResults int `json:"num_results"` |
|
| 621 |
- Results []map[string]string `json:"results"` |
|
| 627 |
+ Query string `json:"query"` |
|
| 628 |
+ NumResults int `json:"num_results"` |
|
| 629 |
+ Results []SearchResult `json:"results"` |
|
| 622 | 630 |
} |
| 623 | 631 |
|
| 624 | 632 |
type RepositoryData struct {
|
| ... | ... |
@@ -24,6 +24,7 @@ type Capabilities struct {
|
| 24 | 24 |
MemoryLimit bool |
| 25 | 25 |
SwapLimit bool |
| 26 | 26 |
IPv4ForwardingDisabled bool |
| 27 |
+ AppArmor bool |
|
| 27 | 28 |
} |
| 28 | 29 |
|
| 29 | 30 |
type Runtime struct {
|
| ... | ... |
@@ -112,6 +113,9 @@ func (runtime *Runtime) Register(container *Container) error {
|
| 112 | 112 |
if err := validateID(container.ID); err != nil {
|
| 113 | 113 |
return err |
| 114 | 114 |
} |
| 115 |
+ if err := runtime.ensureName(container); err != nil {
|
|
| 116 |
+ return err |
|
| 117 |
+ } |
|
| 115 | 118 |
|
| 116 | 119 |
// init the wait lock |
| 117 | 120 |
container.waitLock = make(chan struct{})
|
| ... | ... |
@@ -149,8 +153,7 @@ func (runtime *Runtime) Register(container *Container) error {
|
| 149 | 149 |
utils.Debugf("Restarting")
|
| 150 | 150 |
container.State.Ghost = false |
| 151 | 151 |
container.State.setStopped(0) |
| 152 |
- hostConfig, _ := container.ReadHostConfig() |
|
| 153 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 152 |
+ if err := container.Start(); err != nil {
|
|
| 154 | 153 |
return err |
| 155 | 154 |
} |
| 156 | 155 |
nomonitor = true |
| ... | ... |
@@ -169,9 +172,27 @@ func (runtime *Runtime) Register(container *Container) error {
|
| 169 | 169 |
if !container.State.Running {
|
| 170 | 170 |
close(container.waitLock) |
| 171 | 171 |
} else if !nomonitor {
|
| 172 |
- hostConfig, _ := container.ReadHostConfig() |
|
| 173 |
- container.allocateNetwork(hostConfig) |
|
| 174 |
- go container.monitor(hostConfig) |
|
| 172 |
+ go container.monitor() |
|
| 173 |
+ } |
|
| 174 |
+ return nil |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+func (runtime *Runtime) ensureName(container *Container) error {
|
|
| 178 |
+ if container.Name == "" {
|
|
| 179 |
+ name, err := generateRandomName(runtime) |
|
| 180 |
+ if err != nil {
|
|
| 181 |
+ name = container.ShortID() |
|
| 182 |
+ } |
|
| 183 |
+ container.Name = name |
|
| 184 |
+ |
|
| 185 |
+ if err := container.ToDisk(); err != nil {
|
|
| 186 |
+ utils.Debugf("Error saving container name %s", err)
|
|
| 187 |
+ } |
|
| 188 |
+ if !runtime.containerGraph.Exists(name) {
|
|
| 189 |
+ if _, err := runtime.containerGraph.Set(name, container.ID); err != nil {
|
|
| 190 |
+ utils.Debugf("Setting default id - %s", err)
|
|
| 191 |
+ } |
|
| 192 |
+ } |
|
| 175 | 193 |
} |
| 176 | 194 |
return nil |
| 177 | 195 |
} |
| ... | ... |
@@ -265,7 +286,10 @@ func (runtime *Runtime) restore() error {
|
| 265 | 265 |
// Any containers that are left over do not exist in the graph |
| 266 | 266 |
for _, container := range containers {
|
| 267 | 267 |
// Try to set the default name for a container if it exists prior to links |
| 268 |
- name := generateRandomName(runtime) |
|
| 268 |
+ name, err := generateRandomName(runtime) |
|
| 269 |
+ if err != nil {
|
|
| 270 |
+ container.Name = container.ShortID() |
|
| 271 |
+ } |
|
| 269 | 272 |
container.Name = name |
| 270 | 273 |
|
| 271 | 274 |
if _, err := runtime.containerGraph.Set(name, container.ID); err != nil {
|
| ... | ... |
@@ -307,6 +331,15 @@ func (runtime *Runtime) UpdateCapabilities(quiet bool) {
|
| 307 | 307 |
if runtime.capabilities.IPv4ForwardingDisabled && !quiet {
|
| 308 | 308 |
log.Printf("WARNING: IPv4 forwarding is disabled.")
|
| 309 | 309 |
} |
| 310 |
+ |
|
| 311 |
+ // Check if AppArmor seems to be enabled on this system. |
|
| 312 |
+ if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) {
|
|
| 313 |
+ utils.Debugf("/sys/kernel/security/apparmor not found; assuming AppArmor is not enabled.")
|
|
| 314 |
+ runtime.capabilities.AppArmor = false |
|
| 315 |
+ } else {
|
|
| 316 |
+ utils.Debugf("/sys/kernel/security/apparmor found; assuming AppArmor is enabled.")
|
|
| 317 |
+ runtime.capabilities.AppArmor = true |
|
| 318 |
+ } |
|
| 310 | 319 |
} |
| 311 | 320 |
|
| 312 | 321 |
// Create creates a new container from the given configuration with a given name. |
| ... | ... |
@@ -356,7 +389,10 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin |
| 356 | 356 |
id := GenerateID() |
| 357 | 357 |
|
| 358 | 358 |
if name == "" {
|
| 359 |
- name = generateRandomName(runtime) |
|
| 359 |
+ name, err = generateRandomName(runtime) |
|
| 360 |
+ if err != nil {
|
|
| 361 |
+ name = utils.TruncateID(id) |
|
| 362 |
+ } |
|
| 360 | 363 |
} |
| 361 | 364 |
if name[0] != '/' {
|
| 362 | 365 |
name = "/" + name |
| ... | ... |
@@ -394,6 +430,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin |
| 394 | 394 |
Path: entrypoint, |
| 395 | 395 |
Args: args, //FIXME: de-duplicate from config |
| 396 | 396 |
Config: config, |
| 397 |
+ hostConfig: &HostConfig{},
|
|
| 397 | 398 |
Image: img.ID, // Always use the resolved image id |
| 398 | 399 |
NetworkSettings: &NetworkSettings{},
|
| 399 | 400 |
// FIXME: do we need to store this in the container? |
| ... | ... |
@@ -504,15 +541,22 @@ func (runtime *Runtime) Commit(container *Container, repository, tag, comment, a |
| 504 | 504 |
return img, nil |
| 505 | 505 |
} |
| 506 | 506 |
|
| 507 |
-func (runtime *Runtime) getFullName(name string) string {
|
|
| 507 |
+func (runtime *Runtime) getFullName(name string) (string, error) {
|
|
| 508 |
+ if name == "" {
|
|
| 509 |
+ return "", fmt.Errorf("Container name cannot be empty")
|
|
| 510 |
+ } |
|
| 508 | 511 |
if name[0] != '/' {
|
| 509 | 512 |
name = "/" + name |
| 510 | 513 |
} |
| 511 |
- return name |
|
| 514 |
+ return name, nil |
|
| 512 | 515 |
} |
| 513 | 516 |
|
| 514 | 517 |
func (runtime *Runtime) GetByName(name string) (*Container, error) {
|
| 515 |
- entity := runtime.containerGraph.Get(runtime.getFullName(name)) |
|
| 518 |
+ fullName, err := runtime.getFullName(name) |
|
| 519 |
+ if err != nil {
|
|
| 520 |
+ return nil, err |
|
| 521 |
+ } |
|
| 522 |
+ entity := runtime.containerGraph.Get(fullName) |
|
| 516 | 523 |
if entity == nil {
|
| 517 | 524 |
return nil, fmt.Errorf("Could not find entity for %s", name)
|
| 518 | 525 |
} |
| ... | ... |
@@ -524,10 +568,13 @@ func (runtime *Runtime) GetByName(name string) (*Container, error) {
|
| 524 | 524 |
} |
| 525 | 525 |
|
| 526 | 526 |
func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
|
| 527 |
- name = runtime.getFullName(name) |
|
| 527 |
+ name, err := runtime.getFullName(name) |
|
| 528 |
+ if err != nil {
|
|
| 529 |
+ return nil, err |
|
| 530 |
+ } |
|
| 528 | 531 |
children := make(map[string]*Container) |
| 529 | 532 |
|
| 530 |
- err := runtime.containerGraph.Walk(name, func(p string, e *gograph.Entity) error {
|
|
| 533 |
+ err = runtime.containerGraph.Walk(name, func(p string, e *gograph.Entity) error {
|
|
| 531 | 534 |
c := runtime.Get(e.ID()) |
| 532 | 535 |
if c == nil {
|
| 533 | 536 |
return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
|
| ... | ... |
@@ -557,34 +604,29 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) {
|
| 557 | 557 |
if err != nil {
|
| 558 | 558 |
return nil, err |
| 559 | 559 |
} |
| 560 |
- |
|
| 561 |
- if k, err := utils.GetKernelVersion(); err != nil {
|
|
| 562 |
- log.Printf("WARNING: %s\n", err)
|
|
| 563 |
- } else {
|
|
| 564 |
- if utils.CompareKernelVersion(k, &utils.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 {
|
|
| 565 |
- log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String())
|
|
| 566 |
- } |
|
| 567 |
- } |
|
| 568 | 560 |
runtime.UpdateCapabilities(false) |
| 569 | 561 |
return runtime, nil |
| 570 | 562 |
} |
| 571 | 563 |
|
| 572 | 564 |
func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
|
| 573 |
- runtimeRepo := path.Join(config.GraphPath, "containers") |
|
| 565 |
+ runtimeRepo := path.Join(config.Root, "containers") |
|
| 574 | 566 |
|
| 575 | 567 |
if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
|
| 576 | 568 |
return nil, err |
| 577 | 569 |
} |
| 578 | 570 |
|
| 579 |
- g, err := NewGraph(path.Join(config.GraphPath, "graph")) |
|
| 571 |
+ if err := linkLxcStart(config.Root); err != nil {
|
|
| 572 |
+ return nil, err |
|
| 573 |
+ } |
|
| 574 |
+ g, err := NewGraph(path.Join(config.Root, "graph")) |
|
| 580 | 575 |
if err != nil {
|
| 581 | 576 |
return nil, err |
| 582 | 577 |
} |
| 583 |
- volumes, err := NewGraph(path.Join(config.GraphPath, "volumes")) |
|
| 578 |
+ volumes, err := NewGraph(path.Join(config.Root, "volumes")) |
|
| 584 | 579 |
if err != nil {
|
| 585 | 580 |
return nil, err |
| 586 | 581 |
} |
| 587 |
- repositories, err := NewTagStore(path.Join(config.GraphPath, "repositories"), g) |
|
| 582 |
+ repositories, err := NewTagStore(path.Join(config.Root, "repositories"), g) |
|
| 588 | 583 |
if err != nil {
|
| 589 | 584 |
return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
|
| 590 | 585 |
} |
| ... | ... |
@@ -596,7 +638,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
|
| 596 | 596 |
return nil, err |
| 597 | 597 |
} |
| 598 | 598 |
|
| 599 |
- gographPath := path.Join(config.GraphPath, "linkgraph.db") |
|
| 599 |
+ gographPath := path.Join(config.Root, "linkgraph.db") |
|
| 600 | 600 |
initDatabase := false |
| 601 | 601 |
if _, err := os.Stat(gographPath); err != nil {
|
| 602 | 602 |
if os.IsNotExist(err) {
|
| ... | ... |
@@ -638,6 +680,23 @@ func (runtime *Runtime) Close() error {
|
| 638 | 638 |
return runtime.containerGraph.Close() |
| 639 | 639 |
} |
| 640 | 640 |
|
| 641 |
+func linkLxcStart(root string) error {
|
|
| 642 |
+ sourcePath, err := exec.LookPath("lxc-start")
|
|
| 643 |
+ if err != nil {
|
|
| 644 |
+ return err |
|
| 645 |
+ } |
|
| 646 |
+ targetPath := path.Join(root, "lxc-start-unconfined") |
|
| 647 |
+ |
|
| 648 |
+ if _, err := os.Stat(targetPath); err != nil && !os.IsNotExist(err) {
|
|
| 649 |
+ return err |
|
| 650 |
+ } else if err == nil {
|
|
| 651 |
+ if err := os.Remove(targetPath); err != nil {
|
|
| 652 |
+ return err |
|
| 653 |
+ } |
|
| 654 |
+ } |
|
| 655 |
+ return os.Symlink(sourcePath, targetPath) |
|
| 656 |
+} |
|
| 657 |
+ |
|
| 641 | 658 |
// History is a convenience type for storing a list of containers, |
| 642 | 659 |
// ordered by creation date. |
| 643 | 660 |
type History []*Container |
| ... | ... |
@@ -46,8 +46,8 @@ func nuke(runtime *Runtime) error {
|
| 46 | 46 |
wg.Wait() |
| 47 | 47 |
runtime.Close() |
| 48 | 48 |
|
| 49 |
- os.Remove(filepath.Join(runtime.config.GraphPath, "linkgraph.db")) |
|
| 50 |
- return os.RemoveAll(runtime.config.GraphPath) |
|
| 49 |
+ os.Remove(filepath.Join(runtime.config.Root, "linkgraph.db")) |
|
| 50 |
+ return os.RemoveAll(runtime.config.Root) |
|
| 51 | 51 |
} |
| 52 | 52 |
|
| 53 | 53 |
func cleanup(runtime *Runtime) error {
|
| ... | ... |
@@ -119,7 +119,7 @@ func init() {
|
| 119 | 119 |
|
| 120 | 120 |
func setupBaseImage() {
|
| 121 | 121 |
config := &DaemonConfig{
|
| 122 |
- GraphPath: unitTestStoreBase, |
|
| 122 |
+ Root: unitTestStoreBase, |
|
| 123 | 123 |
AutoRestart: false, |
| 124 | 124 |
BridgeIface: unitTestNetworkBridge, |
| 125 | 125 |
} |
| ... | ... |
@@ -325,13 +325,13 @@ func TestGet(t *testing.T) {
|
| 325 | 325 |
runtime := mkRuntime(t) |
| 326 | 326 |
defer nuke(runtime) |
| 327 | 327 |
|
| 328 |
- container1, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
|
|
| 328 |
+ container1, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
|
|
| 329 | 329 |
defer runtime.Destroy(container1) |
| 330 | 330 |
|
| 331 |
- container2, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
|
|
| 331 |
+ container2, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
|
|
| 332 | 332 |
defer runtime.Destroy(container2) |
| 333 | 333 |
|
| 334 |
- container3, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
|
|
| 334 |
+ container3, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
|
|
| 335 | 335 |
defer runtime.Destroy(container3) |
| 336 | 336 |
|
| 337 | 337 |
if runtime.Get(container1.ID) != container1 {
|
| ... | ... |
@@ -390,13 +390,13 @@ func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container, |
| 390 | 390 |
t.Logf("Port %v already in use, trying another one", strPort)
|
| 391 | 391 |
} |
| 392 | 392 |
|
| 393 |
- hostConfig := &HostConfig{
|
|
| 393 |
+ container.hostConfig = &HostConfig{
|
|
| 394 | 394 |
PortBindings: make(map[Port][]PortBinding), |
| 395 | 395 |
} |
| 396 |
- hostConfig.PortBindings[p] = []PortBinding{
|
|
| 396 |
+ container.hostConfig.PortBindings[p] = []PortBinding{
|
|
| 397 | 397 |
{},
|
| 398 | 398 |
} |
| 399 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 399 |
+ if err := container.Start(); err != nil {
|
|
| 400 | 400 |
nuke(runtime) |
| 401 | 401 |
t.Fatal(err) |
| 402 | 402 |
} |
| ... | ... |
@@ -503,16 +503,15 @@ func TestRestore(t *testing.T) {
|
| 503 | 503 |
runtime1 := mkRuntime(t) |
| 504 | 504 |
defer nuke(runtime1) |
| 505 | 505 |
// Create a container with one instance of docker |
| 506 |
- container1, _, _ := mkContainer(runtime1, []string{"_", "ls", "-al"}, t)
|
|
| 506 |
+ container1, _ := mkContainer(runtime1, []string{"_", "ls", "-al"}, t)
|
|
| 507 | 507 |
defer runtime1.Destroy(container1) |
| 508 | 508 |
|
| 509 | 509 |
// Create a second container meant to be killed |
| 510 |
- container2, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t)
|
|
| 510 |
+ container2, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t)
|
|
| 511 | 511 |
defer runtime1.Destroy(container2) |
| 512 | 512 |
|
| 513 | 513 |
// Start the container non blocking |
| 514 |
- hostConfig := &HostConfig{}
|
|
| 515 |
- if err := container2.Start(hostConfig); err != nil {
|
|
| 514 |
+ if err := container2.Start(); err != nil {
|
|
| 516 | 515 |
t.Fatal(err) |
| 517 | 516 |
} |
| 518 | 517 |
|
| ... | ... |
@@ -575,25 +574,23 @@ func TestReloadContainerLinks(t *testing.T) {
|
| 575 | 575 |
runtime1 := mkRuntime(t) |
| 576 | 576 |
defer nuke(runtime1) |
| 577 | 577 |
// Create a container with one instance of docker |
| 578 |
- container1, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/sh"}, t)
|
|
| 578 |
+ container1, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/sh"}, t)
|
|
| 579 | 579 |
defer runtime1.Destroy(container1) |
| 580 | 580 |
|
| 581 | 581 |
// Create a second container meant to be killed |
| 582 |
- container2, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t)
|
|
| 582 |
+ container2, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t)
|
|
| 583 | 583 |
defer runtime1.Destroy(container2) |
| 584 | 584 |
|
| 585 | 585 |
// Start the container non blocking |
| 586 |
- hostConfig := &HostConfig{}
|
|
| 587 |
- if err := container2.Start(hostConfig); err != nil {
|
|
| 586 |
+ if err := container2.Start(); err != nil {
|
|
| 588 | 587 |
t.Fatal(err) |
| 589 | 588 |
} |
| 590 |
- h1 := &HostConfig{}
|
|
| 591 | 589 |
// Add a link to container 2 |
| 592 |
- h1.Links = []string{"/" + container2.ID + ":first"}
|
|
| 590 |
+ container1.hostConfig.Links = []string{"/" + container2.ID + ":first"}
|
|
| 593 | 591 |
if err := runtime1.RegisterLink(container1, container2, "first"); err != nil {
|
| 594 | 592 |
t.Fatal(err) |
| 595 | 593 |
} |
| 596 |
- if err := container1.Start(h1); err != nil {
|
|
| 594 |
+ if err := container1.Start(); err != nil {
|
|
| 597 | 595 |
t.Fatal(err) |
| 598 | 596 |
} |
| 599 | 597 |
|
| ... | ... |
@@ -623,7 +620,6 @@ func TestReloadContainerLinks(t *testing.T) {
|
| 623 | 623 |
runningCount := 0 |
| 624 | 624 |
for _, c := range runtime2.List() {
|
| 625 | 625 |
if c.State.Running {
|
| 626 |
- t.Logf("Running container found: %v (%v)", c.ID, c.Path)
|
|
| 627 | 626 |
runningCount++ |
| 628 | 627 |
} |
| 629 | 628 |
} |
| ... | ... |
@@ -638,7 +634,6 @@ func TestReloadContainerLinks(t *testing.T) {
|
| 638 | 638 |
t.Fatalf("Container 2 %s should be registered first in the runtime", container2.ID)
|
| 639 | 639 |
} |
| 640 | 640 |
|
| 641 |
- t.Logf("Number of links: %d", runtime2.containerGraph.Refs("0"))
|
|
| 642 | 641 |
// Verify that the link is still registered in the runtime |
| 643 | 642 |
entity := runtime2.containerGraph.Get(container1.Name) |
| 644 | 643 |
if entity == nil {
|
| ... | ... |
@@ -833,3 +828,19 @@ func TestGetAllChildren(t *testing.T) {
|
| 833 | 833 |
} |
| 834 | 834 |
} |
| 835 | 835 |
} |
| 836 |
+ |
|
| 837 |
+func TestGetFullName(t *testing.T) {
|
|
| 838 |
+ runtime := mkRuntime(t) |
|
| 839 |
+ defer nuke(runtime) |
|
| 840 |
+ |
|
| 841 |
+ name, err := runtime.getFullName("testing")
|
|
| 842 |
+ if err != nil {
|
|
| 843 |
+ t.Fatal(err) |
|
| 844 |
+ } |
|
| 845 |
+ if name != "/testing" {
|
|
| 846 |
+ t.Fatalf("Expected /testing got %s", name)
|
|
| 847 |
+ } |
|
| 848 |
+ if _, err := runtime.getFullName(""); err == nil {
|
|
| 849 |
+ t.Fatal("Error should not be nil")
|
|
| 850 |
+ } |
|
| 851 |
+} |
| ... | ... |
@@ -5,7 +5,9 @@ import ( |
| 5 | 5 |
"encoding/json" |
| 6 | 6 |
"errors" |
| 7 | 7 |
"fmt" |
| 8 |
+ "github.com/dotcloud/docker/archive" |
|
| 8 | 9 |
"github.com/dotcloud/docker/auth" |
| 10 |
+ "github.com/dotcloud/docker/engine" |
|
| 9 | 11 |
"github.com/dotcloud/docker/gograph" |
| 10 | 12 |
"github.com/dotcloud/docker/registry" |
| 11 | 13 |
"github.com/dotcloud/docker/utils" |
| ... | ... |
@@ -16,11 +18,13 @@ import ( |
| 16 | 16 |
"net/url" |
| 17 | 17 |
"os" |
| 18 | 18 |
"os/exec" |
| 19 |
+ "os/signal" |
|
| 19 | 20 |
"path" |
| 20 | 21 |
"path/filepath" |
| 21 | 22 |
"runtime" |
| 22 | 23 |
"strings" |
| 23 | 24 |
"sync" |
| 25 |
+ "syscall" |
|
| 24 | 26 |
"time" |
| 25 | 27 |
) |
| 26 | 28 |
|
| ... | ... |
@@ -28,6 +32,70 @@ func (srv *Server) Close() error {
|
| 28 | 28 |
return srv.runtime.Close() |
| 29 | 29 |
} |
| 30 | 30 |
|
| 31 |
+func init() {
|
|
| 32 |
+ engine.Register("serveapi", JobServeApi)
|
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+func JobServeApi(job *engine.Job) string {
|
|
| 36 |
+ srv, err := NewServer(ConfigFromJob(job)) |
|
| 37 |
+ if err != nil {
|
|
| 38 |
+ return err.Error() |
|
| 39 |
+ } |
|
| 40 |
+ defer srv.Close() |
|
| 41 |
+ if err := srv.Daemon(); err != nil {
|
|
| 42 |
+ return err.Error() |
|
| 43 |
+ } |
|
| 44 |
+ return "0" |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+// Daemon runs the remote api server `srv` as a daemon, |
|
| 48 |
+// Only one api server can run at the same time - this is enforced by a pidfile. |
|
| 49 |
+// The signals SIGINT, SIGKILL and SIGTERM are intercepted for cleanup. |
|
| 50 |
+func (srv *Server) Daemon() error {
|
|
| 51 |
+ if err := utils.CreatePidFile(srv.runtime.config.Pidfile); err != nil {
|
|
| 52 |
+ log.Fatal(err) |
|
| 53 |
+ } |
|
| 54 |
+ defer utils.RemovePidFile(srv.runtime.config.Pidfile) |
|
| 55 |
+ |
|
| 56 |
+ c := make(chan os.Signal, 1) |
|
| 57 |
+ signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) |
|
| 58 |
+ go func() {
|
|
| 59 |
+ sig := <-c |
|
| 60 |
+ log.Printf("Received signal '%v', exiting\n", sig)
|
|
| 61 |
+ utils.RemovePidFile(srv.runtime.config.Pidfile) |
|
| 62 |
+ srv.Close() |
|
| 63 |
+ os.Exit(0) |
|
| 64 |
+ }() |
|
| 65 |
+ |
|
| 66 |
+ protoAddrs := srv.runtime.config.ProtoAddresses |
|
| 67 |
+ chErrors := make(chan error, len(protoAddrs)) |
|
| 68 |
+ for _, protoAddr := range protoAddrs {
|
|
| 69 |
+ protoAddrParts := strings.SplitN(protoAddr, "://", 2) |
|
| 70 |
+ switch protoAddrParts[0] {
|
|
| 71 |
+ case "unix": |
|
| 72 |
+ if err := syscall.Unlink(protoAddrParts[1]); err != nil && !os.IsNotExist(err) {
|
|
| 73 |
+ log.Fatal(err) |
|
| 74 |
+ } |
|
| 75 |
+ case "tcp": |
|
| 76 |
+ if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") {
|
|
| 77 |
+ log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
|
| 78 |
+ } |
|
| 79 |
+ default: |
|
| 80 |
+ return fmt.Errorf("Invalid protocol format.")
|
|
| 81 |
+ } |
|
| 82 |
+ go func() {
|
|
| 83 |
+ chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], srv, true) |
|
| 84 |
+ }() |
|
| 85 |
+ } |
|
| 86 |
+ for i := 0; i < len(protoAddrs); i += 1 {
|
|
| 87 |
+ err := <-chErrors |
|
| 88 |
+ if err != nil {
|
|
| 89 |
+ return err |
|
| 90 |
+ } |
|
| 91 |
+ } |
|
| 92 |
+ return nil |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 31 | 95 |
func (srv *Server) DockerVersion() APIVersion {
|
| 32 | 96 |
return APIVersion{
|
| 33 | 97 |
Version: VERSION, |
| ... | ... |
@@ -118,8 +186,8 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
|
| 118 | 118 |
return fmt.Errorf("No such container: %s", name)
|
| 119 | 119 |
} |
| 120 | 120 |
|
| 121 |
-func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
|
|
| 122 |
- r, err := registry.NewRegistry(srv.runtime.config.GraphPath, nil, srv.HTTPRequestFactory(nil)) |
|
| 121 |
+func (srv *Server) ImagesSearch(term string) ([]registry.SearchResult, error) {
|
|
| 122 |
+ r, err := registry.NewRegistry(srv.runtime.config.Root, nil, srv.HTTPRequestFactory(nil)) |
|
| 123 | 123 |
if err != nil {
|
| 124 | 124 |
return nil, err |
| 125 | 125 |
} |
| ... | ... |
@@ -127,15 +195,7 @@ func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
|
| 127 | 127 |
if err != nil {
|
| 128 | 128 |
return nil, err |
| 129 | 129 |
} |
| 130 |
- |
|
| 131 |
- var outs []APISearch |
|
| 132 |
- for _, repo := range results.Results {
|
|
| 133 |
- var out APISearch |
|
| 134 |
- out.Description = repo["description"] |
|
| 135 |
- out.Name = repo["name"] |
|
| 136 |
- outs = append(outs, out) |
|
| 137 |
- } |
|
| 138 |
- return outs, nil |
|
| 130 |
+ return results.Results, nil |
|
| 139 | 131 |
} |
| 140 | 132 |
|
| 141 | 133 |
func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.StreamFormatter) (string, error) {
|
| ... | ... |
@@ -320,10 +380,11 @@ func (srv *Server) ImageHistory(name string) ([]APIHistory, error) {
|
| 320 | 320 |
outs := []APIHistory{} //produce [] when empty instead of 'null'
|
| 321 | 321 |
err = image.WalkHistory(func(img *Image) error {
|
| 322 | 322 |
var out APIHistory |
| 323 |
- out.ID = srv.runtime.repositories.ImageName(img.ShortID()) |
|
| 323 |
+ out.ID = img.ID |
|
| 324 | 324 |
out.Created = img.Created.Unix() |
| 325 | 325 |
out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ") |
| 326 | 326 |
out.Tags = lookupMap[img.ID] |
| 327 |
+ out.Size = img.Size |
|
| 327 | 328 |
outs = append(outs, out) |
| 328 | 329 |
return nil |
| 329 | 330 |
}) |
| ... | ... |
@@ -350,7 +411,11 @@ func (srv *Server) ContainerTop(name, ps_args string) (*APITop, error) {
|
| 350 | 350 |
} |
| 351 | 351 |
// no scanner.Text because we skip container id |
| 352 | 352 |
for scanner.Scan() {
|
| 353 |
- words = append(words, scanner.Text()) |
|
| 353 |
+ if i != 0 && len(words) == len(procs.Titles) {
|
|
| 354 |
+ words[len(words)-1] = fmt.Sprintf("%s %s", words[len(words)-1], scanner.Text())
|
|
| 355 |
+ } else {
|
|
| 356 |
+ words = append(words, scanner.Text()) |
|
| 357 |
+ } |
|
| 354 | 358 |
} |
| 355 | 359 |
if i == 0 {
|
| 356 | 360 |
procs.Titles = words |
| ... | ... |
@@ -663,7 +728,7 @@ func (srv *Server) poolRemove(kind, key string) error {
|
| 663 | 663 |
} |
| 664 | 664 |
|
| 665 | 665 |
func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig, metaHeaders map[string][]string, parallel bool) error {
|
| 666 |
- r, err := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders)) |
|
| 666 |
+ r, err := registry.NewRegistry(srv.runtime.config.Root, authConfig, srv.HTTPRequestFactory(metaHeaders)) |
|
| 667 | 667 |
if err != nil {
|
| 668 | 668 |
return err |
| 669 | 669 |
} |
| ... | ... |
@@ -836,7 +901,7 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID, |
| 836 | 836 |
return "", err |
| 837 | 837 |
} |
| 838 | 838 |
|
| 839 |
- layerData, err := srv.runtime.graph.TempLayerArchive(imgID, Uncompressed, sf, out) |
|
| 839 |
+ layerData, err := srv.runtime.graph.TempLayerArchive(imgID, archive.Uncompressed, sf, out) |
|
| 840 | 840 |
if err != nil {
|
| 841 | 841 |
return "", fmt.Errorf("Failed to generate layer archive: %s", err)
|
| 842 | 842 |
} |
| ... | ... |
@@ -872,7 +937,7 @@ func (srv *Server) ImagePush(localName string, out io.Writer, sf *utils.StreamFo |
| 872 | 872 |
|
| 873 | 873 |
out = utils.NewWriteFlusher(out) |
| 874 | 874 |
img, err := srv.runtime.graph.Get(localName) |
| 875 |
- r, err2 := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders)) |
|
| 875 |
+ r, err2 := registry.NewRegistry(srv.runtime.config.Root, authConfig, srv.HTTPRequestFactory(metaHeaders)) |
|
| 876 | 876 |
if err2 != nil {
|
| 877 | 877 |
return err2 |
| 878 | 878 |
} |
| ... | ... |
@@ -985,7 +1050,10 @@ func (srv *Server) ContainerDestroy(name string, removeVolume, removeLink bool) |
| 985 | 985 |
if container == nil {
|
| 986 | 986 |
return fmt.Errorf("No such link: %s", name)
|
| 987 | 987 |
} |
| 988 |
- name = srv.runtime.getFullName(name) |
|
| 988 |
+ name, err := srv.runtime.getFullName(name) |
|
| 989 |
+ if err != nil {
|
|
| 990 |
+ return err |
|
| 991 |
+ } |
|
| 989 | 992 |
parent, n := path.Split(name) |
| 990 | 993 |
if parent == "/" {
|
| 991 | 994 |
return fmt.Errorf("Conflict, cannot remove the default name of the container")
|
| ... | ... |
@@ -1238,7 +1306,7 @@ func (srv *Server) RegisterLinks(name string, hostConfig *HostConfig) error {
|
| 1238 | 1238 |
// After we load all the links into the runtime |
| 1239 | 1239 |
// set them to nil on the hostconfig |
| 1240 | 1240 |
hostConfig.Links = nil |
| 1241 |
- if err := container.SaveHostConfig(hostConfig); err != nil {
|
|
| 1241 |
+ if err := container.writeHostConfig(); err != nil {
|
|
| 1242 | 1242 |
return err |
| 1243 | 1243 |
} |
| 1244 | 1244 |
} |
| ... | ... |
@@ -1248,11 +1316,33 @@ func (srv *Server) RegisterLinks(name string, hostConfig *HostConfig) error {
|
| 1248 | 1248 |
func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error {
|
| 1249 | 1249 |
runtime := srv.runtime |
| 1250 | 1250 |
container := runtime.Get(name) |
| 1251 |
+ |
|
| 1252 |
+ if hostConfig != nil {
|
|
| 1253 |
+ for _, bind := range hostConfig.Binds {
|
|
| 1254 |
+ splitBind := strings.Split(bind, ":") |
|
| 1255 |
+ source := splitBind[0] |
|
| 1256 |
+ |
|
| 1257 |
+ // refuse to bind mount "/" to the container |
|
| 1258 |
+ if source == "/" {
|
|
| 1259 |
+ return fmt.Errorf("Invalid bind mount '%s' : source can't be '/'", bind)
|
|
| 1260 |
+ } |
|
| 1261 |
+ |
|
| 1262 |
+ // ensure the source exists on the host |
|
| 1263 |
+ _, err := os.Stat(source) |
|
| 1264 |
+ if err != nil && os.IsNotExist(err) {
|
|
| 1265 |
+ return fmt.Errorf("Invalid bind mount '%s' : source doesn't exist", bind)
|
|
| 1266 |
+ } |
|
| 1267 |
+ } |
|
| 1268 |
+ } |
|
| 1269 |
+ |
|
| 1251 | 1270 |
if container == nil {
|
| 1252 | 1271 |
return fmt.Errorf("No such container: %s", name)
|
| 1253 | 1272 |
} |
| 1254 |
- |
|
| 1255 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 1273 |
+ if hostConfig != nil {
|
|
| 1274 |
+ container.hostConfig = hostConfig |
|
| 1275 |
+ container.ToDisk() |
|
| 1276 |
+ } |
|
| 1277 |
+ if err := container.Start(); err != nil {
|
|
| 1256 | 1278 |
return fmt.Errorf("Cannot start container %s: %s", name, err)
|
| 1257 | 1279 |
} |
| 1258 | 1280 |
srv.LogEvent("start", container.ShortID(), runtime.repositories.ImageName(container.Image))
|
| ... | ... |
@@ -1409,9 +1499,6 @@ func (srv *Server) ContainerCopy(name string, resource string, out io.Writer) er |
| 1409 | 1409 |
} |
| 1410 | 1410 |
|
| 1411 | 1411 |
func NewServer(config *DaemonConfig) (*Server, error) {
|
| 1412 |
- if runtime.GOARCH != "amd64" {
|
|
| 1413 |
- log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
|
|
| 1414 |
- } |
|
| 1415 | 1412 |
runtime, err := NewRuntime(config) |
| 1416 | 1413 |
if err != nil {
|
| 1417 | 1414 |
return nil, err |
| ... | ... |
@@ -246,14 +246,14 @@ func TestContainerTop(t *testing.T) {
|
| 246 | 246 |
|
| 247 | 247 |
srv := &Server{runtime: runtime}
|
| 248 | 248 |
|
| 249 |
- c, hostConfig, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t)
|
|
| 250 |
- c, hostConfig, err := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t)
|
|
| 249 |
+ c, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t)
|
|
| 250 |
+ c, err := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t)
|
|
| 251 | 251 |
if err != nil {
|
| 252 | 252 |
t.Fatal(err) |
| 253 | 253 |
} |
| 254 | 254 |
|
| 255 | 255 |
defer runtime.Destroy(c) |
| 256 |
- if err := c.Start(hostConfig); err != nil {
|
|
| 256 |
+ if err := c.Start(); err != nil {
|
|
| 257 | 257 |
t.Fatal(err) |
| 258 | 258 |
} |
| 259 | 259 |
|
| ... | ... |
@@ -1,10 +1,12 @@ |
| 1 | 1 |
package sysinit |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "encoding/json" |
|
| 4 | 5 |
"flag" |
| 5 | 6 |
"fmt" |
| 6 | 7 |
"github.com/dotcloud/docker/netlink" |
| 7 | 8 |
"github.com/dotcloud/docker/utils" |
| 9 |
+ "io/ioutil" |
|
| 8 | 10 |
"log" |
| 9 | 11 |
"net" |
| 10 | 12 |
"os" |
| ... | ... |
@@ -69,9 +71,18 @@ func changeUser(u string) {
|
| 69 | 69 |
} |
| 70 | 70 |
|
| 71 | 71 |
// Clear environment pollution introduced by lxc-start |
| 72 |
-func cleanupEnv(env utils.ListOpts) {
|
|
| 72 |
+func cleanupEnv() {
|
|
| 73 | 73 |
os.Clearenv() |
| 74 |
- for _, kv := range env {
|
|
| 74 |
+ var lines []string |
|
| 75 |
+ content, err := ioutil.ReadFile("/.dockerenv")
|
|
| 76 |
+ if err != nil {
|
|
| 77 |
+ log.Fatalf("Unable to load environment variables: %v", err)
|
|
| 78 |
+ } |
|
| 79 |
+ err = json.Unmarshal(content, &lines) |
|
| 80 |
+ if err != nil {
|
|
| 81 |
+ log.Fatalf("Unable to unmarshal environment variables: %v", err)
|
|
| 82 |
+ } |
|
| 83 |
+ for _, kv := range lines {
|
|
| 75 | 84 |
parts := strings.SplitN(kv, "=", 2) |
| 76 | 85 |
if len(parts) == 1 {
|
| 77 | 86 |
parts = append(parts, "") |
| ... | ... |
@@ -104,12 +115,9 @@ func SysInit() {
|
| 104 | 104 |
var gw = flag.String("g", "", "gateway address")
|
| 105 | 105 |
var workdir = flag.String("w", "", "workdir")
|
| 106 | 106 |
|
| 107 |
- var flEnv utils.ListOpts |
|
| 108 |
- flag.Var(&flEnv, "e", "Set environment variables") |
|
| 109 |
- |
|
| 110 | 107 |
flag.Parse() |
| 111 | 108 |
|
| 112 |
- cleanupEnv(flEnv) |
|
| 109 |
+ cleanupEnv() |
|
| 113 | 110 |
setupNetworking(*gw) |
| 114 | 111 |
setupWorkingDirectory(*workdir) |
| 115 | 112 |
changeUser(*u) |
| ... | ... |
@@ -225,6 +225,12 @@ func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding,
|
| 225 | 225 |
if containerPort == "" {
|
| 226 | 226 |
return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
|
| 227 | 227 |
} |
| 228 |
+ if _, err := strconv.ParseUint(containerPort, 10, 16); err != nil {
|
|
| 229 |
+ return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
|
|
| 230 |
+ } |
|
| 231 |
+ if _, err := strconv.ParseUint(hostPort, 10, 16); hostPort != "" && err != nil {
|
|
| 232 |
+ return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
|
|
| 233 |
+ } |
|
| 228 | 234 |
|
| 229 | 235 |
port := NewPort(proto, containerPort) |
| 230 | 236 |
if _, exists := exposedPorts[port]; !exists {
|
| ... | ... |
@@ -304,10 +310,6 @@ func (c *checker) Exists(name string) bool {
|
| 304 | 304 |
} |
| 305 | 305 |
|
| 306 | 306 |
// Generate a random and unique name |
| 307 |
-func generateRandomName(runtime *Runtime) string {
|
|
| 308 |
- n, err := namesgenerator.GenerateRandomName(&checker{runtime})
|
|
| 309 |
- if err != nil {
|
|
| 310 |
- panic(err) |
|
| 311 |
- } |
|
| 312 |
- return n |
|
| 307 |
+func generateRandomName(runtime *Runtime) (string, error) {
|
|
| 308 |
+ return namesgenerator.GenerateRandomName(&checker{runtime})
|
|
| 313 | 309 |
} |
| 314 | 310 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,36 @@ |
| 0 |
+package utils |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "os" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "io/ioutil" |
|
| 6 |
+ "log" |
|
| 7 |
+ "strconv" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func CreatePidFile(pidfile string) error {
|
|
| 11 |
+ if pidString, err := ioutil.ReadFile(pidfile); err == nil {
|
|
| 12 |
+ pid, err := strconv.Atoi(string(pidString)) |
|
| 13 |
+ if err == nil {
|
|
| 14 |
+ if _, err := os.Stat(fmt.Sprintf("/proc/%d/", pid)); err == nil {
|
|
| 15 |
+ return fmt.Errorf("pid file found, ensure docker is not running or delete %s", pidfile)
|
|
| 16 |
+ } |
|
| 17 |
+ } |
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ file, err := os.Create(pidfile) |
|
| 21 |
+ if err != nil {
|
|
| 22 |
+ return err |
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ defer file.Close() |
|
| 26 |
+ |
|
| 27 |
+ _, err = fmt.Fprintf(file, "%d", os.Getpid()) |
|
| 28 |
+ return err |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+func RemovePidFile(pidfile string) {
|
|
| 32 |
+ if err := os.Remove(pidfile); err != nil {
|
|
| 33 |
+ log.Printf("Error removing %s: %s", pidfile, err)
|
|
| 34 |
+ } |
|
| 35 |
+} |
| 0 | 36 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,16 @@ |
| 0 |
+package utils |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "io" |
|
| 4 |
+ "crypto/rand" |
|
| 5 |
+ "encoding/hex" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func RandomString() string {
|
|
| 9 |
+ id := make([]byte, 32) |
|
| 10 |
+ _, err := io.ReadFull(rand.Reader, id) |
|
| 11 |
+ if err != nil {
|
|
| 12 |
+ panic(err) // This shouldn't happen |
|
| 13 |
+ } |
|
| 14 |
+ return hex.EncodeToString(id) |
|
| 15 |
+} |
| ... | ... |
@@ -16,6 +16,7 @@ import ( |
| 16 | 16 |
"os/exec" |
| 17 | 17 |
"path/filepath" |
| 18 | 18 |
"runtime" |
| 19 |
+ "regexp" |
|
| 19 | 20 |
"strconv" |
| 20 | 21 |
"strings" |
| 21 | 22 |
"sync" |
| ... | ... |
@@ -695,6 +696,13 @@ func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
|
| 695 | 695 |
return n, err |
| 696 | 696 |
} |
| 697 | 697 |
|
| 698 |
+// Flush the stream immediately. |
|
| 699 |
+func (wf *WriteFlusher) Flush() {
|
|
| 700 |
+ wf.Lock() |
|
| 701 |
+ defer wf.Unlock() |
|
| 702 |
+ wf.flusher.Flush() |
|
| 703 |
+} |
|
| 704 |
+ |
|
| 698 | 705 |
func NewWriteFlusher(w io.Writer) *WriteFlusher {
|
| 699 | 706 |
var flusher http.Flusher |
| 700 | 707 |
if f, ok := w.(http.Flusher); ok {
|
| ... | ... |
@@ -896,6 +904,23 @@ func StripComments(input []byte, commentMarker []byte) []byte {
|
| 896 | 896 |
return output |
| 897 | 897 |
} |
| 898 | 898 |
|
| 899 |
+// GetNameserversAsCIDR returns nameservers (if any) listed in |
|
| 900 |
+// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") |
|
| 901 |
+// This function's output is intended for net.ParseCIDR |
|
| 902 |
+func GetNameserversAsCIDR(resolvConf []byte) []string {
|
|
| 903 |
+ var parsedResolvConf = StripComments(resolvConf, []byte("#"))
|
|
| 904 |
+ nameservers := []string{}
|
|
| 905 |
+ re := regexp.MustCompile(`^\s*nameserver\s*(([0-9]\.){3}([0-9]))\s*$`)
|
|
| 906 |
+ for _, line := range bytes.Split(parsedResolvConf, []byte("\n")) {
|
|
| 907 |
+ var ns = re.FindSubmatch(line) |
|
| 908 |
+ if len(ns) > 0 {
|
|
| 909 |
+ nameservers = append(nameservers, string(ns[1])+"/32") |
|
| 910 |
+ } |
|
| 911 |
+ } |
|
| 912 |
+ |
|
| 913 |
+ return nameservers |
|
| 914 |
+} |
|
| 915 |
+ |
|
| 899 | 916 |
func ParseHost(host string, port int, addr string) (string, error) {
|
| 900 | 917 |
var proto string |
| 901 | 918 |
switch {
|
| ... | ... |
@@ -444,3 +444,41 @@ func TestParsePortMapping(t *testing.T) {
|
| 444 | 444 |
t.Fail() |
| 445 | 445 |
} |
| 446 | 446 |
} |
| 447 |
+ |
|
| 448 |
+func TestGetNameserversAsCIDR(t *testing.T) {
|
|
| 449 |
+ for resolv, result := range map[string][]string{`
|
|
| 450 |
+nameserver 1.2.3.4 |
|
| 451 |
+nameserver 4.3.2.1 |
|
| 452 |
+search example.com`: {"1.2.3.4/32", "4.3.2.1/32"},
|
|
| 453 |
+ `search example.com`: {},
|
|
| 454 |
+ `nameserver 1.2.3.4 |
|
| 455 |
+search example.com |
|
| 456 |
+nameserver 4.3.2.1`: []string{"1.2.3.4/32", "4.3.2.1/32"},
|
|
| 457 |
+ ``: []string{},
|
|
| 458 |
+ ` nameserver 1.2.3.4 `: []string{"1.2.3.4/32"},
|
|
| 459 |
+ `search example.com |
|
| 460 |
+nameserver 1.2.3.4 |
|
| 461 |
+#nameserver 4.3.2.1`: []string{"1.2.3.4/32"},
|
|
| 462 |
+ `search example.com |
|
| 463 |
+nameserver 1.2.3.4 # not 4.3.2.1`: []string{"1.2.3.4/32"},
|
|
| 464 |
+ } {
|
|
| 465 |
+ test := GetNameserversAsCIDR([]byte(resolv)) |
|
| 466 |
+ if !StrSlicesEqual(test, result) {
|
|
| 467 |
+ t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv)
|
|
| 468 |
+ } |
|
| 469 |
+ } |
|
| 470 |
+} |
|
| 471 |
+ |
|
| 472 |
+func StrSlicesEqual(a, b []string) bool {
|
|
| 473 |
+ if len(a) != len(b) {
|
|
| 474 |
+ return false |
|
| 475 |
+ } |
|
| 476 |
+ |
|
| 477 |
+ for i, v := range a {
|
|
| 478 |
+ if v != b[i] {
|
|
| 479 |
+ return false |
|
| 480 |
+ } |
|
| 481 |
+ } |
|
| 482 |
+ |
|
| 483 |
+ return true |
|
| 484 |
+} |
| ... | ... |
@@ -67,7 +67,7 @@ func newTestRuntime(prefix string) (runtime *Runtime, err error) {
|
| 67 | 67 |
} |
| 68 | 68 |
|
| 69 | 69 |
config := &DaemonConfig{
|
| 70 |
- GraphPath: root, |
|
| 70 |
+ Root: root, |
|
| 71 | 71 |
AutoRestart: false, |
| 72 | 72 |
} |
| 73 | 73 |
runtime, err = NewRuntimeFromDirectory(config) |
| ... | ... |
@@ -116,7 +116,7 @@ func readFile(src string, t *testing.T) (content string) {
|
| 116 | 116 |
// dynamically replaced by the current test image. |
| 117 | 117 |
// The caller is responsible for destroying the container. |
| 118 | 118 |
// Call t.Fatal() at the first error. |
| 119 |
-func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConfig, error) {
|
|
| 119 |
+func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, error) {
|
|
| 120 | 120 |
config, hostConfig, _, err := ParseRun(args, nil) |
| 121 | 121 |
defer func() {
|
| 122 | 122 |
if err != nil && t != nil {
|
| ... | ... |
@@ -124,16 +124,17 @@ func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConf |
| 124 | 124 |
} |
| 125 | 125 |
}() |
| 126 | 126 |
if err != nil {
|
| 127 |
- return nil, nil, err |
|
| 127 |
+ return nil, err |
|
| 128 | 128 |
} |
| 129 | 129 |
if config.Image == "_" {
|
| 130 | 130 |
config.Image = GetTestImage(r).ID |
| 131 | 131 |
} |
| 132 | 132 |
c, _, err := r.Create(config, "") |
| 133 | 133 |
if err != nil {
|
| 134 |
- return nil, nil, err |
|
| 134 |
+ return nil, err |
|
| 135 | 135 |
} |
| 136 |
- return c, hostConfig, nil |
|
| 136 |
+ c.hostConfig = hostConfig |
|
| 137 |
+ return c, nil |
|
| 137 | 138 |
} |
| 138 | 139 |
|
| 139 | 140 |
// Create a test container, start it, wait for it to complete, destroy it, |
| ... | ... |
@@ -146,7 +147,7 @@ func runContainer(r *Runtime, args []string, t *testing.T) (output string, err e |
| 146 | 146 |
t.Fatal(err) |
| 147 | 147 |
} |
| 148 | 148 |
}() |
| 149 |
- container, hostConfig, err := mkContainer(r, args, t) |
|
| 149 |
+ container, err := mkContainer(r, args, t) |
|
| 150 | 150 |
if err != nil {
|
| 151 | 151 |
return "", err |
| 152 | 152 |
} |
| ... | ... |
@@ -156,7 +157,7 @@ func runContainer(r *Runtime, args []string, t *testing.T) (output string, err e |
| 156 | 156 |
return "", err |
| 157 | 157 |
} |
| 158 | 158 |
defer stdout.Close() |
| 159 |
- if err := container.Start(hostConfig); err != nil {
|
|
| 159 |
+ if err := container.Start(); err != nil {
|
|
| 160 | 160 |
return "", err |
| 161 | 161 |
} |
| 162 | 162 |
container.Wait() |