mirror of
https://github.com/moby/moby.git
synced 2026-01-12 19:21:41 +00:00
Compare commits
395 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8502ad4ba7 | ||
|
|
58ec7855bc | ||
|
|
949fde88df | ||
|
|
5a9f45cb7a | ||
|
|
8f4a54734f | ||
|
|
9359d79c4f | ||
|
|
69db6ea867 | ||
|
|
3b89187d03 | ||
|
|
82a47b0e82 | ||
|
|
e0f07bc186 | ||
|
|
194eb246ef | ||
|
|
81e596e272 | ||
|
|
acfdfa81be | ||
|
|
7fd6dcc831 | ||
|
|
add97f7eb0 | ||
|
|
5f55784224 | ||
|
|
f3816ee024 | ||
|
|
0b3e153588 | ||
|
|
2226989410 | ||
|
|
c23b15b9d8 | ||
|
|
055b32e3f4 | ||
|
|
907d9ce13c | ||
|
|
74d45789dd | ||
|
|
40522c0380 | ||
|
|
d5bb0ff80a | ||
|
|
ad80da3389 | ||
|
|
1f80c2a652 | ||
|
|
1bc3f6b7b5 | ||
|
|
643621133f | ||
|
|
fd240413ff | ||
|
|
392b1e99b2 | ||
|
|
0dfebf2d93 | ||
|
|
40aaebe56a | ||
|
|
a1dba16fe8 | ||
|
|
e31f1f1eba | ||
|
|
7e720d0a77 | ||
|
|
237868e9c3 | ||
|
|
fc197188d7 | ||
|
|
d59080d119 | ||
|
|
484a75f354 | ||
|
|
434cf6c8ca | ||
|
|
e93b7b4647 | ||
|
|
06a818616b | ||
|
|
f50b8b08b5 | ||
|
|
cda146547e | ||
|
|
a17fd7b294 | ||
|
|
22162687df | ||
|
|
d256f3049b | ||
|
|
a1a4a99d7e | ||
|
|
4986958e7e | ||
|
|
cd735496da | ||
|
|
894d4a23fb | ||
|
|
fc9f4d8bad | ||
|
|
1d4b7d8fa1 | ||
|
|
360078d761 | ||
|
|
808f2d39bd | ||
|
|
d1ca12e81b | ||
|
|
a042c9fb1b | ||
|
|
721bb410f6 | ||
|
|
029625981d | ||
|
|
0fccf0f686 | ||
|
|
efaf2cac5c | ||
|
|
cb1fe939a8 | ||
|
|
c654aea4f2 | ||
|
|
d2d8a4a6c5 | ||
|
|
4100e9b7df | ||
|
|
5875953d9b | ||
|
|
f4ce106e02 | ||
|
|
7ec1236cee | ||
|
|
2b4bb67ce0 | ||
|
|
6155f07561 | ||
|
|
e6e35e5984 | ||
|
|
0d207abf8e | ||
|
|
a009d4ae8d | ||
|
|
b75f385abd | ||
|
|
7ce7516c12 | ||
|
|
f6b91262a7 | ||
|
|
d16d748132 | ||
|
|
3fc9de3d03 | ||
|
|
652c2c2a80 | ||
|
|
8e7db0432e | ||
|
|
e1a15b25dc | ||
|
|
b1a3a55802 | ||
|
|
614bc5c1e1 | ||
|
|
3fe4d5477a | ||
|
|
cda24e345c | ||
|
|
88037b2877 | ||
|
|
6cdd1aa350 | ||
|
|
ea8a3438f7 | ||
|
|
954158ce52 | ||
|
|
bf17383e35 | ||
|
|
83d81758b0 | ||
|
|
e3b878ce98 | ||
|
|
1e5f9334e0 | ||
|
|
3edbf416bf | ||
|
|
c2364b978d | ||
|
|
158e3d60ec | ||
|
|
e4e579b40d | ||
|
|
071528e103 | ||
|
|
a2fcd3d8f0 | ||
|
|
7d2e851d8e | ||
|
|
85f9b778f5 | ||
|
|
369cde4ad7 | ||
|
|
3ffc52bcf5 | ||
|
|
8dcca2125a | ||
|
|
cdd14b1a31 | ||
|
|
37ed178611 | ||
|
|
c995c9bb91 | ||
|
|
aa619de748 | ||
|
|
6fde28c293 | ||
|
|
f4358fc647 | ||
|
|
57e19b1475 | ||
|
|
8051b6c1a1 | ||
|
|
566ff54d0d | ||
|
|
f9359f59a8 | ||
|
|
e4561438f1 | ||
|
|
f7ba1c34bb | ||
|
|
df87919165 | ||
|
|
733bf5d3dd | ||
|
|
efde305c05 | ||
|
|
636dfc82b0 | ||
|
|
93abcc3a3b | ||
|
|
c3ec696284 | ||
|
|
fdd81b423b | ||
|
|
cd89fe5c4f | ||
|
|
1636ed9826 | ||
|
|
8072d3a4e0 | ||
|
|
d215724ad6 | ||
|
|
0e6f0c4e02 | ||
|
|
629cc2fce4 | ||
|
|
8c52140059 | ||
|
|
f21bd80e90 | ||
|
|
4bdd4599f0 | ||
|
|
ed93dab9a8 | ||
|
|
62a81370ff | ||
|
|
e74c65c3db | ||
|
|
248eadd341 | ||
|
|
e829d5b6d2 | ||
|
|
35d8ac94f3 | ||
|
|
94821a3353 | ||
|
|
d14c162fd6 | ||
|
|
14d1c5a2c3 | ||
|
|
329d154209 | ||
|
|
7bc96aec7b | ||
|
|
a6fdc5d208 | ||
|
|
681b40c801 | ||
|
|
536da93380 | ||
|
|
45d7dcfea2 | ||
|
|
210fa0871c | ||
|
|
f768c6adb7 | ||
|
|
fde909ffb8 | ||
|
|
553b4dae45 | ||
|
|
929662a4d5 | ||
|
|
fbac812540 | ||
|
|
e481c82fa9 | ||
|
|
73a1ef7c22 | ||
|
|
c91c365f88 | ||
|
|
b8a4f570fb | ||
|
|
70c7220a99 | ||
|
|
0f45e3c6e0 | ||
|
|
be0beb897a | ||
|
|
8fa4c4b062 | ||
|
|
c06ab5f9c2 | ||
|
|
3ec39ad01a | ||
|
|
1940015824 | ||
|
|
1acefac97e | ||
|
|
f630fbc7cf | ||
|
|
e61f327ec9 | ||
|
|
c4444ce48f | ||
|
|
7ba0f1f421 | ||
|
|
30454bb85c | ||
|
|
2deb0c3365 | ||
|
|
efc0610c0e | ||
|
|
391676b598 | ||
|
|
5204feeaa9 | ||
|
|
81d112cb7f | ||
|
|
25be0b1e98 | ||
|
|
c56b045270 | ||
|
|
d9a1cc7e2b | ||
|
|
30b4a0f76a | ||
|
|
7d95145b76 | ||
|
|
379a7fab07 | ||
|
|
36e060299f | ||
|
|
a96a26c62f | ||
|
|
c3705e83e7 | ||
|
|
5e9b4a23e6 | ||
|
|
a1c5e276f4 | ||
|
|
eddda577a4 | ||
|
|
2ed1001c57 | ||
|
|
f02d766f9a | ||
|
|
2035af44aa | ||
|
|
746ae155fb | ||
|
|
a26801c73f | ||
|
|
670b326c1b | ||
|
|
15a6854119 | ||
|
|
3f9416b58d | ||
|
|
7afd7a82bd | ||
|
|
124da338fd | ||
|
|
69a31c3386 | ||
|
|
20605eb310 | ||
|
|
945a1f06f9 | ||
|
|
64136071c6 | ||
|
|
28b162eeb4 | ||
|
|
e960152a1e | ||
|
|
fe956ad449 | ||
|
|
47375ddf54 | ||
|
|
f0d6a91a1b | ||
|
|
62213ee314 | ||
|
|
fa48f17493 | ||
|
|
41d972baf1 | ||
|
|
b3ad330782 | ||
|
|
6721525068 | ||
|
|
5cfcb05486 | ||
|
|
78c22c24b3 | ||
|
|
4faba4fae7 | ||
|
|
e1efd4cb8c | ||
|
|
606cacdca0 | ||
|
|
d526038503 | ||
|
|
58daccab26 | ||
|
|
12fb508262 | ||
|
|
0a3eedd4c9 | ||
|
|
a6928e70ac | ||
|
|
20197385b2 | ||
|
|
85b9338205 | ||
|
|
51e2c1794b | ||
|
|
20899cdb34 | ||
|
|
f5ab2516d8 | ||
|
|
d5f5ecb658 | ||
|
|
4b5ceb0f24 | ||
|
|
906b481148 | ||
|
|
930ec9f52c | ||
|
|
aaa1c48d24 | ||
|
|
d7123a597f | ||
|
|
9a9ecda7c8 | ||
|
|
071338172c | ||
|
|
4975c1b549 | ||
|
|
73e8a39ff2 | ||
|
|
847cf5b599 | ||
|
|
bf91636558 | ||
|
|
1e85aabf71 | ||
|
|
4fe0a9b6a0 | ||
|
|
f63cdf0260 | ||
|
|
9fb1ba97b1 | ||
|
|
59dc2876a7 | ||
|
|
23ab0af2ff | ||
|
|
b8a16b3459 | ||
|
|
a530b8d981 | ||
|
|
89beb55c32 | ||
|
|
f9328ad9cc | ||
|
|
20759c3ef7 | ||
|
|
5d81776714 | ||
|
|
0ef1ff91cb | ||
|
|
a68d7f3d70 | ||
|
|
a8af12f80a | ||
|
|
10cd902f90 | ||
|
|
818c249bae | ||
|
|
5a89c6f6df | ||
|
|
2e6dbe87ad | ||
|
|
e877294321 | ||
|
|
ecc51cd465 | ||
|
|
f7c7f7978c | ||
|
|
8224e13bd2 | ||
|
|
912bf8ff92 | ||
|
|
e43ff2f6f2 | ||
|
|
b8f1c73705 | ||
|
|
1572989201 | ||
|
|
bd02d6e662 | ||
|
|
2d1f61ef0e | ||
|
|
54df95f26c | ||
|
|
5b33ae5971 | ||
|
|
0db1c60542 | ||
|
|
f216448c82 | ||
|
|
f26a9d456c | ||
|
|
bf5b949ffc | ||
|
|
621523a041 | ||
|
|
8fd9633a6b | ||
|
|
1124261158 | ||
|
|
b722f809e7 | ||
|
|
f396c42cad | ||
|
|
8874f2aef9 | ||
|
|
e8ec3dba7b | ||
|
|
4eda2a54de | ||
|
|
d3292078dc | ||
|
|
6ba456ff87 | ||
|
|
44984602c7 | ||
|
|
d534e1c3a1 | ||
|
|
d56d8ab96e | ||
|
|
6cf8ec606e | ||
|
|
db3019d50b | ||
|
|
42c38bf34d | ||
|
|
11b3fbb3bd | ||
|
|
036f41fde3 | ||
|
|
6e9c1590c6 | ||
|
|
39cc8a32b1 | ||
|
|
31961ccd94 | ||
|
|
eec48f93a3 | ||
|
|
dbe1915fee | ||
|
|
bef8de9319 | ||
|
|
81fc368a6d | ||
|
|
bd292759f0 | ||
|
|
5fd3c8204d | ||
|
|
af21908493 | ||
|
|
7edd1f6bad | ||
|
|
d878632b25 | ||
|
|
be13735001 | ||
|
|
fb9ddc5de5 | ||
|
|
27646c4459 | ||
|
|
b98d51dddb | ||
|
|
0025e9bd71 | ||
|
|
4c6e528f13 | ||
|
|
95f061b408 | ||
|
|
761184df52 | ||
|
|
78b85220be | ||
|
|
8814c11b14 | ||
|
|
09d2c2351c | ||
|
|
c618a906a4 | ||
|
|
9c1e9a5157 | ||
|
|
0b0b0ca0f9 | ||
|
|
ac1093b83a | ||
|
|
c9cedb4c04 | ||
|
|
a74be95b23 | ||
|
|
8291f00a0e | ||
|
|
b7bc80cba9 | ||
|
|
864729b96f | ||
|
|
a67571668e | ||
|
|
776bb43c9e | ||
|
|
75bd5bea70 | ||
|
|
e2ee5c71fc | ||
|
|
f0879a1e14 | ||
|
|
ca405786f4 | ||
|
|
cdc07f7d5c | ||
|
|
f379f667a2 | ||
|
|
45cea94a82 | ||
|
|
8ec96c9605 | ||
|
|
c094807a1b | ||
|
|
bac3a8e6f5 | ||
|
|
dcfc4ada4d | ||
|
|
416b16e1e2 | ||
|
|
f832b76bdf | ||
|
|
d502f0cfac | ||
|
|
16fad96007 | ||
|
|
de35b346d1 | ||
|
|
869a11bc93 | ||
|
|
f806818154 | ||
|
|
a7a171b6c2 | ||
|
|
a80c059bae | ||
|
|
edace08327 | ||
|
|
9656cdf0c2 | ||
|
|
50f3a696bd | ||
|
|
f4676f0ffa | ||
|
|
3c1f3be032 | ||
|
|
aeba4e6482 | ||
|
|
3569d080af | ||
|
|
427bdb60e7 | ||
|
|
9b1930c5a0 | ||
|
|
2546a2c645 | ||
|
|
fdb3de7b11 | ||
|
|
04ffa53ba8 | ||
|
|
07f7643bbc | ||
|
|
228091c79e | ||
|
|
6fa1463614 | ||
|
|
9b644ff246 | ||
|
|
2c646b2d46 | ||
|
|
becb13dc26 | ||
|
|
05f416d869 | ||
|
|
7fd64e0196 | ||
|
|
13da09d22b | ||
|
|
6720bfb243 | ||
|
|
d75fc6e529 | ||
|
|
4a148919c3 | ||
|
|
c7d75588f4 | ||
|
|
dfade9e2d8 | ||
|
|
b655406faa | ||
|
|
a015f38f4a | ||
|
|
02ef8ec3ca | ||
|
|
25d3db048e | ||
|
|
a69bb25820 | ||
|
|
5f5949f6a6 | ||
|
|
58b75f8f29 | ||
|
|
aea7418d8a | ||
|
|
f9147effac | ||
|
|
0e2b0f284c | ||
|
|
80dfa23da8 | ||
|
|
4bea68dfa6 | ||
|
|
ea0ed9a915 | ||
|
|
eac95671f5 | ||
|
|
7ab4f37d60 | ||
|
|
5d022f0445 | ||
|
|
61fbf3d8e2 | ||
|
|
f49eb29497 | ||
|
|
03f8a3bbae | ||
|
|
f95f2789f2 | ||
|
|
5a17c208cd | ||
|
|
04c32495f6 | ||
|
|
6d34c50e89 |
4
AUTHORS
4
AUTHORS
@@ -20,6 +20,7 @@ Antony Messerli <amesserl@rackspace.com>
|
||||
Asbjørn Enge <asbjorn@hanafjedle.net>
|
||||
Barry Allard <barry.allard@gmail.com>
|
||||
Ben Toews <mastahyeti@gmail.com>
|
||||
Ben Wiklund <ben@daisyowl.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
Bhiraj Butala <abhiraj.butala@gmail.com>
|
||||
Bouke Haarsma <bouke@webatoom.nl>
|
||||
@@ -47,6 +48,7 @@ Daniel YC Lin <dlin.tw@gmail.com>
|
||||
Darren Coxall <darren@darrencoxall.com>
|
||||
David Calavera <david.calavera@gmail.com>
|
||||
David Sissitka <me@dsissitka.com>
|
||||
Dinesh Subhraveti <dineshs@altiscale.com>
|
||||
Deni Bertovic <deni@kset.org>
|
||||
Dominik Honnef <dominik@honnef.co>
|
||||
Don Spaulding <donspauldingii@gmail.com>
|
||||
@@ -68,6 +70,7 @@ Francisco Souza <f@souza.cc>
|
||||
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
|
||||
Gabriel Monroy <gabriel@opdemand.com>
|
||||
Gareth Rushgrove <gareth@morethanseven.net>
|
||||
Graydon Hoare <graydon@pobox.com>
|
||||
Greg Thornton <xdissent@me.com>
|
||||
Guillaume J. Charmes <guillaume.charmes@dotcloud.com>
|
||||
Gurjeet Singh <gurjeet@singh.im>
|
||||
@@ -113,6 +116,7 @@ Kyle Conroy <kyle.j.conroy@gmail.com>
|
||||
Laurie Voss <github@seldo.com>
|
||||
Louis Opter <kalessin@kalessin.fr>
|
||||
Manuel Meurer <manuel@krautcomputing.com>
|
||||
Manuel Woelker <docker@manuel.woelker.org>
|
||||
Marco Hennings <marco.hennings@freiheit.com>
|
||||
Marcus Farkas <toothlessgear@finitebox.com>
|
||||
Marcus Ramberg <marcus@nordaaker.com>
|
||||
|
||||
121
CHANGELOG.md
121
CHANGELOG.md
@@ -1,5 +1,104 @@
|
||||
# Changelog
|
||||
|
||||
## 0.7.3 (2013-01-02)
|
||||
|
||||
#### Builder
|
||||
|
||||
+ Update ADD to use the image cache, based on a hash of the added content
|
||||
* Add error message for empty Dockerfile
|
||||
|
||||
#### Documentation
|
||||
|
||||
- Fix outdated link to the "Introduction" on www.docker.io
|
||||
+ Update the docs to get wider when the screen does
|
||||
- Add information about needing to install LXC when using raw binaries
|
||||
* Update Fedora documentation to disentangle the docker and docker.io conflict
|
||||
* Add a note about using the new `-mtu` flag in several GCE zones
|
||||
+ Add FrugalWare installation instructions
|
||||
+ Add a more complete example of `docker run`
|
||||
- Fix API documentation for creating and starting Privileged containers
|
||||
- Add missing "name" parameter documentation on "/containers/create"
|
||||
* Add a mention of `lxc-checkconfig` as a way to check for some of the necessary kernel configuration
|
||||
- Update the 1.8 API documentation with some additions that were added to the docs for 1.7
|
||||
|
||||
#### Hack
|
||||
|
||||
- Add missing libdevmapper dependency to the packagers documentation
|
||||
* Update minimum Go requirement to a hard line at Go 1.2+
|
||||
* Many minor improvements to the Vagrantfile
|
||||
+ Add ability to customize dockerinit search locations when compiling (to be used very sparingly only by packagers of platforms who require a nonstandard location)
|
||||
+ Add coverprofile generation reporting
|
||||
- Add `-a` to our Go build flags, removing the need for recompiling the stdlib manually
|
||||
* Update Dockerfile to be more canonical and have less spurious warnings during build
|
||||
- Fix some miscellaneous `docker pull` progress bar display issues
|
||||
* Migrate more miscellaneous packages under the "pkg" folder
|
||||
* Update TextMate highlighting to automatically be enabled for files named "Dockerfile"
|
||||
* Reorganize syntax highlighting files under a common "contrib/syntax" directory
|
||||
* Update install.sh script (https://get.docker.io/) to not fail if busybox fails to download or run at the end of the Ubuntu/Debian installation
|
||||
* Add support for container names in bash completion
|
||||
|
||||
#### Packaging
|
||||
|
||||
+ Add an official Docker client binary for Darwin (Mac OS X)
|
||||
* Remove empty "Vendor" string and added "License" on deb package
|
||||
+ Add a stubbed version of "/etc/default/docker" in the deb package
|
||||
|
||||
#### Runtime
|
||||
|
||||
* Update layer application to extract tars in place, avoiding file churn while handling whiteouts
|
||||
- Fix permissiveness of mtime comparisons in tar handling (since GNU tar and Go tar do not yet support sub-second mtime precision)
|
||||
* Reimplement `docker top` in pure Go to work more consistently, and even inside Docker-in-Docker (thus removing the shell injection vulnerability present in some versions of `lxc-ps`)
|
||||
+ Update `-H unix://` to work similarly to `-H tcp://` by inserting the default values for missing portions
|
||||
- Fix more edge cases regarding dockerinit and deleted or replaced docker or dockerinit files
|
||||
* Update container name validation to include '.'
|
||||
- Fix use of a symlink or non-absolute path as the argument to `-g` to work as expected
|
||||
* Update to handle external mounts outside of LXC, fixing many small mounting quirks and making future execution backends and other features simpler
|
||||
* Update to use proper box-drawing characters everywhere in `docker images -tree`
|
||||
* Move MTU setting from LXC configuration to directly use netlink
|
||||
* Add `-S` option to external tar invocation for more efficient spare file handling
|
||||
+ Add arch/os info to User-Agent string, especially for registry requests
|
||||
+ Add `-mtu` option to Docker daemon for configuring MTU
|
||||
- Fix `docker build` to exit with a non-zero exit code on error
|
||||
+ Add `DOCKER_HOST` environment variable to configure the client `-H` flag without specifying it manually for every invocation
|
||||
|
||||
## 0.7.2 (2013-12-16)
|
||||
|
||||
#### Runtime
|
||||
|
||||
+ Validate container names on creation with standard regex
|
||||
* Increase maximum image depth to 127 from 42
|
||||
* Continue to move api endpoints to the job api
|
||||
+ Add -bip flag to allow specification of dynamic bridge IP via CIDR
|
||||
- Allow bridge creation when ipv6 is not enabled on certain systems
|
||||
* Set hostname and IP address from within dockerinit
|
||||
* Drop capabilities from within dockerinit
|
||||
- Fix volumes on host when symlink is present the image
|
||||
- Prevent deletion of image if ANY container is depending on it even if the container is not running
|
||||
* Update docker push to use new progress display
|
||||
* Use os.Lstat to allow mounting unix sockets when inspecting volumes
|
||||
- Adjust handling of inactive user login
|
||||
- Add missing defines in devicemapper for older kernels
|
||||
- Allow untag operations with no container validation
|
||||
- Add auth config to docker build
|
||||
|
||||
#### Documentation
|
||||
|
||||
* Add more information about Docker logging
|
||||
+ Add RHEL documentation
|
||||
* Add a direct example for changing the CMD that is run in a container
|
||||
* Update Arch installation documentation
|
||||
+ Add section on Trusted Builds
|
||||
+ Add Network documentation page
|
||||
|
||||
#### Other
|
||||
|
||||
+ Add new cover bundle for providing code coverage reporting
|
||||
* Separate integration tests in bundles
|
||||
* Make Tianon the hack maintainer
|
||||
* Update mkimage-debootstrap with more tweaks for keeping images small
|
||||
* Use https to get the install script
|
||||
* Remove vendored dotcloud/tar now that Go 1.2 has been released
|
||||
|
||||
## 0.7.1 (2013-12-05)
|
||||
|
||||
#### Documentation
|
||||
@@ -72,7 +171,7 @@
|
||||
|
||||
#### Runtime
|
||||
|
||||
* Improved stability, fixes some race conditons
|
||||
* Improve stability, fixes some race conditons
|
||||
* Skip the volumes mounted when deleting the volumes of container.
|
||||
* Fix layer size computation: handle hard links correctly
|
||||
* Use the work Path for docker cp CONTAINER:PATH
|
||||
@@ -115,7 +214,7 @@
|
||||
+ Add lock around write operations in graph
|
||||
* Check if port is valid
|
||||
* Fix restart runtime error with ghost container networking
|
||||
+ Added some more colors and animals to increase the pool of generated names
|
||||
+ Add some more colors and animals to increase the pool of generated names
|
||||
* Fix issues in docker inspect
|
||||
+ Escape apparmor confinement
|
||||
+ Set environment variables using a file.
|
||||
@@ -269,7 +368,7 @@
|
||||
* Improve network performance for VirtualBox
|
||||
* Revamp install.sh to be usable by more people, and to use official install methods whenever possible (apt repo, portage tree, etc.)
|
||||
- Fix contrib/mkimage-debian.sh apt caching prevention
|
||||
+ Added Dockerfile.tmLanguage to contrib
|
||||
+ Add Dockerfile.tmLanguage to contrib
|
||||
* Configured FPM to make /etc/init/docker.conf a config file
|
||||
* Enable SSH Agent forwarding in Vagrant VM
|
||||
* Several small tweaks/fixes for contrib/mkimage-debian.sh
|
||||
@@ -383,7 +482,7 @@
|
||||
* Mount /dev/shm as a tmpfs
|
||||
- Switch from http to https for get.docker.io
|
||||
* Let userland proxy handle container-bound traffic
|
||||
* Updated the Docker CLI to specify a value for the "Host" header.
|
||||
* Update the Docker CLI to specify a value for the "Host" header.
|
||||
- Change network range to avoid conflict with EC2 DNS
|
||||
- Reduce connect and read timeout when pinging the registry
|
||||
* Parallel pull
|
||||
@@ -579,7 +678,7 @@
|
||||
|
||||
+ Builder: 'docker build git://URL' fetches and builds a remote git repository
|
||||
* Runtime: 'docker ps -s' optionally prints container size
|
||||
* Tests: Improved and simplified
|
||||
* Tests: improved and simplified
|
||||
- Runtime: fix a regression introduced in 0.4.3 which caused the logs command to fail.
|
||||
- Builder: fix a regression when using ADD with single regular file.
|
||||
|
||||
@@ -594,7 +693,7 @@
|
||||
+ ADD of a local file will detect tar archives and unpack them
|
||||
* ADD improvements: use tar for copy + automatically unpack local archives
|
||||
* ADD uses tar/untar for copies instead of calling 'cp -ar'
|
||||
* Fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented.
|
||||
* Fix the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented.
|
||||
- Fix a bug which caused builds to fail if ADD was the first command
|
||||
* Nicer output for 'docker build'
|
||||
|
||||
@@ -639,7 +738,7 @@
|
||||
+ Detect faulty DNS configuration and replace it with a public default
|
||||
+ Allow docker run <name>:<id>
|
||||
+ You can now specify public port (ex: -p 80:4500)
|
||||
* Improved image removal to garbage-collect unreferenced parents
|
||||
* Improve image removal to garbage-collect unreferenced parents
|
||||
|
||||
#### Client
|
||||
|
||||
@@ -693,7 +792,7 @@
|
||||
|
||||
#### Documentation
|
||||
|
||||
* Improved install instructions.
|
||||
* Improve install instructions.
|
||||
|
||||
## 0.3.3 (2013-05-23)
|
||||
|
||||
@@ -778,7 +877,7 @@
|
||||
|
||||
+ Support for data volumes ('docker run -v=PATH')
|
||||
+ Share data volumes between containers ('docker run -volumes-from')
|
||||
+ Improved documentation
|
||||
+ Improve documentation
|
||||
* Upgrade to Go 1.0.3
|
||||
* Various upgrades to the dev environment for contributors
|
||||
|
||||
@@ -834,7 +933,7 @@
|
||||
- Add debian packaging
|
||||
- Documentation: installing on Arch Linux
|
||||
- Documentation: running Redis on docker
|
||||
- Fixed lxc 0.9 compatibility
|
||||
- Fix lxc 0.9 compatibility
|
||||
- Automatically load aufs module
|
||||
- Various bugfixes and stability improvements
|
||||
|
||||
@@ -869,7 +968,7 @@
|
||||
- Stabilize process management
|
||||
- Layers can include a commit message
|
||||
- Simplified 'docker attach'
|
||||
- Fixed support for re-attaching
|
||||
- Fix support for re-attaching
|
||||
- Various bugfixes and stability improvements
|
||||
- Auto-download at run
|
||||
- Auto-login on push
|
||||
|
||||
72
Dockerfile
72
Dockerfile
@@ -24,40 +24,32 @@
|
||||
#
|
||||
|
||||
docker-version 0.6.1
|
||||
FROM ubuntu:12.04
|
||||
MAINTAINER Solomon Hykes <solomon@dotcloud.com>
|
||||
FROM stackbrew/ubuntu:12.04
|
||||
MAINTAINER Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
|
||||
# Build dependencies
|
||||
RUN echo 'deb http://archive.ubuntu.com/ubuntu precise main universe' > /etc/apt/sources.list
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y -q curl
|
||||
RUN apt-get install -y -q git
|
||||
RUN apt-get install -y -q mercurial
|
||||
RUN apt-get install -y -q build-essential libsqlite3-dev
|
||||
# Add precise-backports to get s3cmd >= 1.1.0 (so we get ENV variable support in our .s3cfg)
|
||||
RUN echo 'deb http://archive.ubuntu.com/ubuntu precise-backports main universe' > /etc/apt/sources.list.d/backports.list
|
||||
|
||||
# Install Go
|
||||
RUN curl -s https://go.googlecode.com/files/go1.2.src.tar.gz | tar -v -C /usr/local -xz
|
||||
ENV PATH /usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
|
||||
ENV GOPATH /go:/go/src/github.com/dotcloud/docker/vendor
|
||||
RUN cd /usr/local/go/src && ./make.bash && go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std
|
||||
|
||||
# Ubuntu stuff
|
||||
RUN apt-get install -y -q ruby1.9.3 rubygems libffi-dev
|
||||
RUN gem install --no-rdoc --no-ri fpm
|
||||
RUN apt-get install -y -q reprepro dpkg-sig
|
||||
|
||||
RUN apt-get install -y -q python-pip
|
||||
RUN pip install s3cmd==1.1.0-beta3
|
||||
RUN pip install python-magic==0.4.6
|
||||
RUN /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY\n' > /.s3cfg
|
||||
|
||||
# Runtime dependencies
|
||||
RUN apt-get install -y -q iptables
|
||||
RUN apt-get install -y -q lxc
|
||||
RUN apt-get install -y -q aufs-tools
|
||||
# Packaged dependencies
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq \
|
||||
apt-utils \
|
||||
aufs-tools \
|
||||
build-essential \
|
||||
curl \
|
||||
dpkg-sig \
|
||||
git \
|
||||
iptables \
|
||||
libsqlite3-dev \
|
||||
lxc \
|
||||
mercurial \
|
||||
reprepro \
|
||||
ruby1.9.1 \
|
||||
ruby1.9.1-dev \
|
||||
s3cmd=1.1.0* \
|
||||
--no-install-recommends
|
||||
|
||||
# Get lvm2 source for compiling statically
|
||||
RUN git clone https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2 && cd /usr/local/lvm2 && git checkout v2_02_103
|
||||
RUN git clone https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2 && cd /usr/local/lvm2 && git checkout -q v2_02_103
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
||||
# note: we can't use "git clone -b" above because it requires at least git 1.7.10 to be able to use that on a tag instead of a branch and we only have 1.7.9.5
|
||||
|
||||
@@ -65,6 +57,26 @@ RUN git clone https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2 && cd /u
|
||||
RUN cd /usr/local/lvm2 && ./configure --enable-static_link && make device-mapper && make install_device-mapper
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
|
||||
# Install Go
|
||||
RUN curl -s https://go.googlecode.com/files/go1.2.src.tar.gz | tar -v -C /usr/local -xz
|
||||
ENV PATH /usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go:/go/src/github.com/dotcloud/docker/vendor
|
||||
RUN cd /usr/local/go/src && ./make.bash --no-clean 2>&1
|
||||
|
||||
# Compile Go for cross compilation
|
||||
ENV DOCKER_CROSSPLATFORMS darwin/amd64 darwin/386
|
||||
# TODO add linux/386 and linux/arm
|
||||
RUN cd /usr/local/go/src && bash -xc 'for platform in $DOCKER_CROSSPLATFORMS; do GOOS=${platform%/*} GOARCH=${platform##*/} ./make.bash --no-clean 2>&1; done'
|
||||
|
||||
# Grab Go's cover tool for dead-simple code coverage testing
|
||||
RUN go get code.google.com/p/go.tools/cmd/cover
|
||||
|
||||
# TODO replace FPM with some very minimal debhelper stuff
|
||||
RUN gem install --no-rdoc --no-ri fpm --version 1.0.1
|
||||
|
||||
# Setup s3cmd config
|
||||
RUN /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY' > /.s3cfg
|
||||
|
||||
VOLUME /var/lib/docker
|
||||
WORKDIR /go/src/github.com/dotcloud/docker
|
||||
|
||||
|
||||
@@ -3,4 +3,6 @@ Guillaume Charmes <guillaume@dotcloud.com> (@creack)
|
||||
Victor Vieux <victor@dotcloud.com> (@vieux)
|
||||
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
||||
api.go: Victor Vieux <victor@dotcloud.com> (@vieux)
|
||||
Dockerfile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
Makefile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
Vagrantfile: Daniel Mizyrycki <daniel@dotcloud.com> (@mzdaniel)
|
||||
|
||||
9
Makefile
9
Makefile
@@ -1,4 +1,4 @@
|
||||
.PHONY: all binary build default doc shell test
|
||||
.PHONY: all binary build cross default docs shell test
|
||||
|
||||
DOCKER_RUN_DOCKER := docker run -rm -i -t -privileged -e TESTFLAGS -v $(CURDIR)/bundles:/go/src/github.com/dotcloud/docker/bundles docker
|
||||
|
||||
@@ -10,11 +10,14 @@ all: build
|
||||
binary: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary
|
||||
|
||||
doc:
|
||||
cross: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary cross
|
||||
|
||||
docs:
|
||||
docker build -t docker-docs docs && docker run -p 8000:8000 docker-docs
|
||||
|
||||
test: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh test
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh test test-integration
|
||||
|
||||
shell: build
|
||||
$(DOCKER_RUN_DOCKER) bash
|
||||
|
||||
11
Vagrantfile
vendored
11
Vagrantfile
vendored
@@ -26,7 +26,7 @@ fi
|
||||
# Adding an apt gpg key is idempotent.
|
||||
wget -q -O - https://get.docker.io/gpg | apt-key add -
|
||||
|
||||
# Creating the docker.list file is idempotent, but it may overrite desired
|
||||
# Creating the docker.list file is idempotent, but it may overwrite desired
|
||||
# settings if it already exists. This could be solved with md5sum but it
|
||||
# doesn't seem worth it.
|
||||
echo 'deb http://get.docker.io/ubuntu docker main' > \
|
||||
@@ -41,7 +41,7 @@ apt-get install -q -y lxc-docker
|
||||
usermod -a -G docker "$user"
|
||||
|
||||
tmp=`mktemp -q` && {
|
||||
# Only install the backport kernel, don't bother upgrade if the backport is
|
||||
# Only install the backport kernel, don't bother upgrading if the backport is
|
||||
# already installed. We want parse the output of apt so we need to save it
|
||||
# with 'tee'. NOTE: The installation of the kernel will trigger dkms to
|
||||
# install vboxguest if needed.
|
||||
@@ -70,7 +70,7 @@ SCRIPT
|
||||
# trigger dkms to build the virtualbox guest module install.
|
||||
$vbox_script = <<VBOX_SCRIPT + $script
|
||||
# Install the VirtualBox guest additions if they aren't already installed.
|
||||
if [ ! -d /opt/VBoxGuestAdditions-4.3.2/ ]; then
|
||||
if [ ! -d /opt/VBoxGuestAdditions-4.3.4/ ]; then
|
||||
# Update remote package metadata. 'apt-get update' is idempotent.
|
||||
apt-get update -q
|
||||
|
||||
@@ -79,9 +79,10 @@ if [ ! -d /opt/VBoxGuestAdditions-4.3.2/ ]; then
|
||||
apt-get install -q -y linux-headers-generic-lts-raring dkms
|
||||
|
||||
echo 'Downloading VBox Guest Additions...'
|
||||
wget -cq http://dlc.sun.com.edgesuite.net/virtualbox/4.3.2/VBoxGuestAdditions_4.3.2.iso
|
||||
wget -cq http://dlc.sun.com.edgesuite.net/virtualbox/4.3.4/VBoxGuestAdditions_4.3.4.iso
|
||||
echo "f120793fa35050a8280eacf9c930cf8d9b88795161520f6515c0cc5edda2fe8a VBoxGuestAdditions_4.3.4.iso" | sha256sum --check || exit 1
|
||||
|
||||
mount -o loop,ro /home/vagrant/VBoxGuestAdditions_4.3.2.iso /mnt
|
||||
mount -o loop,ro /home/vagrant/VBoxGuestAdditions_4.3.4.iso /mnt
|
||||
/mnt/VBoxLinuxAdditions.run --nox11
|
||||
umount /mnt
|
||||
fi
|
||||
|
||||
125
api.go
125
api.go
@@ -10,7 +10,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/archive"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
"github.com/dotcloud/docker/systemd"
|
||||
"github.com/dotcloud/docker/pkg/systemd"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/gorilla/mux"
|
||||
"io"
|
||||
@@ -140,7 +140,8 @@ func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reque
|
||||
}
|
||||
|
||||
func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
return writeJSON(w, http.StatusOK, srv.DockerVersion())
|
||||
srv.Eng.ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -150,19 +151,11 @@ func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
signal := 0
|
||||
if r != nil {
|
||||
if s := r.Form.Get("signal"); s != "" {
|
||||
s, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signal = s
|
||||
}
|
||||
job := srv.Eng.Job("kill", vars["name"])
|
||||
if sig := r.Form.Get("signal"); sig != "" {
|
||||
job.Args = append(job.Args, sig)
|
||||
}
|
||||
if err := srv.ContainerKill(name, signal); err != nil {
|
||||
if err := job.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
@@ -173,10 +166,11 @@ func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
if err := srv.ContainerExport(name, w); err != nil {
|
||||
utils.Errorf("%s", err)
|
||||
job := srv.Eng.Job("export", vars["name"])
|
||||
if err := job.Stdout.Add(w); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -222,7 +216,8 @@ func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.R
|
||||
}
|
||||
|
||||
func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
return writeJSON(w, http.StatusOK, srv.DockerInfo())
|
||||
srv.Eng.ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -362,18 +357,13 @@ func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
repo := r.Form.Get("repo")
|
||||
tag := r.Form.Get("tag")
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
force, err := getBoolParam(r.Form.Get("force"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := srv.ContainerTag(name, repo, tag, force); err != nil {
|
||||
job := srv.Eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag"))
|
||||
job.Setenv("force", r.Form.Get("force"))
|
||||
if err := job.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
@@ -388,13 +378,17 @@ func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Req
|
||||
if err := json.NewDecoder(r.Body).Decode(config); err != nil && err != io.EOF {
|
||||
utils.Errorf("%s", err)
|
||||
}
|
||||
repo := r.Form.Get("repo")
|
||||
tag := r.Form.Get("tag")
|
||||
container := r.Form.Get("container")
|
||||
author := r.Form.Get("author")
|
||||
comment := r.Form.Get("comment")
|
||||
id, err := srv.ContainerCommit(container, repo, tag, author, comment, config)
|
||||
if err != nil {
|
||||
|
||||
job := srv.Eng.Job("commit", r.Form.Get("container"))
|
||||
job.Setenv("repo", r.Form.Get("repo"))
|
||||
job.Setenv("tag", r.Form.Get("tag"))
|
||||
job.Setenv("author", r.Form.Get("author"))
|
||||
job.Setenv("comment", r.Form.Get("comment"))
|
||||
job.SetenvJson("config", config)
|
||||
|
||||
var id string
|
||||
job.Stdout.AddString(&id)
|
||||
if err := job.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -689,17 +683,12 @@ func postContainersStop(srv *Server, version float64, w http.ResponseWriter, r *
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
t, err := strconv.Atoi(r.Form.Get("t"))
|
||||
if err != nil || t < 0 {
|
||||
t = 10
|
||||
}
|
||||
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
if err := srv.ContainerStop(name, t); err != nil {
|
||||
job := srv.Eng.Job("stop", vars["name"])
|
||||
job.Setenv("t", r.Form.Get("t"))
|
||||
if err := job.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
@@ -710,33 +699,28 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
status, err := srv.ContainerWait(name)
|
||||
job := srv.Eng.Job("wait", vars["name"])
|
||||
var statusStr string
|
||||
job.Stdout.AddString(&statusStr)
|
||||
if err := job.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Parse a 16-bit encoded integer to map typical unix exit status.
|
||||
status, err := strconv.ParseInt(statusStr, 10, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, &APIWait{StatusCode: status})
|
||||
return writeJSON(w, http.StatusOK, &APIWait{StatusCode: int(status)})
|
||||
}
|
||||
|
||||
func postContainersResize(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
height, err := strconv.Atoi(r.Form.Get("h"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
width, err := strconv.Atoi(r.Form.Get("w"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
if err := srv.ContainerResize(name, height, width); err != nil {
|
||||
if err := srv.Eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -905,12 +889,25 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
|
||||
if version < 1.3 {
|
||||
return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
|
||||
}
|
||||
remoteURL := r.FormValue("remote")
|
||||
repoName := r.FormValue("t")
|
||||
rawSuppressOutput := r.FormValue("q")
|
||||
rawNoCache := r.FormValue("nocache")
|
||||
rawRm := r.FormValue("rm")
|
||||
repoName, tag := utils.ParseRepositoryTag(repoName)
|
||||
var (
|
||||
remoteURL = r.FormValue("remote")
|
||||
repoName = r.FormValue("t")
|
||||
rawSuppressOutput = r.FormValue("q")
|
||||
rawNoCache = r.FormValue("nocache")
|
||||
rawRm = r.FormValue("rm")
|
||||
authEncoded = r.Header.Get("X-Registry-Auth")
|
||||
authConfig = &auth.AuthConfig{}
|
||||
tag string
|
||||
)
|
||||
repoName, tag = utils.ParseRepositoryTag(repoName)
|
||||
if authEncoded != "" {
|
||||
authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
||||
// for a pull it is not an error if no auth was given
|
||||
// to increase compatibility with the existing api it is defaulting to be empty
|
||||
authConfig = &auth.AuthConfig{}
|
||||
}
|
||||
}
|
||||
|
||||
var context io.Reader
|
||||
|
||||
@@ -978,7 +975,7 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
|
||||
Writer: utils.NewWriteFlusher(w),
|
||||
StreamFormatter: sf,
|
||||
},
|
||||
!suppressOutput, !noCache, rm, utils.NewWriteFlusher(w), sf)
|
||||
!suppressOutput, !noCache, rm, utils.NewWriteFlusher(w), sf, authConfig)
|
||||
id, err := b.Build(context)
|
||||
if err != nil {
|
||||
if sf.Used() {
|
||||
|
||||
@@ -29,23 +29,6 @@ type (
|
||||
VirtualSize int64
|
||||
}
|
||||
|
||||
APIInfo struct {
|
||||
Debug bool
|
||||
Containers int
|
||||
Images int
|
||||
Driver string `json:",omitempty"`
|
||||
DriverStatus [][2]string `json:",omitempty"`
|
||||
NFd int `json:",omitempty"`
|
||||
NGoroutines int `json:",omitempty"`
|
||||
MemoryLimit bool `json:",omitempty"`
|
||||
SwapLimit bool `json:",omitempty"`
|
||||
IPv4Forwarding bool `json:",omitempty"`
|
||||
LXCVersion string `json:",omitempty"`
|
||||
NEventsListener int `json:",omitempty"`
|
||||
KernelVersion string `json:",omitempty"`
|
||||
IndexServerAddress string `json:",omitempty"`
|
||||
}
|
||||
|
||||
APITop struct {
|
||||
Titles []string
|
||||
Processes [][]string
|
||||
@@ -95,12 +78,6 @@ type (
|
||||
IP string
|
||||
}
|
||||
|
||||
APIVersion struct {
|
||||
Version string
|
||||
GitCommit string `json:",omitempty"`
|
||||
GoVersion string `json:",omitempty"`
|
||||
}
|
||||
|
||||
APIWait struct {
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package archive
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"compress/bzip2"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
@@ -59,6 +61,43 @@ func DetectCompression(source []byte) Compression {
|
||||
return Uncompressed
|
||||
}
|
||||
|
||||
func xzDecompress(archive io.Reader) (io.Reader, error) {
|
||||
args := []string{"xz", "-d", "-c", "-q"}
|
||||
|
||||
return CmdStream(exec.Command(args[0], args[1:]...), archive, nil)
|
||||
}
|
||||
|
||||
func DecompressStream(archive io.Reader) (io.Reader, error) {
|
||||
buf := make([]byte, 10)
|
||||
totalN := 0
|
||||
for totalN < 10 {
|
||||
n, err := archive.Read(buf[totalN:])
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil, fmt.Errorf("Tarball too short")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
totalN += n
|
||||
utils.Debugf("[tar autodetect] n: %d", n)
|
||||
}
|
||||
compression := DetectCompression(buf)
|
||||
wrap := io.MultiReader(bytes.NewReader(buf), archive)
|
||||
|
||||
switch compression {
|
||||
case Uncompressed:
|
||||
return wrap, nil
|
||||
case Gzip:
|
||||
return gzip.NewReader(wrap)
|
||||
case Bzip2:
|
||||
return bzip2.NewReader(wrap), nil
|
||||
case Xz:
|
||||
return xzDecompress(wrap)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
|
||||
}
|
||||
}
|
||||
|
||||
func (compression *Compression) Flag() string {
|
||||
switch *compression {
|
||||
case Bzip2:
|
||||
@@ -110,7 +149,7 @@ func escapeName(name string) string {
|
||||
// Tar creates an archive from the directory at `path`, only including files whose relative
|
||||
// paths are included in `filter`. If `filter` is nil, then all files are included.
|
||||
func TarFilter(path string, options *TarOptions) (io.Reader, error) {
|
||||
args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path, "-T", "-"}
|
||||
args := []string{"tar", "-S", "--numeric-owner", "-f", "-", "-C", path, "-T", "-"}
|
||||
if options.Includes == nil {
|
||||
options.Includes = []string{"."}
|
||||
}
|
||||
@@ -155,7 +194,7 @@ func TarFilter(path string, options *TarOptions) (io.Reader, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return CmdStream(exec.Command(args[0], args[1:]...), &files, func() {
|
||||
return CmdStream(exec.Command(args[0], args[1:]...), bytes.NewBufferString(files), func() {
|
||||
if tmpDir != "" {
|
||||
_ = os.RemoveAll(tmpDir)
|
||||
}
|
||||
@@ -189,7 +228,7 @@ func Untar(archive io.Reader, path string, options *TarOptions) error {
|
||||
compression := DetectCompression(buf)
|
||||
|
||||
utils.Debugf("Archive compression detected: %s", compression.Extension())
|
||||
args := []string{"--numeric-owner", "-f", "-", "-C", path, "-x" + compression.Flag()}
|
||||
args := []string{"-S", "--numeric-owner", "-f", "-", "-C", path, "-x" + compression.Flag()}
|
||||
|
||||
if options != nil {
|
||||
for _, exclude := range options.Excludes {
|
||||
@@ -301,7 +340,7 @@ func CopyFileWithTar(src, dst string) error {
|
||||
// CmdStream executes a command, and returns its stdout as a stream.
|
||||
// If the command fails to run or doesn't complete successfully, an error
|
||||
// will be returned, including anything written on stderr.
|
||||
func CmdStream(cmd *exec.Cmd, input *string, atEnd func()) (io.Reader, error) {
|
||||
func CmdStream(cmd *exec.Cmd, input io.Reader, atEnd func()) (io.Reader, error) {
|
||||
if input != nil {
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
@@ -312,7 +351,7 @@ func CmdStream(cmd *exec.Cmd, input *string, atEnd func()) (io.Reader, error) {
|
||||
}
|
||||
// Write stdin if any
|
||||
go func() {
|
||||
_, _ = stdin.Write([]byte(*input))
|
||||
io.Copy(stdin, input)
|
||||
stdin.Close()
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ChangeType int
|
||||
@@ -34,6 +35,21 @@ func (change *Change) String() string {
|
||||
return fmt.Sprintf("%s %s", kind, change.Path)
|
||||
}
|
||||
|
||||
// Gnu tar and the go tar writer don't have sub-second mtime
|
||||
// precision, which is problematic when we apply changes via tar
|
||||
// files, we handle this by comparing for exact times, *or* same
|
||||
// second count and either a or b having exactly 0 nanoseconds
|
||||
func sameFsTime(a, b time.Time) bool {
|
||||
return a == b ||
|
||||
(a.Unix() == b.Unix() &&
|
||||
(a.Nanosecond() == 0 || b.Nanosecond() == 0))
|
||||
}
|
||||
|
||||
func sameFsTimeSpec(a, b syscall.Timespec) bool {
|
||||
return a.Sec == b.Sec &&
|
||||
(a.Nsec == b.Nsec || a.Nsec == 0 || b.Nsec == 0)
|
||||
}
|
||||
|
||||
func Changes(layers []string, rw string) ([]Change, error) {
|
||||
var changes []Change
|
||||
err := filepath.Walk(rw, func(path string, f os.FileInfo, err error) error {
|
||||
@@ -85,7 +101,7 @@ func Changes(layers []string, rw string) ([]Change, error) {
|
||||
// However, if it's a directory, maybe it wasn't actually modified.
|
||||
// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
|
||||
if stat.IsDir() && f.IsDir() {
|
||||
if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
|
||||
if f.Size() == stat.Size() && f.Mode() == stat.Mode() && sameFsTime(f.ModTime(), stat.ModTime()) {
|
||||
// Both directories are the same, don't record the change
|
||||
return nil
|
||||
}
|
||||
@@ -181,7 +197,7 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
|
||||
oldStat.Rdev != newStat.Rdev ||
|
||||
// Don't look at size for dirs, its not a good measure of change
|
||||
(oldStat.Size != newStat.Size && oldStat.Mode&syscall.S_IFDIR != syscall.S_IFDIR) ||
|
||||
getLastModification(oldStat) != getLastModification(newStat) {
|
||||
!sameFsTimeSpec(getLastModification(oldStat), getLastModification(newStat)) {
|
||||
change := Change{
|
||||
Path: newChild.path(),
|
||||
Kind: ChangeModify,
|
||||
|
||||
@@ -258,48 +258,44 @@ func TestChangesDirsMutated(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestApplyLayer(t *testing.T) {
|
||||
t.Skip("Skipping TestApplyLayer due to known failures") // Disable this for now as it is broken
|
||||
return
|
||||
src, err := ioutil.TempDir("", "docker-changes-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
createSampleDir(t, src)
|
||||
defer os.RemoveAll(src)
|
||||
dst := src + "-copy"
|
||||
if err := copyDir(src, dst); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mutateSampleDir(t, dst)
|
||||
defer os.RemoveAll(dst)
|
||||
|
||||
// src, err := ioutil.TempDir("", "docker-changes-test")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// createSampleDir(t, src)
|
||||
// dst := src + "-copy"
|
||||
// if err := copyDir(src, dst); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// mutateSampleDir(t, dst)
|
||||
changes, err := ChangesDirs(dst, src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// changes, err := ChangesDirs(dst, src)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
layer, err := ExportChanges(dst, changes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// layer, err := ExportChanges(dst, changes)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
layerCopy, err := NewTempArchive(layer, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// layerCopy, err := NewTempArchive(layer, "")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
if err := ApplyLayer(src, layerCopy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// if err := ApplyLayer(src, layerCopy); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
changes2, err := ChangesDirs(src, dst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// changes2, err := ChangesDirs(src, dst)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// if len(changes2) != 0 {
|
||||
// t.Fatalf("Unexpected differences after re applying mutation: %v", changes)
|
||||
// }
|
||||
|
||||
// os.RemoveAll(src)
|
||||
// os.RemoveAll(dst)
|
||||
if len(changes2) != 0 {
|
||||
t.Fatalf("Unexpected differences after reapplying mutation: %v", changes2)
|
||||
}
|
||||
}
|
||||
|
||||
233
archive/diff.go
233
archive/diff.go
@@ -1,6 +1,9 @@
|
||||
package archive
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -8,87 +11,181 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes.
|
||||
// They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major,
|
||||
// then the top 12 bits of the minor
|
||||
func mkdev(major int64, minor int64) uint32 {
|
||||
return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff))
|
||||
}
|
||||
func timeToTimespec(time time.Time) (ts syscall.Timespec) {
|
||||
if time.IsZero() {
|
||||
// Return UTIME_OMIT special value
|
||||
ts.Sec = 0
|
||||
ts.Nsec = ((1 << 30) - 2)
|
||||
return
|
||||
}
|
||||
return syscall.NsecToTimespec(time.UnixNano())
|
||||
}
|
||||
|
||||
// ApplyLayer parses a diff in the standard layer format from `layer`, and
|
||||
// applies it to the directory `dest`.
|
||||
func ApplyLayer(dest string, layer Archive) error {
|
||||
// Poor man's diff applyer in 2 steps:
|
||||
// We need to be able to set any perms
|
||||
oldmask := syscall.Umask(0)
|
||||
defer syscall.Umask(oldmask)
|
||||
|
||||
// Step 1: untar everything in place
|
||||
if err := Untar(layer, dest, nil); err != nil {
|
||||
layer, err := DecompressStream(layer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
modifiedDirs := make(map[string]*syscall.Stat_t)
|
||||
addDir := func(file string) {
|
||||
d := filepath.Dir(file)
|
||||
if _, exists := modifiedDirs[d]; !exists {
|
||||
if s, err := os.Lstat(d); err == nil {
|
||||
if sys := s.Sys(); sys != nil {
|
||||
if stat, ok := sys.(*syscall.Stat_t); ok {
|
||||
modifiedDirs[d] = stat
|
||||
tr := tar.NewReader(layer)
|
||||
|
||||
var dirs []*tar.Header
|
||||
|
||||
// Iterate through the files in the archive.
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
// end of tar archive
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Normalize name, for safety and for a simple is-root check
|
||||
hdr.Name = filepath.Clean(hdr.Name)
|
||||
|
||||
if !strings.HasSuffix(hdr.Name, "/") {
|
||||
// Not the root directory, ensure that the parent directory exists.
|
||||
// This happened in some tests where an image had a tarfile without any
|
||||
// parent directories.
|
||||
parent := filepath.Dir(hdr.Name)
|
||||
parentPath := filepath.Join(dest, parent)
|
||||
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
||||
err = os.MkdirAll(parentPath, 600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip AUFS metadata dirs
|
||||
if strings.HasPrefix(hdr.Name, ".wh..wh.") {
|
||||
continue
|
||||
}
|
||||
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
base := filepath.Base(path)
|
||||
if strings.HasPrefix(base, ".wh.") {
|
||||
originalBase := base[len(".wh."):]
|
||||
originalPath := filepath.Join(filepath.Dir(path), originalBase)
|
||||
if err := os.RemoveAll(originalPath); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// If path exits we almost always just want to remove and replace it.
|
||||
// The only exception is when it is a directory *and* the file from
|
||||
// the layer is also a directory. Then we want to merge them (i.e.
|
||||
// just apply the metadata from the layer).
|
||||
hasDir := false
|
||||
if fi, err := os.Lstat(path); err == nil {
|
||||
if fi.IsDir() && hdr.Typeflag == tar.TypeDir {
|
||||
hasDir = true
|
||||
} else {
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch hdr.Typeflag {
|
||||
case tar.TypeDir:
|
||||
if !hasDir {
|
||||
err = os.Mkdir(path, os.FileMode(hdr.Mode))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
dirs = append(dirs, hdr)
|
||||
|
||||
case tar.TypeReg, tar.TypeRegA:
|
||||
// Source is regular file
|
||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(file, tr); err != nil {
|
||||
file.Close()
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
|
||||
case tar.TypeBlock, tar.TypeChar, tar.TypeFifo:
|
||||
mode := uint32(hdr.Mode & 07777)
|
||||
switch hdr.Typeflag {
|
||||
case tar.TypeBlock:
|
||||
mode |= syscall.S_IFBLK
|
||||
case tar.TypeChar:
|
||||
mode |= syscall.S_IFCHR
|
||||
case tar.TypeFifo:
|
||||
mode |= syscall.S_IFIFO
|
||||
}
|
||||
|
||||
if err := syscall.Mknod(path, mode, int(mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case tar.TypeLink:
|
||||
if err := os.Link(filepath.Join(dest, hdr.Linkname), path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case tar.TypeSymlink:
|
||||
if err := os.Symlink(hdr.Linkname, path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
utils.Debugf("unhandled type %d\n", hdr.Typeflag)
|
||||
}
|
||||
|
||||
if err = syscall.Lchown(path, hdr.Uid, hdr.Gid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// There is no LChmod, so ignore mode for symlink. Also, this
|
||||
// must happen after chown, as that can modify the file mode
|
||||
if hdr.Typeflag != tar.TypeSymlink {
|
||||
err = syscall.Chmod(path, uint32(hdr.Mode&07777))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Directories must be handled at the end to avoid further
|
||||
// file creation in them to modify the mtime
|
||||
if hdr.Typeflag != tar.TypeDir {
|
||||
ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
|
||||
// syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and
|
||||
if hdr.Typeflag != tar.TypeSymlink {
|
||||
if err := syscall.UtimesNano(path, ts); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := LUtimesNano(path, ts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: walk for whiteouts and apply them, removing them in the process
|
||||
err := filepath.Walk(dest, func(fullPath string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// This happens in the case of whiteouts in parent dir removing a directory
|
||||
// We just ignore it
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Rebase path
|
||||
path, err := filepath.Rel(dest, fullPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path = filepath.Join("/", path)
|
||||
|
||||
// Skip AUFS metadata
|
||||
if matched, err := filepath.Match("/.wh..wh.*", path); err != nil {
|
||||
return err
|
||||
} else if matched {
|
||||
addDir(fullPath)
|
||||
if err := os.RemoveAll(fullPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
filename := filepath.Base(path)
|
||||
if strings.HasPrefix(filename, ".wh.") {
|
||||
rmTargetName := filename[len(".wh."):]
|
||||
rmTargetPath := filepath.Join(filepath.Dir(fullPath), rmTargetName)
|
||||
|
||||
// Remove the file targeted by the whiteout
|
||||
addDir(rmTargetPath)
|
||||
if err := os.RemoveAll(rmTargetPath); err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove the whiteout itself
|
||||
addDir(fullPath)
|
||||
if err := os.RemoveAll(fullPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for k, v := range modifiedDirs {
|
||||
lastAccess := getLastAccess(v)
|
||||
lastModification := getLastModification(v)
|
||||
aTime := time.Unix(lastAccess.Unix())
|
||||
mTime := time.Unix(lastModification.Unix())
|
||||
|
||||
if err := os.Chtimes(k, aTime, mTime); err != nil {
|
||||
for _, hdr := range dirs {
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
|
||||
if err := syscall.UtimesNano(path, ts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,3 +9,7 @@ func getLastAccess(stat *syscall.Stat_t) syscall.Timespec {
|
||||
func getLastModification(stat *syscall.Stat_t) syscall.Timespec {
|
||||
return stat.Mtimespec
|
||||
}
|
||||
|
||||
func LUtimesNano(path string, ts []syscall.Timespec) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package archive
|
||||
|
||||
import "syscall"
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getLastAccess(stat *syscall.Stat_t) syscall.Timespec {
|
||||
return stat.Atim
|
||||
@@ -9,3 +12,21 @@ func getLastAccess(stat *syscall.Stat_t) syscall.Timespec {
|
||||
func getLastModification(stat *syscall.Stat_t) syscall.Timespec {
|
||||
return stat.Mtim
|
||||
}
|
||||
|
||||
func LUtimesNano(path string, ts []syscall.Timespec) error {
|
||||
// These are not currently available in syscall
|
||||
AT_FDCWD := -100
|
||||
AT_SYMLINK_NOFOLLOW := 0x100
|
||||
|
||||
var _path *byte
|
||||
_path, err := syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_UTIMENSAT, uintptr(AT_FDCWD), uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), uintptr(AT_SYMLINK_NOFOLLOW), 0, 0); err != 0 && err != syscall.ENOSYS {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
27
auth/auth.go
27
auth/auth.go
@@ -163,7 +163,7 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
|
||||
|
||||
loginAgainstOfficialIndex := serverAddress == IndexServerAddress()
|
||||
|
||||
// to avoid sending the server address to the server it should be removed before marshalled
|
||||
// to avoid sending the server address to the server it should be removed before being marshalled
|
||||
authCopy := *authConfig
|
||||
authCopy.ServerAddress = ""
|
||||
|
||||
@@ -192,13 +192,6 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
|
||||
} else {
|
||||
status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it."
|
||||
}
|
||||
} else if reqStatusCode == 403 {
|
||||
if loginAgainstOfficialIndex {
|
||||
return "", fmt.Errorf("Login: Your account hasn't been activated. " +
|
||||
"Please check your e-mail for a confirmation link.")
|
||||
}
|
||||
return "", fmt.Errorf("Login: Your account hasn't been activated. " +
|
||||
"Please see the documentation of the registry " + serverAddress + " for instructions how to activate it.")
|
||||
} else if reqStatusCode == 400 {
|
||||
if string(reqBody) == "\"Username or email already exists\"" {
|
||||
req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
|
||||
@@ -216,9 +209,13 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
|
||||
status = "Login Succeeded"
|
||||
} else if resp.StatusCode == 401 {
|
||||
return "", fmt.Errorf("Wrong login/password, please try again")
|
||||
} else if resp.StatusCode == 403 {
|
||||
if loginAgainstOfficialIndex {
|
||||
return "", fmt.Errorf("Login: Account is not Active. Please check your e-mail for a confirmation link.")
|
||||
}
|
||||
return "", fmt.Errorf("Login: Account is not Active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)
|
||||
} else {
|
||||
return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
|
||||
resp.StatusCode, resp.Header)
|
||||
return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, resp.StatusCode, resp.Header)
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("Registration: %s", reqBody)
|
||||
@@ -236,7 +233,7 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if resp.StatusCode == 200 {
|
||||
status = "Login Succeeded"
|
||||
} else if resp.StatusCode == 401 {
|
||||
@@ -257,11 +254,11 @@ func (config *ConfigFile) ResolveAuthConfig(registry string) AuthConfig {
|
||||
// default to the index server
|
||||
return config.Configs[IndexServerAddress()]
|
||||
}
|
||||
// if its not the index server there are three cases:
|
||||
// if it's not the index server there are three cases:
|
||||
//
|
||||
// 1. this is a full config url -> it should be used as is
|
||||
// 2. it could be a full url, but with the wrong protocol
|
||||
// 3. it can be the hostname optionally with a port
|
||||
// 1. a full config url -> it should be used as is
|
||||
// 2. a full url, but with the wrong protocol
|
||||
// 3. a hostname, with an optional port
|
||||
//
|
||||
// as there is only one auth entry which is fully qualified we need to start
|
||||
// parsing and matching
|
||||
|
||||
235
buildfile.go
235
buildfile.go
@@ -1,20 +1,30 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/archive"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty")
|
||||
)
|
||||
|
||||
type BuildFile interface {
|
||||
Build(io.Reader) (string, error)
|
||||
CmdFrom(string) error
|
||||
@@ -25,14 +35,19 @@ type buildFile struct {
|
||||
runtime *Runtime
|
||||
srv *Server
|
||||
|
||||
image string
|
||||
maintainer string
|
||||
config *Config
|
||||
context string
|
||||
image string
|
||||
maintainer string
|
||||
config *Config
|
||||
|
||||
contextPath string
|
||||
context *utils.TarSum
|
||||
|
||||
verbose bool
|
||||
utilizeCache bool
|
||||
rm bool
|
||||
|
||||
authConfig *auth.AuthConfig
|
||||
|
||||
tmpContainers map[string]struct{}
|
||||
tmpImages map[string]struct{}
|
||||
|
||||
@@ -57,7 +72,7 @@ func (b *buildFile) CmdFrom(name string) error {
|
||||
if err != nil {
|
||||
if b.runtime.graph.IsNotExist(err) {
|
||||
remote, tag := utils.ParseRepositoryTag(name)
|
||||
if err := b.srv.ImagePull(remote, tag, b.outOld, b.sf, nil, nil, true); err != nil {
|
||||
if err := b.srv.ImagePull(remote, tag, b.outOld, b.sf, b.authConfig, nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
image, err = b.runtime.repositories.LookupImage(name)
|
||||
@@ -84,6 +99,27 @@ func (b *buildFile) CmdMaintainer(name string) error {
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("MAINTAINER %s", name))
|
||||
}
|
||||
|
||||
// probeCache checks to see if image-caching is enabled (`b.utilizeCache`)
|
||||
// and if so attempts to look up the current `b.image` and `b.config` pair
|
||||
// in the current server `b.srv`. If an image is found, probeCache returns
|
||||
// `(true, nil)`. If no image is found, it returns `(false, nil)`. If there
|
||||
// is any error, it returns `(false, err)`.
|
||||
func (b *buildFile) probeCache() (bool, error) {
|
||||
if b.utilizeCache {
|
||||
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
|
||||
return false, err
|
||||
} else if cache != nil {
|
||||
fmt.Fprintf(b.outStream, " ---> Using cache\n")
|
||||
utils.Debugf("[BUILDER] Use cached version")
|
||||
b.image = cache.ID
|
||||
return true, nil
|
||||
} else {
|
||||
utils.Debugf("[BUILDER] Cache miss")
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdRun(args string) error {
|
||||
if b.image == "" {
|
||||
return fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||
@@ -101,17 +137,12 @@ func (b *buildFile) CmdRun(args string) error {
|
||||
|
||||
utils.Debugf("Command to be executed: %v", b.config.Cmd)
|
||||
|
||||
if b.utilizeCache {
|
||||
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
|
||||
return err
|
||||
} else if cache != nil {
|
||||
fmt.Fprintf(b.outStream, " ---> Using cache\n")
|
||||
utils.Debugf("[BUILDER] Use cached version")
|
||||
b.image = cache.ID
|
||||
return nil
|
||||
} else {
|
||||
utils.Debugf("[BUILDER] Cache miss")
|
||||
}
|
||||
hit, err := b.probeCache()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hit {
|
||||
return nil
|
||||
}
|
||||
|
||||
cid, err := b.run()
|
||||
@@ -257,44 +288,27 @@ func (b *buildFile) CmdVolume(args string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) addRemote(container *Container, orig, dest string) error {
|
||||
file, err := utils.Download(orig)
|
||||
func (b *buildFile) checkPathForAddition(orig string) error {
|
||||
origPath := path.Join(b.contextPath, orig)
|
||||
if !strings.HasPrefix(origPath, b.contextPath) {
|
||||
return fmt.Errorf("Forbidden path outside the build context: %s (%s)", orig, origPath)
|
||||
}
|
||||
_, err := os.Stat(origPath)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("%s: no such file or directory", orig)
|
||||
}
|
||||
defer file.Body.Close()
|
||||
|
||||
// If the destination is a directory, figure out the filename.
|
||||
if strings.HasSuffix(dest, "/") {
|
||||
u, err := url.Parse(orig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := u.Path
|
||||
if strings.HasSuffix(path, "/") {
|
||||
path = path[:len(path)-1]
|
||||
}
|
||||
parts := strings.Split(path, "/")
|
||||
filename := parts[len(parts)-1]
|
||||
if filename == "" {
|
||||
return fmt.Errorf("cannot determine filename from url: %s", u)
|
||||
}
|
||||
dest = dest + filename
|
||||
}
|
||||
|
||||
return container.Inject(file.Body, dest)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) addContext(container *Container, orig, dest string) error {
|
||||
origPath := path.Join(b.context, orig)
|
||||
destPath := path.Join(container.RootfsPath(), dest)
|
||||
var (
|
||||
origPath = path.Join(b.contextPath, orig)
|
||||
destPath = path.Join(container.RootfsPath(), dest)
|
||||
)
|
||||
// Preserve the trailing '/'
|
||||
if strings.HasSuffix(dest, "/") {
|
||||
destPath = destPath + "/"
|
||||
}
|
||||
if !strings.HasPrefix(origPath, b.context) {
|
||||
return fmt.Errorf("Forbidden path outside the build context: %s (%s)", orig, origPath)
|
||||
}
|
||||
fi, err := os.Stat(origPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: no such file or directory", orig)
|
||||
@@ -318,7 +332,7 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error {
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdAdd(args string) error {
|
||||
if b.context == "" {
|
||||
if b.context == nil {
|
||||
return fmt.Errorf("No context given. Impossible to use ADD")
|
||||
}
|
||||
tmp := strings.SplitN(args, " ", 2)
|
||||
@@ -338,8 +352,90 @@ func (b *buildFile) CmdAdd(args string) error {
|
||||
|
||||
cmd := b.config.Cmd
|
||||
b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)}
|
||||
|
||||
b.config.Image = b.image
|
||||
|
||||
// FIXME: do we really need this?
|
||||
var (
|
||||
origPath = orig
|
||||
destPath = dest
|
||||
)
|
||||
|
||||
if utils.IsURL(orig) {
|
||||
resp, err := utils.Download(orig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpDirName, err := ioutil.TempDir(b.contextPath, "docker-remote")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpFileName := path.Join(tmpDirName, "tmp")
|
||||
tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpDirName)
|
||||
if _, err = io.Copy(tmpFile, resp.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName))
|
||||
tmpFile.Close()
|
||||
|
||||
// If the destination is a directory, figure out the filename.
|
||||
if strings.HasSuffix(dest, "/") {
|
||||
u, err := url.Parse(orig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := u.Path
|
||||
if strings.HasSuffix(path, "/") {
|
||||
path = path[:len(path)-1]
|
||||
}
|
||||
parts := strings.Split(path, "/")
|
||||
filename := parts[len(parts)-1]
|
||||
if filename == "" {
|
||||
return fmt.Errorf("cannot determine filename from url: %s", u)
|
||||
}
|
||||
destPath = dest + filename
|
||||
}
|
||||
}
|
||||
|
||||
if err := b.checkPathForAddition(origPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Hash path and check the cache
|
||||
if b.utilizeCache {
|
||||
var (
|
||||
hash string
|
||||
sums = b.context.GetSums()
|
||||
)
|
||||
if fi, err := os.Stat(path.Join(b.contextPath, origPath)); err != nil {
|
||||
return err
|
||||
} else if fi.IsDir() {
|
||||
var subfiles []string
|
||||
for file, sum := range sums {
|
||||
if strings.HasPrefix(file, origPath) {
|
||||
subfiles = append(subfiles, sum)
|
||||
}
|
||||
}
|
||||
sort.Strings(subfiles)
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(strings.Join(subfiles, ",")))
|
||||
hash = "dir:" + hex.EncodeToString(hasher.Sum(nil))
|
||||
} else {
|
||||
hash = "file:" + sums[origPath]
|
||||
}
|
||||
b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", hash, dest)}
|
||||
hit, err := b.probeCache()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hit {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Create the container and start it
|
||||
container, _, err := b.runtime.Create(b.config, "")
|
||||
if err != nil {
|
||||
@@ -352,14 +448,8 @@ func (b *buildFile) CmdAdd(args string) error {
|
||||
}
|
||||
defer container.Unmount()
|
||||
|
||||
if utils.IsURL(orig) {
|
||||
if err := b.addRemote(container, orig, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := b.addContext(container, orig, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.addContext(container, origPath, destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := b.commit(container.ID, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil {
|
||||
@@ -457,17 +547,12 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
|
||||
b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
|
||||
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
|
||||
|
||||
if b.utilizeCache {
|
||||
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
|
||||
return err
|
||||
} else if cache != nil {
|
||||
fmt.Fprintf(b.outStream, " ---> Using cache\n")
|
||||
utils.Debugf("[BUILDER] Use cached version")
|
||||
b.image = cache.ID
|
||||
return nil
|
||||
} else {
|
||||
utils.Debugf("[BUILDER] Cache miss")
|
||||
}
|
||||
hit, err := b.probeCache()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hit {
|
||||
return nil
|
||||
}
|
||||
|
||||
container, warnings, err := b.runtime.Create(b.config, "")
|
||||
@@ -508,17 +593,17 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
|
||||
var lineContinuation = regexp.MustCompile(`\s*\\\s*\n`)
|
||||
|
||||
func (b *buildFile) Build(context io.Reader) (string, error) {
|
||||
// FIXME: @creack "name" is a terrible variable name
|
||||
name, err := ioutil.TempDir("", "docker-build")
|
||||
tmpdirPath, err := ioutil.TempDir("", "docker-build")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := archive.Untar(context, name, nil); err != nil {
|
||||
b.context = &utils.TarSum{Reader: context}
|
||||
if err := archive.Untar(b.context, tmpdirPath, nil); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.RemoveAll(name)
|
||||
b.context = name
|
||||
filename := path.Join(name, "Dockerfile")
|
||||
defer os.RemoveAll(tmpdirPath)
|
||||
b.contextPath = tmpdirPath
|
||||
filename := path.Join(tmpdirPath, "Dockerfile")
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("Can't build a directory with no Dockerfile")
|
||||
}
|
||||
@@ -526,6 +611,9 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(fileBytes) == 0 {
|
||||
return "", ErrDockerfileEmpty
|
||||
}
|
||||
dockerfile := string(fileBytes)
|
||||
dockerfile = lineContinuation.ReplaceAllString(dockerfile, "")
|
||||
stepN := 0
|
||||
@@ -568,7 +656,7 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
|
||||
return "", fmt.Errorf("An error occurred during the build\n")
|
||||
}
|
||||
|
||||
func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, outOld io.Writer, sf *utils.StreamFormatter) BuildFile {
|
||||
func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *auth.AuthConfig) BuildFile {
|
||||
return &buildFile{
|
||||
runtime: srv.runtime,
|
||||
srv: srv,
|
||||
@@ -581,6 +669,7 @@ func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeC
|
||||
utilizeCache: utilizeCache,
|
||||
rm: rm,
|
||||
sf: sf,
|
||||
authConfig: auth,
|
||||
outOld: outOld,
|
||||
}
|
||||
}
|
||||
|
||||
210
commands.go
210
commands.go
@@ -11,8 +11,9 @@ import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/archive"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/pkg/term"
|
||||
"github.com/dotcloud/docker/registry"
|
||||
"github.com/dotcloud/docker/term"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -226,11 +227,21 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
}
|
||||
|
||||
headers := http.Header(make(map[string][]string))
|
||||
buf, err := json.Marshal(cli.configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
headers.Add("X-Registry-Auth", base64.URLEncoding.EncodeToString(buf))
|
||||
|
||||
if context != nil {
|
||||
headers.Set("Content-Type", "application/tar")
|
||||
}
|
||||
err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers)
|
||||
if jerr, ok := err.(*utils.JSONError); ok {
|
||||
// If no error code is set, default to 1
|
||||
if jerr.Code == 0 {
|
||||
jerr.Code = 1
|
||||
}
|
||||
return &utils.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
|
||||
}
|
||||
return err
|
||||
@@ -391,26 +402,24 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var out APIVersion
|
||||
err = json.Unmarshal(body, &out)
|
||||
out := engine.NewOutput()
|
||||
remoteVersion, err := out.AddEnv()
|
||||
if err != nil {
|
||||
utils.Errorf("Error unmarshal: body: %s, err: %s\n", body, err)
|
||||
utils.Errorf("Error reading remote version: %s\n", err)
|
||||
return err
|
||||
}
|
||||
if out.Version != "" {
|
||||
fmt.Fprintf(cli.out, "Server version: %s\n", out.Version)
|
||||
if _, err := out.Write(body); err != nil {
|
||||
utils.Errorf("Error reading remote version: %s\n", err)
|
||||
return err
|
||||
}
|
||||
if out.GitCommit != "" {
|
||||
fmt.Fprintf(cli.out, "Git commit (server): %s\n", out.GitCommit)
|
||||
}
|
||||
if out.GoVersion != "" {
|
||||
fmt.Fprintf(cli.out, "Go version (server): %s\n", out.GoVersion)
|
||||
}
|
||||
|
||||
out.Close()
|
||||
fmt.Fprintf(cli.out, "Server version: %s\n", remoteVersion.Get("Version"))
|
||||
fmt.Fprintf(cli.out, "Git commit (server): %s\n", remoteVersion.Get("GitCommit"))
|
||||
fmt.Fprintf(cli.out, "Go version (server): %s\n", remoteVersion.Get("GoVersion"))
|
||||
release := utils.GetReleaseVersion()
|
||||
if release != "" {
|
||||
fmt.Fprintf(cli.out, "Last stable version: %s", release)
|
||||
if (VERSION != "" || out.Version != "") && (strings.Trim(VERSION, "-dev") != release || strings.Trim(out.Version, "-dev") != release) {
|
||||
if (VERSION != "" || remoteVersion.Exists("Version")) && (strings.Trim(VERSION, "-dev") != release || strings.Trim(remoteVersion.Get("Version"), "-dev") != release) {
|
||||
fmt.Fprintf(cli.out, ", please update docker")
|
||||
}
|
||||
fmt.Fprintf(cli.out, "\n")
|
||||
@@ -434,42 +443,60 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var out APIInfo
|
||||
if err := json.Unmarshal(body, &out); err != nil {
|
||||
out := engine.NewOutput()
|
||||
remoteInfo, err := out.AddEnv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.out, "Containers: %d\n", out.Containers)
|
||||
fmt.Fprintf(cli.out, "Images: %d\n", out.Images)
|
||||
fmt.Fprintf(cli.out, "Driver: %s\n", out.Driver)
|
||||
for _, pair := range out.DriverStatus {
|
||||
if _, err := out.Write(body); err != nil {
|
||||
utils.Errorf("Error reading remote info: %s\n", err)
|
||||
return err
|
||||
}
|
||||
out.Close()
|
||||
|
||||
fmt.Fprintf(cli.out, "Containers: %d\n", remoteInfo.GetInt("Containers"))
|
||||
fmt.Fprintf(cli.out, "Images: %d\n", remoteInfo.GetInt("Images"))
|
||||
fmt.Fprintf(cli.out, "Driver: %s\n", remoteInfo.Get("Driver"))
|
||||
var driverStatus [][2]string
|
||||
if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pair := range driverStatus {
|
||||
fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1])
|
||||
}
|
||||
if out.Debug || os.Getenv("DEBUG") != "" {
|
||||
fmt.Fprintf(cli.out, "Debug mode (server): %v\n", out.Debug)
|
||||
if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" {
|
||||
fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug"))
|
||||
fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
|
||||
fmt.Fprintf(cli.out, "Fds: %d\n", out.NFd)
|
||||
fmt.Fprintf(cli.out, "Goroutines: %d\n", out.NGoroutines)
|
||||
fmt.Fprintf(cli.out, "LXC Version: %s\n", out.LXCVersion)
|
||||
fmt.Fprintf(cli.out, "EventsListeners: %d\n", out.NEventsListener)
|
||||
fmt.Fprintf(cli.out, "Kernel Version: %s\n", out.KernelVersion)
|
||||
}
|
||||
fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd"))
|
||||
fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines"))
|
||||
fmt.Fprintf(cli.out, "LXC Version: %s\n", remoteInfo.Get("LXCVersion"))
|
||||
fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener"))
|
||||
fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
|
||||
|
||||
if len(out.IndexServerAddress) != 0 {
|
||||
cli.LoadConfigFile()
|
||||
u := cli.configFile.Configs[out.IndexServerAddress].Username
|
||||
if len(u) > 0 {
|
||||
fmt.Fprintf(cli.out, "Username: %v\n", u)
|
||||
fmt.Fprintf(cli.out, "Registry: %v\n", out.IndexServerAddress)
|
||||
if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" {
|
||||
fmt.Fprintf(cli.out, "Init SHA1: %s\n", initSha1)
|
||||
}
|
||||
if initPath := remoteInfo.Get("InitPath"); initPath != "" {
|
||||
fmt.Fprintf(cli.out, "Init Path: %s\n", initPath)
|
||||
}
|
||||
}
|
||||
if !out.MemoryLimit {
|
||||
|
||||
if len(remoteInfo.GetList("IndexServerAddress")) != 0 {
|
||||
cli.LoadConfigFile()
|
||||
u := cli.configFile.Configs[remoteInfo.Get("IndexServerAddress")].Username
|
||||
if len(u) > 0 {
|
||||
fmt.Fprintf(cli.out, "Username: %v\n", u)
|
||||
fmt.Fprintf(cli.out, "Registry: %v\n", remoteInfo.GetList("IndexServerAddress"))
|
||||
}
|
||||
}
|
||||
if !remoteInfo.GetBool("MemoryLimit") {
|
||||
fmt.Fprintf(cli.err, "WARNING: No memory limit support\n")
|
||||
}
|
||||
if !out.SwapLimit {
|
||||
if !remoteInfo.GetBool("SwapLimit") {
|
||||
fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
|
||||
}
|
||||
if !out.IPv4Forwarding {
|
||||
if !remoteInfo.GetBool("IPv4Forwarding") {
|
||||
fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled.\n")
|
||||
}
|
||||
return nil
|
||||
@@ -1102,33 +1129,9 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if *flViz {
|
||||
body, _, err := cli.call("GET", "/images/json?all=1", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filter := cmd.Arg(0)
|
||||
|
||||
var outs []APIImages
|
||||
err = json.Unmarshal(body, &outs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.out, "digraph docker {\n")
|
||||
|
||||
for _, image := range outs {
|
||||
if image.ParentId == "" {
|
||||
fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", utils.TruncateID(image.ID))
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", utils.TruncateID(image.ParentId), utils.TruncateID(image.ID))
|
||||
}
|
||||
if image.RepoTags[0] != "<none>:<none>" {
|
||||
fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n", utils.TruncateID(image.ID), utils.TruncateID(image.ID), strings.Join(image.RepoTags, "\\n"))
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.out, " base [style=invisible]\n}\n")
|
||||
} else if *flTree {
|
||||
if *flViz || *flTree {
|
||||
body, _, err := cli.call("GET", "/images/json?all=1", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1140,8 +1143,8 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
||||
}
|
||||
|
||||
var (
|
||||
startImageArg = cmd.Arg(0)
|
||||
startImage APIImages
|
||||
printNode func(cli *DockerCli, noTrunc bool, image APIImages, prefix string)
|
||||
startImage APIImages
|
||||
|
||||
roots []APIImages
|
||||
byParent = make(map[string][]APIImages)
|
||||
@@ -1158,28 +1161,38 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if startImageArg != "" {
|
||||
if startImageArg == image.ID || startImageArg == utils.TruncateID(image.ID) {
|
||||
if filter != "" {
|
||||
if filter == image.ID || filter == utils.TruncateID(image.ID) {
|
||||
startImage = image
|
||||
}
|
||||
|
||||
for _, repotag := range image.RepoTags {
|
||||
if repotag == startImageArg {
|
||||
if repotag == filter {
|
||||
startImage = image
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if startImageArg != "" {
|
||||
WalkTree(cli, noTrunc, []APIImages{startImage}, byParent, "")
|
||||
if *flViz {
|
||||
fmt.Fprintf(cli.out, "digraph docker {\n")
|
||||
printNode = (*DockerCli).printVizNode
|
||||
} else {
|
||||
WalkTree(cli, noTrunc, roots, byParent, "")
|
||||
printNode = (*DockerCli).printTreeNode
|
||||
}
|
||||
|
||||
if startImage.ID != "" {
|
||||
cli.WalkTree(*noTrunc, &[]APIImages{startImage}, byParent, "", printNode)
|
||||
} else if filter == "" {
|
||||
cli.WalkTree(*noTrunc, &roots, byParent, "", printNode)
|
||||
}
|
||||
if *flViz {
|
||||
fmt.Fprintf(cli.out, " base [style=invisible]\n}\n")
|
||||
}
|
||||
} else {
|
||||
v := url.Values{}
|
||||
if cmd.NArg() == 1 {
|
||||
v.Set("filter", cmd.Arg(0))
|
||||
v.Set("filter", filter)
|
||||
}
|
||||
if *all {
|
||||
v.Set("all", "1")
|
||||
@@ -1225,41 +1238,64 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func WalkTree(cli *DockerCli, noTrunc *bool, images []APIImages, byParent map[string][]APIImages, prefix string) {
|
||||
if len(images) > 1 {
|
||||
length := len(images)
|
||||
for index, image := range images {
|
||||
func (cli *DockerCli) WalkTree(noTrunc bool, images *[]APIImages, byParent map[string][]APIImages, prefix string, printNode func(cli *DockerCli, noTrunc bool, image APIImages, prefix string)) {
|
||||
length := len(*images)
|
||||
if length > 1 {
|
||||
for index, image := range *images {
|
||||
if index+1 == length {
|
||||
PrintTreeNode(cli, noTrunc, image, prefix+"└─")
|
||||
printNode(cli, noTrunc, image, prefix+"└─")
|
||||
if subimages, exists := byParent[image.ID]; exists {
|
||||
WalkTree(cli, noTrunc, subimages, byParent, prefix+" ")
|
||||
cli.WalkTree(noTrunc, &subimages, byParent, prefix+" ", printNode)
|
||||
}
|
||||
} else {
|
||||
PrintTreeNode(cli, noTrunc, image, prefix+"|─")
|
||||
printNode(cli, noTrunc, image, prefix+"├─")
|
||||
if subimages, exists := byParent[image.ID]; exists {
|
||||
WalkTree(cli, noTrunc, subimages, byParent, prefix+"| ")
|
||||
cli.WalkTree(noTrunc, &subimages, byParent, prefix+"│ ", printNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, image := range images {
|
||||
PrintTreeNode(cli, noTrunc, image, prefix+"└─")
|
||||
for _, image := range *images {
|
||||
printNode(cli, noTrunc, image, prefix+"└─")
|
||||
if subimages, exists := byParent[image.ID]; exists {
|
||||
WalkTree(cli, noTrunc, subimages, byParent, prefix+" ")
|
||||
cli.WalkTree(noTrunc, &subimages, byParent, prefix+" ", printNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func PrintTreeNode(cli *DockerCli, noTrunc *bool, image APIImages, prefix string) {
|
||||
func (cli *DockerCli) printVizNode(noTrunc bool, image APIImages, prefix string) {
|
||||
var (
|
||||
imageID string
|
||||
parentID string
|
||||
)
|
||||
if noTrunc {
|
||||
imageID = image.ID
|
||||
parentID = image.ParentId
|
||||
} else {
|
||||
imageID = utils.TruncateID(image.ID)
|
||||
parentID = utils.TruncateID(image.ParentId)
|
||||
}
|
||||
if image.ParentId == "" {
|
||||
fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID)
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID)
|
||||
}
|
||||
if image.RepoTags[0] != "<none>:<none>" {
|
||||
fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n",
|
||||
imageID, imageID, strings.Join(image.RepoTags, "\\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) printTreeNode(noTrunc bool, image APIImages, prefix string) {
|
||||
var imageID string
|
||||
if *noTrunc {
|
||||
if noTrunc {
|
||||
imageID = image.ID
|
||||
} else {
|
||||
imageID = utils.TruncateID(image.ID)
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.out, "%s%s Size: %s (virtual %s)", prefix, imageID, utils.HumanSize(image.Size), utils.HumanSize(image.VirtualSize))
|
||||
fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, utils.HumanSize(image.VirtualSize))
|
||||
if image.RepoTags[0] != "<none>:<none>" {
|
||||
fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.RepoTags, ", "))
|
||||
} else {
|
||||
@@ -1789,6 +1825,8 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co
|
||||
flVolumes.Set(dstDir)
|
||||
binds = append(binds, bind)
|
||||
flVolumes.Delete(bind)
|
||||
} else if bind == "/" {
|
||||
return nil, nil, cmd, fmt.Errorf("Invalid volume: path can't be '/'")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,9 @@ func TestParseRunVolumes(t *testing.T) {
|
||||
t.Fatalf("Error parsing volume flags, without volume, no volume should be present. Received %v", config.Volumes)
|
||||
}
|
||||
|
||||
mustParse(t, "-v /")
|
||||
if _, _, err := parse(t, "-v /"); err == nil {
|
||||
t.Fatalf("Expected error, but got none")
|
||||
}
|
||||
|
||||
if _, _, err := parse(t, "-v /:/"); err == nil {
|
||||
t.Fatalf("Error parsing volume flags, `-v /:/` should fail but didn't")
|
||||
|
||||
@@ -14,9 +14,11 @@ type DaemonConfig struct {
|
||||
Dns []string
|
||||
EnableIptables bool
|
||||
BridgeIface string
|
||||
BridgeIp string
|
||||
DefaultIp net.IP
|
||||
InterContainerCommunication bool
|
||||
GraphDriver string
|
||||
Mtu int
|
||||
}
|
||||
|
||||
// ConfigFromJob creates and returns a new DaemonConfig object
|
||||
@@ -36,8 +38,14 @@ func ConfigFromJob(job *engine.Job) *DaemonConfig {
|
||||
} else {
|
||||
config.BridgeIface = DefaultNetworkBridge
|
||||
}
|
||||
config.BridgeIp = job.Getenv("BridgeIp")
|
||||
config.DefaultIp = net.ParseIP(job.Getenv("DefaultIp"))
|
||||
config.InterContainerCommunication = job.GetenvBool("InterContainerCommunication")
|
||||
config.GraphDriver = job.Getenv("GraphDriver")
|
||||
if mtu := job.GetenvInt("Mtu"); mtu != -1 {
|
||||
config.Mtu = mtu
|
||||
} else {
|
||||
config.Mtu = DefaultNetworkMtu
|
||||
}
|
||||
return &config
|
||||
}
|
||||
|
||||
141
container.go
141
container.go
@@ -7,7 +7,8 @@ import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/archive"
|
||||
"github.com/dotcloud/docker/graphdriver"
|
||||
"github.com/dotcloud/docker/term"
|
||||
"github.com/dotcloud/docker/mount"
|
||||
"github.com/dotcloud/docker/pkg/term"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/kr/pty"
|
||||
"io"
|
||||
@@ -48,7 +49,6 @@ type Container struct {
|
||||
network *NetworkInterface
|
||||
NetworkSettings *NetworkSettings
|
||||
|
||||
SysInitPath string
|
||||
ResolvConfPath string
|
||||
HostnamePath string
|
||||
HostsPath string
|
||||
@@ -297,7 +297,11 @@ func (container *Container) generateEnvConfig(env []string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ioutil.WriteFile(container.EnvConfigPath(), data, 0600)
|
||||
p, err := container.EnvConfigPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ioutil.WriteFile(p, data, 0600)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -574,7 +578,12 @@ func (container *Container) Start() (err error) {
|
||||
|
||||
// Networking
|
||||
if !container.Config.NetworkDisabled {
|
||||
params = append(params, "-g", container.network.Gateway.String())
|
||||
network := container.NetworkSettings
|
||||
params = append(params,
|
||||
"-g", network.Gateway,
|
||||
"-i", fmt.Sprintf("%s/%d", network.IPAddress, network.IPPrefixLen),
|
||||
"-mtu", strconv.Itoa(container.runtime.config.Mtu),
|
||||
)
|
||||
}
|
||||
|
||||
// User
|
||||
@@ -586,7 +595,6 @@ func (container *Container) Start() (err error) {
|
||||
env := []string{
|
||||
"HOME=/",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"container=lxc",
|
||||
"HOSTNAME=" + container.Config.Hostname,
|
||||
}
|
||||
|
||||
@@ -594,6 +602,10 @@ func (container *Container) Start() (err error) {
|
||||
env = append(env, "TERM=xterm")
|
||||
}
|
||||
|
||||
if container.hostConfig.Privileged {
|
||||
params = append(params, "-privileged")
|
||||
}
|
||||
|
||||
// Init any links between the parent and children
|
||||
runtime := container.runtime
|
||||
|
||||
@@ -674,6 +686,45 @@ func (container *Container) Start() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
root := container.RootfsPath()
|
||||
envPath, err := container.EnvConfigPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Mount docker specific files into the containers root fs
|
||||
if err := mount.Mount(runtime.sysInitPath, path.Join(root, "/.dockerinit"), "none", "bind,ro"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mount.Mount(envPath, path.Join(root, "/.dockerenv"), "none", "bind,ro"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mount.Mount(container.ResolvConfPath, path.Join(root, "/etc/resolv.conf"), "none", "bind,ro"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if container.HostnamePath != "" && container.HostsPath != "" {
|
||||
if err := mount.Mount(container.HostnamePath, path.Join(root, "/etc/hostname"), "none", "bind,ro"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mount.Mount(container.HostsPath, path.Join(root, "/etc/hosts"), "none", "bind,ro"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Mount user specified volumes
|
||||
|
||||
for r, v := range container.Volumes {
|
||||
mountAs := "ro"
|
||||
if container.VolumesRW[v] {
|
||||
mountAs = "rw"
|
||||
}
|
||||
|
||||
if err := mount.Mount(v, path.Join(root, r), "none", fmt.Sprintf("bind,%s", mountAs)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
container.cmd = exec.Command(params[0], params[1:]...)
|
||||
|
||||
// Setup logging of stdout and stderr to disk
|
||||
@@ -774,14 +825,14 @@ func (container *Container) getBindMap() (map[string]BindMap, error) {
|
||||
}
|
||||
binds[path.Clean(dst)] = bindMap
|
||||
}
|
||||
return binds, nil
|
||||
return binds, nil
|
||||
}
|
||||
|
||||
func (container *Container) createVolumes() error {
|
||||
binds, err := container.getBindMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
binds, err := container.getBindMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
volumesDriver := container.runtime.volumes.driver
|
||||
// Create the requested volumes if they don't exist
|
||||
for volPath := range container.Config.Volumes {
|
||||
@@ -801,15 +852,10 @@ func (container *Container) createVolumes() error {
|
||||
if strings.ToLower(bindMap.Mode) == "rw" {
|
||||
srcRW = true
|
||||
}
|
||||
if file, err := os.Open(bindMap.SrcPath); err != nil {
|
||||
if stat, err := os.Lstat(bindMap.SrcPath); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer file.Close()
|
||||
if stat, err := file.Stat(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
volIsDir = stat.IsDir()
|
||||
}
|
||||
volIsDir = stat.IsDir()
|
||||
}
|
||||
// Otherwise create an directory in $ROOT/volumes/ and use that
|
||||
} else {
|
||||
@@ -829,26 +875,25 @@ func (container *Container) createVolumes() error {
|
||||
}
|
||||
container.Volumes[volPath] = srcPath
|
||||
container.VolumesRW[volPath] = srcRW
|
||||
|
||||
// Create the mountpoint
|
||||
rootVolPath := path.Join(container.RootfsPath(), volPath)
|
||||
if volIsDir {
|
||||
if err := os.MkdirAll(rootVolPath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
volPath = path.Join(container.RootfsPath(), volPath)
|
||||
rootVolPath, err := utils.FollowSymlinkInScope(volPath, container.RootfsPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
volPath = path.Join(container.RootfsPath(), volPath)
|
||||
if _, err := os.Stat(volPath); err != nil {
|
||||
if _, err := os.Stat(rootVolPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if volIsDir {
|
||||
if err := os.MkdirAll(volPath, 0755); err != nil {
|
||||
if err := os.MkdirAll(rootVolPath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := os.MkdirAll(path.Dir(volPath), 0755); err != nil {
|
||||
if err := os.MkdirAll(path.Dir(rootVolPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if f, err := os.OpenFile(volPath, os.O_CREATE, 0755); err != nil {
|
||||
if f, err := os.OpenFile(rootVolPath, os.O_CREATE, 0755); err != nil {
|
||||
return err
|
||||
} else {
|
||||
f.Close()
|
||||
@@ -1357,6 +1402,32 @@ func (container *Container) GetImage() (*Image, error) {
|
||||
}
|
||||
|
||||
func (container *Container) Unmount() error {
|
||||
var (
|
||||
err error
|
||||
root = container.RootfsPath()
|
||||
mounts = []string{
|
||||
path.Join(root, "/.dockerinit"),
|
||||
path.Join(root, "/.dockerenv"),
|
||||
path.Join(root, "/etc/resolv.conf"),
|
||||
}
|
||||
)
|
||||
|
||||
if container.HostnamePath != "" && container.HostsPath != "" {
|
||||
mounts = append(mounts, path.Join(root, "/etc/hostname"), path.Join(root, "/etc/hosts"))
|
||||
}
|
||||
|
||||
for r := range container.Volumes {
|
||||
mounts = append(mounts, path.Join(root, r))
|
||||
}
|
||||
|
||||
for _, m := range mounts {
|
||||
if lastError := mount.Unmount(m); lastError != nil {
|
||||
err = lastError
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return container.runtime.Unmount(container)
|
||||
}
|
||||
|
||||
@@ -1376,8 +1447,20 @@ func (container *Container) jsonPath() string {
|
||||
return path.Join(container.root, "config.json")
|
||||
}
|
||||
|
||||
func (container *Container) EnvConfigPath() string {
|
||||
return path.Join(container.root, "config.env")
|
||||
func (container *Container) EnvConfigPath() (string, error) {
|
||||
p := path.Join(container.root, "config.env")
|
||||
if _, err := os.Stat(p); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
f, err := os.Create(p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
f.Close()
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (container *Container) lxcConfigPath() string {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# This script provides supports completion of:
|
||||
# - commands and their options
|
||||
# - container ids
|
||||
# - container ids and names
|
||||
# - image repos and tags
|
||||
# - filepaths
|
||||
#
|
||||
@@ -25,21 +25,24 @@ __docker_containers_all()
|
||||
{
|
||||
local containers
|
||||
containers="$( docker ps -a -q )"
|
||||
COMPREPLY=( $( compgen -W "$containers" -- "$cur" ) )
|
||||
names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )"
|
||||
COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) )
|
||||
}
|
||||
|
||||
__docker_containers_running()
|
||||
{
|
||||
local containers
|
||||
containers="$( docker ps -q )"
|
||||
COMPREPLY=( $( compgen -W "$containers" -- "$cur" ) )
|
||||
names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )"
|
||||
COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) )
|
||||
}
|
||||
|
||||
__docker_containers_stopped()
|
||||
{
|
||||
local containers
|
||||
containers="$( comm -13 <(docker ps -q | sort -u) <(docker ps -a -q | sort -u) )"
|
||||
COMPREPLY=( $( compgen -W "$containers" -- "$cur" ) )
|
||||
names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )"
|
||||
COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) )
|
||||
}
|
||||
|
||||
__docker_image_repos()
|
||||
@@ -70,8 +73,9 @@ __docker_containers_and_images()
|
||||
{
|
||||
local containers images
|
||||
containers="$( docker ps -a -q )"
|
||||
names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )"
|
||||
images="$( docker images | awk 'NR>1{print $1":"$2}' )"
|
||||
COMPREPLY=( $( compgen -W "$images $containers" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "$images $names $containers" -- "$cur" ) )
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
|
||||
@@ -142,14 +142,22 @@ if [ -z "$strictDebootstrap" ]; then
|
||||
# this forces dpkg not to call sync() after package extraction and speeds up install
|
||||
# the benefit is huge on spinning disks, and the penalty is nonexistent on SSD or decent server virtualization
|
||||
echo 'force-unsafe-io' | sudo tee etc/dpkg/dpkg.cfg.d/02apt-speedup > /dev/null
|
||||
# we want to effectively run "apt-get clean" after every install to keep images small
|
||||
echo 'DPkg::Post-Invoke {"/bin/rm -f /var/cache/apt/archives/*.deb || true";};' | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null
|
||||
# we want to effectively run "apt-get clean" after every install to keep images small (see output of "apt-get clean -s" for context)
|
||||
{
|
||||
aptGetClean='"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true";'
|
||||
echo "DPkg::Post-Invoke { ${aptGetClean} };"
|
||||
echo "APT::Update::Post-Invoke { ${aptGetClean} };"
|
||||
echo 'Dir::Cache::pkgcache ""; Dir::Cache::srcpkgcache "";'
|
||||
} | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null
|
||||
# and remove the translations, too
|
||||
echo 'Acquire::Languages "none";' | sudo tee etc/apt/apt.conf.d/no-languages > /dev/null
|
||||
|
||||
# helpful undo lines for each the above tweaks (for lack of a better home to keep track of them):
|
||||
# rm /usr/sbin/policy-rc.d
|
||||
# rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl
|
||||
# rm /etc/dpkg/dpkg.cfg.d/02apt-speedup
|
||||
# rm /etc/apt/apt.conf.d/no-cache
|
||||
# rm /etc/apt/apt.conf.d/no-languages
|
||||
|
||||
if [ -z "$skipDetection" ]; then
|
||||
# see also rudimentary platform detection in hack/install.sh
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Dockerfile</string>
|
||||
<key>fileTypes</key>
|
||||
<array>
|
||||
<string>Dockerfile</string>
|
||||
</array>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
@@ -11,7 +11,8 @@ branch named [zfs_driver].
|
||||
|
||||
# Status
|
||||
|
||||
Pre-alpha
|
||||
Alpha: The code is now capable of creating, running and destroying containers
|
||||
and images.
|
||||
|
||||
The code is under development. Contributions in the form of suggestions,
|
||||
code-reviews, and patches are welcome.
|
||||
|
||||
@@ -30,6 +30,7 @@ func main() {
|
||||
flDebug = flag.Bool("D", false, "Enable debug mode")
|
||||
flAutoRestart = flag.Bool("r", true, "Restart previously running containers")
|
||||
bridgeName = flag.String("b", "", "Attach containers to a pre-existing network bridge; use 'none' to disable container networking")
|
||||
bridgeIp = flag.String("bip", "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
|
||||
pidfile = flag.String("p", "/var/run/docker.pid", "Path to use for daemon PID file")
|
||||
flRoot = flag.String("g", "/var/lib/docker", "Path to use as the root of the docker runtime")
|
||||
flEnableCors = flag.Bool("api-enable-cors", false, "Enable CORS headers in the remote API")
|
||||
@@ -39,6 +40,7 @@ func main() {
|
||||
flInterContainerComm = flag.Bool("icc", true, "Enable inter-container communication")
|
||||
flGraphDriver = flag.String("s", "", "Force the docker runtime to use a specific storage driver")
|
||||
flHosts = docker.NewListOpts(docker.ValidateHost)
|
||||
flMtu = flag.Int("mtu", docker.DefaultNetworkMtu, "Set the containers network mtu")
|
||||
)
|
||||
flag.Var(&flDns, "dns", "Force docker to use specific DNS servers")
|
||||
flag.Var(&flHosts, "H", "Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise")
|
||||
@@ -50,8 +52,17 @@ func main() {
|
||||
return
|
||||
}
|
||||
if flHosts.Len() == 0 {
|
||||
// If we do not have a host, default to unix socket
|
||||
flHosts.Set(fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET))
|
||||
defaultHost := os.Getenv("DOCKER_HOST")
|
||||
|
||||
if defaultHost == "" || *flDaemon {
|
||||
// If we do not have a host, default to unix socket
|
||||
defaultHost = fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)
|
||||
}
|
||||
flHosts.Set(defaultHost)
|
||||
}
|
||||
|
||||
if *bridgeName != "" && *bridgeIp != "" {
|
||||
log.Fatal("You specified -b & -bip, mutually exclusive options. Please specify only one.")
|
||||
}
|
||||
|
||||
if *flDebug {
|
||||
@@ -64,6 +75,7 @@ func main() {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
eng, err := engine.New(*flRoot)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -77,9 +89,11 @@ func main() {
|
||||
job.SetenvList("Dns", flDns.GetAll())
|
||||
job.SetenvBool("EnableIptables", *flEnableIptables)
|
||||
job.Setenv("BridgeIface", *bridgeName)
|
||||
job.Setenv("BridgeIp", *bridgeIp)
|
||||
job.Setenv("DefaultIp", *flDefaultIp)
|
||||
job.SetenvBool("InterContainerCommunication", *flInterContainerComm)
|
||||
job.Setenv("GraphDriver", *flGraphDriver)
|
||||
job.SetenvInt("Mtu", *flMtu)
|
||||
if err := job.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Andy Rothfusz <andy@dotcloud.com> (@metalivedev)
|
||||
Ken Cochrane <ken@dotcloud.com> (@kencochrane)
|
||||
James Turnbull <james@lovedthanlost.net> (@jamesturnbull)
|
||||
James Turnbull <james@lovedthanlost.net> (@jamtur01)
|
||||
Sven Dowideit <SvenDowideit@fosiki.com> (@SvenDowideit)
|
||||
|
||||
@@ -46,20 +46,20 @@ directory:
|
||||
|
||||
* Linux: `pip install -r docs/requirements.txt`
|
||||
|
||||
* Mac OS X: `[sudo] pip-2.7 -r docs/requirements.txt`
|
||||
* Mac OS X: `[sudo] pip-2.7 install -r docs/requirements.txt`
|
||||
|
||||
###Alternative Installation: Docker Container
|
||||
|
||||
If you're running ``docker`` on your development machine then you may
|
||||
find it easier and cleaner to use the Dockerfile. This installs Sphinx
|
||||
find it easier and cleaner to use the docs Dockerfile. This installs Sphinx
|
||||
in a container, adds the local ``docs/`` directory and builds the HTML
|
||||
docs inside the container, even starting a simple HTTP server on port
|
||||
8000 so that you can connect and see your changes. Just run ``docker
|
||||
build .`` and run the resulting image. This is the equivalent to
|
||||
``make clean server`` since each container starts clean.
|
||||
8000 so that you can connect and see your changes.
|
||||
|
||||
In the ``docs/`` directory, run:
|
||||
```docker build -t docker:docs . && docker run -p 8000:8000 docker:docs```
|
||||
In the ``docker`` source directory, run:
|
||||
```make docs```
|
||||
|
||||
This is the equivalent to ``make clean server`` since each container starts clean.
|
||||
|
||||
Usage
|
||||
-----
|
||||
@@ -128,7 +128,8 @@ Guides on using sphinx
|
||||
|
||||
* Code examples
|
||||
|
||||
* Start without $, so it's easy to copy and paste.
|
||||
* Start typed commands with ``$ `` (dollar space) so that they
|
||||
are easily differentiated from program output.
|
||||
* Use "sudo" with docker to ensure that your command is runnable
|
||||
even if they haven't [used the *docker*
|
||||
group](http://docs.docker.io/en/latest/use/basics/#why-sudo).
|
||||
|
||||
@@ -26,10 +26,10 @@ Docker Remote API
|
||||
2. Versions
|
||||
===========
|
||||
|
||||
The current version of the API is 1.7
|
||||
The current version of the API is 1.8
|
||||
|
||||
Calling /images/<name>/insert is the same as calling
|
||||
/v1.7/images/<name>/insert
|
||||
/v1.8/images/<name>/insert
|
||||
|
||||
You can still call an old version of the api using
|
||||
/v1.0/images/<name>/insert
|
||||
@@ -55,6 +55,13 @@ What's new
|
||||
|
||||
**New!** This endpoint now returns the host config for the container.
|
||||
|
||||
.. http:post:: /images/create
|
||||
.. http:post:: /images/(name)/insert
|
||||
.. http:post:: /images/(name)/push
|
||||
|
||||
**New!** progressDetail object was added in the JSON. It's now possible
|
||||
to get the current value and the total of the progress without having to
|
||||
parse the string.
|
||||
|
||||
v1.7
|
||||
****
|
||||
|
||||
@@ -1078,7 +1078,7 @@ Monitor Docker's events
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /events?since=1374067924
|
||||
GET /events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
||||
@@ -1122,7 +1122,7 @@ Monitor Docker's events
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /events?since=1374067924
|
||||
GET /events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
||||
@@ -1093,7 +1093,7 @@ Monitor Docker's events
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /events?since=1374067924
|
||||
GET /events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
||||
@@ -1228,7 +1228,7 @@ Monitor Docker's events
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /events?since=1374067924
|
||||
GET /events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
||||
@@ -122,7 +122,6 @@ Create a container
|
||||
"AttachStdout":true,
|
||||
"AttachStderr":true,
|
||||
"PortSpecs":null,
|
||||
"Privileged": false,
|
||||
"Tty":false,
|
||||
"OpenStdin":false,
|
||||
"StdinOnce":false,
|
||||
@@ -136,10 +135,12 @@ Create a container
|
||||
"/tmp": {}
|
||||
},
|
||||
"VolumesFrom":"",
|
||||
"WorkingDir":""
|
||||
|
||||
"WorkingDir":"",
|
||||
"ExposedPorts":{
|
||||
"22/tcp": {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
@@ -364,10 +365,11 @@ Start a container
|
||||
{
|
||||
"Binds":["/tmp:/tmp"],
|
||||
"LxcConf":{"lxc.utsname":"docker"},
|
||||
"PortBindings":null
|
||||
"PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] },
|
||||
"Privileged":false,
|
||||
"PublishAllPorts":false
|
||||
}
|
||||
|
||||
|
||||
Binds need to reference Volumes that were defined during container creation.
|
||||
|
||||
**Example response**:
|
||||
@@ -1159,7 +1161,7 @@ Monitor Docker's events
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /events?since=1374067924
|
||||
GET /events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
||||
@@ -122,7 +122,6 @@ Create a container
|
||||
"AttachStdout":true,
|
||||
"AttachStderr":true,
|
||||
"PortSpecs":null,
|
||||
"Privileged": false,
|
||||
"Tty":false,
|
||||
"OpenStdin":false,
|
||||
"StdinOnce":false,
|
||||
@@ -132,12 +131,16 @@ Create a container
|
||||
],
|
||||
"Dns":null,
|
||||
"Image":"base",
|
||||
"Volumes":{},
|
||||
"Volumes":{
|
||||
"/tmp": {}
|
||||
},
|
||||
"VolumesFrom":"",
|
||||
"WorkingDir":""
|
||||
|
||||
"WorkingDir":"",
|
||||
"ExposedPorts":{
|
||||
"22/tcp": {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
@@ -151,6 +154,7 @@ Create a container
|
||||
}
|
||||
|
||||
:jsonparam config: the container's configuration
|
||||
:query name: Assign the specified name to the container. Must match ``/?[a-zA-Z0-9_-]+``.
|
||||
:statuscode 201: no error
|
||||
:statuscode 404: no such container
|
||||
:statuscode 406: impossible to attach (container not running)
|
||||
@@ -377,7 +381,10 @@ Start a container
|
||||
|
||||
{
|
||||
"Binds":["/tmp:/tmp"],
|
||||
"LxcConf":{"lxc.utsname":"docker"}
|
||||
"LxcConf":{"lxc.utsname":"docker"},
|
||||
"PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] },
|
||||
"PublishAllPorts":false,
|
||||
"Privileged":false
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
@@ -696,7 +703,7 @@ Create an image
|
||||
Content-Type: application/json
|
||||
|
||||
{"status":"Pulling..."}
|
||||
{"status":"Pulling", "progress":"1/? (n/a)"}
|
||||
{"status":"Pulling", "progress":"1 B/ 100 B", "progressDetail":{"current":1, "total":100}}
|
||||
{"error":"Invalid..."}
|
||||
...
|
||||
|
||||
@@ -736,7 +743,7 @@ Insert a file in an image
|
||||
Content-Type: application/json
|
||||
|
||||
{"status":"Inserting..."}
|
||||
{"status":"Inserting", "progress":"1/? (n/a)"}
|
||||
{"status":"Inserting", "progress":"1/? (n/a)", "progressDetail":{"current":1}}
|
||||
{"error":"Invalid..."}
|
||||
...
|
||||
|
||||
@@ -857,7 +864,7 @@ Push an image on the registry
|
||||
Content-Type: application/json
|
||||
|
||||
{"status":"Pushing..."}
|
||||
{"status":"Pushing", "progress":"1/? (n/a)"}
|
||||
{"status":"Pushing", "progress":"1/? (n/a)", "progressDetail":{"current":1}}}
|
||||
{"error":"Invalid..."}
|
||||
...
|
||||
|
||||
@@ -1026,6 +1033,7 @@ Build an image from Dockerfile via stdin
|
||||
:query q: suppress verbose build output
|
||||
:query nocache: do not use the cache when building the image
|
||||
:reqheader Content-type: should be set to ``"application/tar"``.
|
||||
:reqheader X-Registry-Auth: base64-encoded AuthConfig object
|
||||
:statuscode 200: no error
|
||||
:statuscode 500: server error
|
||||
|
||||
@@ -1172,7 +1180,7 @@ Monitor Docker's events
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /events?since=1374067924
|
||||
GET /events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@ Docker Registry API
|
||||
- It doesn’t have a local database
|
||||
- It will be open-sourced at some point
|
||||
|
||||
We expect that there will be multiple registries out there. To help to grasp the context, here are some examples of registries:
|
||||
We expect that there will be multiple registries out there. To help to grasp
|
||||
the context, here are some examples of registries:
|
||||
|
||||
- **sponsor registry**: such a registry is provided by a third-party hosting infrastructure as a convenience for their customers and the docker community as a whole. Its costs are supported by the third party, but the management and operation of the registry are supported by dotCloud. It features read/write access, and delegates authentication and authorization to the Index.
|
||||
- **mirror registry**: such a registry is provided by a third-party hosting infrastructure but is targeted at their customers only. Some mechanism (unspecified to date) ensures that public images are pulled from a sponsor registry to the mirror registry, to make sure that the customers of the third-party provider can “docker pull” those images locally.
|
||||
@@ -37,7 +38,10 @@ We expect that there will be multiple registries out there. To help to grasp the
|
||||
- local mount point;
|
||||
- remote docker addressed through SSH.
|
||||
|
||||
The latter would only require two new commands in docker, e.g. “registryget” and “registryput”, wrapping access to the local filesystem (and optionally doing consistency checks). Authentication and authorization are then delegated to SSH (e.g. with public keys).
|
||||
The latter would only require two new commands in docker, e.g. ``registryget``
|
||||
and ``registryput``, wrapping access to the local filesystem (and optionally
|
||||
doing consistency checks). Authentication and authorization are then delegated
|
||||
to SSH (e.g. with public keys).
|
||||
|
||||
2. Endpoints
|
||||
============
|
||||
|
||||
@@ -15,11 +15,13 @@ Registry & Index Spec
|
||||
---------
|
||||
|
||||
The Index is responsible for centralizing information about:
|
||||
|
||||
- User accounts
|
||||
- Checksums of the images
|
||||
- Public namespaces
|
||||
|
||||
The Index has different components:
|
||||
|
||||
- Web UI
|
||||
- Meta-data store (comments, stars, list public repositories)
|
||||
- Authentication service
|
||||
@@ -27,7 +29,7 @@ The Index has different components:
|
||||
|
||||
The index is authoritative for those information.
|
||||
|
||||
We expect that there will be only one instance of the index, run and managed by dotCloud.
|
||||
We expect that there will be only one instance of the index, run and managed by Docker Inc.
|
||||
|
||||
1.2 Registry
|
||||
------------
|
||||
@@ -53,12 +55,16 @@ We expect that there will be multiple registries out there. To help to grasp the
|
||||
- local mount point;
|
||||
- remote docker addressed through SSH.
|
||||
|
||||
The latter would only require two new commands in docker, e.g. “registryget” and “registryput”, wrapping access to the local filesystem (and optionally doing consistency checks). Authentication and authorization are then delegated to SSH (e.g. with public keys).
|
||||
The latter would only require two new commands in docker, e.g. ``registryget``
|
||||
and ``registryput``, wrapping access to the local filesystem (and optionally
|
||||
doing consistency checks). Authentication and authorization are then delegated
|
||||
to SSH (e.g. with public keys).
|
||||
|
||||
1.3 Docker
|
||||
----------
|
||||
|
||||
On top of being a runtime for LXC, Docker is the Registry client. It supports:
|
||||
|
||||
- Push / Pull on the registry
|
||||
- Client authentication on the Index
|
||||
|
||||
@@ -72,21 +78,33 @@ On top of being a runtime for LXC, Docker is the Registry client. It supports:
|
||||
|
||||
1. Contact the Index to know where I should download “samalba/busybox”
|
||||
2. Index replies:
|
||||
a. “samalba/busybox” is on Registry A
|
||||
b. here are the checksums for “samalba/busybox” (for all layers)
|
||||
a. ``samalba/busybox`` is on Registry A
|
||||
b. here are the checksums for ``samalba/busybox`` (for all layers)
|
||||
c. token
|
||||
3. Contact Registry A to receive the layers for “samalba/busybox” (all of them to the base image). Registry A is authoritative for “samalba/busybox” but keeps a copy of all inherited layers and serve them all from the same location.
|
||||
3. Contact Registry A to receive the layers for ``samalba/busybox`` (all of them to the base image). Registry A is authoritative for “samalba/busybox” but keeps a copy of all inherited layers and serve them all from the same location.
|
||||
4. registry contacts index to verify if token/user is allowed to download images
|
||||
5. Index returns true/false lettings registry know if it should proceed or error out
|
||||
6. Get the payload for all layers
|
||||
|
||||
It’s possible to run docker pull \https://<registry>/repositories/samalba/busybox. In this case, docker bypasses the Index. However the security is not guaranteed (in case Registry A is corrupted) because there won’t be any checksum checks.
|
||||
It's possible to run:
|
||||
|
||||
Currently registry redirects to s3 urls for downloads, going forward all downloads need to be streamed through the registry. The Registry will then abstract the calls to S3 by a top-level class which implements sub-classes for S3 and local storage.
|
||||
.. code-block:: bash
|
||||
|
||||
Token is only returned when the 'X-Docker-Token' header is sent with request.
|
||||
docker pull https://<registry>/repositories/samalba/busybox
|
||||
|
||||
Basic Auth is required to pull private repos. Basic auth isn't required for pulling public repos, but if one is provided, it needs to be valid and for an active account.
|
||||
In this case, Docker bypasses the Index. However the security is not guaranteed
|
||||
(in case Registry A is corrupted) because there won’t be any checksum checks.
|
||||
|
||||
Currently registry redirects to s3 urls for downloads, going forward all
|
||||
downloads need to be streamed through the registry. The Registry will then
|
||||
abstract the calls to S3 by a top-level class which implements sub-classes for
|
||||
S3 and local storage.
|
||||
|
||||
Token is only returned when the ``X-Docker-Token`` header is sent with request.
|
||||
|
||||
Basic Auth is required to pull private repos. Basic auth isn't required for
|
||||
pulling public repos, but if one is provided, it needs to be valid and for an
|
||||
active account.
|
||||
|
||||
API (pulling repository foo/bar):
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -155,7 +173,9 @@ API (pulling repository foo/bar):
|
||||
|
||||
**Index can be replaced!** For a private Registry deployed, a custom Index can be used to serve and validate token according to different policies.
|
||||
|
||||
Docker computes the checksums and submit them to the Index at the end of the push. When a repository name does not have checksums on the Index, it means that the push is in progress (since checksums are submitted at the end).
|
||||
Docker computes the checksums and submit them to the Index at the end of the
|
||||
push. When a repository name does not have checksums on the Index, it means
|
||||
that the push is in progress (since checksums are submitted at the end).
|
||||
|
||||
API (pushing repos foo/bar):
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -237,10 +257,11 @@ API (pushing repos foo/bar):
|
||||
2.3 Delete
|
||||
----------
|
||||
|
||||
If you need to delete something from the index or registry, we need a nice clean way to do that. Here is the workflow.
|
||||
If you need to delete something from the index or registry, we need a nice
|
||||
clean way to do that. Here is the workflow.
|
||||
|
||||
1. Docker contacts the index to request a delete of a repository “samalba/busybox” (authentication required with user credentials)
|
||||
2. If authentication works and repository is valid, “samalba/busybox” is marked as deleted and a temporary token is returned
|
||||
1. Docker contacts the index to request a delete of a repository ``samalba/busybox`` (authentication required with user credentials)
|
||||
2. If authentication works and repository is valid, ``samalba/busybox`` is marked as deleted and a temporary token is returned
|
||||
3. Send a delete request to the registry for the repository (along with the token)
|
||||
4. Registry A contacts the Index to verify the token (token must corresponds to the repository name)
|
||||
5. Index validates the token. Registry A deletes the repository and everything associated to it.
|
||||
@@ -312,24 +333,40 @@ The Index has two main purposes (along with its fancy social features):
|
||||
|
||||
3.1 Without an Index
|
||||
--------------------
|
||||
Using the Registry without the Index can be useful to store the images on a private network without having to rely on an external entity controlled by dotCloud.
|
||||
|
||||
In this case, the registry will be launched in a special mode (--standalone? --no-index?). In this mode, the only thing which changes is that Registry will never contact the Index to verify a token. It will be the Registry owner responsibility to authenticate the user who pushes (or even pulls) an image using any mechanism (HTTP auth, IP based, etc...).
|
||||
Using the Registry without the Index can be useful to store the images on a
|
||||
private network without having to rely on an external entity controlled by
|
||||
Docker Inc.
|
||||
|
||||
In this scenario, the Registry is responsible for the security in case of data corruption since the checksums are not delivered by a trusted entity.
|
||||
In this case, the registry will be launched in a special mode (--standalone?
|
||||
--no-index?). In this mode, the only thing which changes is that Registry will
|
||||
never contact the Index to verify a token. It will be the Registry owner
|
||||
responsibility to authenticate the user who pushes (or even pulls) an image
|
||||
using any mechanism (HTTP auth, IP based, etc...).
|
||||
|
||||
As hinted previously, a standalone registry can also be implemented by any HTTP server handling GET/PUT requests (or even only GET requests if no write access is necessary).
|
||||
In this scenario, the Registry is responsible for the security in case of data
|
||||
corruption since the checksums are not delivered by a trusted entity.
|
||||
|
||||
As hinted previously, a standalone registry can also be implemented by any HTTP
|
||||
server handling GET/PUT requests (or even only GET requests if no write access
|
||||
is necessary).
|
||||
|
||||
3.2 With an Index
|
||||
-----------------
|
||||
|
||||
The Index data needed by the Registry are simple:
|
||||
|
||||
- Serve the checksums
|
||||
- Provide and authorize a Token
|
||||
|
||||
In the scenario of a Registry running on a private network with the need of centralizing and authorizing, it’s easy to use a custom Index.
|
||||
In the scenario of a Registry running on a private network with the need of
|
||||
centralizing and authorizing, it’s easy to use a custom Index.
|
||||
|
||||
The only challenge will be to tell Docker to contact (and trust) this custom Index. Docker will be configurable at some point to use a specific Index, it’ll be the private entity responsibility (basically the organization who uses Docker in a private environment) to maintain the Index and the Docker’s configuration among its consumers.
|
||||
The only challenge will be to tell Docker to contact (and trust) this custom
|
||||
Index. Docker will be configurable at some point to use a specific Index, it’ll
|
||||
be the private entity responsibility (basically the organization who uses
|
||||
Docker in a private environment) to maintain the Index and the Docker’s
|
||||
configuration among its consumers.
|
||||
|
||||
4. The API
|
||||
==========
|
||||
@@ -339,16 +376,22 @@ The first version of the api is available here: https://github.com/jpetazzo/dock
|
||||
4.1 Images
|
||||
----------
|
||||
|
||||
The format returned in the images is not defined here (for layer and json), basically because Registry stores exactly the same kind of information as Docker uses to manage them.
|
||||
The format returned in the images is not defined here (for layer and JSON),
|
||||
basically because Registry stores exactly the same kind of information as
|
||||
Docker uses to manage them.
|
||||
|
||||
The format of ancestry is a line-separated list of image ids, in age order. I.e. the image’s parent is on the last line, the parent of the parent on the next-to-last line, etc.; if the image has no parent, the file is empty.
|
||||
The format of ancestry is a line-separated list of image ids, in age order,
|
||||
i.e. the image’s parent is on the last line, the parent of the parent on the
|
||||
next-to-last line, etc.; if the image has no parent, the file is empty.
|
||||
|
||||
GET /v1/images/<image_id>/layer
|
||||
PUT /v1/images/<image_id>/layer
|
||||
GET /v1/images/<image_id>/json
|
||||
PUT /v1/images/<image_id>/json
|
||||
GET /v1/images/<image_id>/ancestry
|
||||
PUT /v1/images/<image_id>/ancestry
|
||||
.. code-block:: bash
|
||||
|
||||
GET /v1/images/<image_id>/layer
|
||||
PUT /v1/images/<image_id>/layer
|
||||
GET /v1/images/<image_id>/json
|
||||
PUT /v1/images/<image_id>/json
|
||||
GET /v1/images/<image_id>/ancestry
|
||||
PUT /v1/images/<image_id>/ancestry
|
||||
|
||||
4.2 Users
|
||||
---------
|
||||
@@ -393,7 +436,9 @@ PUT /v1/users/<username>
|
||||
|
||||
4.2.3 Login (Index)
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Does nothing else but asking for a user authentication. Can be used to validate credentials. HTTP Basic Auth for now, maybe change in future.
|
||||
|
||||
Does nothing else but asking for a user authentication. Can be used to validate
|
||||
credentials. HTTP Basic Auth for now, maybe change in future.
|
||||
|
||||
GET /v1/users
|
||||
|
||||
@@ -405,7 +450,10 @@ GET /v1/users
|
||||
4.3 Tags (Registry)
|
||||
-------------------
|
||||
|
||||
The Registry does not know anything about users. Even though repositories are under usernames, it’s just a namespace for the registry. Allowing us to implement organizations or different namespaces per user later, without modifying the Registry’s API.
|
||||
The Registry does not know anything about users. Even though repositories are
|
||||
under usernames, it’s just a namespace for the registry. Allowing us to
|
||||
implement organizations or different namespaces per user later, without
|
||||
modifying the Registry’s API.
|
||||
|
||||
The following naming restrictions apply:
|
||||
|
||||
@@ -439,7 +487,10 @@ DELETE /v1/repositories/<namespace>/<repo_name>/tags/<tag>
|
||||
4.4 Images (Index)
|
||||
------------------
|
||||
|
||||
For the Index to “resolve” the repository name to a Registry location, it uses the X-Docker-Endpoints header. In other terms, this requests always add a “X-Docker-Endpoints” to indicate the location of the registry which hosts this repository.
|
||||
For the Index to “resolve” the repository name to a Registry location, it uses
|
||||
the X-Docker-Endpoints header. In other terms, this requests always add a
|
||||
``X-Docker-Endpoints`` to indicate the location of the registry which hosts this
|
||||
repository.
|
||||
|
||||
4.4.1 Get the images
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -484,17 +535,20 @@ Return 202 OK
|
||||
======================
|
||||
|
||||
It’s possible to chain Registries server for several reasons:
|
||||
|
||||
- Load balancing
|
||||
- Delegate the next request to another server
|
||||
|
||||
When a Registry is a reference for a repository, it should host the entire images chain in order to avoid breaking the chain during the download.
|
||||
When a Registry is a reference for a repository, it should host the entire
|
||||
images chain in order to avoid breaking the chain during the download.
|
||||
|
||||
The Index and Registry use this mechanism to redirect on one or the other.
|
||||
|
||||
Example with an image download:
|
||||
On every request, a special header can be returned:
|
||||
|
||||
X-Docker-Endpoints: server1,server2
|
||||
On every request, a special header can be returned::
|
||||
|
||||
X-Docker-Endpoints: server1,server2
|
||||
|
||||
On the next request, the client will always pick a server from this list.
|
||||
|
||||
@@ -504,7 +558,8 @@ On the next request, the client will always pick a server from this list.
|
||||
6.1 On the Index
|
||||
-----------------
|
||||
|
||||
The Index supports both “Basic” and “Token” challenges. Usually when there is a “401 Unauthorized”, the Index replies this::
|
||||
The Index supports both “Basic” and “Token” challenges. Usually when there is a
|
||||
``401 Unauthorized``, the Index replies this::
|
||||
|
||||
401 Unauthorized
|
||||
WWW-Authenticate: Basic realm="auth required",Token
|
||||
@@ -543,11 +598,13 @@ The Registry only supports the Token challenge::
|
||||
401 Unauthorized
|
||||
WWW-Authenticate: Token
|
||||
|
||||
The only way is to provide a token on “401 Unauthorized” responses::
|
||||
The only way is to provide a token on ``401 Unauthorized`` responses::
|
||||
|
||||
Authorization: Token signature=123abc,repository=”foo/bar”,access=read
|
||||
Authorization: Token signature=123abc,repository="foo/bar",access=read
|
||||
|
||||
Usually, the Registry provides a Cookie when a Token verification succeeded. Every time the Registry passes a Cookie, you have to pass it back the same cookie.::
|
||||
Usually, the Registry provides a Cookie when a Token verification succeeded.
|
||||
Every time the Registry passes a Cookie, you have to pass it back the same
|
||||
cookie.::
|
||||
|
||||
200 OK
|
||||
Set-Cookie: session="wD/J7LqL5ctqw8haL10vgfhrb2Q=?foo=UydiYXInCnAxCi4=×tamp=RjEzNjYzMTQ5NDcuNDc0NjQzCi4="; Path=/; HttpOnly
|
||||
|
||||
@@ -12,7 +12,7 @@ To list available commands, either run ``docker`` with no parameters or execute
|
||||
|
||||
$ sudo docker
|
||||
Usage: docker [OPTIONS] COMMAND [arg...]
|
||||
-H=[unix:///var/run/docker.sock]: tcp://host:port to bind/connect to or unix://path/to/socket to use
|
||||
-H=[unix:///var/run/docker.sock]: tcp://[host[:port]] to bind/connect to or unix://[/path/to/socket] to use. When host=[0.0.0.0], port=[4243] or path=[/var/run/docker.sock] is omitted, default values are used.
|
||||
|
||||
A self-sufficient runtime for linux containers.
|
||||
|
||||
@@ -27,28 +27,42 @@ To list available commands, either run ``docker`` with no parameters or execute
|
||||
|
||||
Usage of docker:
|
||||
-D=false: Enable debug mode
|
||||
-H=[unix:///var/run/docker.sock]: Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise
|
||||
-H=[unix:///var/run/docker.sock]: tcp://[host[:port]] to bind or unix://[/path/to/socket] to use. When host=[0.0.0.0], port=[4243] or path=[/var/run/docker.sock] is omitted, default values are used.
|
||||
-api-enable-cors=false: Enable CORS headers in the remote API
|
||||
-b="": Attach containers to a pre-existing network bridge; use 'none' to disable container networking
|
||||
-bip="": Use the provided CIDR notation address for the dynamically created bridge (docker0); Mutually exclusive of -b
|
||||
-d=false: Enable daemon mode
|
||||
-dns="": Force docker to use specific DNS servers
|
||||
-g="/var/lib/docker": Path to use as the root of the docker runtime
|
||||
-icc=true: Enable inter-container communication
|
||||
-ip="0.0.0.0": Default IP address to use when binding container ports
|
||||
-iptables=true: Disable docker's addition of iptables rules
|
||||
-mtu=1500: Set the containers network mtu
|
||||
-p="/var/run/docker.pid": Path to use for daemon PID file
|
||||
-r=true: Restart previously running containers
|
||||
-s="": Force the docker runtime to use a specific storage driver
|
||||
-v=false: Print version information and quit
|
||||
|
||||
The docker daemon is the persistent process that manages containers. Docker uses the same binary for both the
|
||||
The Docker daemon is the persistent process that manages containers. Docker uses the same binary for both the
|
||||
daemon and client. To run the daemon you provide the ``-d`` flag.
|
||||
|
||||
To force docker to use devicemapper as the storage driver, use ``docker -d -s devicemapper``
|
||||
To force Docker to use devicemapper as the storage driver, use ``docker -d -s devicemapper``.
|
||||
|
||||
To set the dns server for all docker containers, use ``docker -d -dns 8.8.8.8``
|
||||
To set the DNS server for all Docker containers, use ``docker -d -dns 8.8.8.8``.
|
||||
|
||||
To run the daemon with debug output, use ``docker -d -D``.
|
||||
|
||||
The docker client will also honor the ``DOCKER_HOST`` environment variable to set
|
||||
the ``-H`` flag for the client.
|
||||
|
||||
::
|
||||
|
||||
docker -H tcp://0.0.0.0:4243 ps
|
||||
# or
|
||||
export DOCKER_HOST="tcp://0.0.0.0:4243"
|
||||
docker ps
|
||||
# both are equal
|
||||
|
||||
To run the daemon with debug output, use ``docker -d -D``
|
||||
|
||||
.. _cli_attach:
|
||||
|
||||
@@ -67,11 +81,11 @@ To run the daemon with debug output, use ``docker -d -D``
|
||||
You can detach from the container again (and leave it running) with
|
||||
``CTRL-c`` (for a quiet exit) or ``CTRL-\`` to get a stacktrace of
|
||||
the Docker client when it quits. When you detach from the container's
|
||||
process the exit code will be retuned to the client.
|
||||
process the exit code will be returned to the client.
|
||||
|
||||
To stop a container, use ``docker stop``
|
||||
To stop a container, use ``docker stop``.
|
||||
|
||||
To kill the container, use ``docker kill``
|
||||
To kill the container, use ``docker kill``.
|
||||
|
||||
.. _cli_attach_examples:
|
||||
|
||||
@@ -127,12 +141,11 @@ Examples:
|
||||
-no-cache: Do not use the cache when building the image.
|
||||
-rm: Remove intermediate containers after a successful build
|
||||
|
||||
The files at PATH or URL are called the "context" of the build. The
|
||||
build process may refer to any of the files in the context, for
|
||||
example when using an :ref:`ADD <dockerfile_add>` instruction. When a
|
||||
single ``Dockerfile`` is given as URL, then no context is set. When a
|
||||
git repository is set as URL, then the repository is used as the
|
||||
context
|
||||
The files at ``PATH`` or ``URL`` are called the "context" of the build. The
|
||||
build process may refer to any of the files in the context, for example when
|
||||
using an :ref:`ADD <dockerfile_add>` instruction. When a single ``Dockerfile``
|
||||
is given as ``URL``, then no context is set. When a Git repository is set as
|
||||
``URL``, then the repository is used as the context
|
||||
|
||||
.. _cli_build_examples:
|
||||
|
||||
@@ -167,13 +180,13 @@ Examples:
|
||||
---> f52f38b7823e
|
||||
Successfully built f52f38b7823e
|
||||
|
||||
This example specifies that the PATH is ``.``, and so all the files in
|
||||
the local directory get tar'd and sent to the Docker daemon. The PATH
|
||||
This example specifies that the ``PATH`` is ``.``, and so all the files in
|
||||
the local directory get tar'd and sent to the Docker daemon. The ``PATH``
|
||||
specifies where to find the files for the "context" of the build on
|
||||
the Docker daemon. Remember that the daemon could be running on a
|
||||
remote machine and that no parsing of the Dockerfile happens at the
|
||||
remote machine and that no parsing of the ``Dockerfile`` happens at the
|
||||
client side (where you're running ``docker build``). That means that
|
||||
*all* the files at PATH get sent, not just the ones listed to
|
||||
*all* the files at ``PATH`` get sent, not just the ones listed to
|
||||
:ref:`ADD <dockerfile_add>` in the ``Dockerfile``.
|
||||
|
||||
The transfer of context from the local machine to the Docker daemon is
|
||||
@@ -196,16 +209,16 @@ tag will be ``2.0``
|
||||
|
||||
This will read a ``Dockerfile`` from *stdin* without context. Due to
|
||||
the lack of a context, no contents of any local directory will be sent
|
||||
to the ``docker`` daemon. Since there is no context, a Dockerfile
|
||||
to the ``docker`` daemon. Since there is no context, a ``Dockerfile``
|
||||
``ADD`` only works if it refers to a remote URL.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker build github.com/creack/docker-firefox
|
||||
|
||||
This will clone the Github repository and use the cloned repository as
|
||||
This will clone the GitHub repository and use the cloned repository as
|
||||
context. The ``Dockerfile`` at the root of the repository is used as
|
||||
``Dockerfile``. Note that you can specify an arbitrary git repository
|
||||
``Dockerfile``. Note that you can specify an arbitrary Git repository
|
||||
by using the ``git://`` schema.
|
||||
|
||||
|
||||
@@ -225,8 +238,10 @@ by using the ``git://`` schema.
|
||||
-run="": Configuration to be applied when the image is launched with `docker run`.
|
||||
(ex: -run='{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')
|
||||
|
||||
Simple commit of an existing container
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.. _cli_commit_examples:
|
||||
|
||||
Commit an existing container
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -240,13 +255,36 @@ Simple commit of an existing container
|
||||
REPOSITORY TAG ID CREATED VIRTUAL SIZE
|
||||
SvenDowideit/testimage version3 f5283438590d 16 seconds ago 335.7 MB
|
||||
|
||||
Change the command that a container runs
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sometimes you have an application container running just a service and you need
|
||||
to make a quick change and then change it back.
|
||||
|
||||
In this example, we run a container with ``ls`` and then change the image to
|
||||
run ``ls /etc``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ docker run -t -name test ubuntu ls
|
||||
bin boot dev etc home lib lib64 media mnt opt proc root run sbin selinux srv sys tmp usr var
|
||||
$ docker commit -run='{"Cmd": ["ls","/etc"]}' test test2
|
||||
933d16de9e70005304c1717b5c6f2f39d6fd50752834c6f34a155c70790011eb
|
||||
$ docker run -t test2
|
||||
adduser.conf gshadow login.defs rc0.d
|
||||
alternatives gshadow- logrotate.d rc1.d
|
||||
apt host.conf lsb-base rc2.d
|
||||
...
|
||||
|
||||
Full -run example
|
||||
.................
|
||||
|
||||
(multiline is ok within a single quote ``'``)
|
||||
The ``-run`` JSON hash changes the ``Config`` section when running ``docker inspect CONTAINERID``
|
||||
or ``config`` when running ``docker inspect IMAGEID``.
|
||||
|
||||
::
|
||||
(Multiline is okay within a single quote ``'``)
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker commit -run='
|
||||
{
|
||||
@@ -289,7 +327,7 @@ Full -run example
|
||||
|
||||
Copy files/folders from the containers filesystem to the host
|
||||
path. Paths are relative to the root of the filesystem.
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker cp 7bb0e258aefe:/etc/debian_version .
|
||||
@@ -303,7 +341,7 @@ Full -run example
|
||||
::
|
||||
|
||||
Usage: docker diff CONTAINER
|
||||
|
||||
|
||||
List the changed files and directories in a container's filesystem
|
||||
|
||||
There are 3 events that are listed in the 'diff':
|
||||
@@ -312,7 +350,7 @@ There are 3 events that are listed in the 'diff':
|
||||
2. ```D``` - Delete
|
||||
3. ```C``` - Change
|
||||
|
||||
for example:
|
||||
For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -340,7 +378,7 @@ for example:
|
||||
Usage: docker events
|
||||
|
||||
Get real time events from the server
|
||||
|
||||
|
||||
-since="": Show previously created events and then stream.
|
||||
(either seconds since epoch, or date string as below)
|
||||
|
||||
@@ -403,8 +441,8 @@ Show events in the past from a specified time
|
||||
Usage: docker export CONTAINER
|
||||
|
||||
Export the contents of a filesystem as a tar archive to STDOUT
|
||||
|
||||
for example:
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -424,7 +462,7 @@ for example:
|
||||
-notrunc=false: Don't truncate output
|
||||
-q=false: only show numeric IDs
|
||||
|
||||
To see how the docker:latest image was built:
|
||||
To see how the ``docker:latest`` image was built:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -456,7 +494,7 @@ To see how the docker:latest image was built:
|
||||
d5e85dc5b1d8 2 weeks ago /bin/sh -c apt-get update
|
||||
13e642467c11 2 weeks ago /bin/sh -c echo 'deb http://archive.ubuntu.com/ubuntu precise main universe' > /etc/apt/sources.list
|
||||
ae6dde92a94e 2 weeks ago /bin/sh -c #(nop) MAINTAINER Solomon Hykes <solomon@dotcloud.com>
|
||||
ubuntu:12.04 6 months ago
|
||||
ubuntu:12.04 6 months ago
|
||||
|
||||
.. _cli_images:
|
||||
|
||||
@@ -474,7 +512,7 @@ To see how the docker:latest image was built:
|
||||
-q=false: only show numeric IDs
|
||||
-tree=false: output graph in tree format
|
||||
-viz=false: output graph in graphviz format
|
||||
|
||||
|
||||
Listing the most recently created images
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -527,15 +565,15 @@ Displaying image hierarchy
|
||||
|
||||
$ sudo docker images -tree
|
||||
|
||||
|─8dbd9e392a96 Size: 131.5 MB (virtual 131.5 MB) Tags: ubuntu:12.04,ubuntu:latest,ubuntu:precise
|
||||
├─8dbd9e392a96 Size: 131.5 MB (virtual 131.5 MB) Tags: ubuntu:12.04,ubuntu:latest,ubuntu:precise
|
||||
└─27cf78414709 Size: 180.1 MB (virtual 180.1 MB)
|
||||
└─b750fe79269d Size: 24.65 kB (virtual 180.1 MB) Tags: ubuntu:12.10,ubuntu:quantal
|
||||
|─f98de3b610d5 Size: 12.29 kB (virtual 180.1 MB)
|
||||
| └─7da80deb7dbf Size: 16.38 kB (virtual 180.1 MB)
|
||||
| └─65ed2fee0a34 Size: 20.66 kB (virtual 180.2 MB)
|
||||
| └─a2b9ea53dddc Size: 819.7 MB (virtual 999.8 MB)
|
||||
| └─a29b932eaba8 Size: 28.67 kB (virtual 999.9 MB)
|
||||
| └─e270a44f124d Size: 12.29 kB (virtual 999.9 MB) Tags: progrium/buildstep:latest
|
||||
├─f98de3b610d5 Size: 12.29 kB (virtual 180.1 MB)
|
||||
│ └─7da80deb7dbf Size: 16.38 kB (virtual 180.1 MB)
|
||||
│ └─65ed2fee0a34 Size: 20.66 kB (virtual 180.2 MB)
|
||||
│ └─a2b9ea53dddc Size: 819.7 MB (virtual 999.8 MB)
|
||||
│ └─a29b932eaba8 Size: 28.67 kB (virtual 999.9 MB)
|
||||
│ └─e270a44f124d Size: 12.29 kB (virtual 999.9 MB) Tags: progrium/buildstep:latest
|
||||
└─17e74ac162d8 Size: 53.93 kB (virtual 180.2 MB)
|
||||
└─339a3f56b760 Size: 24.65 kB (virtual 180.2 MB)
|
||||
└─904fcc40e34d Size: 96.7 MB (virtual 276.9 MB)
|
||||
@@ -562,10 +600,9 @@ Displaying image hierarchy
|
||||
(.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it.
|
||||
|
||||
At this time, the URL must start with ``http`` and point to a single
|
||||
file archive (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) containing a
|
||||
file archive (.tar, .tar.gz, .tgz, .bzip, .tar.xz, or .txz) containing a
|
||||
root filesystem. If you would like to import from a local directory or
|
||||
archive, you can use the ``-`` parameter to take the data from
|
||||
standard in.
|
||||
archive, you can use the ``-`` parameter to take the data from *stdin*.
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
@@ -575,24 +612,30 @@ Import from a remote location
|
||||
|
||||
This will create a new untagged image.
|
||||
|
||||
``$ sudo docker import http://example.com/exampleimage.tgz``
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker import http://example.com/exampleimage.tgz
|
||||
|
||||
Import from a local file
|
||||
........................
|
||||
|
||||
Import to docker via pipe and standard in
|
||||
Import to docker via pipe and *stdin*.
|
||||
|
||||
``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal:new``
|
||||
.. code-block:: bash
|
||||
|
||||
$ cat exampleimage.tgz | sudo docker import - exampleimagelocal:new
|
||||
|
||||
Import from a local directory
|
||||
.............................
|
||||
|
||||
``$ sudo tar -c . | docker import - exampleimagedir``
|
||||
.. code-block:: bash
|
||||
|
||||
Note the ``sudo`` in this example -- you must preserve the ownership
|
||||
of the files (especially root ownership) during the archiving with
|
||||
tar. If you are not root (or sudo) when you tar, then the ownerships
|
||||
might not get preserved.
|
||||
$ sudo tar -c . | docker import - exampleimagedir
|
||||
|
||||
Note the ``sudo`` in this example -- you must preserve the ownership of the
|
||||
files (especially root ownership) during the archiving with tar. If you are not
|
||||
root (or the sudo command) when you tar, then the ownerships might not get
|
||||
preserved.
|
||||
|
||||
.. _cli_info:
|
||||
|
||||
@@ -631,16 +674,16 @@ might not get preserved.
|
||||
|
||||
Insert a file from URL in the IMAGE at PATH
|
||||
|
||||
Use the specified IMAGE as the parent for a new image which adds a
|
||||
:ref:`layer <layer_def>` containing the new file. ``insert`` does not modify
|
||||
the original image, and the new image has the contents of the parent image,
|
||||
plus the new file.
|
||||
Use the specified ``IMAGE`` as the parent for a new image which adds a
|
||||
:ref:`layer <layer_def>` containing the new file. The ``insert`` command does
|
||||
not modify the original image, and the new image has the contents of the parent
|
||||
image, plus the new file.
|
||||
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
|
||||
Insert file from github
|
||||
Insert file from GitHub
|
||||
.......................
|
||||
|
||||
.. code-block:: bash
|
||||
@@ -655,16 +698,16 @@ Insert file from github
|
||||
|
||||
::
|
||||
|
||||
Usage: docker inspect [OPTIONS] CONTAINER
|
||||
Usage: docker inspect CONTAINER|IMAGE [CONTAINER|IMAGE...]
|
||||
|
||||
Return low-level information on a container
|
||||
Return low-level information on a container/image
|
||||
|
||||
-format="": template to output results
|
||||
-format="": Format the output using the given go template.
|
||||
|
||||
By default, this will render all results in a JSON array. If a format
|
||||
is specified, the given template will be executed for each result.
|
||||
|
||||
Go's `text/template <http://golang.org/pkg/text/template/>` package
|
||||
Go's `text/template <http://golang.org/pkg/text/template/>`_ package
|
||||
describes all the details of the format.
|
||||
|
||||
Examples
|
||||
@@ -769,6 +812,15 @@ Known Issues (kill)
|
||||
|
||||
Fetch the logs of a container
|
||||
|
||||
The ``docker logs`` command is a convenience which batch-retrieves whatever
|
||||
logs are present at the time of execution. This does not guarantee execution
|
||||
order when combined with a ``docker run`` (i.e. your run may not have generated
|
||||
any logs at the time you execute ``docker logs``).
|
||||
|
||||
The ``docker logs -f`` command combines ``docker logs`` and ``docker attach``:
|
||||
it will first return all logs from the beginning and then continue streaming
|
||||
new output from the container's stdout and stderr.
|
||||
|
||||
|
||||
.. _cli_port:
|
||||
|
||||
@@ -900,6 +952,38 @@ containers will not be deleted.
|
||||
Usage: docker rmi IMAGE [IMAGE...]
|
||||
|
||||
Remove one or more images
|
||||
|
||||
Removing tagged images
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Images can be removed either by their short or long ID's, or their image names.
|
||||
If an image has more than one name, each of them needs to be removed before the
|
||||
image is removed.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
test1 latest fd484f19954f 23 seconds ago 7 B (virtual 4.964 MB)
|
||||
test latest fd484f19954f 23 seconds ago 7 B (virtual 4.964 MB)
|
||||
test2 latest fd484f19954f 23 seconds ago 7 B (virtual 4.964 MB)
|
||||
|
||||
$ sudo docker rmi fd484f19954f
|
||||
Error: Conflict, cannot delete image fd484f19954f because it is tagged in multiple repositories
|
||||
2013/12/11 05:47:16 Error: failed to remove one or more images
|
||||
|
||||
$ sudo docker rmi test1
|
||||
Untagged: fd484f19954f4920da7ff372b5067f5b7ddb2fd3830cecd17b96ea9e286ba5b8
|
||||
$ sudo docker rmi test2
|
||||
Untagged: fd484f19954f4920da7ff372b5067f5b7ddb2fd3830cecd17b96ea9e286ba5b8
|
||||
|
||||
$ sudo docker images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
test1 latest fd484f19954f 23 seconds ago 7 B (virtual 4.964 MB)
|
||||
$ sudo docker rmi test
|
||||
Untagged: fd484f19954f4920da7ff372b5067f5b7ddb2fd3830cecd17b96ea9e286ba5b8
|
||||
Deleted: fd484f19954f4920da7ff372b5067f5b7ddb2fd3830cecd17b96ea9e286ba5b8
|
||||
|
||||
|
||||
.. _cli_run:
|
||||
|
||||
@@ -938,6 +1022,14 @@ containers will not be deleted.
|
||||
-name="": Assign the specified name to the container. If no name is specific docker will generate a random name
|
||||
-P=false: Publish all exposed ports to the host interfaces
|
||||
|
||||
The ``docker run`` command first ``creates`` a writeable container layer over
|
||||
the specified image, and then ``starts`` it using the specified command. That
|
||||
is, ``docker run`` is equivalent to the API ``/containers/create`` then
|
||||
``/containers/(id)/start``.
|
||||
|
||||
The ``docker run`` command can be used in combination with ``docker commit`` to
|
||||
:ref:`change the command that a container runs <cli_commit_examples>`.
|
||||
|
||||
Known Issues (run -volumes-from)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -952,10 +1044,10 @@ Examples:
|
||||
|
||||
$ sudo docker run -cidfile /tmp/docker_test.cid ubuntu echo "test"
|
||||
|
||||
This will create a container and print "test" to the console. The
|
||||
``cidfile`` flag makes docker attempt to create a new file and write the
|
||||
container ID to it. If the file exists already, docker will return an
|
||||
error. Docker will close this file when docker run exits.
|
||||
This will create a container and print ``test`` to the console. The
|
||||
``cidfile`` flag makes Docker attempt to create a new file and write the
|
||||
container ID to it. If the file exists already, Docker will return an
|
||||
error. Docker will close this file when ``docker run`` exits.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -989,7 +1081,7 @@ use-cases, like running Docker within Docker.
|
||||
$ sudo docker run -w /path/to/dir/ -i -t ubuntu pwd
|
||||
|
||||
The ``-w`` lets the command being executed inside directory given,
|
||||
here /path/to/dir/. If the path does not exists it is created inside the
|
||||
here ``/path/to/dir/``. If the path does not exists it is created inside the
|
||||
container.
|
||||
|
||||
.. code-block:: bash
|
||||
@@ -1006,7 +1098,7 @@ using the container, but inside the current working directory.
|
||||
|
||||
$ sudo docker run -p 127.0.0.1:80:8080 ubuntu bash
|
||||
|
||||
This binds port ``8080`` of the container to port ``80`` on 127.0.0.1 of the
|
||||
This binds port ``8080`` of the container to port ``80`` on ``127.0.0.1`` of the
|
||||
host machine. :ref:`port_redirection` explains in detail how to manipulate ports
|
||||
in Docker.
|
||||
|
||||
@@ -1040,11 +1132,31 @@ to the newly created container.
|
||||
$ sudo docker run -volumes-from 777f7dc92da7,ba8c0c54f0f2:ro -i -t ubuntu pwd
|
||||
|
||||
The ``-volumes-from`` flag mounts all the defined volumes from the
|
||||
refrence containers. Containers can be specified by a comma seperated
|
||||
referenced containers. Containers can be specified by a comma seperated
|
||||
list or by repetitions of the ``-volumes-from`` argument. The container
|
||||
id may be optionally suffixed with ``:ro`` or ``:rw`` to mount the volumes in
|
||||
ID may be optionally suffixed with ``:ro`` or ``:rw`` to mount the volumes in
|
||||
read-only or read-write mode, respectively. By default, the volumes are mounted
|
||||
in the same mode (rw or ro) as the reference container.
|
||||
in the same mode (read write or read only) as the reference container.
|
||||
|
||||
A complete example
|
||||
..................
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker run -d -name static static-web-files sh
|
||||
$ sudo docker run -d -expose=8098 -name riak riakserver
|
||||
$ sudo docker run -d -m 100m -e DEVELOPMENT=1 -e BRANCH=example-code -v $(pwd):/app/bin:ro -name app appserver
|
||||
$ sudo docker run -d -p 1443:443 -dns=dns.dev.org -v /var/log/httpd -volumes-from static -link riak -link app -h www.sven.dev.org -name web webserver
|
||||
$ sudo docker run -t -i -rm -volumes-from web -w /var/log/httpd busybox tail -f access.log
|
||||
|
||||
This example shows 5 containers that might be set up to test a web application change:
|
||||
|
||||
1. Start a pre-prepared volume image ``static-web-files`` (in the background) that has CSS, image and static HTML in it, (with a ``VOLUME`` instruction in the ``Dockerfile`` to allow the web server to use those files);
|
||||
2. Start a pre-prepared ``riakserver`` image, give the container name ``riak`` and expose port ``8098`` to any containers that link to it;
|
||||
3. Start the ``appserver`` image, restricting its memory usage to 100MB, setting two environment variables ``DEVELOPMENT`` and ``BRANCH`` and bind-mounting the current directory (``$(pwd)``) in the container in read-only mode as ``/app/bin``;
|
||||
4. Start the ``webserver``, mapping port ``443`` in the container to port ``1443`` on the Docker server, setting the DNS server to ``dns.dev.org``, creating a volume to put the log files into (so we can access it from another container), then importing the files from the volume exposed by the ``static`` container, and linking to all exposed ports from ``riak`` and ``app``. Lastly, we set the hostname to ``web.sven.dev.org`` so its consistent with the pre-generated SSL certificate;
|
||||
5. Finally, we create a container that runs ``tail -f access.log`` using the logs volume from the ``web`` container, setting the workdir to ``/var/log/httpd``. The ``-rm`` option means that when the container exits, the container's layer is removed.
|
||||
|
||||
|
||||
.. _cli_save:
|
||||
|
||||
@@ -1080,7 +1192,7 @@ in the same mode (rw or ro) as the reference container.
|
||||
|
||||
::
|
||||
|
||||
Usage: docker start [OPTIONS] NAME
|
||||
Usage: docker start [OPTIONS] CONTAINER
|
||||
|
||||
Start a stopped container
|
||||
|
||||
@@ -1131,7 +1243,7 @@ The main process inside the container will receive SIGTERM, and after a grace pe
|
||||
``version``
|
||||
-----------
|
||||
|
||||
Show the version of the docker client, daemon, and latest released version.
|
||||
Show the version of the Docker client, daemon, and latest released version.
|
||||
|
||||
|
||||
.. _cli_wait:
|
||||
|
||||
@@ -44,7 +44,8 @@ This following command will build a development environment using the Dockerfile
|
||||
|
||||
sudo make build
|
||||
|
||||
If the build is successful, congratulations! You have produced a clean build of docker, neatly encapsulated in a standard build environment.
|
||||
If the build is successful, congratulations! You have produced a clean build of
|
||||
docker, neatly encapsulated in a standard build environment.
|
||||
|
||||
|
||||
Step 4: Build the Docker Binary
|
||||
@@ -58,6 +59,19 @@ To create the Docker binary, run this command:
|
||||
|
||||
This will create the Docker binary in ``./bundles/<version>-dev/binary/``
|
||||
|
||||
Using your built Docker binary
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The binary is available outside the container in the directory
|
||||
``./bundles/<version>-dev/binary/``. You can swap your host docker executable
|
||||
with this binary for live testing - for example, on ubuntu:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo service docker stop ; sudo cp $(which docker) $(which docker)_ ; sudo cp ./bundles/<version>-dev/binary/docker-<version>-dev $(which docker);sudo service docker start
|
||||
|
||||
.. note:: Its safer to run the tests below before swapping your hosts docker binary.
|
||||
|
||||
|
||||
Step 5: Run the Tests
|
||||
---------------------
|
||||
@@ -121,22 +135,19 @@ You can run an interactive session in the newly built container:
|
||||
# type 'exit' or Ctrl-D to exit
|
||||
|
||||
|
||||
Extra Step: Build and view the Documenation
|
||||
-------------------------------------------
|
||||
Extra Step: Build and view the Documentation
|
||||
--------------------------------------------
|
||||
|
||||
If you want to read the documentation from a local website, or are making changes
|
||||
to it, you can build the documentation and then serve it by:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo make doc
|
||||
sudo make docs
|
||||
# when its done, you can point your browser to http://yourdockerhost:8000
|
||||
# type Ctrl-C to exit
|
||||
|
||||
|
||||
.. note:: The binary is available outside the container in the directory ``./bundles/<version>-dev/binary/``. You can swap your host docker executable with this binary for live testing - for example, on ubuntu: ``sudo service docker stop ; sudo cp $(which docker) $(which docker)_ ; sudo cp ./bundles/<version>-dev/binary/docker-<version>-dev $(which docker);sudo service docker start``.
|
||||
|
||||
|
||||
**Need More Help?**
|
||||
|
||||
If you need more help then hop on to the `#docker-dev IRC channel <irc://chat.freenode.net#docker-dev>`_ or post a message on the `Docker developer mailinglist <https://groups.google.com/d/forum/docker-dev>`_.
|
||||
If you need more help then hop on to the `#docker-dev IRC channel <irc://chat.freenode.net#docker-dev>`_ or post a message on the `Docker developer mailing list <https://groups.google.com/d/forum/docker-dev>`_.
|
||||
|
||||
@@ -94,5 +94,13 @@ The password is ``screencast``.
|
||||
$ ifconfig
|
||||
$ ssh root@192.168.33.10 -p 49154
|
||||
# Thanks for watching, Thatcher thatcher@dotcloud.com
|
||||
|
||||
Update:
|
||||
-------
|
||||
|
||||
For Ubuntu 13.10 using stackbrew/ubuntu, you may need do these additional steps:
|
||||
|
||||
1. change /etc/pam.d/sshd, pam_loginuid line 'required' to 'optional'
|
||||
2. echo LANG=\"en_US.UTF-8\" > /etc/default/locale
|
||||
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ What does Docker add to just plain LXC?
|
||||
registry to store and transfer private containers, for internal
|
||||
server deployments for example.
|
||||
|
||||
* *Tool ecosystem.*
|
||||
* *Tool ecosystem.*
|
||||
Docker defines an API for automating and customizing the
|
||||
creation and deployment of containers. There are a huge number
|
||||
of tools integrating with Docker to extend its
|
||||
@@ -122,6 +122,11 @@ What does Docker add to just plain LXC?
|
||||
(Jenkins, Strider, Travis), etc. Docker is rapidly establishing
|
||||
itself as the standard for container-based tooling.
|
||||
|
||||
What is different between a Docker container and a VM?
|
||||
......................................................
|
||||
|
||||
There's a great StackOverflow answer `showing the differences <http://stackoverflow.com/questions/16047306/how-is-docker-io-different-from-a-normal-virtual-machine>`_.
|
||||
|
||||
Do I lose my data when the container exits?
|
||||
...........................................
|
||||
|
||||
@@ -129,6 +134,53 @@ Not at all! Any data that your application writes to disk gets preserved
|
||||
in its container until you explicitly delete the container. The file
|
||||
system for the container persists even after the container halts.
|
||||
|
||||
How far do Docker containers scale?
|
||||
...................................
|
||||
|
||||
Some of the largest server farms in the world today are based on containers.
|
||||
Large web deployments like Google and Twitter, and platform providers such as
|
||||
Heroku and dotCloud all run on container technology, at a scale of hundreds of
|
||||
thousands or even millions of containers running in parallel.
|
||||
|
||||
How do I connect Docker containers?
|
||||
...................................
|
||||
|
||||
Currently the recommended way to link containers is via the `link` primitive.
|
||||
You can see details of how to `work with links here
|
||||
<http://docs.docker.io/en/latest/use/working_with_links_names/>`_.
|
||||
|
||||
Also of useful when enabling more flexible service portability is the
|
||||
`Ambassador linking pattern
|
||||
<http://docs.docker.io/en/latest/use/ambassador_pattern_linking/>`_.
|
||||
|
||||
How do I run more than one process in a Docker container?
|
||||
.........................................................
|
||||
|
||||
Any capable process supervisor such as http://supervisord.org/, runit, s6, or
|
||||
daemontools can do the trick. Docker will start up the process management
|
||||
daemon which will then fork to run additional processes. As long as the
|
||||
processor manager daemon continues to run, the container will continue to as
|
||||
well. You can see a more substantial example `that uses supervisord here
|
||||
<http://docs.docker.io/en/latest/examples/using_supervisord/>`_.
|
||||
|
||||
What platforms does Docker run on?
|
||||
..................................
|
||||
|
||||
Linux:
|
||||
|
||||
- Ubuntu 12.04, 13.04 et al
|
||||
- Fedora 19/20+
|
||||
- RHEL 6.5+
|
||||
- Centos 6+
|
||||
- Gento
|
||||
- ArchLinux
|
||||
|
||||
Cloud:
|
||||
|
||||
- Amazon EC2
|
||||
- Google Compute Engine
|
||||
- Rackspace
|
||||
|
||||
Can I help by adding some questions and answers?
|
||||
................................................
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ currently in active development, so this documentation will change
|
||||
frequently.
|
||||
|
||||
For an overview of Docker, please see the `Introduction
|
||||
<http://www.docker.io>`_. When you're ready to start working with
|
||||
<http://www.docker.io/learn_more/>`_. When you're ready to start working with
|
||||
Docker, we have a `quick start <http://www.docker.io/gettingstarted>`_
|
||||
and a more in-depth guide to :ref:`ubuntu_linux` and other
|
||||
:ref:`installation_list` paths including prebuilt binaries,
|
||||
|
||||
@@ -11,41 +11,50 @@ Arch Linux
|
||||
|
||||
.. include:: install_unofficial.inc
|
||||
|
||||
Installing on Arch Linux is not officially supported but can be handled via
|
||||
one of the following AUR packages:
|
||||
Installing on Arch Linux can be handled via the package in community:
|
||||
|
||||
* `lxc-docker <https://aur.archlinux.org/packages/lxc-docker/>`_
|
||||
* `lxc-docker-git <https://aur.archlinux.org/packages/lxc-docker-git/>`_
|
||||
* `lxc-docker-nightly <https://aur.archlinux.org/packages/lxc-docker-nightly/>`_
|
||||
* `docker <https://www.archlinux.org/packages/community/x86_64/docker/>`_
|
||||
|
||||
The lxc-docker package will install the latest tagged version of docker.
|
||||
The lxc-docker-git package will build from the current master branch.
|
||||
The lxc-docker-nightly package will install the latest build.
|
||||
or the following AUR package:
|
||||
|
||||
* `docker-git <https://aur.archlinux.org/packages/docker-git/>`_
|
||||
|
||||
The docker package will install the latest tagged version of docker.
|
||||
The docker-git package will build from the current master branch.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
Docker depends on several packages which are specified as dependencies in
|
||||
the AUR packages. The core dependencies are:
|
||||
the packages. The core dependencies are:
|
||||
|
||||
* bridge-utils
|
||||
* device-mapper
|
||||
* iproute2
|
||||
* lxc
|
||||
* sqlite
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
For the normal package a simple
|
||||
::
|
||||
|
||||
pacman -S docker
|
||||
|
||||
is all that is needed.
|
||||
|
||||
For the AUR package execute:
|
||||
::
|
||||
|
||||
yaourt -S docker-git
|
||||
|
||||
The instructions here assume **yaourt** is installed. See
|
||||
`Arch User Repository <https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages>`_
|
||||
for information on building and installing packages from the AUR if you have not
|
||||
done so before.
|
||||
|
||||
::
|
||||
|
||||
yaourt -S lxc-docker
|
||||
|
||||
|
||||
Starting Docker
|
||||
---------------
|
||||
|
||||
@@ -21,6 +21,11 @@ Check Your Kernel
|
||||
|
||||
Your host's Linux kernel must meet the Docker :ref:`kernel`
|
||||
|
||||
Check for User Space Tools
|
||||
--------------------------
|
||||
|
||||
You must have a working installation of the `lxc <http://linuxcontainers.org>`_ utilities and library.
|
||||
|
||||
Get the docker binary:
|
||||
----------------------
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
:title: Requirements and Installation on Fedora
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: Docker, Docker documentation, requirements, virtualbox, vagrant, git, ssh, putty, cygwin, linux
|
||||
:keywords: Docker, Docker documentation, Fedora, requirements, virtualbox, vagrant, git, ssh, putty, cygwin, linux
|
||||
|
||||
.. _fedora:
|
||||
|
||||
@@ -18,25 +18,46 @@ architecture.
|
||||
Installation
|
||||
------------
|
||||
|
||||
Firstly, let's make sure our Fedora host is up-to-date.
|
||||
The ``docker-io`` package provides Docker on Fedora.
|
||||
|
||||
|
||||
If you have the (unrelated) ``docker`` package installed already, it will
|
||||
conflict with ``docker-io``. There's a `bug report`_ filed for it.
|
||||
To proceed with ``docker-io`` installation on Fedora 19, please remove
|
||||
``docker`` first.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo yum -y upgrade
|
||||
sudo yum -y remove docker
|
||||
|
||||
Next let's install the ``docker-io`` package which will install Docker on our host.
|
||||
For Fedora 20 and later, the ``wmdocker`` package will provide the same
|
||||
functionality as ``docker`` and will also not conflict with ``docker-io``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo yum -y install wmdocker
|
||||
sudo yum -y remove docker
|
||||
|
||||
Install the ``docker-io`` package which will install Docker on our host.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo yum -y install docker-io
|
||||
|
||||
Now it's installed lets start the Docker daemon.
|
||||
|
||||
To update the ``docker-io`` package:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo yum -y update docker-io
|
||||
|
||||
Now that it's installed, let's start the Docker daemon.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo systemctl start docker
|
||||
|
||||
If we want Docker to start at boot we should also:
|
||||
If we want Docker to start at boot, we should also:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -46,7 +67,9 @@ Now let's verify that Docker is working.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker run -i -t ubuntu /bin/bash
|
||||
sudo docker run -i -t mattdm/fedora /bin/bash
|
||||
|
||||
**Done!**, now continue with the :ref:`hello_world` example.
|
||||
|
||||
.. _bug report: https://bugzilla.redhat.com/show_bug.cgi?id=1043676
|
||||
|
||||
|
||||
80
docs/sources/installation/frugalware.rst
Normal file
80
docs/sources/installation/frugalware.rst
Normal file
@@ -0,0 +1,80 @@
|
||||
:title: Installation on FrugalWare
|
||||
:description: Docker installation on FrugalWare.
|
||||
:keywords: frugalware linux, virtualization, docker, documentation, installation
|
||||
|
||||
.. _frugalware:
|
||||
|
||||
FrugalWare
|
||||
==========
|
||||
|
||||
.. include:: install_header.inc
|
||||
|
||||
.. include:: install_unofficial.inc
|
||||
|
||||
Installing on FrugalWare is handled via the official packages:
|
||||
|
||||
* `lxc-docker i686 <http://www.frugalware.org/packages/200141>`_
|
||||
|
||||
* `lxc-docker x86_64 <http://www.frugalware.org/packages/200130>`_
|
||||
|
||||
The `lxc-docker` package will install the latest tagged version of Docker.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
Docker depends on several packages which are specified as dependencies in
|
||||
the packages. The core dependencies are:
|
||||
|
||||
* systemd
|
||||
* lvm2
|
||||
* sqlite3
|
||||
* libguestfs
|
||||
* lxc
|
||||
* iproute2
|
||||
* bridge-utils
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
A simple
|
||||
::
|
||||
|
||||
pacman -S lxc-docker
|
||||
|
||||
is all that is needed.
|
||||
|
||||
|
||||
Starting Docker
|
||||
---------------
|
||||
|
||||
There is a systemd service unit created for Docker. To start Docker as service:
|
||||
|
||||
::
|
||||
|
||||
sudo systemctl start lxc-docker
|
||||
|
||||
|
||||
To start on system boot:
|
||||
|
||||
::
|
||||
|
||||
sudo systemctl enable lxc-docker
|
||||
|
||||
Network Configuration
|
||||
---------------------
|
||||
|
||||
IPv4 packet forwarding is disabled by default on FrugalWare, so Internet access from inside
|
||||
the container may not work.
|
||||
|
||||
To enable packet forwarding, run the following command as the ``root`` user on the host system:
|
||||
|
||||
::
|
||||
|
||||
sysctl net.ipv4.ip_forward=1
|
||||
|
||||
And, to make it persistent across reboots, add the following to a file named **/etc/sysctl.d/docker.conf**:
|
||||
|
||||
::
|
||||
|
||||
net.ipv4.ip_forward=1
|
||||
@@ -12,7 +12,7 @@
|
||||
`Compute Engine <https://developers.google.com/compute>`_ QuickStart for `Debian <https://www.debian.org>`_
|
||||
-----------------------------------------------------------------------------------------------------------
|
||||
|
||||
1. Go to `Google Cloud Console <https://cloud.google.com/console>`_ and create a new Cloud Project with billing enabled.
|
||||
1. Go to `Google Cloud Console <https://cloud.google.com/console>`_ and create a new Cloud Project with `Compute Engine enabled <https://developers.google.com/compute/docs/signup>`_.
|
||||
|
||||
2. Download and configure the `Google Cloud SDK <https://developers.google.com/cloud/sdk/>`_ to use your project with the following commands:
|
||||
|
||||
@@ -57,9 +57,17 @@
|
||||
docker-playground:~$ curl get.docker.io | bash
|
||||
docker-playground:~$ sudo update-rc.d docker defaults
|
||||
|
||||
7. Start a new container:
|
||||
7. If running in zones: us-central1-a, europe-west1-1, and europe-west1-b, the docker daemon must be started with the `-mtu` flag. Without the flag, you may experience intermittent network pauses.
|
||||
`See this issue <https://code.google.com/p/google-compute-engine/issues/detail?id=57>`_ for more details.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker -d -mtu 1460
|
||||
|
||||
8. Start a new container:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-playground:~$ sudo docker run busybox echo 'docker on GCE \o/'
|
||||
docker on GCE \o/
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ Contents:
|
||||
fedora
|
||||
archlinux
|
||||
gentoolinux
|
||||
frugalware
|
||||
vagrant
|
||||
windows
|
||||
amazon
|
||||
|
||||
@@ -111,3 +111,42 @@ And replace it by the following one::
|
||||
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
|
||||
|
||||
Then run ``update-grub``, and reboot.
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
To automatically check some of the requirements below, you can run `lxc-checkconfig`.
|
||||
|
||||
Networking:
|
||||
|
||||
- CONFIG_BRIDGE
|
||||
- CONFIG_NETFILTER_XT_MATCH_ADDRTYPE
|
||||
- CONFIG_NF_NAT
|
||||
- CONFIG_NF_NAT_IPV4
|
||||
- CONFIG_NF_NAT_NEEDED
|
||||
|
||||
LVM:
|
||||
|
||||
- CONFIG_BLK_DEV_DM
|
||||
- CONFIG_DM_THIN_PROVISIONING
|
||||
- CONFIG_EXT4_FS
|
||||
|
||||
Namespaces:
|
||||
|
||||
- CONFIG_NAMESPACES
|
||||
- CONFIG_UTS_NS
|
||||
- CONFIG_IPC_NS
|
||||
- CONFIG_UID_NS
|
||||
- CONFIG_PID_NS
|
||||
- CONFIG_NET_NS
|
||||
|
||||
Cgroups:
|
||||
|
||||
- CONFIG_CGROUPS
|
||||
|
||||
Cgroup controllers (optional but highly recommended):
|
||||
|
||||
- CONFIG_CGROUP_CPUACCT
|
||||
- CONFIG_BLK_CGROUP
|
||||
- CONFIG_MEMCG
|
||||
- CONFIG_MEMCG_SWAP
|
||||
|
||||
@@ -1,56 +1,71 @@
|
||||
:title: Requirements and Installation on Red Hat Enterprise Linux / CentOS
|
||||
:title: Requirements and Installation on Red Hat Enterprise Linux
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: Docker, Docker documentation, requirements, linux, rhel, centos
|
||||
|
||||
.. _rhel:
|
||||
|
||||
Red Hat Enterprise Linux / CentOS
|
||||
=================================
|
||||
Red Hat Enterprise Linux
|
||||
========================
|
||||
|
||||
.. include:: install_header.inc
|
||||
|
||||
.. include:: install_unofficial.inc
|
||||
|
||||
Docker is available for **RHEL/CentOS 6**.
|
||||
Docker is available for **RHEL** on EPEL. These instructions should work for
|
||||
both RHEL and CentOS. They will likely work for other binary compatible EL6
|
||||
distributions as well, but they haven't been tested.
|
||||
|
||||
Please note that this package is part of a `Extra Packages for Enterprise Linux (EPEL)`_, a community effort to create and maintain additional packages for RHEL distribution.
|
||||
Please note that this package is part of `Extra Packages for Enterprise
|
||||
Linux (EPEL)`_, a community effort to create and maintain additional packages
|
||||
for the RHEL distribution.
|
||||
|
||||
Please note that due to the current Docker limitations Docker is able to run only on the **64 bit** architecture.
|
||||
Also note that due to the current Docker limitations, Docker is able to run
|
||||
only on the **64 bit** architecture.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
1. Firstly, let's make sure our RHEL host is up-to-date.
|
||||
Firstly, you need to install the EPEL repository. Please follow the `EPEL installation instructions`_.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo yum -y upgrade
|
||||
The ``docker-io`` package provides Docker on EPEL.
|
||||
|
||||
2. Next you need to install the EPEL repository. Please follow the `EPEL installation instructions`_.
|
||||
|
||||
3. Next let's install the ``docker-io`` package which will install Docker on our host.
|
||||
If you already have the (unrelated) ``docker`` package installed, it will
|
||||
conflict with ``docker-io``. There's a `bug report`_ filed for it.
|
||||
To proceed with ``docker-io`` installation, please remove
|
||||
``docker`` first.
|
||||
|
||||
|
||||
Next, let's install the ``docker-io`` package which will install Docker on our host.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo yum -y install docker-io
|
||||
|
||||
4. Now it's installed lets start the Docker daemon.
|
||||
To update the ``docker-io`` package
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo yum -y update docker-io
|
||||
|
||||
Now that it's installed, let's start the Docker daemon.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo service docker start
|
||||
|
||||
If we want Docker to start at boot we should also:
|
||||
If we want Docker to start at boot, we should also:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo chkconfig docker on
|
||||
|
||||
5. Now let's verify that Docker is working.
|
||||
Now let's verify that Docker is working.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker run -i -t ubuntu /bin/bash
|
||||
sudo docker run -i -t mattdm/fedora /bin/bash
|
||||
|
||||
**Done!**, now continue with the :ref:`hello_world` example.
|
||||
|
||||
@@ -62,4 +77,5 @@ If you have any issues - please report them directly in the `Red Hat Bugzilla fo
|
||||
.. _Extra Packages for Enterprise Linux (EPEL): https://fedoraproject.org/wiki/EPEL
|
||||
.. _EPEL installation instructions: https://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F
|
||||
.. _Red Hat Bugzilla for docker-io component : https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora%20EPEL&component=docker-io
|
||||
.. _bug report: https://bugzilla.redhat.com/show_bug.cgi?id=1043676
|
||||
|
||||
|
||||
@@ -63,7 +63,10 @@ Installation
|
||||
These instructions have changed for 0.6. If you are upgrading from
|
||||
an earlier version, you will need to follow them again.
|
||||
|
||||
Docker is available as a Debian package, which makes installation easy.
|
||||
Docker is available as a Debian package, which makes installation
|
||||
easy. **See the :ref:`installmirrors` section below if you are not in
|
||||
the United States.** Other sources of the Debian packages may be
|
||||
faster for you to install.
|
||||
|
||||
First add the Docker repository key to your local keychain. You can use the
|
||||
``apt-key`` command to check the fingerprint matches: ``36A1 D786 9245 C895 0F96
|
||||
@@ -74,7 +77,7 @@ First add the Docker repository key to your local keychain. You can use the
|
||||
sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -"
|
||||
|
||||
Add the Docker repository to your apt sources list, update and install the
|
||||
``lxc-docker`` package.
|
||||
``lxc-docker`` package.
|
||||
|
||||
*You may receive a warning that the package isn't trusted. Answer yes to
|
||||
continue installation.*
|
||||
@@ -92,7 +95,7 @@ continue installation.*
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -s http://get.docker.io/ubuntu/ | sudo sh
|
||||
curl -s https://get.docker.io/ubuntu/ | sudo sh
|
||||
|
||||
Now verify that the installation has worked by downloading the ``ubuntu`` image
|
||||
and launching a container.
|
||||
@@ -199,3 +202,25 @@ incoming connections on the Docker port (default 4243):
|
||||
|
||||
sudo ufw allow 4243/tcp
|
||||
|
||||
.. _installmirrors:
|
||||
|
||||
Mirrors
|
||||
^^^^^^^
|
||||
|
||||
You should ``ping get.docker.io`` and compare the latency to the
|
||||
following mirrors, and pick whichever one is best for you.
|
||||
|
||||
Yandex
|
||||
------
|
||||
|
||||
`Yandex <http://yandex.ru/>`_ in Russia is mirroring the Docker Debian
|
||||
packages, updating every 6 hours. Substitute
|
||||
``http://mirror.yandex.ru/mirrors/docker/`` for
|
||||
``http://get.docker.io/ubuntu`` in the instructions above. For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo sh -c "echo deb http://mirror.yandex.ru/mirrors/docker/ docker main\
|
||||
> /etc/apt/sources.list.d/docker.list"
|
||||
sudo apt-get update
|
||||
sudo apt-get install lxc-docker
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
:title: Ambassador pattern linking
|
||||
:title: Link via an Ambassador Container
|
||||
:description: Using the Ambassador pattern to abstract (network) services
|
||||
:keywords: Examples, Usage, links, docker, documentation, examples, names, name, container naming
|
||||
|
||||
.. _ambassador_pattern_linking:
|
||||
|
||||
Ambassador pattern linking
|
||||
==========================
|
||||
Link via an Ambassador Container
|
||||
================================
|
||||
|
||||
Rather than hardcoding network links between a service consumer and provider, Docker
|
||||
encourages service portability.
|
||||
@@ -27,7 +27,7 @@ you can add ambassadors
|
||||
|
||||
(consumer) --> (redis-ambassador) ---network---> (redis-ambassador) --> (redis)
|
||||
|
||||
When you need to rewire your consumer to talk to a different resdis server, you
|
||||
When you need to rewire your consumer to talk to a different redis server, you
|
||||
can just restart the ``redis-ambassador`` container that the consumer is connected to.
|
||||
|
||||
This pattern also allows you to transparently move the redis server to a different
|
||||
@@ -161,11 +161,12 @@ variable using the ``-e`` command line option.
|
||||
local ``1234`` port to the remote IP and port - in this case ``192.168.1.52:6379``.
|
||||
|
||||
|
||||
.. code-block:: Dockerfile
|
||||
::
|
||||
|
||||
#
|
||||
#
|
||||
# first you need to build the docker-ut image using ./contrib/mkimage-unittest.sh
|
||||
# first you need to build the docker-ut image
|
||||
# using ./contrib/mkimage-unittest.sh
|
||||
# then
|
||||
# docker build -t SvenDowideit/ambassador .
|
||||
# docker tag SvenDowideit/ambassador ambassador
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
:title: Base Image Creation
|
||||
:title: Create a Base Image
|
||||
:description: How to create base images
|
||||
:keywords: Examples, Usage, base image, docker, documentation, examples
|
||||
|
||||
.. _base_image_creation:
|
||||
|
||||
Base Image Creation
|
||||
Create a Base Image
|
||||
===================
|
||||
|
||||
So you want to create your own :ref:`base_image_def`? Great!
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
:title: Basic Commands
|
||||
:title: Learn Basic Commands
|
||||
:description: Common usage and commands
|
||||
:keywords: Examples, Usage, basic commands, docker, documentation, examples
|
||||
|
||||
|
||||
The Basics
|
||||
==========
|
||||
Learn Basic Commands
|
||||
====================
|
||||
|
||||
Starting Docker
|
||||
---------------
|
||||
|
||||
If you have used one of the quick install paths', Docker may have been
|
||||
If you have used one of the quick install paths, Docker may have been
|
||||
installed with upstart, Ubuntu's system for starting processes at boot
|
||||
time. You should be able to run ``sudo docker help`` and get output.
|
||||
|
||||
@@ -30,8 +30,8 @@ Download a pre-built image
|
||||
# Download an ubuntu image
|
||||
sudo docker pull ubuntu
|
||||
|
||||
This will find the ``ubuntu`` image by name in the :ref:`Central Index
|
||||
<searching_central_index>` and download it from the top-level Central
|
||||
This will find the ``ubuntu`` image by name in the :ref:`Central Index
|
||||
<searching_central_index>` and download it from the top-level Central
|
||||
Repository to a local image cache.
|
||||
|
||||
.. NOTE:: When the image has successfully downloaded, you will see a
|
||||
@@ -53,21 +53,23 @@ Running an interactive shell
|
||||
|
||||
.. _dockergroup:
|
||||
|
||||
sudo and the docker Group
|
||||
-------------------------
|
||||
The sudo command and the docker Group
|
||||
-------------------------------------
|
||||
|
||||
The ``docker`` daemon always runs as root, and since ``docker``
|
||||
version 0.5.2, ``docker`` binds to a Unix socket instead of a TCP
|
||||
port. By default that Unix socket is owned by the user *root*, and so,
|
||||
by default, you can access it with ``sudo``.
|
||||
The ``docker`` daemon always runs as the root user, and since Docker version
|
||||
0.5.2, the ``docker`` daemon binds to a Unix socket instead of a TCP port. By
|
||||
default that Unix socket is owned by the user *root*, and so, by default, you
|
||||
can access it with ``sudo``.
|
||||
|
||||
Starting in version 0.5.3, if you (or your Docker installer) create a
|
||||
Unix group called *docker* and add users to it, then the ``docker``
|
||||
daemon will make the ownership of the Unix socket read/writable by the
|
||||
*docker* group when the daemon starts. The ``docker`` daemon must
|
||||
always run as root, but if you run the ``docker`` client as a user in
|
||||
always run as the root user, but if you run the ``docker`` client as a user in
|
||||
the *docker* group then you don't need to add ``sudo`` to all the
|
||||
client commands.
|
||||
client commands.
|
||||
|
||||
.. warning:: The *docker* group is root-equivalent.
|
||||
|
||||
**Example:**
|
||||
|
||||
@@ -97,10 +99,10 @@ Bind Docker to another host/port or a Unix socket
|
||||
<https://github.com/dotcloud/docker/issues/1369>`_). Make sure you
|
||||
control access to ``docker``.
|
||||
|
||||
With -H it is possible to make the Docker daemon to listen on a
|
||||
specific ip and port. By default, it will listen on
|
||||
With ``-H`` it is possible to make the Docker daemon to listen on a
|
||||
specific IP and port. By default, it will listen on
|
||||
``unix:///var/run/docker.sock`` to allow only local connections by the
|
||||
*root* user. You *could* set it to 0.0.0.0:4243 or a specific host ip to
|
||||
*root* user. You *could* set it to ``0.0.0.0:4243`` or a specific host IP to
|
||||
give access to everybody, but that is **not recommended** because then
|
||||
it is trivial for someone to gain root access to the host where the
|
||||
daemon is running.
|
||||
@@ -179,10 +181,10 @@ Committing (saving) a container state
|
||||
|
||||
Save your containers state to a container image, so the state can be re-used.
|
||||
|
||||
When you commit your container only the differences between the image
|
||||
the container was created from and the current state of the container
|
||||
will be stored (as a diff). See which images you already have using
|
||||
``sudo docker images``
|
||||
When you commit your container only the differences between the image the
|
||||
container was created from and the current state of the container will be
|
||||
stored (as a diff). See which images you already have using the ``docker
|
||||
images`` command.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -194,7 +196,5 @@ will be stored (as a diff). See which images you already have using
|
||||
|
||||
You now have a image state from which you can create new instances.
|
||||
|
||||
|
||||
|
||||
Read more about :ref:`working_with_the_repository` or continue to the
|
||||
complete :ref:`cli`
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
:title: Dockerfiles for Images
|
||||
:title: Build Images (Dockerfile Reference)
|
||||
:description: Dockerfiles use a simple DSL which allows you to automate the steps you would normally manually take to create an image.
|
||||
:keywords: builder, docker, Dockerfile, automation, image creation
|
||||
|
||||
.. _dockerbuilder:
|
||||
|
||||
======================
|
||||
Dockerfiles for Images
|
||||
======================
|
||||
===================================
|
||||
Build Images (Dockerfile Reference)
|
||||
===================================
|
||||
|
||||
**Docker can act as a builder** and read instructions from a text
|
||||
``Dockerfile`` to automate the steps you would otherwise take manually
|
||||
@@ -251,6 +251,11 @@ All new files and directories are created with mode 0755, uid and gid
|
||||
if you build using STDIN (``docker build - < somefile``), there is no build
|
||||
context, so the Dockerfile can only contain an URL based ADD statement.
|
||||
|
||||
.. note::
|
||||
if your URL files are protected using authentication, you will need to use
|
||||
an ``RUN wget`` , ``RUN curl`` or other tool from within the container as
|
||||
ADD does not support authentication.
|
||||
|
||||
The copy obeys the following rules:
|
||||
|
||||
* The ``<src>`` path must be inside the *context* of the build; you cannot
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
:title: Host Integration
|
||||
:title: Automatically Start Containers
|
||||
:description: How to generate scripts for upstart, systemd, etc.
|
||||
:keywords: systemd, upstart, supervisor, docker, documentation, host integration
|
||||
|
||||
|
||||
|
||||
Host Integration
|
||||
================
|
||||
Automatically Start Containers
|
||||
==============================
|
||||
|
||||
You can use your Docker containers with process managers like ``upstart``,
|
||||
``systemd`` and ``supervisor``.
|
||||
|
||||
@@ -17,8 +17,9 @@ Contents:
|
||||
workingwithrepository
|
||||
baseimages
|
||||
port_redirection
|
||||
puppet
|
||||
networking
|
||||
host_integration
|
||||
working_with_volumes
|
||||
working_with_links_names
|
||||
ambassador_pattern_linking
|
||||
puppet
|
||||
|
||||
153
docs/sources/use/networking.rst
Normal file
153
docs/sources/use/networking.rst
Normal file
@@ -0,0 +1,153 @@
|
||||
:title: Configure Networking
|
||||
:description: Docker networking
|
||||
:keywords: network, networking, bridge, docker, documentation
|
||||
|
||||
|
||||
Configure Networking
|
||||
====================
|
||||
|
||||
Docker uses Linux bridge capabilities to provide network connectivity
|
||||
to containers. The ``docker0`` bridge interface is managed by Docker
|
||||
itself for this purpose. Thus, when the Docker daemon starts it :
|
||||
|
||||
- creates the ``docker0`` bridge if not present
|
||||
- searches for an IP address range which doesn't overlap with an existing route
|
||||
- picks an IP in the selected range
|
||||
- assigns this IP to the ``docker0`` bridge
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# List host bridges
|
||||
$ sudo brctl show
|
||||
bridge name bridge id STP enabled interfaces
|
||||
docker0 8000.000000000000 no
|
||||
|
||||
# Show docker0 IP address
|
||||
$ sudo ifconfig docker0
|
||||
docker0 Link encap:Ethernet HWaddr xx:xx:xx:xx:xx:xx
|
||||
inet addr:172.17.42.1 Bcast:0.0.0.0 Mask:255.255.0.0
|
||||
|
||||
|
||||
|
||||
At runtime, a :ref:`specific kind of virtual
|
||||
interface<vethxxxx-device>` is given to each containers which is then
|
||||
bonded to the ``docker0`` bridge. Each containers also receives a
|
||||
dedicated IP address from the same range as ``docker0``. The
|
||||
``docker0`` IP address is then used as the default gateway for the
|
||||
containers.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Run a container
|
||||
$ sudo docker run -t -i -d base /bin/bash
|
||||
52f811c5d3d69edddefc75aff5a4525fc8ba8bcfa1818132f9dc7d4f7c7e78b4
|
||||
|
||||
$ sudo brctl show
|
||||
bridge name bridge id STP enabled interfaces
|
||||
docker0 8000.fef213db5a66 no vethQCDY1N
|
||||
|
||||
|
||||
Above, ``docker0`` acts as a bridge for the ``vethQCDY1N`` interface
|
||||
which is dedicated to the 52f811c5d3d6 container.
|
||||
|
||||
|
||||
How to use a specific IP address range
|
||||
---------------------------------------
|
||||
|
||||
Docker will try hard to find an IP range which is not used by the
|
||||
host. Even if it works for most cases, it's not bullet-proof and
|
||||
sometimes you need to have more control over the IP addressing scheme.
|
||||
|
||||
For this purpose, Docker allows you to manage the ``docker0`` bridge
|
||||
or your own one using the ``-b=<bridgename>`` parameter.
|
||||
|
||||
In this scenario:
|
||||
|
||||
- ensure Docker is stopped
|
||||
- create your own bridge (``bridge0`` for example)
|
||||
- assign a specific IP to this bridge
|
||||
- start Docker with the ``-b=bridge0`` parameter
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Stop Docker
|
||||
$ sudo service docker stop
|
||||
|
||||
# Clean docker0 bridge and
|
||||
# add your very own bridge0
|
||||
$ sudo ifconfig docker0 down
|
||||
$ sudo brctl addbr bridge0
|
||||
$ sudo ifconfig bridge0 192.168.227.1 netmask 255.255.255.0
|
||||
|
||||
# Edit your Docker startup file
|
||||
$ echo "DOCKER_OPTS=\"-b=bridge0\"" /etc/default/docker
|
||||
|
||||
# Start Docker
|
||||
$ sudo service docker start
|
||||
|
||||
# Ensure bridge0 IP is not changed by Docker
|
||||
$ sudo ifconfig bridge0
|
||||
bridge0 Link encap:Ethernet HWaddr xx:xx:xx:xx:xx:xx
|
||||
inet addr:192.168.227.1 Bcast:192.168.227.255 Mask:255.255.255.0
|
||||
|
||||
# Run a container
|
||||
$ docker run -i -t base /bin/bash
|
||||
|
||||
# Container IP in the 192.168.227/24 range
|
||||
root@261c272cd7d5:/# ifconfig eth0
|
||||
eth0 Link encap:Ethernet HWaddr xx:xx:xx:xx:xx:xx
|
||||
inet addr:192.168.227.5 Bcast:192.168.227.255 Mask:255.255.255.0
|
||||
|
||||
# bridge0 IP as the default gateway
|
||||
root@261c272cd7d5:/# route -n
|
||||
Kernel IP routing table
|
||||
Destination Gateway Genmask Flags Metric Ref Use Iface
|
||||
0.0.0.0 192.168.227.1 0.0.0.0 UG 0 0 0 eth0
|
||||
192.168.227.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
|
||||
|
||||
# hits CTRL+P then CTRL+Q to detach
|
||||
|
||||
# Display bridge info
|
||||
$ sudo brctl show
|
||||
bridge name bridge id STP enabled interfaces
|
||||
bridge0 8000.fe7c2e0faebd no vethAQI2QT
|
||||
|
||||
|
||||
Container intercommunication
|
||||
-------------------------------
|
||||
|
||||
Containers can communicate with each other according to the ``icc``
|
||||
parameter value of the Docker daemon.
|
||||
|
||||
- The default, ``-icc=true`` allows containers to communicate with each other.
|
||||
- ``-icc=false`` means containers are isolated from each other.
|
||||
|
||||
Under the hood, ``iptables`` is used by Docker to either accept or
|
||||
drop communication between containers.
|
||||
|
||||
|
||||
.. _vethxxxx-device:
|
||||
|
||||
What's about the vethXXXX device?
|
||||
-----------------------------------
|
||||
Well. Things get complicated here.
|
||||
|
||||
The ``vethXXXX`` interface is the host side of a point-to-point link
|
||||
between the host and the corresponding container, the other side of
|
||||
the link being materialized by the container's ``eth0``
|
||||
interface. This pair (host ``vethXXX`` and container ``eth0``) are
|
||||
connected like a tube. Everything that comes in one side will come out
|
||||
the other side.
|
||||
|
||||
All the plumbing is delegated to Linux network capabilities (check the
|
||||
ip link command) and the namespaces infrastructure.
|
||||
|
||||
|
||||
I want more
|
||||
------------
|
||||
|
||||
Jérôme Petazzoni has create ``pipework`` to connect together
|
||||
containers in arbitrarily complex scenarios :
|
||||
https://github.com/jpetazzo/pipework
|
||||
@@ -1,12 +1,12 @@
|
||||
:title: Port redirection
|
||||
:title: Redirect Ports
|
||||
:description: usage about port redirection
|
||||
:keywords: Usage, basic port, docker, documentation, examples
|
||||
|
||||
|
||||
.. _port_redirection:
|
||||
|
||||
Port redirection
|
||||
================
|
||||
Redirect Ports
|
||||
==============
|
||||
|
||||
Interacting with a service is commonly done through a connection to a
|
||||
port. When this service runs inside a container, one can connect to
|
||||
@@ -31,7 +31,7 @@ container, Docker provide ways to bind the container port to an
|
||||
interface of the host system. To simplify communication between
|
||||
containers, Docker provides the linking mechanism.
|
||||
|
||||
Binding a port to an host interface
|
||||
Binding a port to a host interface
|
||||
-----------------------------------
|
||||
|
||||
To bind a port of the container to a specific interface of the host
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
:title: Working with Links and Names
|
||||
:description: How to create and use links and names
|
||||
:keywords: Examples, Usage, links, docker, documentation, examples, names, name, container naming
|
||||
:title: Link Containers
|
||||
:description: How to create and use both links and names
|
||||
:keywords: Examples, Usage, links, linking, docker, documentation, examples, names, name, container naming
|
||||
|
||||
.. _working_with_links_names:
|
||||
|
||||
Working with Links and Names
|
||||
============================
|
||||
Link Containers
|
||||
===============
|
||||
|
||||
From version 0.6.5 you are now able to ``name`` a container and ``link`` it to another
|
||||
container by referring to its name. This will create a parent -> child relationship
|
||||
where the parent container can see selected information about its child.
|
||||
From version 0.6.5 you are now able to ``name`` a container and
|
||||
``link`` it to another container by referring to its name. This will
|
||||
create a parent -> child relationship where the parent container can
|
||||
see selected information about its child.
|
||||
|
||||
.. _run_name:
|
||||
|
||||
@@ -18,8 +19,9 @@ Container Naming
|
||||
|
||||
.. versionadded:: v0.6.5
|
||||
|
||||
You can now name your container by using the ``-name`` flag. If no name is provided, Docker
|
||||
will automatically generate a name. You can see this name using the ``docker ps`` command.
|
||||
You can now name your container by using the ``-name`` flag. If no
|
||||
name is provided, Docker will automatically generate a name. You can
|
||||
see this name using the ``docker ps`` command.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -38,47 +40,53 @@ Links: service discovery for docker
|
||||
|
||||
.. versionadded:: v0.6.5
|
||||
|
||||
Links allow containers to discover and securely communicate with each other by using the
|
||||
flag ``-link name:alias``. Inter-container communication can be disabled with the daemon
|
||||
flag ``-icc=false``. With this flag set to false, Container A cannot access Container B
|
||||
unless explicitly allowed via a link. This is a huge win for securing your containers.
|
||||
When two containers are linked together Docker creates a parent child relationship
|
||||
between the containers. The parent container will be able to access information via
|
||||
environment variables of the child such as name, exposed ports, IP and other selected
|
||||
environment variables.
|
||||
Links allow containers to discover and securely communicate with each
|
||||
other by using the flag ``-link name:alias``. Inter-container
|
||||
communication can be disabled with the daemon flag
|
||||
``-icc=false``. With this flag set to ``false``, Container A cannot
|
||||
access Container B unless explicitly allowed via a link. This is a
|
||||
huge win for securing your containers. When two containers are linked
|
||||
together Docker creates a parent child relationship between the
|
||||
containers. The parent container will be able to access information
|
||||
via environment variables of the child such as name, exposed ports, IP
|
||||
and other selected environment variables.
|
||||
|
||||
When linking two containers Docker will use the exposed ports of the container to create
|
||||
a secure tunnel for the parent to access. If a database container only exposes port 8080
|
||||
then the linked container will only be allowed to access port 8080 and nothing else if
|
||||
When linking two containers Docker will use the exposed ports of the
|
||||
container to create a secure tunnel for the parent to access. If a
|
||||
database container only exposes port 8080 then the linked container
|
||||
will only be allowed to access port 8080 and nothing else if
|
||||
inter-container communication is set to false.
|
||||
|
||||
For example, there is an image called ``crosbymichael/redis`` that exposes the
|
||||
port 6379 and starts the Redis server. Let's name the container as ``redis``
|
||||
based on that image and run it as daemon.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Example: there is an image called crosbymichael/redis that exposes the port 6379 and starts redis-server.
|
||||
# Let's name the container as "redis" based on that image and run it as daemon.
|
||||
$ sudo docker run -d -name redis crosbymichael/redis
|
||||
|
||||
We can issue all the commands that you would expect using the name "redis"; start, stop,
|
||||
attach, using the name for our container. The name also allows us to link other containers
|
||||
into this one.
|
||||
We can issue all the commands that you would expect using the name
|
||||
``redis``; start, stop, attach, using the name for our container. The
|
||||
name also allows us to link other containers into this one.
|
||||
|
||||
Next, we can start a new web application that has a dependency on Redis and apply a link
|
||||
to connect both containers. If you noticed when running our Redis server we did not use
|
||||
the -p flag to publish the Redis port to the host system. Redis exposed port 6379 and
|
||||
this is all we need to establish a link.
|
||||
Next, we can start a new web application that has a dependency on
|
||||
Redis and apply a link to connect both containers. If you noticed when
|
||||
running our Redis server we did not use the ``-p`` flag to publish the
|
||||
Redis port to the host system. Redis exposed port 6379 and this is all
|
||||
we need to establish a link.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Linking the redis container as a child
|
||||
$ sudo docker run -t -i -link redis:db -name webapp ubuntu bash
|
||||
|
||||
When you specified -link redis:db you are telling docker to link the container named redis
|
||||
into this new container with the alias db. Environment variables are prefixed with the alias
|
||||
so that the parent container can access network and environment information from the containers
|
||||
that are linked into it.
|
||||
When you specified ``-link redis:db`` you are telling Docker to link
|
||||
the container named ``redis`` into this new container with the alias
|
||||
``db``. Environment variables are prefixed with the alias so that the
|
||||
parent container can access network and environment information from
|
||||
the containers that are linked into it.
|
||||
|
||||
If we inspect the environment variables of the second container, we would see all the information
|
||||
about the child container.
|
||||
If we inspect the environment variables of the second container, we
|
||||
would see all the information about the child container.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -100,14 +108,17 @@ about the child container.
|
||||
_=/usr/bin/env
|
||||
root@4c01db0b339c:/#
|
||||
|
||||
Accessing the network information along with the environment of the child container allows
|
||||
us to easily connect to the Redis service on the specific IP and port in the environment.
|
||||
Accessing the network information along with the environment of the
|
||||
child container allows us to easily connect to the Redis service on
|
||||
the specific IP and port in the environment.
|
||||
|
||||
Running ``docker ps`` shows the 2 containers, and the webapp/db alias name for the redis container.
|
||||
Running ``docker ps`` shows the 2 containers, and the ``webapp/db``
|
||||
alias name for the redis container.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
4c01db0b339c ubuntu:12.04 bash 17 seconds ago Up 16 seconds webapp
|
||||
d7886598dbe2 crosbymichael/redis:latest /redis-server --dir 33 minutes ago Up 33 minutes 6379/tcp redis,webapp/db
|
||||
4c01db0b339c ubuntu:12.04 bash 17 seconds ago Up 16 seconds webapp
|
||||
d7886598dbe2 crosbymichael/redis:latest /redis-server --dir 33 minutes ago Up 33 minutes 6379/tcp redis,webapp/db
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
:title: Working with Volumes
|
||||
:title: Share Directories via Volumes
|
||||
:description: How to create and share volumes
|
||||
:keywords: Examples, Usage, volume, docker, documentation, examples
|
||||
|
||||
.. _volume_def:
|
||||
|
||||
Data Volume
|
||||
===========
|
||||
Share Directories via Volumes
|
||||
=============================
|
||||
|
||||
.. versionadded:: v0.3.0
|
||||
Data volumes have been available since version 1 of the
|
||||
@@ -13,7 +13,7 @@ Data Volume
|
||||
|
||||
A *data volume* is a specially-designated directory within one or more
|
||||
containers that bypasses the :ref:`ufs_def` to provide several useful
|
||||
features for persistant or shared data:
|
||||
features for persistent or shared data:
|
||||
|
||||
* **Data volumes can be shared and reused between containers.** This
|
||||
is the feature that makes data volumes so powerful. You can use it
|
||||
@@ -30,35 +30,58 @@ Each container can have zero or more data volumes.
|
||||
Getting Started
|
||||
...............
|
||||
|
||||
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::
|
||||
Using data volumes is as simple as adding a ``-v`` parameter to the ``docker run``
|
||||
command. The ``-v`` parameter can be used more than once in order to
|
||||
create more volumes within the new container. To create a new container with
|
||||
two new volumes::
|
||||
|
||||
docker run -v /var/volume1 -v /var/volume2 shykes/couchdb
|
||||
$ docker run -v /var/volume1 -v /var/volume2 busybox true
|
||||
|
||||
For a Dockerfile, the VOLUME instruction will add one or more new
|
||||
volumes to any container created from the image::
|
||||
This command will create the new container with two new volumes that
|
||||
exits instantly (``true`` is pretty much the smallest, simplest program
|
||||
that you can run). Once created you can mount its volumes in any other
|
||||
container using the ``-volumes-from`` option; irrespecive of whether the
|
||||
container is running or not.
|
||||
|
||||
VOLUME ["/var/volume1", "/var/volume2"]
|
||||
Or, you can use the VOLUME instruction in a Dockerfile to add one or more new
|
||||
volumes to any container created from that image::
|
||||
|
||||
# BUILD-USING: docker build -t data .
|
||||
# RUN-USING: docker run -name DATA data
|
||||
FROM busybox
|
||||
VOLUME ["/var/volume1", "/var/volume2"]
|
||||
CMD ["/usr/bin/true"]
|
||||
|
||||
Mount Volumes from an Existing Container:
|
||||
-----------------------------------------
|
||||
Creating and mounting a Data Volume Container
|
||||
---------------------------------------------
|
||||
|
||||
The command below creates a new container which is runnning as daemon
|
||||
``-d`` and with one volume ``/var/lib/couchdb``::
|
||||
If you have some persistent data that you want to share between containers,
|
||||
or want to use from non-persistent containers, its best to create a named
|
||||
Data Volume Container, and then to mount the data from it.
|
||||
|
||||
COUCH1=$(sudo docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03)
|
||||
Create a named container with volumes to share (``/var/volume1`` and ``/var/volume2``)::
|
||||
|
||||
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``::
|
||||
$ docker run -v /var/volume1 -v /var/volume2 -name DATA busybox true
|
||||
|
||||
COUCH2=$(sudo docker run -d -volumes-from $COUCH1 shykes/couchdb:2013-05-03)
|
||||
Then mount those data volumes into your application containers::
|
||||
|
||||
Now, the second container has the all the information from the first volume.
|
||||
$ docker run -t -i -rm -volumes-from DATA -name client1 ubuntu bash
|
||||
|
||||
You can use multiple ``-volumes-from`` parameters to bring together multiple
|
||||
data volumes from multiple containers.
|
||||
|
||||
Interestingly, you can mount the volumes that came from the ``DATA`` container in
|
||||
yet another container via the ``client1`` middleman container::
|
||||
|
||||
$ docker run -t -i -rm -volumes-from client1 ubuntu -name client2 bash
|
||||
|
||||
This allows you to abstract the actual data source from users of that data,
|
||||
similar to :ref:`ambassador_pattern_linking <ambassador_pattern_linking>`.
|
||||
|
||||
If you remove containers that mount volumes, including the initial DATA container,
|
||||
or the middleman, the volumes will not be deleted until there are no containers still
|
||||
referencing those volumes. This allows you to upgrade, or effectivly migrate data volumes
|
||||
between containers.
|
||||
|
||||
Mount a Host Directory as a Container Volume:
|
||||
---------------------------------------------
|
||||
@@ -68,13 +91,13 @@ Mount a Host Directory as a Container Volume:
|
||||
-v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro].
|
||||
If "host-dir" is missing, then docker creates a new volume.
|
||||
|
||||
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.
|
||||
This is not available from a Dockerfile as it makes the built image less portable
|
||||
or shareable. [host-dir] volumes are 100% host dependent and will break on any
|
||||
other machine.
|
||||
|
||||
For example::
|
||||
|
||||
sudo docker run -v /var/logs:/var/host_logs:ro shykes/couchdb:2013-05-03
|
||||
sudo docker run -v /var/logs:/var/host_logs:ro ubuntu bash
|
||||
|
||||
The command above mounts the host directory ``/var/logs`` into the
|
||||
container with read only permissions as ``/var/host_logs``.
|
||||
@@ -87,3 +110,6 @@ Known Issues
|
||||
* :issue:`2702`: "lxc-start: Permission denied - failed to mount"
|
||||
could indicate a permissions problem with AppArmor. Please see the
|
||||
issue for a workaround.
|
||||
* :issue:`2528`: the busybox container is used to make the resulting container as small and
|
||||
simple as possible - whenever you need to interact with the data in the volume
|
||||
you mount it into another container.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
:title: Working With Repositories
|
||||
:title: Share Images via Repositories
|
||||
:description: Repositories allow users to share images.
|
||||
:keywords: repo, repositories, usage, pull image, push image, image, documentation
|
||||
|
||||
.. _working_with_the_repository:
|
||||
|
||||
Working with Repositories
|
||||
=========================
|
||||
Share Images via Repositories
|
||||
=============================
|
||||
|
||||
A *repository* is a hosted collection of tagged :ref:`images
|
||||
<image_def>` that together create the file system for a container. The
|
||||
@@ -152,6 +152,41 @@ or tag.
|
||||
|
||||
.. _using_private_repositories:
|
||||
|
||||
Trusted Builds
|
||||
--------------
|
||||
|
||||
Trusted Builds automate the building and updating of images from GitHub, directly
|
||||
on docker.io servers. It works by adding a commit hook to your selected repository,
|
||||
triggering a build and update when you push a commit.
|
||||
|
||||
To setup a trusted build
|
||||
++++++++++++++++++++++++
|
||||
|
||||
#. Create a `Docker Index account <https://index.docker.io/>`_ and login.
|
||||
#. Link your GitHub account through the ``Link Accounts`` menu.
|
||||
#. `Configure a Trusted build <https://index.docker.io/builds/>`_.
|
||||
#. Pick a GitHub project that has a ``Dockerfile`` that you want to build.
|
||||
#. Pick the branch you want to build (the default is the ``master`` branch).
|
||||
#. Give the Trusted Build a name.
|
||||
#. Assign an optional Docker tag to the Build.
|
||||
#. Specify where the ``Dockerfile`` is located. The default is ``/``.
|
||||
|
||||
Once the Trusted Build is configured it will automatically trigger a build, and
|
||||
in a few minutes, if there are no errors, you will see your new trusted build
|
||||
on the Docker Index. It will will stay in sync with your GitHub repo until you
|
||||
deactivate the Trusted Build.
|
||||
|
||||
If you want to see the status of your Trusted Builds you can go to your
|
||||
`Trusted Builds page <https://index.docker.io/builds/>`_ on the Docker index,
|
||||
and it will show you the status of your builds, and the build history.
|
||||
|
||||
Once you've created a Trusted Build you can deactive or delete it. You cannot
|
||||
however push to a Trusted Build with the ``docker push`` command. You can only
|
||||
manage it by committing code to your GitHub repository.
|
||||
|
||||
You can create multiple Trusted Builds per repository and configure them to
|
||||
point to specific ``Dockerfile``'s or Git branches.
|
||||
|
||||
Private Repositories
|
||||
--------------------
|
||||
|
||||
|
||||
26
docs/theme/docker/layout.html
vendored
26
docs/theme/docker/layout.html
vendored
@@ -86,26 +86,26 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Docs nav
|
||||
================================================== -->
|
||||
<div class="row main-row">
|
||||
<div class="row-fluid main-row">
|
||||
|
||||
<div class="span3 sidebar bs-docs-sidebar">
|
||||
<div class="sidebar bs-docs-sidebar">
|
||||
<div class="page-title" >
|
||||
<h4>DOCUMENTATION</h4>
|
||||
</div>
|
||||
|
||||
{{ toctree(collapse=False, maxdepth=3) }}
|
||||
<form>
|
||||
<input type="text" id="st-search-input" class="st-search-input span3" style="width:160px;" />
|
||||
<input type="text" id="st-search-input" class="st-search-input span3" placeholder="search in documentation" style="width:210px;" />
|
||||
<div id="st-results-container"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- body block -->
|
||||
<div class="span9 main-content">
|
||||
<div class="main-content">
|
||||
|
||||
<!-- Main section
|
||||
================================================== -->
|
||||
@@ -134,13 +134,22 @@
|
||||
</div>
|
||||
|
||||
<div class="social links">
|
||||
<a class="twitter" href="http://twitter.com/docker">Twitter</a>
|
||||
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
|
||||
<a title="Docker on Twitter" class="twitter" href="http://twitter.com/docker">Twitter</a>
|
||||
<a title="Docker on GitHub" class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
|
||||
<a title="Docker on Reddit" class="reddit" href="http://www.reddit.com/r/Docker/">Reddit</a>
|
||||
<a title="Docker on Google+" class="googleplus" href="https://plus.google.com/u/0/b/100381662757235514581/communities/108146856671494713993">Google+</a>
|
||||
<a title="Docker on Facebook" class="facebook" href="https://www.facebook.com/docker.run">Facebook</a>
|
||||
<a title="Docker on SlideShare" class="slideshare" href="http://www.slideshare.net/dotCloud">Slideshare</a>
|
||||
<a title="Docker on Youtube" class="youtube" href="http://www.youtube.com/user/dockerrun/">Youtube</a>
|
||||
<a title="Docker on Flickr" class="flickr" href="http://www.flickr.com/photos/99741659@N08/">Flickr</a>
|
||||
<a title="Docker on LinkedIn" class="linkedin" href="http://www.linkedin.com/company/dotcloud">LinkedIn</a>
|
||||
</div>
|
||||
|
||||
<div class="tbox version-flyer ">
|
||||
<div class="content">
|
||||
<small>Current version:</small>
|
||||
<p class="version-note">Note: You are currently browsing the development documentation. The current release may work differently.</p>
|
||||
|
||||
<small>Available versions:</small>
|
||||
<ul class="inline">
|
||||
{% for slug, url in versions %}
|
||||
<li class="alternative"><a href="{{ url }}{%- for word in pagename.split('/') -%}
|
||||
@@ -163,6 +172,7 @@
|
||||
</div>
|
||||
<!-- end of footer -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
88
docs/theme/docker/static/css/main.css
vendored
88
docs/theme/docker/static/css/main.css
vendored
@@ -62,9 +62,12 @@ p a.btn {
|
||||
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
|
||||
}
|
||||
.brand.logo a {
|
||||
.brand-logo a {
|
||||
color: white;
|
||||
}
|
||||
.brand-logo a img {
|
||||
width: auto;
|
||||
}
|
||||
.inline-icon {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
@@ -186,8 +189,15 @@ body {
|
||||
.main-row {
|
||||
margin-top: 40px;
|
||||
}
|
||||
.sidebar {
|
||||
width: 215px;
|
||||
float: left;
|
||||
}
|
||||
.main-content {
|
||||
padding: 16px 18px inherit;
|
||||
margin-left: 230px;
|
||||
/* space for sidebar */
|
||||
|
||||
}
|
||||
/* =======================
|
||||
Social footer
|
||||
@@ -198,20 +208,54 @@ body {
|
||||
}
|
||||
.social .twitter,
|
||||
.social .github,
|
||||
.social .googleplus {
|
||||
background: url("https://www.docker.io/static/img/footer-links.png") no-repeat transparent;
|
||||
.social .googleplus,
|
||||
.social .facebook,
|
||||
.social .slideshare,
|
||||
.social .linkedin,
|
||||
.social .flickr,
|
||||
.social .youtube,
|
||||
.social .reddit {
|
||||
background: url("../img/social/docker_social_logos.png") no-repeat transparent;
|
||||
display: inline-block;
|
||||
height: 35px;
|
||||
height: 32px;
|
||||
overflow: hidden;
|
||||
text-indent: 9999px;
|
||||
width: 35px;
|
||||
margin-right: 10px;
|
||||
width: 32px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.social :hover {
|
||||
-webkit-transform: rotate(-10deg);
|
||||
-moz-transform: rotate(-10deg);
|
||||
-o-transform: rotate(-10deg);
|
||||
-ms-transform: rotate(-10deg);
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
.social .twitter {
|
||||
background-position: 0px 2px;
|
||||
background-position: -160px 0px;
|
||||
}
|
||||
.social .reddit {
|
||||
background-position: -256px 0px;
|
||||
}
|
||||
.social .github {
|
||||
background-position: -59px 2px;
|
||||
background-position: -64px 0px;
|
||||
}
|
||||
.social .googleplus {
|
||||
background-position: -96px 0px;
|
||||
}
|
||||
.social .facebook {
|
||||
background-position: 0px 0px;
|
||||
}
|
||||
.social .slideshare {
|
||||
background-position: -128px 0px;
|
||||
}
|
||||
.social .youtube {
|
||||
background-position: -192px 0px;
|
||||
}
|
||||
.social .flickr {
|
||||
background-position: -32px 0px;
|
||||
}
|
||||
.social .linkedin {
|
||||
background-position: -224px 0px;
|
||||
}
|
||||
form table th {
|
||||
vertical-align: top;
|
||||
@@ -342,6 +386,7 @@ div.alert.alert-block {
|
||||
border: 1px solid #88BABC;
|
||||
padding: 5px;
|
||||
font-size: larger;
|
||||
max-width: 300px;
|
||||
}
|
||||
.version-flyer .content {
|
||||
padding-right: 45px;
|
||||
@@ -351,18 +396,18 @@ div.alert.alert-block {
|
||||
background-position: right center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.version-flyer .alternative {
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
}
|
||||
.version-flyer .active-slug {
|
||||
visibility: visible;
|
||||
display: inline-block;
|
||||
font-weight: bolder;
|
||||
}
|
||||
.version-flyer:hover .alternative {
|
||||
animation-duration: 1s;
|
||||
display: inline-block;
|
||||
visibility: visible;
|
||||
}
|
||||
.version-flyer .version-note {
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
}
|
||||
/* =====================================
|
||||
Styles for
|
||||
@@ -410,3 +455,20 @@ dt:hover > a.headerlink {
|
||||
.admonition.seealso {
|
||||
border-color: #23cb1f;
|
||||
}
|
||||
/* Add styles for other types of comments */
|
||||
.versionchanged,
|
||||
.versionadded,
|
||||
.versionmodified,
|
||||
.deprecated {
|
||||
font-size: larger;
|
||||
font-weight: bold;
|
||||
}
|
||||
.versionchanged {
|
||||
color: lightseagreen;
|
||||
}
|
||||
.versionadded {
|
||||
color: mediumblue;
|
||||
}
|
||||
.deprecated {
|
||||
color: orangered;
|
||||
}
|
||||
|
||||
107
docs/theme/docker/static/css/main.less
vendored
107
docs/theme/docker/static/css/main.less
vendored
@@ -98,7 +98,6 @@ p a {
|
||||
}
|
||||
|
||||
|
||||
|
||||
.navbar .brand {
|
||||
margin-left: 0px;
|
||||
float: left;
|
||||
@@ -126,9 +125,11 @@ p a {
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
|
||||
}
|
||||
|
||||
.brand.logo a {
|
||||
.brand-logo a {
|
||||
color: white;
|
||||
|
||||
img {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
@@ -317,10 +318,18 @@ body {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 215px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
padding: 16px 18px inherit;
|
||||
margin-left: 230px; /* space for sidebar */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* =======================
|
||||
Social footer
|
||||
======================= */
|
||||
@@ -330,24 +339,64 @@ body {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.social .twitter, .social .github, .social .googleplus {
|
||||
background: url("https://www.docker.io/static/img/footer-links.png") no-repeat transparent;
|
||||
display: inline-block;
|
||||
height: 35px;
|
||||
overflow: hidden;
|
||||
text-indent: 9999px;
|
||||
width: 35px;
|
||||
margin-right: 10px;
|
||||
.social {
|
||||
.twitter, .github, .googleplus, .facebook, .slideshare, .linkedin, .flickr, .youtube, .reddit {
|
||||
background: url("../img/social/docker_social_logos.png") no-repeat transparent;
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
overflow: hidden;
|
||||
text-indent: 9999px;
|
||||
width: 32px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.social :hover {
|
||||
-webkit-transform: rotate(-10deg);
|
||||
-moz-transform: rotate(-10deg);
|
||||
-o-transform: rotate(-10deg);
|
||||
-ms-transform: rotate(-10deg);
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
|
||||
.social .twitter {
|
||||
background-position: 0px 2px;
|
||||
background-position: -160px 0px;
|
||||
}
|
||||
|
||||
.social .reddit {
|
||||
background-position: -256px 0px;
|
||||
}
|
||||
|
||||
.social .github {
|
||||
background-position: -59px 2px;
|
||||
background-position: -64px 0px;
|
||||
}
|
||||
|
||||
.social .googleplus {
|
||||
background-position: -96px 0px;
|
||||
}
|
||||
|
||||
.social .facebook {
|
||||
background-position: -0px 0px;
|
||||
}
|
||||
|
||||
.social .slideshare {
|
||||
background-position: -128px 0px;
|
||||
}
|
||||
|
||||
.social .youtube {
|
||||
background-position: -192px 0px;
|
||||
}
|
||||
|
||||
.social .flickr {
|
||||
background-position: -32px 0px;
|
||||
}
|
||||
|
||||
.social .linkedin {
|
||||
background-position: -224px 0px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Styles on the forms
|
||||
// ----------------------------------
|
||||
|
||||
@@ -528,31 +577,34 @@ div.alert.alert-block {
|
||||
border: 1px solid #88BABC;
|
||||
padding: 5px;
|
||||
font-size: larger;
|
||||
max-width: 300px;
|
||||
|
||||
.content {
|
||||
padding-right: 45px;
|
||||
margin-top: 7px;
|
||||
margin-left: 7px;
|
||||
// display: inline-block;
|
||||
background-image: url('../img/container3.png');
|
||||
background-position: right center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.alternative {
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.active-slug {
|
||||
visibility: visible;
|
||||
display: inline-block;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
&:hover .alternative {
|
||||
animation-duration: 1s;
|
||||
display: inline-block;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.version-note {
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -612,3 +664,24 @@ dt:hover > a.headerlink {
|
||||
|
||||
}
|
||||
|
||||
/* Add styles for other types of comments */
|
||||
|
||||
.versionchanged,
|
||||
.versionadded,
|
||||
.versionmodified,
|
||||
.deprecated {
|
||||
font-size: larger;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.versionchanged {
|
||||
color: lightseagreen;
|
||||
}
|
||||
|
||||
.versionadded {
|
||||
color: mediumblue;
|
||||
}
|
||||
|
||||
.deprecated {
|
||||
color: orangered;
|
||||
}
|
||||
|
||||
BIN
docs/theme/docker/static/img/footer-links.png
vendored
BIN
docs/theme/docker/static/img/footer-links.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB |
BIN
docs/theme/docker/static/img/social/docker_social_logos.png
vendored
Normal file
BIN
docs/theme/docker/static/img/social/docker_social_logos.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
22
docs/theme/docker/static/js/docs.js
vendored
22
docs/theme/docker/static/js/docs.js
vendored
@@ -53,14 +53,6 @@ $(function(){
|
||||
}
|
||||
}
|
||||
|
||||
if (doc_version == "") {
|
||||
$('.version-flyer ul').html('<li class="alternative active-slug"><a href="" title="Switch to local">Local</a></li>');
|
||||
}
|
||||
|
||||
// mark the active documentation in the version widget
|
||||
$(".version-flyer a:contains('" + doc_version + "')").parent().addClass('active-slug');
|
||||
|
||||
|
||||
// attached handler on click
|
||||
// Do not attach to first element or last (intro, faq) so that
|
||||
// first and last link directly instead of accordian
|
||||
@@ -95,4 +87,18 @@ $(function(){
|
||||
// add class to all those which have children
|
||||
$('.sidebar > ul > li').not(':last').not(':first').addClass('has-children');
|
||||
|
||||
|
||||
if (doc_version == "") {
|
||||
$('.version-flyer ul').html('<li class="alternative active-slug"><a href="" title="Switch to local">Local</a></li>');
|
||||
}
|
||||
|
||||
if (doc_version == "master") {
|
||||
$('.version-flyer .version-note').hide();
|
||||
}
|
||||
|
||||
// mark the active documentation in the version widget
|
||||
$(".version-flyer a:contains('" + doc_version + "')").parent().addClass('active-slug').setAttribute("title", "Current version");
|
||||
|
||||
|
||||
|
||||
});
|
||||
@@ -1 +1 @@
|
||||
Solomon Hykes <solomon@dotcloud.com>
|
||||
#Solomon Hykes <solomon@dotcloud.com> Temporarily unavailable
|
||||
|
||||
@@ -3,8 +3,10 @@ package engine
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
@@ -34,6 +36,9 @@ type Engine struct {
|
||||
handlers map[string]Handler
|
||||
hack Hack // data for temporary hackery (see hack.go)
|
||||
id string
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Stdin io.Reader
|
||||
}
|
||||
|
||||
func (eng *Engine) Root() string {
|
||||
@@ -75,13 +80,31 @@ func New(root string) (*Engine, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(root, 0700); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Docker makes some assumptions about the "absoluteness" of root
|
||||
// ... so let's make sure it has no symlinks
|
||||
if p, err := filepath.Abs(root); err != nil {
|
||||
log.Fatalf("Unable to get absolute root (%s): %s", root, err)
|
||||
} else {
|
||||
root = p
|
||||
}
|
||||
if p, err := filepath.EvalSymlinks(root); err != nil {
|
||||
log.Fatalf("Unable to canonicalize root (%s): %s", root, err)
|
||||
} else {
|
||||
root = p
|
||||
}
|
||||
|
||||
eng := &Engine{
|
||||
root: root,
|
||||
handlers: make(map[string]Handler),
|
||||
id: utils.RandomString(),
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
Stdin: os.Stdin,
|
||||
}
|
||||
// Copy existing global handlers
|
||||
for k, v := range globalHandlers {
|
||||
@@ -104,9 +127,9 @@ func (eng *Engine) Job(name string, args ...string) *Job {
|
||||
Stdin: NewInput(),
|
||||
Stdout: NewOutput(),
|
||||
Stderr: NewOutput(),
|
||||
env: &Env{},
|
||||
}
|
||||
job.Stdout.Add(utils.NopWriteCloser(os.Stdout))
|
||||
job.Stderr.Add(utils.NopWriteCloser(os.Stderr))
|
||||
job.Stderr.Add(utils.NopWriteCloser(eng.Stderr))
|
||||
handler, exists := eng.handlers[name]
|
||||
if exists {
|
||||
job.handler = handler
|
||||
@@ -116,5 +139,5 @@ func (eng *Engine) Job(name string, args ...string) *Job {
|
||||
|
||||
func (eng *Engine) Logf(format string, args ...interface{}) (n int, err error) {
|
||||
prefixedFormat := fmt.Sprintf("[%s] %s\n", eng, strings.TrimRight(format, "\n"))
|
||||
return fmt.Fprintf(os.Stderr, prefixedFormat, args...)
|
||||
return fmt.Fprintf(eng.Stderr, prefixedFormat, args...)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestRegister(t *testing.T) {
|
||||
|
||||
eng := newTestEngine(t)
|
||||
|
||||
//Should fail because globan handlers are copied
|
||||
//Should fail because global handlers are copied
|
||||
//at the engine creation
|
||||
if err := eng.Register("dummy1", nil); err == nil {
|
||||
t.Fatalf("Expecting error, got none")
|
||||
|
||||
234
engine/env.go
Normal file
234
engine/env.go
Normal file
@@ -0,0 +1,234 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Env []string
|
||||
|
||||
func (env *Env) Get(key string) (value string) {
|
||||
// FIXME: use Map()
|
||||
for _, kv := range *env {
|
||||
if strings.Index(kv, "=") == -1 {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
if parts[0] != key {
|
||||
continue
|
||||
}
|
||||
if len(parts) < 2 {
|
||||
value = ""
|
||||
} else {
|
||||
value = parts[1]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (env *Env) Exists(key string) bool {
|
||||
_, exists := env.Map()[key]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (env *Env) GetBool(key string) (value bool) {
|
||||
s := strings.ToLower(strings.Trim(env.Get(key), " \t"))
|
||||
if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (env *Env) SetBool(key string, value bool) {
|
||||
if value {
|
||||
env.Set(key, "1")
|
||||
} else {
|
||||
env.Set(key, "0")
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Env) GetInt(key string) int {
|
||||
return int(env.GetInt64(key))
|
||||
}
|
||||
|
||||
func (env *Env) GetInt64(key string) int64 {
|
||||
s := strings.Trim(env.Get(key), " \t")
|
||||
val, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (env *Env) SetInt(key string, value int) {
|
||||
env.Set(key, fmt.Sprintf("%d", value))
|
||||
}
|
||||
|
||||
func (env *Env) SetInt64(key string, value int64) {
|
||||
env.Set(key, fmt.Sprintf("%d", value))
|
||||
}
|
||||
|
||||
// Returns nil if key not found
|
||||
func (env *Env) GetList(key string) []string {
|
||||
sval := env.Get(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
l := make([]string, 0, 1)
|
||||
if err := json.Unmarshal([]byte(sval), &l); err != nil {
|
||||
l = append(l, sval)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (env *Env) GetJson(key string, iface interface{}) error {
|
||||
sval := env.Get(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal([]byte(sval), iface)
|
||||
}
|
||||
|
||||
func (env *Env) SetJson(key string, value interface{}) error {
|
||||
sval, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.Set(key, string(sval))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Env) SetList(key string, value []string) error {
|
||||
return env.SetJson(key, value)
|
||||
}
|
||||
|
||||
func (env *Env) Set(key, value string) {
|
||||
*env = append(*env, key+"="+value)
|
||||
}
|
||||
|
||||
func NewDecoder(src io.Reader) *Decoder {
|
||||
return &Decoder{
|
||||
json.NewDecoder(src),
|
||||
}
|
||||
}
|
||||
|
||||
type Decoder struct {
|
||||
*json.Decoder
|
||||
}
|
||||
|
||||
func (decoder *Decoder) Decode() (*Env, error) {
|
||||
m := make(map[string]interface{})
|
||||
if err := decoder.Decoder.Decode(&m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
env := &Env{}
|
||||
for key, value := range m {
|
||||
env.SetAuto(key, value)
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
// DecodeEnv decodes `src` as a json dictionary, and adds
|
||||
// each decoded key-value pair to the environment.
|
||||
//
|
||||
// If `src` cannot be decoded as a json dictionary, an error
|
||||
// is returned.
|
||||
func (env *Env) Decode(src io.Reader) error {
|
||||
m := make(map[string]interface{})
|
||||
if err := json.NewDecoder(src).Decode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range m {
|
||||
env.SetAuto(k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Env) SetAuto(k string, v interface{}) {
|
||||
// FIXME: we fix-convert float values to int, because
|
||||
// encoding/json decodes integers to float64, but cannot encode them back.
|
||||
// (See http://golang.org/src/pkg/encoding/json/decode.go#L46)
|
||||
if fval, ok := v.(float64); ok {
|
||||
env.SetInt64(k, int64(fval))
|
||||
} else if sval, ok := v.(string); ok {
|
||||
env.Set(k, sval)
|
||||
} else if val, err := json.Marshal(v); err == nil {
|
||||
env.Set(k, string(val))
|
||||
} else {
|
||||
env.Set(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Env) Encode(dst io.Writer) error {
|
||||
m := make(map[string]interface{})
|
||||
for k, v := range env.Map() {
|
||||
var val interface{}
|
||||
if err := json.Unmarshal([]byte(v), &val); err == nil {
|
||||
// FIXME: we fix-convert float values to int, because
|
||||
// encoding/json decodes integers to float64, but cannot encode them back.
|
||||
// (See http://golang.org/src/pkg/encoding/json/decode.go#L46)
|
||||
if fval, isFloat := val.(float64); isFloat {
|
||||
val = int(fval)
|
||||
}
|
||||
m[k] = val
|
||||
} else {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
if err := json.NewEncoder(dst).Encode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Env) WriteTo(dst io.Writer) (n int64, err error) {
|
||||
// FIXME: return the number of bytes written to respect io.WriterTo
|
||||
return 0, env.Encode(dst)
|
||||
}
|
||||
|
||||
func (env *Env) Export(dst interface{}) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("ExportEnv %s", err)
|
||||
}
|
||||
}()
|
||||
var buf bytes.Buffer
|
||||
// step 1: encode/marshal the env to an intermediary json representation
|
||||
if err := env.Encode(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
// step 2: decode/unmarshal the intermediary json into the destination object
|
||||
if err := json.NewDecoder(&buf).Decode(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Env) Import(src interface{}) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("ImportEnv: %s", err)
|
||||
}
|
||||
}()
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(src); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := env.Decode(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Env) Map() map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, kv := range *env {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
m[parts[0]] = parts[1]
|
||||
}
|
||||
return m
|
||||
}
|
||||
40
engine/http.go
Normal file
40
engine/http.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"path"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ServeHTTP executes a job as specified by the http request `r`, and sends the
|
||||
// result as an http response.
|
||||
// This method allows an Engine instance to be passed as a standard http.Handler interface.
|
||||
//
|
||||
// Note that the protocol used in this methid is a convenience wrapper and is not the canonical
|
||||
// implementation of remote job execution. This is because HTTP/1 does not handle stream multiplexing,
|
||||
// and so cannot differentiate stdout from stderr. Additionally, headers cannot be added to a response
|
||||
// once data has been written to the body, which makes it inconvenient to return metadata such
|
||||
// as the exit status.
|
||||
//
|
||||
func (eng *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
jobName := path.Base(r.URL.Path)
|
||||
jobArgs, exists := r.URL.Query()["a"]
|
||||
if !exists {
|
||||
jobArgs = []string{}
|
||||
}
|
||||
w.Header().Set("Job-Name", jobName)
|
||||
for _, arg := range(jobArgs) {
|
||||
w.Header().Add("Job-Args", arg)
|
||||
}
|
||||
job := eng.Job(jobName, jobArgs...)
|
||||
job.Stdout.Add(w)
|
||||
job.Stderr.Add(w)
|
||||
// FIXME: distinguish job status from engine error in Run()
|
||||
// The former should be passed as a special header, the former
|
||||
// should cause a 500 status
|
||||
w.WriteHeader(http.StatusOK)
|
||||
// The exit status cannot be sent reliably with HTTP1, because headers
|
||||
// can only be sent before the body.
|
||||
// (we could possibly use http footers via chunked encoding, but I couldn't find
|
||||
// how to use them in net/http)
|
||||
job.Run()
|
||||
}
|
||||
156
engine/job.go
156
engine/job.go
@@ -1,11 +1,8 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -27,7 +24,7 @@ type Job struct {
|
||||
Eng *Engine
|
||||
Name string
|
||||
Args []string
|
||||
env []string
|
||||
env *Env
|
||||
Stdout *Output
|
||||
Stderr *Output
|
||||
Stdin *Input
|
||||
@@ -105,80 +102,52 @@ func (job *Job) String() string {
|
||||
}
|
||||
|
||||
func (job *Job) Getenv(key string) (value string) {
|
||||
for _, kv := range job.env {
|
||||
if strings.Index(kv, "=") == -1 {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
if parts[0] != key {
|
||||
continue
|
||||
}
|
||||
if len(parts) < 2 {
|
||||
value = ""
|
||||
} else {
|
||||
value = parts[1]
|
||||
}
|
||||
}
|
||||
return
|
||||
return job.env.Get(key)
|
||||
}
|
||||
|
||||
func (job *Job) GetenvBool(key string) (value bool) {
|
||||
s := strings.ToLower(strings.Trim(job.Getenv(key), " \t"))
|
||||
if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return job.env.GetBool(key)
|
||||
}
|
||||
|
||||
func (job *Job) SetenvBool(key string, value bool) {
|
||||
if value {
|
||||
job.Setenv(key, "1")
|
||||
} else {
|
||||
job.Setenv(key, "0")
|
||||
}
|
||||
job.env.SetBool(key, value)
|
||||
}
|
||||
|
||||
func (job *Job) GetenvInt(key string) int64 {
|
||||
s := strings.Trim(job.Getenv(key), " \t")
|
||||
val, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return val
|
||||
func (job *Job) GetenvInt64(key string) int64 {
|
||||
return job.env.GetInt64(key)
|
||||
}
|
||||
|
||||
func (job *Job) SetenvInt(key string, value int64) {
|
||||
job.Setenv(key, fmt.Sprintf("%d", value))
|
||||
func (job *Job) GetenvInt(key string) int {
|
||||
return job.env.GetInt(key)
|
||||
}
|
||||
|
||||
func (job *Job) SetenvInt64(key string, value int64) {
|
||||
job.env.SetInt64(key, value)
|
||||
}
|
||||
|
||||
func (job *Job) SetenvInt(key string, value int) {
|
||||
job.env.SetInt(key, value)
|
||||
}
|
||||
|
||||
// Returns nil if key not found
|
||||
func (job *Job) GetenvList(key string) []string {
|
||||
sval := job.Getenv(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
l := make([]string, 0, 1)
|
||||
if err := json.Unmarshal([]byte(sval), &l); err != nil {
|
||||
l = append(l, sval)
|
||||
}
|
||||
return l
|
||||
return job.env.GetList(key)
|
||||
}
|
||||
|
||||
func (job *Job) GetenvJson(key string, iface interface{}) error {
|
||||
return job.env.GetJson(key, iface)
|
||||
}
|
||||
|
||||
func (job *Job) SetenvJson(key string, value interface{}) error {
|
||||
sval, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
job.Setenv(key, string(sval))
|
||||
return nil
|
||||
return job.env.SetJson(key, value)
|
||||
}
|
||||
|
||||
func (job *Job) SetenvList(key string, value []string) error {
|
||||
return job.SetenvJson(key, value)
|
||||
return job.env.SetJson(key, value)
|
||||
}
|
||||
|
||||
func (job *Job) Setenv(key, value string) {
|
||||
job.env = append(job.env, key+"="+value)
|
||||
job.env.Set(key, value)
|
||||
}
|
||||
|
||||
// DecodeEnv decodes `src` as a json dictionary, and adds
|
||||
@@ -187,90 +156,23 @@ func (job *Job) Setenv(key, value string) {
|
||||
// If `src` cannot be decoded as a json dictionary, an error
|
||||
// is returned.
|
||||
func (job *Job) DecodeEnv(src io.Reader) error {
|
||||
m := make(map[string]interface{})
|
||||
if err := json.NewDecoder(src).Decode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range m {
|
||||
// FIXME: we fix-convert float values to int, because
|
||||
// encoding/json decodes integers to float64, but cannot encode them back.
|
||||
// (See http://golang.org/src/pkg/encoding/json/decode.go#L46)
|
||||
if fval, ok := v.(float64); ok {
|
||||
job.SetenvInt(k, int64(fval))
|
||||
} else if sval, ok := v.(string); ok {
|
||||
job.Setenv(k, sval)
|
||||
} else if val, err := json.Marshal(v); err == nil {
|
||||
job.Setenv(k, string(val))
|
||||
} else {
|
||||
job.Setenv(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return job.env.Decode(src)
|
||||
}
|
||||
|
||||
func (job *Job) EncodeEnv(dst io.Writer) error {
|
||||
m := make(map[string]interface{})
|
||||
for k, v := range job.Environ() {
|
||||
var val interface{}
|
||||
if err := json.Unmarshal([]byte(v), &val); err == nil {
|
||||
// FIXME: we fix-convert float values to int, because
|
||||
// encoding/json decodes integers to float64, but cannot encode them back.
|
||||
// (See http://golang.org/src/pkg/encoding/json/decode.go#L46)
|
||||
if fval, isFloat := val.(float64); isFloat {
|
||||
val = int(fval)
|
||||
}
|
||||
m[k] = val
|
||||
} else {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
if err := json.NewEncoder(dst).Encode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return job.env.Encode(dst)
|
||||
}
|
||||
|
||||
func (job *Job) ExportEnv(dst interface{}) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("ExportEnv %s", err)
|
||||
}
|
||||
}()
|
||||
var buf bytes.Buffer
|
||||
// step 1: encode/marshal the env to an intermediary json representation
|
||||
if err := job.EncodeEnv(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
// step 2: decode/unmarshal the intermediary json into the destination object
|
||||
if err := json.NewDecoder(&buf).Decode(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return job.env.Export(dst)
|
||||
}
|
||||
|
||||
func (job *Job) ImportEnv(src interface{}) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("ImportEnv: %s", err)
|
||||
}
|
||||
}()
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(src); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := job.DecodeEnv(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return job.env.Import(src)
|
||||
}
|
||||
|
||||
func (job *Job) Environ() map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, kv := range job.env {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
m[parts[0]] = parts[1]
|
||||
}
|
||||
return m
|
||||
return job.env.Map()
|
||||
}
|
||||
|
||||
func (job *Job) Logf(format string, args ...interface{}) (n int, err error) {
|
||||
|
||||
@@ -164,3 +164,29 @@ func Tail(src io.Reader, n int, dst *[]string) {
|
||||
*dst = append(*dst, v.(string))
|
||||
})
|
||||
}
|
||||
|
||||
// AddEnv starts a new goroutine which will decode all subsequent data
|
||||
// as a stream of json-encoded objects, and point `dst` to the last
|
||||
// decoded object.
|
||||
// The result `env` can be queried using the type-neutral Env interface.
|
||||
// It is not safe to query `env` until the Output is closed.
|
||||
func (o *Output) AddEnv() (dst *Env, err error) {
|
||||
src, err := o.AddPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dst = &Env{}
|
||||
o.tasks.Add(1)
|
||||
go func() {
|
||||
defer o.tasks.Done()
|
||||
decoder := NewDecoder(src)
|
||||
for {
|
||||
env, err := decoder.Decode()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
*dst = *env
|
||||
}
|
||||
}()
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
@@ -72,6 +72,26 @@ func (w *sentinelWriteCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestOutputAddEnv(t *testing.T) {
|
||||
input := "{\"foo\": \"bar\", \"answer_to_life_the_universe_and_everything\": 42}"
|
||||
o := NewOutput()
|
||||
result, err := o.AddEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
o.Write([]byte(input))
|
||||
o.Close()
|
||||
if v := result.Get("foo"); v != "bar" {
|
||||
t.Errorf("Expected %v, got %v", "bar", v)
|
||||
}
|
||||
if v := result.GetInt("answer_to_life_the_universe_and_everything"); v != 42 {
|
||||
t.Errorf("Expected %v, got %v", 42, v)
|
||||
}
|
||||
if v := result.Get("this-value-doesnt-exist"); v != "" {
|
||||
t.Errorf("Expected %v, got %v", "", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputAddClose(t *testing.T) {
|
||||
o := NewOutput()
|
||||
var s sentinelWriteCloser
|
||||
|
||||
7
graph.go
7
graph.go
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -56,6 +57,7 @@ func (graph *Graph) restore() error {
|
||||
graph.idIndex.Add(id)
|
||||
}
|
||||
}
|
||||
utils.Debugf("Restored %d elements", len(dir))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -130,7 +132,8 @@ func (graph *Graph) Create(layerData archive.Archive, container *Container, comm
|
||||
DockerVersion: VERSION,
|
||||
Author: author,
|
||||
Config: config,
|
||||
Architecture: "x86_64",
|
||||
Architecture: runtime.GOARCH,
|
||||
OS: runtime.GOOS,
|
||||
}
|
||||
if container != nil {
|
||||
img.Parent = container.Image
|
||||
@@ -219,7 +222,7 @@ func (graph *Graph) TempLayerArchive(id string, compression archive.Compression,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf, true, "", "Buffering to disk"), tmp)
|
||||
return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf, false, utils.TruncateID(id), "Buffering to disk"), tmp)
|
||||
}
|
||||
|
||||
// Mktemp creates a temporary sub-directory inside the graph's filesystem.
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/archive"
|
||||
"github.com/dotcloud/docker/graphdriver"
|
||||
mountpk "github.com/dotcloud/docker/mount"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -296,7 +296,7 @@ func (a *Driver) unmount(id string) error {
|
||||
|
||||
func (a *Driver) mounted(id string) (bool, error) {
|
||||
target := path.Join(a.rootPath(), "mnt", id)
|
||||
return Mounted(target)
|
||||
return mountpk.Mounted(target)
|
||||
}
|
||||
|
||||
// During cleanup aufs needs to unmount all mountpoints
|
||||
@@ -313,24 +313,44 @@ func (a *Driver) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Driver) aufsMount(ro []string, rw, target string) error {
|
||||
rwBranch := fmt.Sprintf("%v=rw", rw)
|
||||
roBranches := ""
|
||||
for _, layer := range ro {
|
||||
roBranches += fmt.Sprintf("%v=ro+wh:", layer)
|
||||
}
|
||||
branches := fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches)
|
||||
func (a *Driver) aufsMount(ro []string, rw, target string) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
Unmount(target)
|
||||
}
|
||||
}()
|
||||
|
||||
//if error, try to load aufs kernel module
|
||||
if err := mount("none", target, "aufs", 0, branches); err != nil {
|
||||
log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
|
||||
if err := exec.Command("modprobe", "aufs").Run(); err != nil {
|
||||
return fmt.Errorf("Unable to load the AUFS module")
|
||||
if err = a.tryMount(ro, rw, target); err != nil {
|
||||
if err = a.mountRw(rw, target); err != nil {
|
||||
return
|
||||
}
|
||||
log.Printf("...module loaded.")
|
||||
if err := mount("none", target, "aufs", 0, branches); err != nil {
|
||||
return fmt.Errorf("Unable to mount using aufs %s", err)
|
||||
|
||||
for _, layer := range ro {
|
||||
branch := fmt.Sprintf("append:%s=ro+wh", layer)
|
||||
if err = mount("none", target, "aufs", MsRemount, branch); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// Try to mount using the aufs fast path, if this fails then
|
||||
// append ro layers.
|
||||
func (a *Driver) tryMount(ro []string, rw, target string) (err error) {
|
||||
var (
|
||||
rwBranch = fmt.Sprintf("%s=rw", rw)
|
||||
roBranches = fmt.Sprintf("%s=ro+wh:", strings.Join(ro, "=ro+wh:"))
|
||||
)
|
||||
return mount("none", target, "aufs", 0, fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches))
|
||||
}
|
||||
|
||||
func (a *Driver) mountRw(rw, target string) error {
|
||||
return mount("none", target, "aufs", 0, fmt.Sprintf("br:%s,xino=/dev/shm/aufs.xino", rw))
|
||||
}
|
||||
|
||||
func rollbackMount(target string, err error) {
|
||||
if err != nil {
|
||||
Unmount(target)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package aufs
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/archive"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
@@ -621,3 +625,70 @@ func TestApplyDiff(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func hash(c string) string {
|
||||
h := sha256.New()
|
||||
fmt.Fprint(h, c)
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func TestMountMoreThan42Layers(t *testing.T) {
|
||||
d := newDriver(t)
|
||||
defer os.RemoveAll(tmp)
|
||||
defer d.Cleanup()
|
||||
var last string
|
||||
var expected int
|
||||
|
||||
for i := 1; i < 127; i++ {
|
||||
expected++
|
||||
var (
|
||||
parent = fmt.Sprintf("%d", i-1)
|
||||
current = fmt.Sprintf("%d", i)
|
||||
)
|
||||
|
||||
if parent == "0" {
|
||||
parent = ""
|
||||
} else {
|
||||
parent = hash(parent)
|
||||
}
|
||||
current = hash(current)
|
||||
|
||||
if err := d.Create(current, parent); err != nil {
|
||||
t.Logf("Current layer %d", i)
|
||||
t.Fatal(err)
|
||||
}
|
||||
point, err := d.Get(current)
|
||||
if err != nil {
|
||||
t.Logf("Current layer %d", i)
|
||||
t.Fatal(err)
|
||||
}
|
||||
f, err := os.Create(path.Join(point, current))
|
||||
if err != nil {
|
||||
t.Logf("Current layer %d", i)
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
if i%10 == 0 {
|
||||
if err := os.Remove(path.Join(point, parent)); err != nil {
|
||||
t.Logf("Current layer %d", i)
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected--
|
||||
}
|
||||
last = current
|
||||
}
|
||||
|
||||
// Perform the actual mount for the top most image
|
||||
point, err := d.Get(last)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
files, err := ioutil.ReadDir(point)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(files) != expected {
|
||||
t.Fatalf("Expected %d got %d", expected, len(files))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ package aufs
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -17,21 +15,3 @@ func Unmount(target string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Mounted(mountpoint string) (bool, error) {
|
||||
mntpoint, err := os.Stat(mountpoint)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
parent, err := os.Stat(filepath.Join(mountpoint, ".."))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
mntpointSt := mntpoint.Sys().(*syscall.Stat_t)
|
||||
parentSt := parent.Sys().(*syscall.Stat_t)
|
||||
|
||||
return mntpointSt.Dev != parentSt.Dev, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package aufs
|
||||
|
||||
import "errors"
|
||||
|
||||
const MsRemount = 0
|
||||
|
||||
func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
|
||||
return errors.New("mount is not implemented on darwin")
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package aufs
|
||||
|
||||
import "syscall"
|
||||
|
||||
func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
|
||||
const MsRemount = syscall.MS_REMOUNT
|
||||
|
||||
func mount(source string, target string, fstype string, flags uintptr, data string) error {
|
||||
return syscall.Mount(source, target, fstype, flags, data)
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ func (devices *DeviceSet) allocateTransactionId() uint64 {
|
||||
func (devices *DeviceSet) saveMetadata() error {
|
||||
jsonData, err := json.Marshal(devices.MetaData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error encoding metaadata to json: %s", err)
|
||||
return fmt.Errorf("Error encoding metadata to json: %s", err)
|
||||
}
|
||||
tmpFile, err := ioutil.TempFile(filepath.Dir(devices.jsonFile()), ".json")
|
||||
if err != nil {
|
||||
|
||||
@@ -8,6 +8,14 @@ package devmapper
|
||||
#include <linux/loop.h> // FIXME: present only for defines, maybe we can remove it?
|
||||
#include <linux/fs.h> // FIXME: present only for BLKGETSIZE64, maybe we can remove it?
|
||||
|
||||
#ifndef LOOP_CTL_GET_FREE
|
||||
#define LOOP_CTL_GET_FREE 0x4C82
|
||||
#endif
|
||||
|
||||
#ifndef LO_FLAGS_PARTSCAN
|
||||
#define LO_FLAGS_PARTSCAN 8
|
||||
#endif
|
||||
|
||||
// FIXME: Can't we find a way to do the logging in pure Go?
|
||||
extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str);
|
||||
|
||||
@@ -55,7 +63,6 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
// FIXME: Make sure the values are defined in C
|
||||
// IOCTL consts
|
||||
const (
|
||||
BlkGetSize64 = C.BLKGETSIZE64
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
Solomon Hykes <solomon@dotcloud.com> (@shykes)
|
||||
Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
|
||||
@@ -36,8 +36,9 @@ To build docker, you will need the following system dependencies
|
||||
|
||||
* An amd64 machine
|
||||
* A recent version of git and mercurial
|
||||
* Go version 1.2 or later (see notes below regarding using Go 1.1.2 and dynbinary)
|
||||
* Go version 1.2 or later
|
||||
* SQLite version 3.7.9 or later
|
||||
* libdevmapper from lvm2 version 1.02.77 or later (http://www.sourceware.org/lvm2/)
|
||||
* A clean checkout of the source must be added to a valid Go [workspace](http://golang.org/doc/code.html#Workspaces)
|
||||
under the path *src/github.com/dotcloud/docker*.
|
||||
|
||||
@@ -91,8 +92,7 @@ You would do the users of your distro a disservice and "void the docker warranty
|
||||
A good comparison is Busybox: all distros package it as a statically linked binary, because it just
|
||||
makes sense. Docker is the same way.
|
||||
|
||||
If you *must* have a non-static Docker binary, or require Go 1.1.2 (since Go 1.2 is still freshly released
|
||||
at the time of this writing), please use:
|
||||
If you *must* have a non-static Docker binary, please use:
|
||||
|
||||
```bash
|
||||
./hack/make.sh dynbinary
|
||||
|
||||
@@ -136,7 +136,7 @@ sudo('echo -e "deb http://archive.ubuntu.com/ubuntu raring main universe\n'
|
||||
sudo('DEBIAN_FRONTEND=noninteractive apt-get install -q -y wget python-dev'
|
||||
' python-pip supervisor git mercurial linux-image-extra-$(uname -r)'
|
||||
' aufs-tools make libfontconfig libevent-dev libsqlite3-dev libssl-dev')
|
||||
sudo('wget -O - https://go.googlecode.com/files/go1.1.2.linux-amd64.tar.gz | '
|
||||
sudo('wget -O - https://go.googlecode.com/files/go1.2.linux-amd64.tar.gz | '
|
||||
'tar -v -C /usr/local -xz; ln -s /usr/local/go/bin/go /usr/bin/go')
|
||||
sudo('GOPATH=/go go get -d github.com/dotcloud/docker')
|
||||
sudo('pip install -r {}/requirements.txt'.format(CFG_PATH))
|
||||
|
||||
@@ -116,7 +116,7 @@ case "$lsb_dist" in
|
||||
(
|
||||
set -x
|
||||
$sh_c 'docker run busybox echo "Docker has been successfully installed!"'
|
||||
)
|
||||
) || true
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
|
||||
43
hack/make.sh
43
hack/make.sh
@@ -15,8 +15,9 @@ set -e
|
||||
# - The script is intented to be run inside the docker container specified
|
||||
# in the Dockerfile at the root of the source. In other words:
|
||||
# DO NOT CALL THIS SCRIPT DIRECTLY.
|
||||
# - The right way to call this script is to invoke "docker build ." from
|
||||
# your checkout of the Docker repository, and then
|
||||
# - The right way to call this script is to invoke "make" from
|
||||
# your checkout of the Docker repository.
|
||||
# the Makefile will so a "docker build -t docker ." and then
|
||||
# "docker run hack/make.sh" in the resulting container image.
|
||||
#
|
||||
|
||||
@@ -28,15 +29,19 @@ RESOLVCONF=$(readlink --canonicalize /etc/resolv.conf)
|
||||
grep -q "$RESOLVCONF" /proc/mounts || {
|
||||
echo >&2 "# WARNING! I don't seem to be running in a docker container."
|
||||
echo >&2 "# The result of this command might be an incorrect build, and will not be officially supported."
|
||||
echo >&2 "# Try this: 'docker build -t docker . && docker run docker ./hack/make.sh'"
|
||||
echo >&2 "# Try this: 'make all'"
|
||||
}
|
||||
|
||||
# List of bundles to create when no argument is passed
|
||||
DEFAULT_BUNDLES=(
|
||||
binary
|
||||
test
|
||||
test-integration
|
||||
dynbinary
|
||||
dyntest
|
||||
dyntest-integration
|
||||
cover
|
||||
cross
|
||||
tgz
|
||||
ubuntu
|
||||
)
|
||||
@@ -60,7 +65,37 @@ fi
|
||||
# Use these flags when compiling the tests and final binary
|
||||
LDFLAGS='-X main.GITCOMMIT "'$GITCOMMIT'" -X main.VERSION "'$VERSION'" -w'
|
||||
LDFLAGS_STATIC='-X github.com/dotcloud/docker/utils.IAMSTATIC true -linkmode external -extldflags "-lpthread -static -Wl,--unresolved-symbols=ignore-in-object-files"'
|
||||
BUILDFLAGS='-tags netgo'
|
||||
BUILDFLAGS='-tags netgo -a'
|
||||
|
||||
HAVE_GO_TEST_COVER=
|
||||
if \
|
||||
go help testflag | grep -- -cover > /dev/null \
|
||||
&& go tool -n cover > /dev/null 2>&1 \
|
||||
; then
|
||||
HAVE_GO_TEST_COVER=1
|
||||
fi
|
||||
|
||||
# If $TESTFLAGS is set in the environment, it is passed as extra arguments to 'go test'.
|
||||
# You can use this to select certain tests to run, eg.
|
||||
#
|
||||
# TESTFLAGS='-run ^TestBuild$' ./hack/make.sh test
|
||||
#
|
||||
go_test_dir() {
|
||||
dir=$1
|
||||
testcover=()
|
||||
if [ "$HAVE_GO_TEST_COVER" ]; then
|
||||
# if our current go install has -cover, we want to use it :)
|
||||
mkdir -p "$DEST/coverprofiles"
|
||||
coverprofile="docker${dir#.}"
|
||||
coverprofile="$DEST/coverprofiles/${coverprofile//\//-}"
|
||||
testcover=( -cover -coverprofile "$coverprofile" )
|
||||
fi
|
||||
(
|
||||
set -x
|
||||
cd "$dir"
|
||||
go test ${testcover[@]} -ldflags "$LDFLAGS" $BUILDFLAGS $TESTFLAGS
|
||||
)
|
||||
}
|
||||
|
||||
bundle() {
|
||||
bundlescript=$1
|
||||
|
||||
21
hack/make/cover
Normal file
21
hack/make/cover
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
DEST="$1"
|
||||
|
||||
bundle_cover() {
|
||||
coverprofiles=( "$DEST/../"*"/coverprofiles/"* )
|
||||
for p in "${coverprofiles[@]}"; do
|
||||
echo
|
||||
(
|
||||
set -x
|
||||
go tool cover -func="$p"
|
||||
)
|
||||
done
|
||||
}
|
||||
|
||||
if [ "$HAVE_GO_TEST_COVER" ]; then
|
||||
bundle_cover 2>&1 | tee "$DEST/report.log"
|
||||
else
|
||||
echo >&2 'warning: the current version of go does not support -cover'
|
||||
echo >&2 ' skipping test coverage report'
|
||||
fi
|
||||
23
hack/make/cross
Normal file
23
hack/make/cross
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
DEST=$1
|
||||
|
||||
# if we have our linux/amd64 version compiled, let's symlink it in
|
||||
if [ -x "$DEST/../binary/docker-$VERSION" ]; then
|
||||
mkdir -p "$DEST/linux/amd64"
|
||||
(
|
||||
cd "$DEST/linux/amd64"
|
||||
ln -s ../../../binary/* ./
|
||||
)
|
||||
echo "Created symlinks:" "$DEST/linux/amd64/"*
|
||||
fi
|
||||
|
||||
for platform in $DOCKER_CROSSPLATFORMS; do
|
||||
(
|
||||
mkdir -p "$DEST/$platform" # bundles/VERSION/cross/GOOS/GOARCH/docker-VERSION
|
||||
export GOOS=${platform%/*}
|
||||
export GOARCH=${platform##*/}
|
||||
export LDFLAGS_STATIC="" # we just need a simple client for these platforms (TODO this might change someday)
|
||||
source "$(dirname "$BASH_SOURCE")/binary" "$DEST/$platform"
|
||||
)
|
||||
done
|
||||
@@ -3,7 +3,7 @@
|
||||
DEST=$1
|
||||
|
||||
# dockerinit still needs to be a static binary, even if docker is dynamic
|
||||
CGO_ENABLED=0 go build -a -o $DEST/dockerinit-$VERSION -ldflags "$LDFLAGS -d" $BUILDFLAGS ./dockerinit
|
||||
CGO_ENABLED=0 go build -o $DEST/dockerinit-$VERSION -ldflags "$LDFLAGS -d" $BUILDFLAGS ./dockerinit
|
||||
echo "Created binary: $DEST/dockerinit-$VERSION"
|
||||
ln -sf dockerinit-$VERSION $DEST/dockerinit
|
||||
|
||||
@@ -11,5 +11,7 @@ ln -sf dockerinit-$VERSION $DEST/dockerinit
|
||||
export DOCKER_INITSHA1="$(sha1sum $DEST/dockerinit-$VERSION | cut -d' ' -f1)"
|
||||
# exported so that "dyntest" can easily access it later without recalculating it
|
||||
|
||||
go build -o $DEST/docker-$VERSION -ldflags "$LDFLAGS -X github.com/dotcloud/docker/utils.INITSHA1 \"$DOCKER_INITSHA1\"" $BUILDFLAGS ./docker
|
||||
echo "Created binary: $DEST/docker-$VERSION"
|
||||
(
|
||||
export LDFLAGS_STATIC="-X github.com/dotcloud/docker/utils.INITSHA1 \"$DOCKER_INITSHA1\" -X github.com/dotcloud/docker/utils.INITPATH \"$DOCKER_INITPATH\""
|
||||
source "$(dirname "$BASH_SOURCE")/binary"
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user