mirror of
https://github.com/moby/moby.git
synced 2026-01-17 02:41:41 +00:00
Compare commits
1378 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8da76605f | ||
|
|
f16e60665b | ||
|
|
3ba9893786 | ||
|
|
6ee2964a4f | ||
|
|
49c62879b8 | ||
|
|
34745ee0f4 | ||
|
|
d21bd5b964 | ||
|
|
82220ea2ad | ||
|
|
655b16c712 | ||
|
|
eb2d0d4bf5 | ||
|
|
cc851dbb3f | ||
|
|
e40fd1e3e1 | ||
|
|
dab84f7e8b | ||
|
|
7df8ec2093 | ||
|
|
a2ffa637ce | ||
|
|
ed6ca109bf | ||
|
|
6e48b73955 | ||
|
|
664acd2971 | ||
|
|
3acfc60028 | ||
|
|
0e64a4d7e7 | ||
|
|
104a86936a | ||
|
|
df697b4318 | ||
|
|
27fc01271e | ||
|
|
97ab118fbb | ||
|
|
fc659b68e4 | ||
|
|
99f6b43f4f | ||
|
|
2b92aa71f9 | ||
|
|
f854529ae8 | ||
|
|
e32debcf5f | ||
|
|
0e08e1d695 | ||
|
|
65b62307c3 | ||
|
|
03e5d94b12 | ||
|
|
0ca533ca35 | ||
|
|
d94a027da6 | ||
|
|
7250f94874 | ||
|
|
4196c704f0 | ||
|
|
adb48ef319 | ||
|
|
ee7d9b0bde | ||
|
|
2f4526d5c4 | ||
|
|
261285c2c2 | ||
|
|
cbc49d7d76 | ||
|
|
4dc90405e3 | ||
|
|
b7abe9dab5 | ||
|
|
cd455ca6fa | ||
|
|
ca56b35e53 | ||
|
|
ace059e409 | ||
|
|
9da2b3590e | ||
|
|
e99ab1df97 | ||
|
|
fde157425c | ||
|
|
a0e5657cb6 | ||
|
|
f4a98b4598 | ||
|
|
53a07d5475 | ||
|
|
93b258413f | ||
|
|
c0bfb75e5f | ||
|
|
620bc5b9e1 | ||
|
|
6d6097cddf | ||
|
|
42277d0329 | ||
|
|
d62df6b2b5 | ||
|
|
97779ff7cf | ||
|
|
9afa48a92f | ||
|
|
ad723bbfe7 | ||
|
|
f15cd71bb8 | ||
|
|
94bf5b0011 | ||
|
|
d542318d63 | ||
|
|
6e3068700f | ||
|
|
fac190959f | ||
|
|
1dcdc3deb7 | ||
|
|
ee256ce6bf | ||
|
|
5fe699f813 | ||
|
|
7561e2570c | ||
|
|
640ca68e4f | ||
|
|
f9b9dfc82d | ||
|
|
c978f0fa5c | ||
|
|
d91bf33df3 | ||
|
|
310a174260 | ||
|
|
2eb404ab14 | ||
|
|
60e426938e | ||
|
|
9041d6a47b | ||
|
|
8abd328f53 | ||
|
|
ae780fb5ea | ||
|
|
152037f011 | ||
|
|
0367032bd1 | ||
|
|
edca9f7e9c | ||
|
|
e19cc1e843 | ||
|
|
feabce3bcc | ||
|
|
ded74b0bb3 | ||
|
|
ffee5faae8 | ||
|
|
c1bb33ea4d | ||
|
|
6545e53803 | ||
|
|
d30806590c | ||
|
|
823174de4d | ||
|
|
d5f9160441 | ||
|
|
37df1d92d8 | ||
|
|
dabac81824 | ||
|
|
bb62c281f5 | ||
|
|
3565bf8d00 | ||
|
|
f4c17a202b | ||
|
|
9a4b0b9823 | ||
|
|
9107565d06 | ||
|
|
4fda3314d8 | ||
|
|
e906485b07 | ||
|
|
1daf242c8b | ||
|
|
967010ae8c | ||
|
|
d03022b9ca | ||
|
|
6d67ac2a1b | ||
|
|
2d425af1b1 | ||
|
|
ed19693f50 | ||
|
|
ee27444098 | ||
|
|
194c1dbbd8 | ||
|
|
a9eac7eab4 | ||
|
|
7c28683f08 | ||
|
|
c18e849451 | ||
|
|
a665517151 | ||
|
|
0d3c899528 | ||
|
|
863eebe7bd | ||
|
|
3c5350ba92 | ||
|
|
c52c95f97f | ||
|
|
85a5139665 | ||
|
|
ad4b09cdb8 | ||
|
|
e594c788e4 | ||
|
|
f84dc1e908 | ||
|
|
826aaa0e9b | ||
|
|
1328be7c29 | ||
|
|
ff4f32d58f | ||
|
|
462ac1efcf | ||
|
|
1332f400f7 | ||
|
|
a292c04ddb | ||
|
|
37c4513cf9 | ||
|
|
b584c21ca4 | ||
|
|
c92507341d | ||
|
|
9f0feaa2e1 | ||
|
|
0f830aa431 | ||
|
|
5f05cb4817 | ||
|
|
4778039dd2 | ||
|
|
b180770b1d | ||
|
|
4f5c2cbccc | ||
|
|
c3acd9bece | ||
|
|
cb21a40490 | ||
|
|
5025b4d094 | ||
|
|
509e25cf04 | ||
|
|
135c1fce90 | ||
|
|
d517fd3d77 | ||
|
|
b4e1b8d2e2 | ||
|
|
45bd52d472 | ||
|
|
534a991cff | ||
|
|
23015fa14d | ||
|
|
fb46d911c2 | ||
|
|
85b776995c | ||
|
|
fa44555fb4 | ||
|
|
3e7c50e8a1 | ||
|
|
ccefe47897 | ||
|
|
ff85031980 | ||
|
|
017ecefd66 | ||
|
|
88f710f0e7 | ||
|
|
6496059154 | ||
|
|
bcce3cbdd1 | ||
|
|
bbf644ed62 | ||
|
|
d89ce09a76 | ||
|
|
e3e041b6bf | ||
|
|
0a35b1fb36 | ||
|
|
3894add8a9 | ||
|
|
74d54b9b2e | ||
|
|
31fd11860b | ||
|
|
b081a740b3 | ||
|
|
db869ecce5 | ||
|
|
537149829a | ||
|
|
ba10e28927 | ||
|
|
f4855a7cf0 | ||
|
|
22e7e107ad | ||
|
|
edd90d46c7 | ||
|
|
f435970695 | ||
|
|
cb18a6e1b9 | ||
|
|
ce05083d4b | ||
|
|
723de87681 | ||
|
|
c22e377a6d | ||
|
|
3f0d0075f8 | ||
|
|
f8c9f11f06 | ||
|
|
80319add55 | ||
|
|
818ec10da3 | ||
|
|
34e28ccc88 | ||
|
|
792403f2dc | ||
|
|
8f5c33dc1f | ||
|
|
06c1f000e8 | ||
|
|
00b3acb8ab | ||
|
|
420ba9c85a | ||
|
|
0f829bf5cf | ||
|
|
1363de0934 | ||
|
|
d06116d2e8 | ||
|
|
595210a370 | ||
|
|
c9b916b293 | ||
|
|
1e6370fd4b | ||
|
|
a59a66528b | ||
|
|
b048e9dffc | ||
|
|
c000e6a7fc | ||
|
|
aa3de0b849 | ||
|
|
648d759517 | ||
|
|
5c190fa926 | ||
|
|
03fe5632d0 | ||
|
|
edde4f55e0 | ||
|
|
7447867edd | ||
|
|
c1c74cb0b1 | ||
|
|
17b7194d91 | ||
|
|
15ad2915b9 | ||
|
|
d6887b769c | ||
|
|
10362870db | ||
|
|
a7db125480 | ||
|
|
f6c64827c8 | ||
|
|
30b759ffa9 | ||
|
|
35ca35ffb6 | ||
|
|
a4067b1b44 | ||
|
|
b0a49a30c7 | ||
|
|
6097644e4b | ||
|
|
fa3837abf1 | ||
|
|
baff72bc82 | ||
|
|
99377de7d2 | ||
|
|
0ef5bcb17d | ||
|
|
fd1c43f0e0 | ||
|
|
dc06496dc8 | ||
|
|
cfbe76e559 | ||
|
|
2fafe1efce | ||
|
|
8240e37b87 | ||
|
|
23cf3c7a33 | ||
|
|
e37bb50628 | ||
|
|
2819cfb3f3 | ||
|
|
a479f7659d | ||
|
|
b02a591f57 | ||
|
|
c654075654 | ||
|
|
5a3dcbb3f6 | ||
|
|
cce6f00c0c | ||
|
|
4db64111bd | ||
|
|
e97364ecd7 | ||
|
|
2bd089dadb | ||
|
|
5bd0437eed | ||
|
|
a81393787f | ||
|
|
b45e280ee8 | ||
|
|
5867f9e761 | ||
|
|
6132f378e0 | ||
|
|
3cf4b2c7c1 | ||
|
|
d70cbbf1fc | ||
|
|
b7e7104e92 | ||
|
|
7e60a6b1ea | ||
|
|
45979a7ef7 | ||
|
|
5c9213258a | ||
|
|
68074fce68 | ||
|
|
c42056b969 | ||
|
|
341addeccd | ||
|
|
47904290a5 | ||
|
|
a18c26392f | ||
|
|
77ad95d372 | ||
|
|
fee61895f2 | ||
|
|
f6a78f89ba | ||
|
|
081543c49a | ||
|
|
4054f31471 | ||
|
|
76a07371c5 | ||
|
|
5d364fda3c | ||
|
|
28311f61be | ||
|
|
70b731735c | ||
|
|
67453d6b48 | ||
|
|
c14450ae5c | ||
|
|
593566438d | ||
|
|
ca5913ff3e | ||
|
|
85f3ec88a6 | ||
|
|
b2707dccc1 | ||
|
|
b826351337 | ||
|
|
5797e7e34e | ||
|
|
089a60c2cf | ||
|
|
a99a8386fd | ||
|
|
dfd0deefbb | ||
|
|
2183644578 | ||
|
|
e836b0064b | ||
|
|
986906cd7b | ||
|
|
8d48119340 | ||
|
|
cdce873ea0 | ||
|
|
5a01f7485c | ||
|
|
45b50730e3 | ||
|
|
40eaa82fc6 | ||
|
|
9946981c61 | ||
|
|
d6cf41cbe6 | ||
|
|
c6b4076125 | ||
|
|
b79bd4e864 | ||
|
|
2f24b9e05c | ||
|
|
a63cfe7153 | ||
|
|
cd289cc10c | ||
|
|
894eff64b4 | ||
|
|
71e15d1c3c | ||
|
|
74daeccec5 | ||
|
|
102522bcd1 | ||
|
|
b3cbf424ec | ||
|
|
33972627b7 | ||
|
|
4234fc699f | ||
|
|
4a707a8800 | ||
|
|
b986fd00e0 | ||
|
|
f368fd9f9f | ||
|
|
6c1c404501 | ||
|
|
796d7a49f0 | ||
|
|
ffc850244f | ||
|
|
83623675ce | ||
|
|
46f644c074 | ||
|
|
1bc2ab05ec | ||
|
|
1d935c6307 | ||
|
|
b7a3fc687e | ||
|
|
718b1f9fe7 | ||
|
|
45cedefadb | ||
|
|
1e723bc95a | ||
|
|
082d142024 | ||
|
|
9dd98df1a3 | ||
|
|
e854b7b2e6 | ||
|
|
289350e633 | ||
|
|
8ae5348a51 | ||
|
|
557e3ec88e | ||
|
|
dfe45e1ad5 | ||
|
|
d5480fb78d | ||
|
|
c6dc90ccb9 | ||
|
|
02037a9b53 | ||
|
|
71d46eaf02 | ||
|
|
f4ba0d4267 | ||
|
|
d48ea32186 | ||
|
|
33e4d73629 | ||
|
|
07324a37eb | ||
|
|
dd49cc45c8 | ||
|
|
21be3b76e9 | ||
|
|
9cfbaecfe5 | ||
|
|
ebee8f28ac | ||
|
|
03e36caeb1 | ||
|
|
f482432a76 | ||
|
|
c75926be4f | ||
|
|
bd0e4fde9a | ||
|
|
de75241b62 | ||
|
|
228b7af516 | ||
|
|
d14058bc29 | ||
|
|
5b361f31f7 | ||
|
|
c24c5c3b60 | ||
|
|
44d0f941f2 | ||
|
|
b7826f5666 | ||
|
|
27ca0c225a | ||
|
|
ad152efbed | ||
|
|
14bbbcd571 | ||
|
|
3d39336a46 | ||
|
|
6a4afb7f8e | ||
|
|
c01f6df43e | ||
|
|
e89396809f | ||
|
|
4f3b8033f2 | ||
|
|
ebb934c1b0 | ||
|
|
2801624462 | ||
|
|
6921ca4813 | ||
|
|
59856a20bf | ||
|
|
b187cc40cd | ||
|
|
58281b4f18 | ||
|
|
672f1e0683 | ||
|
|
dd806b4ecd | ||
|
|
843f9091f2 | ||
|
|
53da0507e4 | ||
|
|
bd847f66c6 | ||
|
|
e503f6a878 | ||
|
|
6678a26d1c | ||
|
|
e37dcd726f | ||
|
|
8ddfb4d6aa | ||
|
|
28f2fe9167 | ||
|
|
3c90e96b6d | ||
|
|
46a1cd69a9 | ||
|
|
9b088ada7e | ||
|
|
0508bd8da7 | ||
|
|
446ca4b57b | ||
|
|
4f2e59f94a | ||
|
|
0a436e03b8 | ||
|
|
c66a827c98 | ||
|
|
fe99e51634 | ||
|
|
49b5c44ee9 | ||
|
|
64bc08f1c4 | ||
|
|
2d4ac06426 | ||
|
|
9749d94fb0 | ||
|
|
630ae43e7d | ||
|
|
268928ab35 | ||
|
|
4cd59b96ed | ||
|
|
055bbb79c1 | ||
|
|
19dc3b0792 | ||
|
|
20d24a450c | ||
|
|
eca861a99d | ||
|
|
d757bd0904 | ||
|
|
47838051be | ||
|
|
fa806f26af | ||
|
|
34eab42833 | ||
|
|
3c80bd76cf | ||
|
|
c983023661 | ||
|
|
6a9f4ecf9b | ||
|
|
24e02043a2 | ||
|
|
c8f885a4d0 | ||
|
|
b07314e2e0 | ||
|
|
eed00a4afd | ||
|
|
6679f3b053 | ||
|
|
70ab25a5db | ||
|
|
bcb8d3fd86 | ||
|
|
b9ca311008 | ||
|
|
37186d9ef4 | ||
|
|
03a28da5f7 | ||
|
|
b44d113120 | ||
|
|
35bcba8011 | ||
|
|
f2bc7ebf1e | ||
|
|
e5239b9859 | ||
|
|
8a2fd914b2 | ||
|
|
f3d51bb3d5 | ||
|
|
d06a0ba1a7 | ||
|
|
8374dac021 | ||
|
|
3bc73fa21e | ||
|
|
0d7044955a | ||
|
|
20a763e519 | ||
|
|
5ec2fea6dd | ||
|
|
908bb65dcf | ||
|
|
d368c2dee9 | ||
|
|
82dd417ef6 | ||
|
|
b57d7dca9d | ||
|
|
8e41181591 | ||
|
|
fdf1fccd3e | ||
|
|
bf6e241d97 | ||
|
|
f0cdbaa6c8 | ||
|
|
e0d6bae1eb | ||
|
|
d6ff912917 | ||
|
|
ad04cacd28 | ||
|
|
62823cfb97 | ||
|
|
9ae5054c34 | ||
|
|
8878943ac9 | ||
|
|
8e3844cd34 | ||
|
|
1a32c4a1df | ||
|
|
98edd0e751 | ||
|
|
98a1314251 | ||
|
|
34edbd4f7e | ||
|
|
2ea52dddec | ||
|
|
396274fa6d | ||
|
|
28e75b23b3 | ||
|
|
ad5796de9f | ||
|
|
3a6868bc2f | ||
|
|
34145f9840 | ||
|
|
58ea690b41 | ||
|
|
3b92ab3465 | ||
|
|
76f97a64fa | ||
|
|
5974794942 | ||
|
|
368d0385e1 | ||
|
|
58ffd03bf1 | ||
|
|
9ce1d02ef6 | ||
|
|
251d1261b0 | ||
|
|
a0a5170991 | ||
|
|
b3a70d767d | ||
|
|
91ee679549 | ||
|
|
52fe452497 | ||
|
|
846524115b | ||
|
|
fac4cedcc1 | ||
|
|
ded973219e | ||
|
|
dd4aab8411 | ||
|
|
d04beb7f43 | ||
|
|
a2603477dd | ||
|
|
ad322d7cca | ||
|
|
da3bb9a7c6 | ||
|
|
0e6ee9632c | ||
|
|
fcee6056dc | ||
|
|
ea813d8593 | ||
|
|
d13c2ed24e | ||
|
|
75df4953f4 | ||
|
|
f703b8b839 | ||
|
|
6380b42edb | ||
|
|
ce53e21ea6 | ||
|
|
35fef275b3 | ||
|
|
fa3266efa5 | ||
|
|
113bb396cd | ||
|
|
1fca99ad90 | ||
|
|
e42d3a1bfa | ||
|
|
3528990c73 | ||
|
|
8ee4473d92 | ||
|
|
212b582362 | ||
|
|
969e48dd5a | ||
|
|
d9b7ec6458 | ||
|
|
46c9c5c843 | ||
|
|
7a9c711832 | ||
|
|
6756e786ac | ||
|
|
84431ec03c | ||
|
|
d605e82bad | ||
|
|
2ea19238ff | ||
|
|
df36f9db05 | ||
|
|
74982bda32 | ||
|
|
6d769f4b9b | ||
|
|
1a8a540209 | ||
|
|
6c206f7d78 | ||
|
|
110d3b7794 | ||
|
|
c0e95fa68a | ||
|
|
e3b58d3027 | ||
|
|
9b029a0854 | ||
|
|
49d35cc0ae | ||
|
|
c6702bebe1 | ||
|
|
690c3839fd | ||
|
|
050cf70136 | ||
|
|
df8f95ac74 | ||
|
|
0f91418b26 | ||
|
|
4ff649ce85 | ||
|
|
c4394decf8 | ||
|
|
f159f4710b | ||
|
|
740a97f1a8 | ||
|
|
eee6d3dae9 | ||
|
|
559724ac35 | ||
|
|
3f141e1fd3 | ||
|
|
9f8e5a93b4 | ||
|
|
78d995bbd6 | ||
|
|
e7ee2f443a | ||
|
|
b8a8962833 | ||
|
|
b54ba5095b | ||
|
|
f18889bf67 | ||
|
|
6e4a818ee6 | ||
|
|
2357fecc92 | ||
|
|
23dc52f528 | ||
|
|
c3154fdf4d | ||
|
|
f1d0625cf8 | ||
|
|
230dc07d37 | ||
|
|
c46d9933ec | ||
|
|
bcb081a269 | ||
|
|
57892365ef | ||
|
|
075253238d | ||
|
|
f6b56e996d | ||
|
|
18d572abb4 | ||
|
|
37a236947e | ||
|
|
c1f6914e43 | ||
|
|
b1eae313ad | ||
|
|
a359e52f29 | ||
|
|
bf98fff925 | ||
|
|
e6c3da42e7 | ||
|
|
d5220bc081 | ||
|
|
66ef6c0f6e | ||
|
|
20772f90ff | ||
|
|
9a60f36ccc | ||
|
|
69a93b36d5 | ||
|
|
466a88b9a8 | ||
|
|
f0620da74a | ||
|
|
dffb72ca89 | ||
|
|
128893062b | ||
|
|
98018df078 | ||
|
|
4a3b039c2a | ||
|
|
c627ff6e20 | ||
|
|
d593f57952 | ||
|
|
c2b273df5d | ||
|
|
cc18a9c650 | ||
|
|
f861c493bf | ||
|
|
d80b50d4b4 | ||
|
|
5b84252c73 | ||
|
|
f352ec945f | ||
|
|
714e424d74 | ||
|
|
f7fa6c2b0b | ||
|
|
b866254a19 | ||
|
|
108b6c644b | ||
|
|
9ce7b1c009 | ||
|
|
076434ef10 | ||
|
|
f2807042c2 | ||
|
|
d4c7340131 | ||
|
|
331f983593 | ||
|
|
1adbadde80 | ||
|
|
843ef645d2 | ||
|
|
4440bde0eb | ||
|
|
f83d556bc0 | ||
|
|
d8a18ea3ae | ||
|
|
c864647721 | ||
|
|
2891e828bd | ||
|
|
465d5313c5 | ||
|
|
02d1d238cd | ||
|
|
1f85ed6825 | ||
|
|
ef82690144 | ||
|
|
f4432d50c3 | ||
|
|
820a3b962a | ||
|
|
99a8306898 | ||
|
|
bd16107e97 | ||
|
|
5105263dac | ||
|
|
7b5c579b77 | ||
|
|
dcfb993ac7 | ||
|
|
dd9ee039a6 | ||
|
|
15d658d36b | ||
|
|
59281d6f80 | ||
|
|
29be20f987 | ||
|
|
5a8c32dc8e | ||
|
|
3d9b4379a5 | ||
|
|
ab882da03b | ||
|
|
e2594d162e | ||
|
|
ebb9b5e85b | ||
|
|
98f9d3e81c | ||
|
|
091fb89294 | ||
|
|
db91cac44f | ||
|
|
8dd3607bd1 | ||
|
|
3ebda17f0f | ||
|
|
9149c45745 | ||
|
|
969ab9c450 | ||
|
|
588f8e1cec | ||
|
|
f753dfe6b3 | ||
|
|
d1ad0e278d | ||
|
|
72cfa3de35 | ||
|
|
3396a183c6 | ||
|
|
1c6af604e8 | ||
|
|
ccc2276469 | ||
|
|
78a71b1273 | ||
|
|
2191419f4c | ||
|
|
f4a4f1ca87 | ||
|
|
f925edd12d | ||
|
|
12715c8ddc | ||
|
|
093b85b72f | ||
|
|
326dadd224 | ||
|
|
a3510c99f1 | ||
|
|
262d57e387 | ||
|
|
551092f9c0 | ||
|
|
3f802f4a13 | ||
|
|
d6f53049c4 | ||
|
|
5e3386473a | ||
|
|
0b9c8e2860 | ||
|
|
42fe550c9e | ||
|
|
f5bd137216 | ||
|
|
348696f3fe | ||
|
|
6da071985f | ||
|
|
56e02dd0c7 | ||
|
|
e0a7013836 | ||
|
|
467dbb75f1 | ||
|
|
2f6ce27fde | ||
|
|
c349b4d56c | ||
|
|
62e84785b6 | ||
|
|
215094903a | ||
|
|
8a7c0495e0 | ||
|
|
885afebe07 | ||
|
|
e06372d6f4 | ||
|
|
b5a48eaed3 | ||
|
|
a8059059c6 | ||
|
|
c8c69a1499 | ||
|
|
0469e47674 | ||
|
|
5b630d436d | ||
|
|
9c06420b18 | ||
|
|
87872006ce | ||
|
|
abfa7a204d | ||
|
|
5b0eaef602 | ||
|
|
9694fb85d7 | ||
|
|
bfee2c726e | ||
|
|
ab4fb9bbfa | ||
|
|
fbd5b20c38 | ||
|
|
ff30eb96b6 | ||
|
|
749a7d0e4f | ||
|
|
d9f769930b | ||
|
|
d750060f0c | ||
|
|
bdbac9f7a1 | ||
|
|
13201775de | ||
|
|
ccc3969536 | ||
|
|
89ee524229 | ||
|
|
9fce6f662a | ||
|
|
9087ef9a77 | ||
|
|
aa2ab5143b | ||
|
|
2c147dd721 | ||
|
|
ee64e099e0 | ||
|
|
30726c3785 | ||
|
|
41973d41e9 | ||
|
|
f69c465231 | ||
|
|
75f4fd978d | ||
|
|
e9a1246527 | ||
|
|
d627ff9697 | ||
|
|
04c16f347b | ||
|
|
9c829cb5b4 | ||
|
|
83acd37161 | ||
|
|
b21f898620 | ||
|
|
646afab28d | ||
|
|
f6653c3fa5 | ||
|
|
18962d0ff3 | ||
|
|
b22c973110 | ||
|
|
319988336c | ||
|
|
67c9ce6dd1 | ||
|
|
687d27ab57 | ||
|
|
6aff117164 | ||
|
|
e69f714219 | ||
|
|
276d2bbf1d | ||
|
|
e6affb1b1a | ||
|
|
f409c11916 | ||
|
|
3885ee00c5 | ||
|
|
5325703c27 | ||
|
|
45543d012e | ||
|
|
8a18999d23 | ||
|
|
07a887032a | ||
|
|
1843a71911 | ||
|
|
9ce577782c | ||
|
|
422d4afdd5 | ||
|
|
09a08e0a9f | ||
|
|
5142e83d93 | ||
|
|
0418702cfc | ||
|
|
674e5c8503 | ||
|
|
2ec141da54 | ||
|
|
20b1e19641 | ||
|
|
f11fb706f6 | ||
|
|
bca19a22c5 | ||
|
|
62b45f0827 | ||
|
|
92a2b635a3 | ||
|
|
d7979ef2d0 | ||
|
|
c5d8844d80 | ||
|
|
c7cda86e84 | ||
|
|
4caa604793 | ||
|
|
7f9ba14b18 | ||
|
|
72660a1a2f | ||
|
|
d4eab77f0c | ||
|
|
9708597b0b | ||
|
|
15bc2240ac | ||
|
|
631c449183 | ||
|
|
84a0274885 | ||
|
|
5ad8840024 | ||
|
|
8dfc47307d | ||
|
|
aae04def7b | ||
|
|
f51eb0e4b3 | ||
|
|
8b6b187a8d | ||
|
|
8b26e4ea3c | ||
|
|
1d8562b290 | ||
|
|
9662f9e56a | ||
|
|
25d71fb01b | ||
|
|
12ffb522a6 | ||
|
|
0077678844 | ||
|
|
53f2c5d6e8 | ||
|
|
49597f0f52 | ||
|
|
c84d74df8c | ||
|
|
3c9f9945c9 | ||
|
|
80ebff0fa4 | ||
|
|
62c0f433fa | ||
|
|
44eb1b3892 | ||
|
|
4cb57a5438 | ||
|
|
2e5642452b | ||
|
|
4947e32acb | ||
|
|
6c87db97a6 | ||
|
|
f127c471a1 | ||
|
|
280901e5fb | ||
|
|
f14db49346 | ||
|
|
f1cdba2937 | ||
|
|
0794f0b518 | ||
|
|
e2409ad337 | ||
|
|
ca92bc7798 | ||
|
|
e4f35dd4cf | ||
|
|
2cebe09924 | ||
|
|
e5f1b6b9a4 | ||
|
|
79fc90b646 | ||
|
|
fb7c4214ce | ||
|
|
06a092bdb5 | ||
|
|
5d25f3232c | ||
|
|
1a1c89556f | ||
|
|
05219d6b52 | ||
|
|
9cc3d7a18b | ||
|
|
e09863fedb | ||
|
|
2ba1300773 | ||
|
|
6cb908bb82 | ||
|
|
5ee3c58d25 | ||
|
|
c3773740d9 | ||
|
|
0ca133dd76 | ||
|
|
68934878f1 | ||
|
|
ef1d1aefa7 | ||
|
|
c015d26e96 | ||
|
|
f6760fca88 | ||
|
|
ec61c46bf7 | ||
|
|
90cb66f08d | ||
|
|
999a8d7249 | ||
|
|
13acf72a3e | ||
|
|
aa213b48a4 | ||
|
|
9b2a5964fc | ||
|
|
1110bb8e98 | ||
|
|
168e2f8c49 | ||
|
|
86ef6422f3 | ||
|
|
3af60bf375 | ||
|
|
123c80467b | ||
|
|
edba1af304 | ||
|
|
875e16c11b | ||
|
|
703905d7ec | ||
|
|
d52c149075 | ||
|
|
3f95d1b9bf | ||
|
|
def9598ed9 | ||
|
|
529ee848da | ||
|
|
64b817a5c1 | ||
|
|
02b8d14bdd | ||
|
|
025c759e44 | ||
|
|
940d58806c | ||
|
|
a2fb870ce3 | ||
|
|
e737856a7f | ||
|
|
ae1909b482 | ||
|
|
d75282eb14 | ||
|
|
cd9886f0a8 | ||
|
|
a43bae4c0b | ||
|
|
91ae135896 | ||
|
|
57b49efc98 | ||
|
|
3bd73a9633 | ||
|
|
e3acbff2ed | ||
|
|
d6e5c2c276 | ||
|
|
4dc04d7690 | ||
|
|
3e12349831 | ||
|
|
7c50221de5 | ||
|
|
3d63087f78 | ||
|
|
1408f08c40 | ||
|
|
3b23f02229 | ||
|
|
fd5099c9fe | ||
|
|
68b09cbe3d | ||
|
|
92cd2f5bad | ||
|
|
8d1cd63dfa | ||
|
|
2c4c10fb4a | ||
|
|
722d4e916a | ||
|
|
4ff649a4ea | ||
|
|
641ddaeb03 | ||
|
|
2ba5c91547 | ||
|
|
25e7227c81 | ||
|
|
04cd0a392b | ||
|
|
db9d68c3e4 | ||
|
|
88cb9f3116 | ||
|
|
55f9610cde | ||
|
|
6178dc7f1b | ||
|
|
b8f8f9d07e | ||
|
|
1643943402 | ||
|
|
e99a99eb6e | ||
|
|
df9712f1c8 | ||
|
|
28d38620f0 | ||
|
|
1ce9b3ca9c | ||
|
|
5c56b597a9 | ||
|
|
f712e10cb2 | ||
|
|
213365c2d2 | ||
|
|
7f02bd3b7a | ||
|
|
ceb33818cd | ||
|
|
18fc707fdf | ||
|
|
c3027fa9ac | ||
|
|
4249867e5b | ||
|
|
c804a5f827 | ||
|
|
be77ee33bc | ||
|
|
5928ed5d45 | ||
|
|
075d30dbce | ||
|
|
6a6a2ad8a4 | ||
|
|
279fe144e1 | ||
|
|
cc80bd41c4 | ||
|
|
06183e6cdc | ||
|
|
6249cc3373 | ||
|
|
80f34c6aeb | ||
|
|
a2f526dadc | ||
|
|
429d2f85cb | ||
|
|
2ca018b2eb | ||
|
|
3e6e08ce00 | ||
|
|
3e491f8698 | ||
|
|
ccffa69766 | ||
|
|
10190be5d7 | ||
|
|
65a4e30825 | ||
|
|
e63960caae | ||
|
|
e2ca600fd8 | ||
|
|
4860df1689 | ||
|
|
ced93bcabd | ||
|
|
0e21de9a25 | ||
|
|
3104fc8d33 | ||
|
|
2409df9285 | ||
|
|
2e37be973f | ||
|
|
6115348dd9 | ||
|
|
416d098688 | ||
|
|
6bbe66d2e6 | ||
|
|
3782e34e67 | ||
|
|
84790aafd8 | ||
|
|
fea2d5f2fe | ||
|
|
9f1c9686e0 | ||
|
|
e0a6f27d1b | ||
|
|
9130ee7513 | ||
|
|
65c8e9242c | ||
|
|
1d654f6156 | ||
|
|
4af24e11a4 | ||
|
|
583f5868c9 | ||
|
|
d94b186080 | ||
|
|
5b8cfbe15c | ||
|
|
0dbc51f4d2 | ||
|
|
754ed9043d | ||
|
|
ba17f4a06a | ||
|
|
b9149f45bf | ||
|
|
b6c4b325a4 | ||
|
|
965de6ef50 | ||
|
|
303490168f | ||
|
|
c7c2399be9 | ||
|
|
120a520a22 | ||
|
|
7c03bd1e7a | ||
|
|
049d28868e | ||
|
|
8934f13615 | ||
|
|
dcf9dfb129 | ||
|
|
7c9604e32b | ||
|
|
b302ae329c | ||
|
|
ff6b6f2ce1 | ||
|
|
d49f141fb3 | ||
|
|
590fc58de7 | ||
|
|
c03561eea8 | ||
|
|
4179f25286 | ||
|
|
e54e8fa920 | ||
|
|
2f1c05d997 | ||
|
|
baa4618e57 | ||
|
|
dcc1e3562f | ||
|
|
f6fa353dd8 | ||
|
|
d4fa619ed1 | ||
|
|
8a851af5e6 | ||
|
|
8aa9985ad0 | ||
|
|
2c85b964e3 | ||
|
|
9159c819c3 | ||
|
|
484ba4a8c5 | ||
|
|
97b7b173b9 | ||
|
|
29f69211c9 | ||
|
|
553ce165c1 | ||
|
|
310ddec823 | ||
|
|
6926ba558f | ||
|
|
97d1d6f5d2 | ||
|
|
5f7abd5347 | ||
|
|
946bbee39a | ||
|
|
bdc0e8f825 | ||
|
|
1b08ab92d1 | ||
|
|
feda3db1dd | ||
|
|
ce97a71adf | ||
|
|
d00fb40967 | ||
|
|
0f249c85ea | ||
|
|
a37b42b57c | ||
|
|
dd8c59892c | ||
|
|
a97cf23355 | ||
|
|
030cc8d5cc | ||
|
|
c22f2617ad | ||
|
|
c860945be2 | ||
|
|
51d0c9238b | ||
|
|
22df1249b5 | ||
|
|
bdaa87ff21 | ||
|
|
db0ccaac9b | ||
|
|
4089a20cf4 | ||
|
|
75ac50a9a0 | ||
|
|
58e0c68132 | ||
|
|
cd6aeaf979 | ||
|
|
708cd34586 | ||
|
|
4dcc0f316c | ||
|
|
dae585c6e4 | ||
|
|
b1d994e3b9 | ||
|
|
dde8f74cea | ||
|
|
9e3d18e606 | ||
|
|
3e9575e275 | ||
|
|
07fee44559 | ||
|
|
b6bff0cbb1 | ||
|
|
ead7eb619e | ||
|
|
29de2432ea | ||
|
|
ffcba1236c | ||
|
|
0f088d28c5 | ||
|
|
16917275ee | ||
|
|
09ab2bfa1d | ||
|
|
b9f0695924 | ||
|
|
7bade49d4c | ||
|
|
3a123bc479 | ||
|
|
5bc344ab73 | ||
|
|
4bd287e107 | ||
|
|
6a56b7b391 | ||
|
|
7dac26ce69 | ||
|
|
793fd983ef | ||
|
|
2424480e2c | ||
|
|
f5a8e90d10 | ||
|
|
2e7df5182c | ||
|
|
ea2486d631 | ||
|
|
2e72882216 | ||
|
|
d1e1a8e78c | ||
|
|
ad3b091d53 | ||
|
|
26229d78f2 | ||
|
|
e0c24ccfc3 | ||
|
|
3b89d13aaf | ||
|
|
108635582f | ||
|
|
0c0077ed6f | ||
|
|
9a604acc23 | ||
|
|
cd9f7f29d1 | ||
|
|
a7068510a5 | ||
|
|
73c6d9f135 | ||
|
|
3043c26419 | ||
|
|
e66e0289ab | ||
|
|
6166380d76 | ||
|
|
e4752c8c1a | ||
|
|
99c27fa0dd | ||
|
|
d5a57a4b5e | ||
|
|
b14c251862 | ||
|
|
bcd6ca3685 | ||
|
|
16225c473f | ||
|
|
46f59dd933 | ||
|
|
e1fa989ec9 | ||
|
|
dd2f0d89bf | ||
|
|
0b57e4483a | ||
|
|
7d0b8c726c | ||
|
|
f2dc49292f | ||
|
|
a7ace535c3 | ||
|
|
c99e8de5a4 | ||
|
|
c06aa62bda | ||
|
|
9ba998312d | ||
|
|
7d68afb2d2 | ||
|
|
bfdf1839e0 | ||
|
|
5dc86d7bca | ||
|
|
f35491190a | ||
|
|
5b27652ac6 | ||
|
|
394941b6b0 | ||
|
|
0f134b4bf8 | ||
|
|
0badda9f15 | ||
|
|
e3f68b22d8 | ||
|
|
8ca7b0646e | ||
|
|
10e37198aa | ||
|
|
f7542664e3 | ||
|
|
950d0312dc | ||
|
|
c8ec36d1b9 | ||
|
|
17ffb0ac84 | ||
|
|
b2aa877bf0 | ||
|
|
bb241c10e2 | ||
|
|
3852d05990 | ||
|
|
63876e7dbd | ||
|
|
97a2dc96f2 | ||
|
|
88b6ea993d | ||
|
|
d4f7039793 | ||
|
|
bb06fe8dd9 | ||
|
|
4399f65fb8 | ||
|
|
2d85a20c71 | ||
|
|
7cc90f2bc5 | ||
|
|
01ce312c2d | ||
|
|
c01d17d77d | ||
|
|
ed0ba04da6 | ||
|
|
b15cfd3530 | ||
|
|
a438d505ba | ||
|
|
5eb590e79d | ||
|
|
bdc79ac8b2 | ||
|
|
a97d858b2a | ||
|
|
e592f1b298 | ||
|
|
513a567483 | ||
|
|
faf103e6ec | ||
|
|
e608296bc6 | ||
|
|
4ebe2cf348 | ||
|
|
f4b63d9eea | ||
|
|
422378cb85 | ||
|
|
594c818d85 | ||
|
|
d86898b014 | ||
|
|
be087c9c82 | ||
|
|
9cc8b72a38 | ||
|
|
3425c1b84c | ||
|
|
12d575a6b1 | ||
|
|
1c509f4350 | ||
|
|
48833c7b07 | ||
|
|
f385f1860b | ||
|
|
01e98bf0dd | ||
|
|
f1dd299227 | ||
|
|
7df6c4b9ad | ||
|
|
8f6b6d5784 | ||
|
|
0fc11699ab | ||
|
|
9332c00ca5 | ||
|
|
fd9ad1a194 | ||
|
|
6ae3305040 | ||
|
|
94053b4225 | ||
|
|
77ff537697 | ||
|
|
362f1735e6 | ||
|
|
7813f2a25e | ||
|
|
3781a2cc4b | ||
|
|
d47df21a33 | ||
|
|
0ac672fea6 | ||
|
|
c19fa83a8a | ||
|
|
844a9ab85e | ||
|
|
a45490243b | ||
|
|
b06f627139 | ||
|
|
cc0e091a6b | ||
|
|
8742649aa7 | ||
|
|
0e71e368a8 | ||
|
|
dfc076a123 | ||
|
|
066873ebd2 | ||
|
|
f6e1055727 | ||
|
|
6057e6ad70 | ||
|
|
ca39f15fa3 | ||
|
|
7953d1becb | ||
|
|
f4b41e1a6c | ||
|
|
4bc3328e80 | ||
|
|
ebe17f57ff | ||
|
|
ee05f97c9a | ||
|
|
78c02d038f | ||
|
|
bc823acc25 | ||
|
|
c21c5afe00 | ||
|
|
1ae54707a0 | ||
|
|
ede1e6d475 | ||
|
|
e701dce339 | ||
|
|
a93a87f64a | ||
|
|
7aba68cd54 | ||
|
|
dfc64d157a | ||
|
|
a41384ad73 | ||
|
|
ed7a4236b3 | ||
|
|
040c3b50d0 | ||
|
|
8b3519c5f7 | ||
|
|
ec559c02b8 | ||
|
|
2e4d4c9f60 | ||
|
|
b8d52ec266 | ||
|
|
b5da816487 | ||
|
|
3bae188b8d | ||
|
|
8165e51ecc | ||
|
|
9a15db21a6 | ||
|
|
58a1c5720a | ||
|
|
bc172e5e5f | ||
|
|
6745bdd0b3 | ||
|
|
5714f0a74e | ||
|
|
ce43f4af1c | ||
|
|
945033f1cc | ||
|
|
5d1609f5a2 | ||
|
|
4714f102d7 | ||
|
|
a675da65e9 | ||
|
|
e39755666b | ||
|
|
9adba5e2e6 | ||
|
|
5c1af383eb | ||
|
|
c81662eae4 | ||
|
|
8ea9ccf3a7 | ||
|
|
74a2b13687 | ||
|
|
4e7f2b757e | ||
|
|
2bba279cf1 | ||
|
|
56da77a548 | ||
|
|
494b575213 | ||
|
|
c383d59880 | ||
|
|
416fdaa3d5 | ||
|
|
2b0ebf5d32 | ||
|
|
f236e62d9d | ||
|
|
964e826a9b | ||
|
|
49673fc45c | ||
|
|
3342bdb331 | ||
|
|
1d02a7ffb6 | ||
|
|
788935175e | ||
|
|
32663bf431 | ||
|
|
df86cb9a5c | ||
|
|
e3be2e959b | ||
|
|
67f1e3f5ed | ||
|
|
23ea9b8968 | ||
|
|
921c6994b1 | ||
|
|
ea12588524 | ||
|
|
e8ad82f9ba | ||
|
|
6e2e4cad73 | ||
|
|
2e0e455fa6 | ||
|
|
d93742fe9a | ||
|
|
2e3b660dd0 | ||
|
|
0bd534adcf | ||
|
|
e59dd2c62c | ||
|
|
25be79208a | ||
|
|
2a3b91e3b6 | ||
|
|
221ee504aa | ||
|
|
64e74cefb7 | ||
|
|
eb4a0271fb | ||
|
|
cfec1c3e1b | ||
|
|
2b5386f039 | ||
|
|
a0eec14c7d | ||
|
|
54f9cdb0c3 | ||
|
|
d6fb313220 | ||
|
|
0aa2470c76 | ||
|
|
0afed3eded | ||
|
|
39ff542142 | ||
|
|
edc68f84f3 | ||
|
|
0089dd05e9 | ||
|
|
51f6c4a737 | ||
|
|
cd209f406e | ||
|
|
f4eaec3e1e | ||
|
|
b083418257 | ||
|
|
5794857f7a | ||
|
|
e7f3f6fa5a | ||
|
|
1b0fd7ead3 | ||
|
|
a926cd4d88 | ||
|
|
aa5671411b | ||
|
|
5d8efc107d | ||
|
|
f8dfd0aa5e | ||
|
|
1a226f0e28 | ||
|
|
3dbf9c6560 | ||
|
|
7c00201222 | ||
|
|
2db99441c8 | ||
|
|
de563a3ea3 | ||
|
|
9cf2b41c05 | ||
|
|
f310b875f8 | ||
|
|
ac14c463d5 | ||
|
|
578e888915 | ||
|
|
5231bf3653 | ||
|
|
8af945f353 | ||
|
|
d0e8ca1257 | ||
|
|
5a934fc923 | ||
|
|
c766d064ac | ||
|
|
0356081c0a | ||
|
|
6e8bfc8d12 | ||
|
|
18e91d5f85 | ||
|
|
48a892bee5 | ||
|
|
fb005a3da8 | ||
|
|
1004d57b85 | ||
|
|
f9e4ef5eb0 | ||
|
|
eefbadd230 | ||
|
|
bc21b3ebf0 | ||
|
|
608fb2a21e | ||
|
|
92cbb7cc80 | ||
|
|
45050d9887 | ||
|
|
75a0052e64 | ||
|
|
c8efd08384 | ||
|
|
454cd147fb | ||
|
|
e41507bde2 | ||
|
|
599f85d4e4 | ||
|
|
5756ba9bc4 | ||
|
|
193a7e1dc1 | ||
|
|
0900d3b7a6 | ||
|
|
24dd50490a | ||
|
|
5ae8c7a985 | ||
|
|
d639f61ec1 | ||
|
|
9b57f9187b | ||
|
|
50e45b485f | ||
|
|
2051ebc0eb | ||
|
|
a3b1a9f01a | ||
|
|
507cef8bce | ||
|
|
166eba3e28 | ||
|
|
080243f040 | ||
|
|
933b9d44e1 | ||
|
|
44b3e8d51b | ||
|
|
9bf8ad741f | ||
|
|
9913ebbe21 | ||
|
|
c7a48e91d8 | ||
|
|
2cbf2200ac | ||
|
|
bac5772312 | ||
|
|
a6e5a397bd | ||
|
|
364f48d6c7 | ||
|
|
4174e7aa7a | ||
|
|
eb38750d99 | ||
|
|
cd0fef633c | ||
|
|
d0c73c28df | ||
|
|
8e6c249e48 | ||
|
|
752f99e8a1 | ||
|
|
a909223ee2 | ||
|
|
8ff271fc74 | ||
|
|
9dfac1dd65 | ||
|
|
a8a6848ce0 | ||
|
|
9232d1ef62 | ||
|
|
e9011122fb | ||
|
|
90483dc912 | ||
|
|
6bdb6f226b | ||
|
|
2ac1141980 | ||
|
|
1104d443cc | ||
|
|
49044a9608 | ||
|
|
71d2ff4946 | ||
|
|
474191dd7b | ||
|
|
637eceb6a7 | ||
|
|
976428f505 | ||
|
|
affe7caf78 | ||
|
|
941e3e2ef0 | ||
|
|
b7937e268f | ||
|
|
5a411fa38e | ||
|
|
bf26ae03cf | ||
|
|
3363cd5cd0 | ||
|
|
5c49a61353 | ||
|
|
f83c31e188 | ||
|
|
8f36467107 | ||
|
|
73e79a3310 | ||
|
|
34cf976866 | ||
|
|
e832b01349 | ||
|
|
26c8eae6fe | ||
|
|
d40efc4648 | ||
|
|
5705a49308 | ||
|
|
65185a565b | ||
|
|
1bb8f60d5a | ||
|
|
1d01189f04 | ||
|
|
fc3a8e409d | ||
|
|
8e49cb453f | ||
|
|
40f1e4edbe | ||
|
|
1267e15b0f | ||
|
|
eb9fef2c42 | ||
|
|
43b346d93b | ||
|
|
d918c7d9de | ||
|
|
e962e9edcf | ||
|
|
b7a62f1f1b | ||
|
|
2e5d1a2d48 | ||
|
|
fac0d87d00 | ||
|
|
a839b36e55 | ||
|
|
316c8328aa | ||
|
|
e8db031112 | ||
|
|
59b785a282 | ||
|
|
1a1daca621 | ||
|
|
837be914ca | ||
|
|
f44eac49fa | ||
|
|
0acdef4549 | ||
|
|
7d8ef90ccb | ||
|
|
91520838fc | ||
|
|
ada0e1fb08 | ||
|
|
33d97e81eb | ||
|
|
019324015b | ||
|
|
72d278fdac | ||
|
|
05d7f85af9 | ||
|
|
7fba358ae2 | ||
|
|
9f1fc40a64 | ||
|
|
3be7bc38e0 | ||
|
|
31c66d5a00 | ||
|
|
e7d36c9590 | ||
|
|
3e8626c4a1 | ||
|
|
e14dd4d33e | ||
|
|
87a69e6753 | ||
|
|
f64dbdbe3a | ||
|
|
2b5553144a | ||
|
|
e43ef364cb | ||
|
|
08a87d4b3b | ||
|
|
90f372af5c | ||
|
|
3ec29eb5da | ||
|
|
3a20e4e15d | ||
|
|
fd97190ee7 | ||
|
|
70480ce7bc | ||
|
|
bf7d6cbb4a | ||
|
|
c059785ffb | ||
|
|
a0f5fb7394 | ||
|
|
ad33e9f388 | ||
|
|
1d1d81b0bc | ||
|
|
f3d2969560 | ||
|
|
758ea61b77 | ||
|
|
8eeff01939 | ||
|
|
4388bef996 | ||
|
|
e2b8ee2723 | ||
|
|
07dc0a5120 | ||
|
|
d3125d8570 | ||
|
|
283ebf3ff9 | ||
|
|
4c174e0bfb | ||
|
|
57a6c83547 | ||
|
|
cfc7684b7d | ||
|
|
be49f0a118 | ||
|
|
66a9d06d9f | ||
|
|
6940cf1ecd | ||
|
|
4e0cdc016a | ||
|
|
8a8109648a | ||
|
|
dc8b359319 | ||
|
|
dea29e7c99 | ||
|
|
ab6379b3e0 | ||
|
|
f7fed2ea5f | ||
|
|
35e87ee571 | ||
|
|
ab3893ff4d | ||
|
|
1277dca335 | ||
|
|
ba9aef6f2c | ||
|
|
dd619d2bd6 | ||
|
|
1e2ef274cd | ||
|
|
bcb5e36dd9 | ||
|
|
19121c16d9 | ||
|
|
27ee261e60 | ||
|
|
da3962266a | ||
|
|
e93afcdd2b | ||
|
|
dd1b9e38e9 | ||
|
|
96bc9ea7c1 | ||
|
|
16c8a10ef9 | ||
|
|
64450ae3f8 | ||
|
|
5dcd11be16 | ||
|
|
dc91a7b641 | ||
|
|
11998ae7d6 | ||
|
|
1cf9c80e97 | ||
|
|
6dbcdd3ed5 | ||
|
|
9632cf09bf | ||
|
|
96ab3c540d | ||
|
|
ff964d327d | ||
|
|
4b8688f1e5 | ||
|
|
55b5889a0f | ||
|
|
dd4c6f6a09 | ||
|
|
6058261a26 | ||
|
|
b461e4607d | ||
|
|
d399f72098 | ||
|
|
c9e1c65c64 | ||
|
|
3042f11666 | ||
|
|
e5e47c9862 | ||
|
|
1c5083315d | ||
|
|
27a137ccab | ||
|
|
7cc294e777 | ||
|
|
a20dcfb049 | ||
|
|
06b53e3fc7 | ||
|
|
8f9dd86146 | ||
|
|
ebba0a6024 | ||
|
|
c9236d99d2 | ||
|
|
f03c1b8eeb | ||
|
|
6f23e39e6b | ||
|
|
fe0378e9b3 | ||
|
|
96a1d7c645 | ||
|
|
79ee8b46f4 | ||
|
|
55a7a8b8c9 | ||
|
|
b47873c5ac | ||
|
|
adf75d402a | ||
|
|
cb1fdb2f03 | ||
|
|
d1d66b9c5f | ||
|
|
6dacbb451f | ||
|
|
ead9cefadb | ||
|
|
185a2fc55e | ||
|
|
fb8fac6c60 | ||
|
|
b6f288a1ce | ||
|
|
aa9bec96b1 | ||
|
|
11e28842ac | ||
|
|
b16ff9f859 | ||
|
|
348c5c4838 | ||
|
|
8dcc6a0280 | ||
|
|
3b5ad44647 | ||
|
|
5e029f7600 | ||
|
|
52cebe19e5 | ||
|
|
d8d33e8b8b | ||
|
|
b37f7d49d8 | ||
|
|
54da339b2c | ||
|
|
648c4f198b | ||
|
|
8589fd6db8 | ||
|
|
2e79719622 | ||
|
|
9bfec5a538 | ||
|
|
800d900688 | ||
|
|
bd144a64f6 | ||
|
|
2a20e85203 | ||
|
|
27d6777376 | ||
|
|
41cdd9b27f | ||
|
|
ec6b35240e | ||
|
|
42bcfcc927 |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -5,13 +5,15 @@ docker/docker
|
||||
a.out
|
||||
*.orig
|
||||
build_src
|
||||
command-line-arguments.test
|
||||
.flymake*
|
||||
docker.test
|
||||
auth/auth.test
|
||||
.idea
|
||||
.DS_Store
|
||||
docs/_build
|
||||
docs/_static
|
||||
docs/_templates
|
||||
.gopath/
|
||||
.dotcloud
|
||||
*.test
|
||||
bundles/
|
||||
.hg/
|
||||
.git/
|
||||
|
||||
17
.mailmap
17
.mailmap
@@ -1,4 +1,4 @@
|
||||
# Generate AUTHORS: git log --all --format='%aN <%aE>' | sort -uf | grep -v vagrant-ubuntu-12
|
||||
# Generate AUTHORS: git log --format='%aN <%aE>' | sort -uf | grep -v vagrant-ubuntu-12
|
||||
<charles.hooper@dotcloud.com> <chooper@plumata.com>
|
||||
<daniel.mizyrycki@dotcloud.com> <daniel@dotcloud.com>
|
||||
<daniel.mizyrycki@dotcloud.com> <mzdaniel@glidelink.net>
|
||||
@@ -23,3 +23,18 @@ Thatcher Peskens <thatcher@dotcloud.com>
|
||||
Walter Stanish <walter@pratyeka.org>
|
||||
<daniel@gasienica.ch> <dgasienica@zynga.com>
|
||||
Roberto Hashioka <roberto_hashioka@hotmail.com>
|
||||
Konstantin Pelykh <kpelykh@zettaset.com>
|
||||
David Sissitka <me@dsissitka.com>
|
||||
Nolan Darilek <nolan@thewordnerd.info>
|
||||
<mastahyeti@gmail.com> <mastahyeti@users.noreply.github.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
Jordan Arentsen <blissdev@gmail.com>
|
||||
Daniel Garcia <daniel@danielgarcia.info>
|
||||
Miguel Angel Fernández <elmendalerenda@gmail.com>
|
||||
Bhiraj Butala <abhiraj.butala@gmail.com>
|
||||
Faiz Khan <faizkhan00@gmail.com>
|
||||
Victor Lyuboslavsky <victor@victoreda.com>
|
||||
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
|
||||
Matthew Mueller <mattmuelle@gmail.com>
|
||||
<mosoni@ebay.com> <mohitsoni1989@gmail.com>
|
||||
Shih-Yuan Lee <fourdollars@gmail.com>
|
||||
|
||||
101
AUTHORS
101
AUTHORS
@@ -4,86 +4,185 @@
|
||||
# For a list of active project maintainers, see the MAINTAINERS file.
|
||||
#
|
||||
Al Tobey <al@ooyala.com>
|
||||
Alex Gaynor <alex.gaynor@gmail.com>
|
||||
Alexander Larsson <alexl@redhat.com>
|
||||
Alexey Shamrin <shamrin@gmail.com>
|
||||
Andrea Luzzardi <aluzzardi@gmail.com>
|
||||
Andreas Savvides <andreas@editd.com>
|
||||
Andreas Tiefenthaler <at@an-ti.eu>
|
||||
Andrew Macgregor <andrew.macgregor@agworld.com.au>
|
||||
Andrew Munsell <andrew@wizardapps.net>
|
||||
Andrews Medina <andrewsmedina@gmail.com>
|
||||
Andy Rothfusz <github@metaliveblog.com>
|
||||
Andy Smith <github@anarkystic.com>
|
||||
Anthony Bishopric <git@anthonybishopric.com>
|
||||
Antony Messerli <amesserl@rackspace.com>
|
||||
Asbjørn Enge <asbjorn@hanafjedle.net>
|
||||
Barry Allard <barry.allard@gmail.com>
|
||||
Ben Toews <mastahyeti@gmail.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
Bhiraj Butala <abhiraj.butala@gmail.com>
|
||||
Bouke Haarsma <bouke@webatoom.nl>
|
||||
Brandon Liu <bdon@bdon.org>
|
||||
Brandon Philips <brandon@ifup.co>
|
||||
Brian McCallister <brianm@skife.org>
|
||||
Brian Olsen <brian@maven-group.org>
|
||||
Brian Shumate <brian@couchbase.com>
|
||||
Briehan Lombaard <briehan.lombaard@gmail.com>
|
||||
Bruno Bigras <bigras.bruno@gmail.com>
|
||||
Caleb Spare <cespare@gmail.com>
|
||||
Calen Pennington <cale@edx.org>
|
||||
Charles Hooper <charles.hooper@dotcloud.com>
|
||||
Christopher Currie <codemonkey+github@gmail.com>
|
||||
Colin Dunklau <colin.dunklau@gmail.com>
|
||||
Colin Rice <colin@daedrum.net>
|
||||
Dan Buch <d.buch@modcloth.com>
|
||||
Daniel Garcia <daniel@danielgarcia.info>
|
||||
Daniel Gasienica <daniel@gasienica.ch>
|
||||
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com>
|
||||
Daniel Nordberg <dnordberg@gmail.com>
|
||||
Daniel Robinson <gottagetmac@gmail.com>
|
||||
Daniel Von Fange <daniel@leancoder.com>
|
||||
Daniel YC Lin <dlin.tw@gmail.com>
|
||||
David Calavera <david.calavera@gmail.com>
|
||||
David Sissitka <me@dsissitka.com>
|
||||
Deni Bertovic <deni@kset.org>
|
||||
Dominik Honnef <dominik@honnef.co>
|
||||
Don Spaulding <donspauldingii@gmail.com>
|
||||
Dr Nic Williams <drnicwilliams@gmail.com>
|
||||
Dražen Lučanin <kermit666@gmail.com>
|
||||
Elias Probst <mail@eliasprobst.eu>
|
||||
Emily Rose <emily@contactvibe.com>
|
||||
Eric Hanchrow <ehanchrow@ine.com>
|
||||
Evan Wies <evan@neomantra.net>
|
||||
Eric Myhre <hash@exultant.us>
|
||||
Erno Hopearuoho <erno.hopearuoho@gmail.com>
|
||||
Evan Phoenix <evan@fallingsnow.net>
|
||||
Evan Wies <evan@neomantra.net>
|
||||
ezbercih <cem.ezberci@gmail.com>
|
||||
Fabrizio Regini <freegenie@gmail.com>
|
||||
Faiz Khan <faizkhan00@gmail.com>
|
||||
Fareed Dudhia <fareeddudhia@googlemail.com>
|
||||
Flavio Castelli <fcastelli@suse.com>
|
||||
Francisco Souza <f@souza.cc>
|
||||
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
|
||||
Gabriel Monroy <gabriel@opdemand.com>
|
||||
Gareth Rushgrove <gareth@morethanseven.net>
|
||||
Greg Thornton <xdissent@me.com>
|
||||
Guillaume J. Charmes <guillaume.charmes@dotcloud.com>
|
||||
Guruprasad <lgp171188@gmail.com>
|
||||
Harley Laue <losinggeneration@gmail.com>
|
||||
Hector Castro <hectcastro@gmail.com>
|
||||
Hunter Blanks <hunter@twilio.com>
|
||||
Isao Jonas <isao.jonas@gmail.com>
|
||||
James Carr <james.r.carr@gmail.com>
|
||||
Jason McVetta <jason.mcvetta@gmail.com>
|
||||
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
|
||||
Jeff Lindsay <progrium@gmail.com>
|
||||
Jeremy Grosser <jeremy@synack.me>
|
||||
Jim Alateras <jima@comware.com.au>
|
||||
Jimmy Cuadra <jimmy@jimmycuadra.com>
|
||||
Joe Van Dyk <joe@tanga.com>
|
||||
Joffrey F <joffrey@dotcloud.com>
|
||||
Johan Euphrosine <proppy@google.com>
|
||||
John Costa <john.costa@gmail.com>
|
||||
Jon Wedaman <jweede@gmail.com>
|
||||
Jonas Pfenniger <jonas@pfenniger.name>
|
||||
Jonathan Mueller <j.mueller@apoveda.ch>
|
||||
Jonathan Rudenberg <jonathan@titanous.com>
|
||||
Joost Cassee <joost@cassee.net>
|
||||
Jordan Arentsen <blissdev@gmail.com>
|
||||
Joseph Anthony Pasquale Holsten <joseph@josephholsten.com>
|
||||
Julien Barbier <write0@gmail.com>
|
||||
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
|
||||
Karan Lyons <karan@karanlyons.com>
|
||||
Karl Grzeszczak <karl@karlgrz.com>
|
||||
Kawsar Saiyeed <kawsar.saiyeed@projiris.com>
|
||||
Keli Hu <dev@keli.hu>
|
||||
Ken Cochrane <kencochrane@gmail.com>
|
||||
Kevin Clark <kevin.clark@gmail.com>
|
||||
Kevin J. Lynagh <kevin@keminglabs.com>
|
||||
kim0 <email.ahmedkamal@googlemail.com>
|
||||
Kimbro Staken <kstaken@kstaken.com>
|
||||
Kiran Gangadharan <kiran.daredevil@gmail.com>
|
||||
Konstantin Pelykh <kpelykh@zettaset.com>
|
||||
Kyle Conroy <kyle.j.conroy@gmail.com>
|
||||
Laurie Voss <github@seldo.com>
|
||||
Louis Opter <kalessin@kalessin.fr>
|
||||
Manuel Meurer <manuel@krautcomputing.com>
|
||||
Marco Hennings <marco.hennings@freiheit.com>
|
||||
Marcus Farkas <toothlessgear@finitebox.com>
|
||||
Marcus Ramberg <marcus@nordaaker.com>
|
||||
Mark McGranaghan <mmcgrana@gmail.com>
|
||||
Marko Mikulicic <mmikulicic@gmail.com>
|
||||
Markus Fix <lispmeister@gmail.com>
|
||||
Martin Redmond <martin@tinychat.com>
|
||||
Matt Apperson <me@mattapperson.com>
|
||||
Matt Bachmann <bachmann.matt@gmail.com>
|
||||
Matthew Mueller <mattmuelle@gmail.com>
|
||||
Maxim Treskin <zerthurd@gmail.com>
|
||||
meejah <meejah@meejah.ca>
|
||||
Michael Crosby <crosby.michael@gmail.com>
|
||||
Michael Gorsuch <gorsuch@github.com>
|
||||
Miguel Angel Fernández <elmendalerenda@gmail.com>
|
||||
Mike Gaffney <mike@uberu.com>
|
||||
Mikhail Sobolev <mss@mawhrin.net>
|
||||
Mohit Soni <mosoni@ebay.com>
|
||||
Morten Siebuhr <sbhr@sbhr.dk>
|
||||
Nan Monnand Deng <monnand@gmail.com>
|
||||
Nate Jones <nate@endot.org>
|
||||
Nelson Chen <crazysim@gmail.com>
|
||||
Niall O'Higgins <niallo@unworkable.org>
|
||||
Nick Payne <nick@kurai.co.uk>
|
||||
Nick Stenning <nick.stenning@digital.cabinet-office.gov.uk>
|
||||
Nick Stinemates <nick@stinemates.org>
|
||||
Nolan Darilek <nolan@thewordnerd.info>
|
||||
odk- <github@odkurzacz.org>
|
||||
Pascal Borreli <pascal@borreli.com>
|
||||
Paul Bowsher <pbowsher@globalpersonals.co.uk>
|
||||
Paul Hammond <paul@paulhammond.org>
|
||||
Phil Spitler <pspitler@gmail.com>
|
||||
Piotr Bogdan <ppbogdan@gmail.com>
|
||||
pysqz <randomq@126.com>
|
||||
Ramon van Alteren <ramon@vanalteren.nl>
|
||||
Renato Riccieri Santos Zannon <renato.riccieri@gmail.com>
|
||||
Rhys Hiltner <rhys@twitch.tv>
|
||||
Robert Obryk <robryk@gmail.com>
|
||||
Roberto Hashioka <roberto_hashioka@hotmail.com>
|
||||
Ryan Fowler <rwfowler@gmail.com>
|
||||
Sam Alba <sam.alba@gmail.com>
|
||||
Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
|
||||
Sean P. Kane <skane@newrelic.com>
|
||||
Shawn Siefkas <shawn.siefkas@meredith.com>
|
||||
Shih-Yuan Lee <fourdollars@gmail.com>
|
||||
Silas Sewell <silas@sewell.org>
|
||||
Solomon Hykes <solomon@dotcloud.com>
|
||||
Song Gao <song@gao.io>
|
||||
Sridatta Thatipamala <sthatipamala@gmail.com>
|
||||
Sridhar Ratnakumar <sridharr@activestate.com>
|
||||
Steeve Morin <steeve.morin@gmail.com>
|
||||
Stefan Praszalowicz <stefan@greplin.com>
|
||||
Thatcher Peskens <thatcher@dotcloud.com>
|
||||
Thermionix <bond711@gmail.com>
|
||||
Thijs Terlouw <thijsterlouw@gmail.com>
|
||||
Thomas Bikeev <thomas.bikeev@mac.com>
|
||||
Thomas Frössman <thomasf@jossystem.se>
|
||||
Thomas Hansen <thomas.hansen@gmail.com>
|
||||
Tianon Gravi <admwiggin@gmail.com>
|
||||
Tim Terhorst <mynamewastaken+git@gmail.com>
|
||||
Tobias Bieniek <Tobias.Bieniek@gmx.de>
|
||||
Tobias Schmidt <ts@soundcloud.com>
|
||||
Tobias Schwab <tobias.schwab@dynport.de>
|
||||
Tom Hulihan <hulihan.tom159@gmail.com>
|
||||
Tommaso Visconti <tommaso.visconti@gmail.com>
|
||||
Tyler Brock <tyler.brock@gmail.com>
|
||||
unclejack <unclejacksons@gmail.com>
|
||||
Victor Coisne <victor.coisne@dotcloud.com>
|
||||
Victor Lyuboslavsky <victor@victoreda.com>
|
||||
Victor Vieux <victor.vieux@dotcloud.com>
|
||||
Vincent Bernat <bernat@luffy.cx>
|
||||
Vivek Agarwal <me@vivek.im>
|
||||
Vladimir Kirillov <proger@wilab.org.ua>
|
||||
Walter Stanish <walter@pratyeka.org>
|
||||
Wes Morgan <cap10morgan@gmail.com>
|
||||
Will Dietz <w@wdtz.org>
|
||||
Zaiste! <oh@zaiste.net>
|
||||
|
||||
259
CHANGELOG.md
259
CHANGELOG.md
@@ -1,5 +1,264 @@
|
||||
# Changelog
|
||||
|
||||
## 0.6.4 (2013-10-15)
|
||||
- Runtime: Add cleanup of container when Start() fails
|
||||
- Testing: Catch errClosing error when TCP and UDP proxies are terminated
|
||||
- Testing: Add aggregated docker-ci email report
|
||||
- Testing: Remove a few errors in tests
|
||||
* Contrib: Reorganize contributed completion scripts to add zsh completion
|
||||
* Contrib: Add vim syntax highlighting for Dockerfiles from @honza
|
||||
* Runtime: Add better comments to utils/stdcopy.go
|
||||
- Testing: add cleanup to remove leftover containers
|
||||
* Documentation: Document how to edit and release docs
|
||||
* Documentation: Add initial draft of the Docker infrastructure doc
|
||||
* Contrib: Add mkimage-arch.sh
|
||||
- Builder: Abort build if mergeConfig returns an error and fix duplicate error message
|
||||
- Runtime: Remove error messages which are not actually errors
|
||||
* Testing: Only run certain tests with TESTFLAGS='-run TestName' make.sh
|
||||
* Testing: Prevent docker-ci to test closing PRs
|
||||
- Documentation: Minor updates to postgresql_service.rst
|
||||
* Testing: Add nightly release to docker-ci
|
||||
* Hack: Improve network performance for VirtualBox
|
||||
* Hack: Add vagrant user to the docker group
|
||||
* Runtime: Add utils.Errorf for error logging
|
||||
- Packaging: Remove deprecated packaging directory
|
||||
* Hack: Revamp install.sh to be usable by more people, and to use official install methods whenever possible (apt repo, portage tree, etc.)
|
||||
- Hack: Fix contrib/mkimage-debian.sh apt caching prevention
|
||||
* Documentation: Clarify LGTM process to contributors
|
||||
- Documentation: Small fixes to parameter names in docs for ADD command
|
||||
* Runtime: Record termination time in state.
|
||||
- Registry: Use correct auth config when logging in.
|
||||
- Documentation: Corrected error in the package name
|
||||
* Documentation: Document what `vagrant up` is actually doing
|
||||
- Runtime: Fix `docker rm` with volumes
|
||||
- Runtime: Use empty string so TempDir uses the OS's temp dir automatically
|
||||
- Runtime: Make sure to close the network allocators
|
||||
* Testing: Replace panic by log.Fatal in tests
|
||||
+ Documentation: improve doc search results
|
||||
- Runtime: Fix some error cases where a HTTP body might not be closed
|
||||
* Hack: Add proper bash completion for "docker push"
|
||||
* Documentation: Add devenvironment link to CONTRIBUTING.md
|
||||
* Documentation: Cleanup whitespace in API 1.5 docs
|
||||
* Documentation: use angle brackets in MAINTAINER example email
|
||||
- Testing: Increase TestRunDetach timeout
|
||||
* Documentation: Fix help text for -v option
|
||||
+ Hack: Added Dockerfile.tmLanguage to contrib
|
||||
+ Runtime: Autorestart containers by default
|
||||
* Testing: Adding more tests around auth.ResolveAuthConfig
|
||||
* Hack: Configured FPM to make /etc/init/docker.conf a config file
|
||||
* Hack: Add xz utils as a runtime dep
|
||||
* Documentation: Add `apt-get install curl` to Ubuntu docs
|
||||
* Documentation: Remove Gentoo install notes about #1422 workaround
|
||||
* Documentation: Fix Ping endpoint documentation
|
||||
* Runtime: Bump vendor kr/pty to commit 3b1f6487b (syscall.O_NOCTTY)
|
||||
* Runtime: lxc: Allow set_file_cap capability in container
|
||||
* Documentation: Update archlinux.rst
|
||||
- Documentation: Fix ironic typo in changelog
|
||||
* Documentation: Add explanation for export restrictions
|
||||
* Hack: Add cleanup/refactor portion of #2010 for hack and Dockerfile updates
|
||||
+ Documentation: Changes to a new style for the docs. Includes version switcher.
|
||||
* Documentation: Formatting, add information about multiline json
|
||||
+ Hack: Add contrib/mkimage-centos.sh back (from #1621), and associated documentation link
|
||||
- Runtime: Fix panic with wrong dockercfg file
|
||||
- Runtime: Fix the attach behavior with -i
|
||||
* Documentation: Add .dockercfg doc
|
||||
- Runtime: Move run -rm to the cli only
|
||||
* Hack: Enable SSH Agent forwarding in Vagrant VM
|
||||
+ Runtime: Add -rm to docker run for removing a container on exit
|
||||
* Documentation: Improve registry and index REST API documentation
|
||||
* Runtime: Split stdout stderr
|
||||
- Documentation: Replace deprecated upgrading reference to docker-latest.tgz, which hasn't been updated since 0.5.3
|
||||
* Documentation: Update Gentoo installation documentation now that we're in the portage tree proper
|
||||
- Registry: Fix the error message so it is the same as the regex
|
||||
* Runtime: Always create a new session for the container
|
||||
* Hack: Add several of the small make.sh fixes from #1920, and make the output more consistent and contributor-friendly
|
||||
* Documentation: Various command fixes in postgres example
|
||||
* Documentation: Cleanup and reorganize docs and tooling for contributors and maintainers
|
||||
- Documentation: Minor spelling correction of protocoll -> protocol
|
||||
* Hack: Several small tweaks/fixes for contrib/mkimage-debian.sh
|
||||
+ Hack: Add @tianon to hack/MAINTAINERS
|
||||
|
||||
## 0.6.3 (2013-09-23)
|
||||
* Packaging: Update tar vendor dependency
|
||||
- Client: Fix detach issue
|
||||
- Runtime: Only copy and change permissions on non-bindmount volumes
|
||||
- Registry: Update regular expression to match index
|
||||
* Runtime: Allow multiple volumes-from
|
||||
* Packaging: Download apt key over HTTPS
|
||||
* Documentation: Update section on extracting the docker binary after build
|
||||
* Documentation: Update development environment docs for new build process
|
||||
* Documentation: Remove 'base' image from documentation
|
||||
* Packaging: Add 'docker' group on install for ubuntu package
|
||||
- Runtime: Fix HTTP imports from STDIN
|
||||
|
||||
## 0.6.2 (2013-09-17)
|
||||
+ Hack: Vendor all dependencies
|
||||
+ Builder: Add -rm option in order to remove intermediate containers
|
||||
+ Runtime: Add domainname support
|
||||
+ Runtime: Implement image filtering with path.Match
|
||||
* Builder: Allow multiline for the RUN instruction
|
||||
* Runtime: Remove unnecesasry warnings
|
||||
* Runtime: Only mount the hostname file when the config exists
|
||||
* Runtime: Handle signals within the `docker login` command
|
||||
* Runtime: Remove os/user dependency
|
||||
* Registry: Implement login with private registry
|
||||
* Remote API: Bump to v1.5
|
||||
* Packaging: Break down hack/make.sh into small scripts, one per 'bundle': test, binary, ubuntu etc.
|
||||
* Documentation: General improvements
|
||||
- Runtime: UID and GID are now also applied to volumes
|
||||
- Runtime: `docker start` set error code upon error
|
||||
- Runtime: `docker run` set the same error code as the process started
|
||||
- Registry: Fix push issues
|
||||
|
||||
## 0.6.1 (2013-08-23)
|
||||
* Registry: Pass "meta" headers in API calls to the registry
|
||||
- Packaging: Use correct upstart script with new build tool
|
||||
- Packaging: Use libffi-dev, don't build it from sources
|
||||
- Packaging: Removed duplicate mercurial install command
|
||||
|
||||
## 0.6.0 (2013-08-22)
|
||||
- Runtime: Load authConfig only when needed and fix useless WARNING
|
||||
+ Runtime: Add lxc-conf flag to allow custom lxc options
|
||||
- Runtime: Fix race conditions in parallel pull
|
||||
- Runtime: Improve CMD, ENTRYPOINT, and attach docs.
|
||||
* Documentation: Small fix to docs regarding adding docker groups
|
||||
* Documentation: Add MongoDB image example
|
||||
+ Builder: Add USER instruction do Dockerfile
|
||||
* Documentation: updated default -H docs
|
||||
* Remote API: Sort Images by most recent creation date.
|
||||
+ Builder: Add workdir support for the Buildfile
|
||||
+ Runtime: Add an option to set the working directory
|
||||
- Runtime: Show tag used when image is missing
|
||||
* Documentation: Update readme with dependencies for building
|
||||
* Documentation: Add instructions for creating and using the docker group
|
||||
* Remote API: Reworking opaque requests in registry module
|
||||
- Runtime: Fix Graph ByParent() to generate list of child images per parent image.
|
||||
* Runtime: Add Image name to LogEvent tests
|
||||
* Documentation: Add sudo to examples and installation to documentation
|
||||
+ Hack: Bash Completion: Limit commands to containers of a relevant state
|
||||
* Remote API: Add image name in /events
|
||||
* Runtime: Apply volumes-from before creating volumes
|
||||
- Runtime: Make docker run handle SIGINT/SIGTERM
|
||||
- Runtime: Prevent crash when .dockercfg not readable
|
||||
* Hack: Add docker dependencies coverage testing into docker-ci
|
||||
+ Runtime: Add -privileged flag and relevant tests, docs, and examples
|
||||
+ Packaging: Docker-brew 0.5.2 support and memory footprint reduction
|
||||
- Runtime: Install script should be fetched over https, not http.
|
||||
* Packaging: Add new docker dependencies into docker-ci
|
||||
* Runtime: Use Go 1.1.2 for dockerbuilder
|
||||
* Registry: Improve auth push
|
||||
* Runtime: API, issue 1471: Use groups for socket permissions
|
||||
* Documentation: PostgreSQL service example in documentation
|
||||
* Contrib: bash completion script
|
||||
* Tests: Improve TestKillDifferentUser to prevent timeout on buildbot
|
||||
* Documentation: Fix typo in docs for docker run -dns
|
||||
* Documentation: Adding a reference to ps -a
|
||||
- Runtime: Correctly detect IPv4 forwarding
|
||||
- Packaging: Revert "docker.upstart: avoid spawning a `sh` process"
|
||||
* Runtime: Use ranged for loop on channels
|
||||
- Runtime: Fix typo: fmt.Sprint -> fmt.Sprintf
|
||||
- Tests: Fix typo in TestBindMounts (runContainer called without image)
|
||||
* Runtime: add websocket support to /container/<name>/attach/ws
|
||||
* Runtime: Mount /dev/shm as a tmpfs
|
||||
- Builder: Only count known instructions as build steps
|
||||
- Builder: Fix docker build and docker events output
|
||||
- Runtime: switch from http to https for get.docker.io
|
||||
* Tests: Improve TestGetContainersTop so it does not rely on sleep
|
||||
+ Packaging: Docker-brew and Docker standard library
|
||||
* Testing: Add some tests in server and utils
|
||||
+ Packaging: Release docker with docker
|
||||
- Builder: Make sure ENV instruction within build perform a commit each time
|
||||
* Packaging: Fix the upstart script generated by get.docker.io
|
||||
- Runtime: fix small \n error un docker build
|
||||
* Runtime: Let userland proxy handle container-bound traffic
|
||||
* Runtime: Updated the Docker CLI to specify a value for the "Host" header.
|
||||
* Runtime: Add warning when net.ipv4.ip_forwarding = 0
|
||||
* Registry: Registry unit tests + mock registry
|
||||
* Runtime: fixed #910. print user name to docker info output
|
||||
- Builder: Forbid certain paths within docker build ADD
|
||||
- Runtime: change network range to avoid conflict with EC2 DNS
|
||||
* Tests: Relax the lo interface test to allow iface index != 1
|
||||
* Documentation: Suggest installing linux-headers by default.
|
||||
* Documentation: Change the twitter handle
|
||||
* Client: Add docker cp command and copy api endpoint to copy container files/folders to the host
|
||||
* Remote API: Use mime pkg to parse Content-Type
|
||||
- Runtime: Reduce connect and read timeout when pinging the registry
|
||||
* Documentation: Update amazon.rst to explain that Vagrant is not necessary for running Docker on ec2
|
||||
* Packaging: Enabled the docs to generate manpages.
|
||||
* Runtime: Parallel pull
|
||||
- Runtime: Handle ip route showing mask-less IP addresses
|
||||
* Documentation: Clarify Amazon EC2 installation
|
||||
* Documentation: 'Base' image is deprecated and should no longer be referenced in the docs.
|
||||
* Runtime: Fix to "Inject dockerinit at /.dockerinit"
|
||||
* Runtime: Allow ENTRYPOINT without CMD
|
||||
- Runtime: Always consider localhost as a domain name when parsing the FQN repos name
|
||||
* Remote API: 650 http utils and user agent field
|
||||
* Documentation: fix a typo in the ubuntu installation guide
|
||||
- Builder: Repository name (and optionally a tag) in build usage
|
||||
* Documentation: Move note about officially supported kernel
|
||||
* Packaging: Revert "Bind daemon to 0.0.0.0 in Vagrant.
|
||||
* Builder: Add no cache for docker build
|
||||
* Runtime: Add hostname to environment
|
||||
* Runtime: Add last stable version in `docker version`
|
||||
- Builder: Make sure ADD will create everything in 0755
|
||||
* Documentation: Add ufw doc
|
||||
* Tests: Add registry functional test to docker-ci
|
||||
- Documentation: Solved the logo being squished in Safari
|
||||
- Runtime: Use utils.ParseRepositoryTag instead of strings.Split(name, ":") in server.ImageDelete
|
||||
* Runtime: Refactor checksum
|
||||
- Runtime: Improve connect message with socket error
|
||||
* Documentation: Added information about Docker's high level tools over LXC.
|
||||
* Don't read from stdout when only attached to stdin
|
||||
|
||||
## 0.5.3 (2013-08-13)
|
||||
* Runtime: Use docker group for socket permissions
|
||||
- Runtime: Spawn shell within upstart script
|
||||
- Builder: Make sure ENV instruction within build perform a commit each time
|
||||
- Runtime: Handle ip route showing mask-less IP addresses
|
||||
- Runtime: Add hostname to environment
|
||||
|
||||
## 0.5.2 (2013-08-08)
|
||||
* Builder: Forbid certain paths within docker build ADD
|
||||
- Runtime: Change network range to avoid conflict with EC2 DNS
|
||||
* API: Change daemon to listen on unix socket by default
|
||||
|
||||
## 0.5.1 (2013-07-30)
|
||||
+ API: Docker client now sets useragent (RFC 2616)
|
||||
+ Runtime: Add `ps` args to `docker top`
|
||||
+ Runtime: Add support for container ID files (pidfile like)
|
||||
+ Runtime: Add container=lxc in default env
|
||||
+ Runtime: Support networkless containers with `docker run -n` and `docker -d -b=none`
|
||||
+ API: Add /events endpoint
|
||||
+ Builder: ADD command now understands URLs
|
||||
+ Builder: CmdAdd and CmdEnv now respect Dockerfile-set ENV variables
|
||||
* Hack: Simplify unit tests with helpers
|
||||
* Hack: Improve docker.upstart event
|
||||
* Hack: Add coverage testing into docker-ci
|
||||
* Runtime: Stdout/stderr logs are now stored in the same file as JSON
|
||||
* Runtime: Allocate a /16 IP range by default, with fallback to /24. Try 12 ranges instead of 3.
|
||||
* Runtime: Change .dockercfg format to json and support multiple auth remote
|
||||
- Runtime: Do not override volumes from config
|
||||
- Runtime: Fix issue with EXPOSE override
|
||||
- Builder: Create directories with 755 instead of 700 within ADD instruction
|
||||
|
||||
## 0.5.0 (2013-07-17)
|
||||
+ Runtime: List all processes running inside a container with 'docker top'
|
||||
+ Runtime: Host directories can be mounted as volumes with 'docker run -v'
|
||||
+ Runtime: Containers can expose public UDP ports (eg, '-p 123/udp')
|
||||
+ Runtime: Optionally specify an exact public port (eg. '-p 80:4500')
|
||||
+ Registry: New image naming scheme inspired by Go packaging convention allows arbitrary combinations of registries
|
||||
+ Builder: ENTRYPOINT instruction sets a default binary entry point to a container
|
||||
+ Builder: VOLUME instruction marks a part of the container as persistent data
|
||||
* Builder: 'docker build' displays the full output of a build by default
|
||||
* Runtime: 'docker login' supports additional options
|
||||
- Runtime: Dont save a container's hostname when committing an image.
|
||||
- Registry: Fix issues when uploading images to a private registry
|
||||
|
||||
## 0.4.8 (2013-07-01)
|
||||
+ Builder: New build operation ENTRYPOINT adds an executable entry point to the container.
|
||||
- Runtime: Fix a bug which caused 'docker run -d' to no longer print the container ID.
|
||||
- Tests: Fix issues in the test suite
|
||||
|
||||
## 0.4.7 (2013-06-28)
|
||||
* Registry: easier push/pull to a custom registry
|
||||
* Remote API: the progress bar updates faster when downloading and uploading large files
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
Want to hack on Docker? Awesome! Here are instructions to get you started. They are probably not perfect, please let us know if anything feels
|
||||
wrong or incomplete.
|
||||
|
||||
## Build Environment
|
||||
|
||||
For instructions on setting up your development environment, please see our dedicated [dev environment setup docs](http://docs.docker.io/en/latest/contributing/devenvironment/).
|
||||
|
||||
## Contribution guidelines
|
||||
|
||||
### Pull requests are always welcome
|
||||
@@ -23,7 +27,7 @@ that feature *on top of* docker.
|
||||
### Discuss your design on the mailing list
|
||||
|
||||
We recommend discussing your plans [on the mailing
|
||||
list](https://groups.google.com/forum/?fromgroups#!forum/docker-club)
|
||||
list](https://groups.google.com/forum/?fromgroups#!forum/docker-dev)
|
||||
before starting to code - especially for more ambitious contributions.
|
||||
This gives other contributors a chance to point you in the right
|
||||
direction, give feedback on your design, and maybe point out if someone
|
||||
@@ -55,8 +59,10 @@ Submit unit tests for your changes. Go has a great test framework built in; use
|
||||
it! Take a look at existing tests for inspiration. Run the full test suite on
|
||||
your branch before submitting a pull request.
|
||||
|
||||
Make sure you include relevant updates or additions to documentation when
|
||||
creating or modifying features.
|
||||
Update the documentation when creating or modifying features. Test
|
||||
your documentation changes for clarity, concision, and correctness, as
|
||||
well as a clean docmuent build. See ``docs/README.md`` for more
|
||||
information on building the docs and how docs get released.
|
||||
|
||||
Write clean code. Universally formatted code promotes ease of writing, reading,
|
||||
and maintenance. Always run `go fmt` before committing your changes. Most
|
||||
@@ -89,53 +95,17 @@ name and email address match your git configuration. The AUTHORS file is
|
||||
regenerated occasionally from the git commit history, so a mismatch may result
|
||||
in your changes being overwritten.
|
||||
|
||||
### Approval
|
||||
|
||||
## Decision process
|
||||
Docker maintainers use LGTM (looks good to me) in comments on the code review
|
||||
to indicate acceptance.
|
||||
|
||||
### How are decisions made?
|
||||
A change requires LGTMs from an absolute majority of the maintainers of each
|
||||
component affected. For example, if a change affects docs/ and registry/, it
|
||||
needs an absolute majority from the maintainers of docs/ AND, separately, an
|
||||
absolute majority of the maintainers of registry
|
||||
|
||||
Short answer: with pull requests to the docker repository.
|
||||
|
||||
Docker is an open-source project with an open design philosophy. This means that the repository is the source of truth for EVERY aspect of the project,
|
||||
including its philosophy, design, roadmap and APIs. *If it's part of the project, it's in the repo. It's in the repo, it's part of the project.*
|
||||
|
||||
As a result, all decisions can be expressed as changes to the repository. An implementation change is a change to the source code. An API change is a change to
|
||||
the API specification. A philosophy change is a change to the philosophy manifesto. And so on.
|
||||
|
||||
All decisions affecting docker, big and small, follow the same 3 steps:
|
||||
|
||||
* Step 1: Open a pull request. Anyone can do this.
|
||||
|
||||
* Step 2: Discuss the pull request. Anyone can do this.
|
||||
|
||||
* Step 3: Accept or refuse a pull request. The relevant maintainer does this (see below "Who decides what?")
|
||||
|
||||
|
||||
### Who decides what?
|
||||
|
||||
So all decisions are pull requests, and the relevant maintainer makes the decision by accepting or refusing the pull request.
|
||||
But how do we identify the relevant maintainer for a given pull request?
|
||||
|
||||
Docker follows the timeless, highly efficient and totally unfair system known as [Benevolent dictator for life](http://en.wikipedia.org/wiki/Benevolent_Dictator_for_Life),
|
||||
with yours truly, Solomon Hykes, in the role of BDFL.
|
||||
This means that all decisions are made by default by me. Since making every decision myself would be highly unscalable, in practice decisions are spread across multiple maintainers.
|
||||
|
||||
The relevant maintainer for a pull request is assigned in 3 steps:
|
||||
|
||||
* Step 1: Determine the subdirectory affected by the pull request. This might be src/registry, docs/source/api, or any other part of the repo.
|
||||
|
||||
* Step 2: Find the MAINTAINERS file which affects this directory. If the directory itself does not have a MAINTAINERS file, work your way up the the repo hierarchy until you find one.
|
||||
|
||||
* Step 3: The first maintainer listed is the primary maintainer. The pull request is assigned to him. He may assign it to other listed maintainers, at his discretion.
|
||||
|
||||
|
||||
### I'm a maintainer, should I make pull requests too?
|
||||
|
||||
Primary maintainers are not required to create pull requests when changing their own subdirectory, but secondary maintainers are.
|
||||
|
||||
### Who assigns maintainers?
|
||||
|
||||
Solomon.
|
||||
For more details see [MAINTAINERS.md](hack/MAINTAINERS.md)
|
||||
|
||||
### How can I become a maintainer?
|
||||
|
||||
@@ -146,15 +116,3 @@ Solomon.
|
||||
Don't forget: being a maintainer is a time investment. Make sure you will have time to make yourself available.
|
||||
You don't have to be a maintainer to make a difference on the project!
|
||||
|
||||
### What are a maintainer's responsibility?
|
||||
|
||||
It is every maintainer's responsibility to:
|
||||
|
||||
* 1) Expose a clear roadmap for improving their component.
|
||||
* 2) Deliver prompt feedback and decisions on pull requests.
|
||||
* 3) Be available to anyone with questions, bug reports, criticism etc. on their component. This includes irc, github requests and the mailing list.
|
||||
* 4) Make sure their component respects the philosophy, design and roadmap of the project.
|
||||
|
||||
### How is this process changed?
|
||||
|
||||
Just like everything else: by making a pull request :)
|
||||
|
||||
69
Dockerfile
Normal file
69
Dockerfile
Normal file
@@ -0,0 +1,69 @@
|
||||
# This file describes the standard way to build Docker, using docker
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# # Assemble the full dev environment. This is slow the first time.
|
||||
# docker build -t docker .
|
||||
# # Apparmor messes with privileged mode: disable it
|
||||
# /etc/init.d/apparmor stop ; /etc/init.d/apparmor teardown
|
||||
#
|
||||
# # Mount your source in an interactive container for quick testing:
|
||||
# docker run -v `pwd`:/go/src/github.com/dotcloud/docker -privileged -lxc-conf=lxc.aa_profile=unconfined -i -t docker bash
|
||||
#
|
||||
#
|
||||
# # Run the test suite:
|
||||
# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh test
|
||||
#
|
||||
# # Publish a release:
|
||||
# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined \
|
||||
# -e AWS_S3_BUCKET=baz \
|
||||
# -e AWS_ACCESS_KEY=foo \
|
||||
# -e AWS_SECRET_KEY=bar \
|
||||
# -e GPG_PASSPHRASE=gloubiboulga \
|
||||
# docker hack/release.sh
|
||||
#
|
||||
|
||||
docker-version 0.6.1
|
||||
from ubuntu:12.04
|
||||
maintainer Solomon Hykes <solomon@dotcloud.com>
|
||||
|
||||
# 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
|
||||
|
||||
# Install Go from source (for eventual cross-compiling)
|
||||
env CGO_ENABLED 0
|
||||
run curl -s https://go.googlecode.com/files/go1.1.2.src.tar.gz | tar -v -C / -xz && mv /go /goroot
|
||||
run cd /goroot/src && ./make.bash
|
||||
env GOROOT /goroot
|
||||
env PATH $PATH:/goroot/bin
|
||||
env GOPATH /go:/go/src/github.com/dotcloud/docker/vendor
|
||||
|
||||
# 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
|
||||
|
||||
# Install s3cmd 1.0.1 (earlier versions don't support env variables in the config)
|
||||
run apt-get install -y -q python-pip
|
||||
run pip install s3cmd
|
||||
run pip install python-magic
|
||||
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
|
||||
|
||||
volume /var/lib/docker
|
||||
workdir /go/src/github.com/dotcloud/docker
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
entrypoint ["hack/dind"]
|
||||
|
||||
# Upload docker source
|
||||
add . /go/src/github.com/dotcloud/docker
|
||||
14
FIXME
14
FIXME
@@ -15,22 +15,16 @@ to put them - so we put them here :)
|
||||
* Run linter on codebase
|
||||
* Unify build commands and regular commands
|
||||
* Move source code into src/ subdir for clarity
|
||||
* Clean up the Makefile, it's a mess
|
||||
* docker build: on non-existent local path for ADD, don't show full absolute path on the host
|
||||
* mount into /dockerinit rather than /sbin/init
|
||||
* docker tag foo REPO:TAG
|
||||
* use size header for progress bar in pull
|
||||
* Clean up context upload in build!!!
|
||||
* Parallel pull
|
||||
* Ensure /proc/sys/net/ipv4/ip_forward is 1
|
||||
* Force DNS to public!
|
||||
* Always generate a resolv.conf per container, to avoid changing resolv.conf under thne container's feet
|
||||
* Save metadata with import/export
|
||||
* Save metadata with import/export (#1974)
|
||||
* Upgrade dockerd without stopping containers
|
||||
* bring back git revision info, looks like it was lost
|
||||
* Simple command to remove all untagged images
|
||||
* Simple command to remove all untagged images (`docker rmi $(docker images | awk '/^<none>/ { print $3 }')`)
|
||||
* Simple command to clean up containers for disk space
|
||||
* Caching after an ADD
|
||||
* entry point config
|
||||
* bring back git revision info, looks like it was lost
|
||||
* Caching after an ADD (#880)
|
||||
* Clean up the ProgressReader api, it's a PITA to use
|
||||
* Use netlink instead of iproute2/iptables (#925)
|
||||
|
||||
11
MAINTAINERS
11
MAINTAINERS
@@ -1,5 +1,6 @@
|
||||
Solomon Hykes <solomon@dotcloud.com>
|
||||
Guillaume Charmes <guillaume@dotcloud.com>
|
||||
Victor Vieux <victor@dotcloud.com>
|
||||
api.go: Victor Vieux <victor@dotcloud.com>
|
||||
Vagrantfile: Daniel Mizyrycki <daniel@dotcloud.com>
|
||||
Solomon Hykes <solomon@dotcloud.com> (@shykes)
|
||||
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)
|
||||
Vagrantfile: Daniel Mizyrycki <daniel@dotcloud.com> (@mzdaniel)
|
||||
|
||||
94
Makefile
94
Makefile
@@ -1,94 +0,0 @@
|
||||
DOCKER_PACKAGE := github.com/dotcloud/docker
|
||||
RELEASE_VERSION := $(shell git tag | grep -E "v[0-9\.]+$$" | sort -nr | head -n 1)
|
||||
SRCRELEASE := docker-$(RELEASE_VERSION)
|
||||
BINRELEASE := docker-$(RELEASE_VERSION).tgz
|
||||
BUILD_SRC := build_src
|
||||
BUILD_PATH := ${BUILD_SRC}/src/${DOCKER_PACKAGE}
|
||||
|
||||
GIT_ROOT := $(shell git rev-parse --show-toplevel)
|
||||
BUILD_DIR := $(CURDIR)/.gopath
|
||||
|
||||
GOPATH ?= $(BUILD_DIR)
|
||||
export GOPATH
|
||||
|
||||
GO_OPTIONS ?=
|
||||
ifeq ($(VERBOSE), 1)
|
||||
GO_OPTIONS += -v
|
||||
endif
|
||||
|
||||
GIT_COMMIT = $(shell git rev-parse --short HEAD)
|
||||
GIT_STATUS = $(shell test -n "`git status --porcelain`" && echo "+CHANGES")
|
||||
|
||||
BUILD_OPTIONS = -a -ldflags "-X main.GITCOMMIT $(GIT_COMMIT)$(GIT_STATUS) -d -w"
|
||||
|
||||
SRC_DIR := $(GOPATH)/src
|
||||
|
||||
DOCKER_DIR := $(SRC_DIR)/$(DOCKER_PACKAGE)
|
||||
DOCKER_MAIN := $(DOCKER_DIR)/docker
|
||||
|
||||
DOCKER_BIN_RELATIVE := bin/docker
|
||||
DOCKER_BIN := $(CURDIR)/$(DOCKER_BIN_RELATIVE)
|
||||
|
||||
.PHONY: all clean test hack release srcrelease $(BINRELEASE) $(SRCRELEASE) $(DOCKER_BIN) $(DOCKER_DIR)
|
||||
|
||||
all: $(DOCKER_BIN)
|
||||
|
||||
$(DOCKER_BIN): $(DOCKER_DIR)
|
||||
@mkdir -p $(dir $@)
|
||||
@(cd $(DOCKER_MAIN); CGO_ENABLED=0 go build $(GO_OPTIONS) $(BUILD_OPTIONS) -o $@)
|
||||
@echo $(DOCKER_BIN_RELATIVE) is created.
|
||||
|
||||
$(DOCKER_DIR):
|
||||
@mkdir -p $(dir $@)
|
||||
@if [ -h $@ ]; then rm -f $@; fi; ln -sf $(CURDIR)/ $@
|
||||
@(cd $(DOCKER_MAIN); go get -d $(GO_OPTIONS))
|
||||
|
||||
whichrelease:
|
||||
echo $(RELEASE_VERSION)
|
||||
|
||||
release: $(BINRELEASE)
|
||||
s3cmd -P put $(BINRELEASE) s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-$(RELEASE_VERSION).tgz
|
||||
s3cmd -P put docker-latest.tgz s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-latest.tgz
|
||||
|
||||
srcrelease: $(SRCRELEASE)
|
||||
deps: $(DOCKER_DIR)
|
||||
|
||||
# A clean checkout of $RELEASE_VERSION, with vendored dependencies
|
||||
$(SRCRELEASE):
|
||||
rm -fr $(SRCRELEASE)
|
||||
git clone $(GIT_ROOT) $(SRCRELEASE)
|
||||
cd $(SRCRELEASE); git checkout -q $(RELEASE_VERSION)
|
||||
|
||||
# A binary release ready to be uploaded to a mirror
|
||||
$(BINRELEASE): $(SRCRELEASE)
|
||||
rm -f $(BINRELEASE)
|
||||
cd $(SRCRELEASE); make; cp -R bin docker-$(RELEASE_VERSION); tar -f ../$(BINRELEASE) -zv -c docker-$(RELEASE_VERSION)
|
||||
cd $(SRCRELEASE); cp -R bin docker-latest; tar -f ../docker-latest.tgz -zv -c docker-latest
|
||||
|
||||
clean:
|
||||
@rm -rf $(dir $(DOCKER_BIN))
|
||||
ifeq ($(GOPATH), $(BUILD_DIR))
|
||||
@rm -rf $(BUILD_DIR)
|
||||
else ifneq ($(DOCKER_DIR), $(realpath $(DOCKER_DIR)))
|
||||
@rm -f $(DOCKER_DIR)
|
||||
endif
|
||||
|
||||
test:
|
||||
# Copy docker source and dependencies for testing
|
||||
rm -rf ${BUILD_SRC}; mkdir -p ${BUILD_PATH}
|
||||
tar --exclude=${BUILD_SRC} -cz . | tar -xz -C ${BUILD_PATH}
|
||||
GOPATH=${CURDIR}/${BUILD_SRC} go get -d
|
||||
# Do the test
|
||||
sudo -E GOPATH=${CURDIR}/${BUILD_SRC} go test ${GO_OPTIONS}
|
||||
|
||||
testall: all
|
||||
@(cd $(DOCKER_DIR); sudo -E go test ./... $(GO_OPTIONS))
|
||||
|
||||
fmt:
|
||||
@gofmt -s -l -w .
|
||||
|
||||
hack:
|
||||
cd $(CURDIR)/hack && vagrant up
|
||||
|
||||
ssh-dev:
|
||||
cd $(CURDIR)/hack && vagrant ssh
|
||||
42
NOTICE
42
NOTICE
@@ -1,11 +1,43 @@
|
||||
Docker
|
||||
Copyright 2012-2013 dotCloud, inc.
|
||||
|
||||
This product includes software developed at dotCloud, inc. (http://www.dotcloud.com).
|
||||
This product includes software developed at dotCloud,
|
||||
inc. (http://www.dotcloud.com).
|
||||
|
||||
This product contains software (https://github.com/kr/pty) developed by Keith Rarick, licensed under the MIT License.
|
||||
This product contains software (https://github.com/kr/pty) developed
|
||||
by Keith Rarick, licensed under the MIT License.
|
||||
|
||||
Transfers of Docker shall be in accordance with applicable export controls of any country and all other applicable
|
||||
legal requirements. Docker shall not be distributed or downloaded to or in Cuba, Iran, North Korea, Sudan or Syria
|
||||
and shall not be distributed or downloaded to any person on the Denied Persons List administered by the U.S.
|
||||
The following is courtesy of our legal counsel:
|
||||
|
||||
Transfers of Docker shall be in accordance with applicable export
|
||||
controls of any country and all other applicable legal requirements.
|
||||
Docker shall not be distributed or downloaded to or in Cuba, Iran,
|
||||
North Korea, Sudan or Syria and shall not be distributed or downloaded
|
||||
to any person on the Denied Persons List administered by the U.S.
|
||||
Department of Commerce.
|
||||
|
||||
What does that mean?
|
||||
Here is a further explanation from our legal counsel:
|
||||
|
||||
Like all software products that utilize cryptography, the export and
|
||||
use of Docker is subject to the U.S. Commerce Department's Export
|
||||
Administration Regulations (EAR) because it uses or contains
|
||||
cryptography (see
|
||||
http://www.bis.doc.gov/index.php/policy-guidance/encryption). Certain
|
||||
free and open source software projects have a lightweight set of
|
||||
requirements, which can generally be met by providing email notice to
|
||||
the appropriate U.S. government agencies that their source code is
|
||||
available on a publicly available repository and making the
|
||||
appropriate statements in the README.
|
||||
|
||||
The restrictions of the EAR apply to certain denied locations
|
||||
(currently Iran, Sudan, Syria, North Korea, or Cuba) and those
|
||||
individuals on the Denied Persons List, which is available here:
|
||||
http://www.bis.doc.gov/index.php/policy-guidance/lists-of-parties-of-concern/denied-persons-list.
|
||||
If you are incorporating Docker into a new open source project, the
|
||||
EAR restrictions apply to your incorporation of Docker into your
|
||||
project in the same manner as other cryptography-enabled projects,
|
||||
such as OpenSSL, almost all Linux distributions, etc.
|
||||
|
||||
For more information, see http://www.apache.org/dev/crypto.html and/or
|
||||
seek legal counsel.
|
||||
|
||||
433
README.md
433
README.md
@@ -1,80 +1,129 @@
|
||||
Docker: the Linux container engine
|
||||
==================================
|
||||
|
||||
Docker is an open-source engine which automates the deployment of applications as highly portable, self-sufficient containers.
|
||||
Docker is an open source project to pack, ship and run any application
|
||||
as a lightweight container
|
||||
|
||||
Docker containers are both *hardware-agnostic* and *platform-agnostic*. This means that they can run anywhere, from your
|
||||
laptop to the largest EC2 compute instance and everything in between - and they don't require that you use a particular
|
||||
language, framework or packaging system. That makes them great building blocks for deploying and scaling web apps, databases
|
||||
and backend services without depending on a particular stack or provider.
|
||||
Docker containers are both *hardware-agnostic* and
|
||||
*platform-agnostic*. This means that they can run anywhere, from your
|
||||
laptop to the largest EC2 compute instance and everything in between -
|
||||
and they don't require that you use a particular language, framework
|
||||
or packaging system. That makes them great building blocks for
|
||||
deploying and scaling web apps, databases and backend services without
|
||||
depending on a particular stack or provider.
|
||||
|
||||
Docker is an open-source implementation of the deployment engine which powers [dotCloud](http://dotcloud.com), a popular Platform-as-a-Service.
|
||||
It benefits directly from the experience accumulated over several years of large-scale operation and support of hundreds of thousands
|
||||
of applications and databases.
|
||||
Docker is an open-source implementation of the deployment engine which
|
||||
powers [dotCloud](http://dotcloud.com), a popular
|
||||
Platform-as-a-Service. It benefits directly from the experience
|
||||
accumulated over several years of large-scale operation and support of
|
||||
hundreds of thousands of applications and databases.
|
||||
|
||||

|
||||

|
||||
|
||||
## Better than VMs
|
||||
|
||||
A common method for distributing applications and sandbox their execution is to use virtual machines, or VMs. Typical VM formats
|
||||
are VMWare's vmdk, Oracle Virtualbox's vdi, and Amazon EC2's ami. In theory these formats should allow every developer to
|
||||
automatically package their application into a "machine" for easy distribution and deployment. In practice, that almost never
|
||||
happens, for a few reasons:
|
||||
A common method for distributing applications and sandbox their
|
||||
execution is to use virtual machines, or VMs. Typical VM formats are
|
||||
VMWare's vmdk, Oracle Virtualbox's vdi, and Amazon EC2's ami. In
|
||||
theory these formats should allow every developer to automatically
|
||||
package their application into a "machine" for easy distribution and
|
||||
deployment. In practice, that almost never happens, for a few reasons:
|
||||
|
||||
* *Size*: VMs are very large which makes them impractical to store and transfer.
|
||||
* *Performance*: running VMs consumes significant CPU and memory, which makes them impractical in many scenarios, for example local development of multi-tier applications, and
|
||||
large-scale deployment of cpu and memory-intensive applications on large numbers of machines.
|
||||
* *Portability*: competing VM environments don't play well with each other. Although conversion tools do exist, they are limited and add even more overhead.
|
||||
* *Hardware-centric*: VMs were designed with machine operators in mind, not software developers. As a result, they offer very limited tooling for what developers need most:
|
||||
building, testing and running their software. For example, VMs offer no facilities for application versioning, monitoring, configuration, logging or service discovery.
|
||||
* *Size*: VMs are very large which makes them impractical to store
|
||||
and transfer.
|
||||
* *Performance*: running VMs consumes significant CPU and memory,
|
||||
which makes them impractical in many scenarios, for example local
|
||||
development of multi-tier applications, and large-scale deployment
|
||||
of cpu and memory-intensive applications on large numbers of
|
||||
machines.
|
||||
* *Portability*: competing VM environments don't play well with each
|
||||
other. Although conversion tools do exist, they are limited and
|
||||
add even more overhead.
|
||||
* *Hardware-centric*: VMs were designed with machine operators in
|
||||
mind, not software developers. As a result, they offer very
|
||||
limited tooling for what developers need most: building, testing
|
||||
and running their software. For example, VMs offer no facilities
|
||||
for application versioning, monitoring, configuration, logging or
|
||||
service discovery.
|
||||
|
||||
By contrast, Docker relies on a different sandboxing method known as *containerization*. Unlike traditional virtualization,
|
||||
containerization takes place at the kernel level. Most modern operating system kernels now support the primitives necessary
|
||||
for containerization, including Linux with [openvz](http://openvz.org), [vserver](http://linux-vserver.org) and more recently [lxc](http://lxc.sourceforge.net),
|
||||
Solaris with [zones](http://docs.oracle.com/cd/E26502_01/html/E29024/preface-1.html#scrolltoc) and FreeBSD with [Jails](http://www.freebsd.org/doc/handbook/jails.html).
|
||||
By contrast, Docker relies on a different sandboxing method known as
|
||||
*containerization*. Unlike traditional virtualization,
|
||||
containerization takes place at the kernel level. Most modern
|
||||
operating system kernels now support the primitives necessary for
|
||||
containerization, including Linux with [openvz](http://openvz.org),
|
||||
[vserver](http://linux-vserver.org) and more recently
|
||||
[lxc](http://lxc.sourceforge.net), Solaris with
|
||||
[zones](http://docs.oracle.com/cd/E26502_01/html/E29024/preface-1.html#scrolltoc)
|
||||
and FreeBSD with
|
||||
[Jails](http://www.freebsd.org/doc/handbook/jails.html).
|
||||
|
||||
Docker builds on top of these low-level primitives to offer developers a portable format and runtime environment that solves
|
||||
all 4 problems. Docker containers are small (and their transfer can be optimized with layers), they have basically zero memory and cpu overhead,
|
||||
they are completely portable and are designed from the ground up with an application-centric design.
|
||||
Docker builds on top of these low-level primitives to offer developers
|
||||
a portable format and runtime environment that solves all 4
|
||||
problems. Docker containers are small (and their transfer can be
|
||||
optimized with layers), they have basically zero memory and cpu
|
||||
overhead, they are completely portable and are designed from the
|
||||
ground up with an application-centric design.
|
||||
|
||||
The best part: because docker operates at the OS level, it can still be run inside a VM!
|
||||
The best part: because ``docker`` operates at the OS level, it can
|
||||
still be run inside a VM!
|
||||
|
||||
## Plays well with others
|
||||
|
||||
Docker does not require that you buy into a particular programming language, framework, packaging system or configuration language.
|
||||
Docker does not require that you buy into a particular programming
|
||||
language, framework, packaging system or configuration language.
|
||||
|
||||
Is your application a unix process? Does it use files, tcp connections, environment variables, standard unix streams and command-line
|
||||
arguments as inputs and outputs? Then docker can run it.
|
||||
Is your application a Unix process? Does it use files, tcp
|
||||
connections, environment variables, standard Unix streams and
|
||||
command-line arguments as inputs and outputs? Then ``docker`` can run
|
||||
it.
|
||||
|
||||
Can your application's build be expressed as a sequence of such commands? Then docker can build it.
|
||||
Can your application's build be expressed as a sequence of such
|
||||
commands? Then ``docker`` can build it.
|
||||
|
||||
|
||||
## Escape dependency hell
|
||||
|
||||
A common problem for developers is the difficulty of managing all their application's dependencies in a simple and automated way.
|
||||
A common problem for developers is the difficulty of managing all
|
||||
their application's dependencies in a simple and automated way.
|
||||
|
||||
This is usually difficult for several reasons:
|
||||
|
||||
* *Cross-platform dependencies*. Modern applications often depend on a combination of system libraries and binaries, language-specific packages, framework-specific modules,
|
||||
internal components developed for another project, etc. These dependencies live in different "worlds" and require different tools - these tools typically don't work
|
||||
well with each other, requiring awkward custom integrations.
|
||||
* *Cross-platform dependencies*. Modern applications often depend on
|
||||
a combination of system libraries and binaries, language-specific
|
||||
packages, framework-specific modules, internal components
|
||||
developed for another project, etc. These dependencies live in
|
||||
different "worlds" and require different tools - these tools
|
||||
typically don't work well with each other, requiring awkward
|
||||
custom integrations.
|
||||
|
||||
* Conflicting dependencies. Different applications may depend on different versions of the same dependency. Packaging tools handle these situations with various degrees of ease -
|
||||
but they all handle them in different and incompatible ways, which again forces the developer to do extra work.
|
||||
* Conflicting dependencies. Different applications may depend on
|
||||
different versions of the same dependency. Packaging tools handle
|
||||
these situations with various degrees of ease - but they all
|
||||
handle them in different and incompatible ways, which again forces
|
||||
the developer to do extra work.
|
||||
|
||||
* Custom dependencies. A developer may need to prepare a custom version of his application's dependency. Some packaging systems can handle custom versions of a dependency,
|
||||
others can't - and all of them handle it differently.
|
||||
* Custom dependencies. A developer may need to prepare a custom
|
||||
version of their application's dependency. Some packaging systems
|
||||
can handle custom versions of a dependency, others can't - and all
|
||||
of them handle it differently.
|
||||
|
||||
|
||||
Docker solves dependency hell by giving the developer a simple way to express *all* his application's dependencies in one place,
|
||||
and streamline the process of assembling them. If this makes you think of [XKCD 927](http://xkcd.com/927/), don't worry. Docker doesn't
|
||||
*replace* your favorite packaging systems. It simply orchestrates their use in a simple and repeatable way. How does it do that? With layers.
|
||||
Docker solves dependency hell by giving the developer a simple way to
|
||||
express *all* their application's dependencies in one place, and
|
||||
streamline the process of assembling them. If this makes you think of
|
||||
[XKCD 927](http://xkcd.com/927/), don't worry. Docker doesn't
|
||||
*replace* your favorite packaging systems. It simply orchestrates
|
||||
their use in a simple and repeatable way. How does it do that? With
|
||||
layers.
|
||||
|
||||
Docker defines a build as running a sequence of unix commands, one after the other, in the same container. Build commands modify the contents of the container
|
||||
(usually by installing new files on the filesystem), the next command modifies it some more, etc. Since each build command inherits the result of the previous
|
||||
commands, the *order* in which the commands are executed expresses *dependencies*.
|
||||
Docker defines a build as running a sequence of Unix commands, one
|
||||
after the other, in the same container. Build commands modify the
|
||||
contents of the container (usually by installing new files on the
|
||||
filesystem), the next command modifies it some more, etc. Since each
|
||||
build command inherits the result of the previous commands, the
|
||||
*order* in which the commands are executed expresses *dependencies*.
|
||||
|
||||
Here's a typical docker build process:
|
||||
Here's a typical Docker build process:
|
||||
|
||||
```bash
|
||||
from ubuntu:12.10
|
||||
@@ -87,295 +136,67 @@ run curl -L https://github.com/shykes/helloflask/archive/master.tar.gz | tar -xz
|
||||
run cd helloflask-master && pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Note that Docker doesn't care *how* dependencies are built - as long as they can be built by running a unix command in a container.
|
||||
Note that Docker doesn't care *how* dependencies are built - as long
|
||||
as they can be built by running a Unix command in a container.
|
||||
|
||||
|
||||
Install instructions
|
||||
==================
|
||||
Getting started
|
||||
===============
|
||||
|
||||
Quick install on Ubuntu 12.04 and 12.10
|
||||
---------------------------------------
|
||||
Docker can be installed on your local machine as well as servers - both bare metal and virtualized.
|
||||
It is available as a binary on most modern Linux systems, or as a VM on Windows, Mac and other systems.
|
||||
|
||||
```bash
|
||||
curl get.docker.io | sudo sh -x
|
||||
```
|
||||
We also offer an interactive tutorial for quickly learning the basics of using Docker.
|
||||
|
||||
Binary installs
|
||||
----------------
|
||||
|
||||
Docker supports the following binary installation methods.
|
||||
Note that some methods are community contributions and not yet officially supported.
|
||||
For up-to-date install instructions and online tutorials, see the [Getting Started page](http://www.docker.io/gettingstarted/).
|
||||
|
||||
* [Ubuntu 12.04 and 12.10 (officially supported)](http://docs.docker.io/en/latest/installation/ubuntulinux/)
|
||||
* [Arch Linux](http://docs.docker.io/en/latest/installation/archlinux/)
|
||||
* [Mac OS X (with Vagrant)](http://docs.docker.io/en/latest/installation/vagrant/)
|
||||
* [Windows (with Vagrant)](http://docs.docker.io/en/latest/installation/windows/)
|
||||
* [Amazon EC2 (with Vagrant)](http://docs.docker.io/en/latest/installation/amazon/)
|
||||
|
||||
Installing from source
|
||||
----------------------
|
||||
|
||||
1. Make sure you have a [Go language](http://golang.org/doc/install) compiler and [git](http://git-scm.com) installed.
|
||||
|
||||
2. Checkout the source code
|
||||
|
||||
```bash
|
||||
git clone http://github.com/dotcloud/docker
|
||||
```
|
||||
|
||||
3. Build the docker binary
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
make VERBOSE=1
|
||||
sudo cp ./bin/docker /usr/local/bin/docker
|
||||
```
|
||||
|
||||
Usage examples
|
||||
==============
|
||||
|
||||
First run the docker daemon
|
||||
---------------------------
|
||||
Docker can be used to run short-lived commands, long-running daemons (app servers, databases etc.),
|
||||
interactive shell sessions, etc.
|
||||
|
||||
All the examples assume your machine is running the docker daemon. To run the docker daemon in the background, simply type:
|
||||
|
||||
```bash
|
||||
# On a production system you want this running in an init script
|
||||
sudo docker -d &
|
||||
```
|
||||
|
||||
Now you can run docker in client mode: all commands will be forwarded to the docker daemon, so the client can run from any account.
|
||||
|
||||
```bash
|
||||
# Now you can run docker commands from any account.
|
||||
docker help
|
||||
```
|
||||
|
||||
|
||||
Throwaway shell in a base ubuntu image
|
||||
--------------------------------------
|
||||
|
||||
```bash
|
||||
docker pull ubuntu:12.10
|
||||
|
||||
# Run an interactive shell, allocate a tty, attach stdin and stdout
|
||||
# To detach the tty without exiting the shell, use the escape sequence Ctrl-p + Ctrl-q
|
||||
docker run -i -t ubuntu:12.10 /bin/bash
|
||||
```
|
||||
|
||||
Starting a long-running worker process
|
||||
--------------------------------------
|
||||
|
||||
```bash
|
||||
# Start a very useful long-running process
|
||||
JOB=$(docker run -d ubuntu /bin/sh -c "while true; do echo Hello world; sleep 1; done")
|
||||
|
||||
# Collect the output of the job so far
|
||||
docker logs $JOB
|
||||
|
||||
# Kill the job
|
||||
docker kill $JOB
|
||||
```
|
||||
|
||||
Running an irc bouncer
|
||||
----------------------
|
||||
|
||||
```bash
|
||||
BOUNCER_ID=$(docker run -d -p 6667 -u irc shykes/znc zncrun $USER $PASSWORD)
|
||||
echo "Configure your irc client to connect to port $(docker port $BOUNCER_ID 6667) of this machine"
|
||||
```
|
||||
|
||||
Running Redis
|
||||
-------------
|
||||
|
||||
```bash
|
||||
REDIS_ID=$(docker run -d -p 6379 shykes/redis redis-server)
|
||||
echo "Configure your redis client to connect to port $(docker port $REDIS_ID 6379) of this machine"
|
||||
```
|
||||
|
||||
Share your own image!
|
||||
---------------------
|
||||
|
||||
```bash
|
||||
CONTAINER=$(docker run -d ubuntu:12.10 apt-get install -y curl)
|
||||
docker commit -m "Installed curl" $CONTAINER $USER/betterbase
|
||||
docker push $USER/betterbase
|
||||
```
|
||||
|
||||
A list of publicly available images is [available here](https://github.com/dotcloud/docker/wiki/Public-docker-images).
|
||||
|
||||
Expose a service on a TCP port
|
||||
------------------------------
|
||||
|
||||
```bash
|
||||
# Expose port 4444 of this container, and tell netcat to listen on it
|
||||
JOB=$(docker run -d -p 4444 base /bin/nc -l -p 4444)
|
||||
|
||||
# Which public port is NATed to my container?
|
||||
PORT=$(docker port $JOB 4444)
|
||||
|
||||
# Connect to the public port via the host's public address
|
||||
# Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work.
|
||||
# Replace *eth0* according to your local interface name.
|
||||
IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }')
|
||||
echo hello world | nc $IP $PORT
|
||||
|
||||
# Verify that the network connection worked
|
||||
echo "Daemon received: $(docker logs $JOB)"
|
||||
```
|
||||
You can find a [list of real-world examples](http://docs.docker.io/en/latest/examples/) in the documentation.
|
||||
|
||||
Under the hood
|
||||
--------------
|
||||
|
||||
Under the hood, Docker is built on the following components:
|
||||
|
||||
|
||||
* The [cgroup](http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-24-c) and [namespacing](http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part) capabilities of the Linux kernel;
|
||||
|
||||
* [AUFS](http://aufs.sourceforge.net/aufs.html), a powerful union filesystem with copy-on-write capabilities;
|
||||
|
||||
* The
|
||||
[cgroup](http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-24-c)
|
||||
and
|
||||
[namespacing](http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part)
|
||||
capabilities of the Linux kernel;
|
||||
* [AUFS](http://aufs.sourceforge.net/aufs.html), a powerful union
|
||||
filesystem with copy-on-write capabilities;
|
||||
* The [Go](http://golang.org) programming language;
|
||||
|
||||
* [lxc](http://lxc.sourceforge.net/), a set of convenience scripts to simplify the creation of linux containers.
|
||||
* [lxc](http://lxc.sourceforge.net/), a set of convenience scripts to
|
||||
simplify the creation of Linux containers.
|
||||
|
||||
|
||||
|
||||
Contributing to Docker
|
||||
======================
|
||||
|
||||
Want to hack on Docker? Awesome! There are instructions to get you started on the website: http://docs.docker.io/en/latest/contributing/contributing/
|
||||
Want to hack on Docker? Awesome! There are instructions to get you
|
||||
started [here](CONTRIBUTING.md).
|
||||
|
||||
They are probably not perfect, please let us know if anything feels wrong or incomplete.
|
||||
They are probably not perfect, please let us know if anything feels
|
||||
wrong or incomplete.
|
||||
|
||||
|
||||
Note
|
||||
----
|
||||
|
||||
We also keep the documentation in this repository. The website documentation is generated using sphinx using these sources.
|
||||
Please find it under docs/sources/ and read more about it https://github.com/dotcloud/docker/tree/master/docs/README.md
|
||||
|
||||
Please feel free to fix / update the documentation and send us pull requests. More tutorials are also welcome.
|
||||
|
||||
|
||||
Setting up a dev environment
|
||||
----------------------------
|
||||
|
||||
Instructions that have been verified to work on Ubuntu 12.10,
|
||||
|
||||
```bash
|
||||
sudo apt-get -y install lxc curl xz-utils golang git
|
||||
|
||||
export GOPATH=~/go/
|
||||
export PATH=$GOPATH/bin:$PATH
|
||||
|
||||
mkdir -p $GOPATH/src/github.com/dotcloud
|
||||
cd $GOPATH/src/github.com/dotcloud
|
||||
git clone https://github.com/dotcloud/docker.git
|
||||
cd docker
|
||||
|
||||
go get -v github.com/dotcloud/docker/...
|
||||
go install -v github.com/dotcloud/docker/...
|
||||
```
|
||||
|
||||
Then run the docker daemon,
|
||||
|
||||
```bash
|
||||
sudo $GOPATH/bin/docker -d
|
||||
```
|
||||
|
||||
Run the `go install` command (above) to recompile docker.
|
||||
|
||||
|
||||
What is a Standard Container?
|
||||
=============================
|
||||
|
||||
Docker defines a unit of software delivery called a Standard Container. The goal of a Standard Container is to encapsulate a software component and all its dependencies in
|
||||
a format that is self-describing and portable, so that any compliant runtime can run it without extra dependencies, regardless of the underlying machine and the contents of the container.
|
||||
|
||||
The spec for Standard Containers is currently a work in progress, but it is very straightforward. It mostly defines 1) an image format, 2) a set of standard operations, and 3) an execution environment.
|
||||
|
||||
A great analogy for this is the shipping container. Just like how Standard Containers are a fundamental unit of software delivery, shipping containers (http://bricks.argz.com/ins/7823-1/12) are a fundamental unit of physical delivery.
|
||||
|
||||
### 1. STANDARD OPERATIONS
|
||||
|
||||
Just like shipping containers, Standard Containers define a set of STANDARD OPERATIONS. Shipping containers can be lifted, stacked, locked, loaded, unloaded and labelled. Similarly, standard containers can be started, stopped, copied, snapshotted, downloaded, uploaded and tagged.
|
||||
|
||||
|
||||
### 2. CONTENT-AGNOSTIC
|
||||
|
||||
Just like shipping containers, Standard Containers are CONTENT-AGNOSTIC: all standard operations have the same effect regardless of the contents. A shipping container will be stacked in exactly the same way whether it contains Vietnamese powder coffee or spare Maserati parts. Similarly, Standard Containers are started or uploaded in the same way whether they contain a postgres database, a php application with its dependencies and application server, or Java build artifacts.
|
||||
|
||||
|
||||
### 3. INFRASTRUCTURE-AGNOSTIC
|
||||
|
||||
Both types of containers are INFRASTRUCTURE-AGNOSTIC: they can be transported to thousands of facilities around the world, and manipulated by a wide variety of equipment. A shipping container can be packed in a factory in Ukraine, transported by truck to the nearest routing center, stacked onto a train, loaded into a German boat by an Australian-built crane, stored in a warehouse at a US facility, etc. Similarly, a standard container can be bundled on my laptop, uploaded to S3, downloaded, run and snapshotted by a build server at Equinix in Virginia, uploaded to 10 staging servers in a home-made Openstack cluster, then sent to 30 production instances across 3 EC2 regions.
|
||||
|
||||
|
||||
### 4. DESIGNED FOR AUTOMATION
|
||||
|
||||
Because they offer the same standard operations regardless of content and infrastructure, Standard Containers, just like their physical counterpart, are extremely well-suited for automation. In fact, you could say automation is their secret weapon.
|
||||
|
||||
Many things that once required time-consuming and error-prone human effort can now be programmed. Before shipping containers, a bag of powder coffee was hauled, dragged, dropped, rolled and stacked by 10 different people in 10 different locations by the time it reached its destination. 1 out of 50 disappeared. 1 out of 20 was damaged. The process was slow, inefficient and cost a fortune - and was entirely different depending on the facility and the type of goods.
|
||||
|
||||
Similarly, before Standard Containers, by the time a software component ran in production, it had been individually built, configured, bundled, documented, patched, vendored, templated, tweaked and instrumented by 10 different people on 10 different computers. Builds failed, libraries conflicted, mirrors crashed, post-it notes were lost, logs were misplaced, cluster updates were half-broken. The process was slow, inefficient and cost a fortune - and was entirely different depending on the language and infrastructure provider.
|
||||
|
||||
|
||||
### 5. INDUSTRIAL-GRADE DELIVERY
|
||||
|
||||
There are 17 million shipping containers in existence, packed with every physical good imaginable. Every single one of them can be loaded onto the same boats, by the same cranes, in the same facilities, and sent anywhere in the World with incredible efficiency. It is embarrassing to think that a 30 ton shipment of coffee can safely travel half-way across the World in *less time* than it takes a software team to deliver its code from one datacenter to another sitting 10 miles away.
|
||||
|
||||
With Standard Containers we can put an end to that embarrassment, by making INDUSTRIAL-GRADE DELIVERY of software a reality.
|
||||
|
||||
|
||||
|
||||
|
||||
Standard Container Specification
|
||||
--------------------------------
|
||||
|
||||
(TODO)
|
||||
|
||||
### Image format
|
||||
|
||||
|
||||
### Standard operations
|
||||
|
||||
* Copy
|
||||
* Run
|
||||
* Stop
|
||||
* Wait
|
||||
* Commit
|
||||
* Attach standard streams
|
||||
* List filesystem changes
|
||||
* ...
|
||||
|
||||
### Execution environment
|
||||
|
||||
#### Root filesystem
|
||||
|
||||
#### Environment variables
|
||||
|
||||
#### Process arguments
|
||||
|
||||
#### Networking
|
||||
|
||||
#### Process namespacing
|
||||
|
||||
#### Resource limits
|
||||
|
||||
#### Process monitoring
|
||||
|
||||
#### Logging
|
||||
|
||||
#### Signals
|
||||
|
||||
#### Pseudo-terminal allocation
|
||||
|
||||
#### Security
|
||||
|
||||
### Legal
|
||||
|
||||
Transfers of Docker shall be in accordance with applicable export controls of any country and all other applicable
|
||||
legal requirements. Docker shall not be distributed or downloaded to or in Cuba, Iran, North Korea, Sudan or Syria
|
||||
and shall not be distributed or downloaded to any person on the Denied Persons List administered by the U.S.
|
||||
*Brought to you courtesy of our legal counsel. For more context,
|
||||
please see the Notice document.*
|
||||
|
||||
Transfers of Docker shall be in accordance with applicable export
|
||||
controls of any country and all other applicable legal requirements.
|
||||
Docker shall not be distributed or downloaded to or in Cuba, Iran,
|
||||
North Korea, Sudan or Syria and shall not be distributed or downloaded
|
||||
to any person on the Denied Persons List administered by the U.S.
|
||||
Department of Commerce.
|
||||
|
||||
|
||||
46
Vagrantfile
vendored
46
Vagrantfile
vendored
@@ -12,22 +12,22 @@ Vagrant::Config.run do |config|
|
||||
# Setup virtual machine box. This VM configuration code is always executed.
|
||||
config.vm.box = BOX_NAME
|
||||
config.vm.box_url = BOX_URI
|
||||
config.vm.forward_port 4243, 4243
|
||||
|
||||
# Provision docker and new kernel if deployment was not done
|
||||
config.ssh.forward_agent = true
|
||||
|
||||
# Provision docker and new kernel if deployment was not done.
|
||||
# It is assumed Vagrant can successfully launch the provider instance.
|
||||
if Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/default/*/id").empty?
|
||||
# Add lxc-docker package
|
||||
pkg_cmd = "apt-get update -qq; apt-get install -q -y python-software-properties; " \
|
||||
"add-apt-repository -y ppa:dotcloud/lxc-docker; apt-get update -qq; " \
|
||||
"apt-get install -q -y lxc-docker; "
|
||||
# Add X.org Ubuntu backported 3.8 kernel
|
||||
pkg_cmd << "add-apt-repository -y ppa:ubuntu-x-swat/r-lts-backport; " \
|
||||
"apt-get update -qq; apt-get install -q -y linux-image-3.8.0-19-generic; "
|
||||
# Add guest additions if local vbox VM
|
||||
is_vbox = true
|
||||
ARGV.each do |arg| is_vbox &&= !arg.downcase.start_with?("--provider") end
|
||||
if is_vbox
|
||||
pkg_cmd << "apt-get install -q -y linux-headers-3.8.0-19-generic dkms; " \
|
||||
pkg_cmd = "wget -q -O - https://get.docker.io/gpg | apt-key add -;" \
|
||||
"echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list;" \
|
||||
"apt-get update -qq; apt-get install -q -y --force-yes lxc-docker; "
|
||||
# Add Ubuntu raring backported kernel
|
||||
pkg_cmd << "apt-get update -qq; apt-get install -q -y linux-image-generic-lts-raring; "
|
||||
# Add guest additions if local vbox VM. As virtualbox is the default provider,
|
||||
# it is assumed it won't be explicitly stated.
|
||||
if ENV["VAGRANT_DEFAULT_PROVIDER"].nil? && ARGV.none? { |arg| arg.downcase.start_with?("--provider") }
|
||||
pkg_cmd << "apt-get install -q -y linux-headers-generic-lts-raring dkms; " \
|
||||
"echo 'Downloading VBox Guest Additions...'; " \
|
||||
"wget -q http://dlc.sun.com.edgesuite.net/virtualbox/4.2.12/VBoxGuestAdditions_4.2.12.iso; "
|
||||
# Prepare the VM to add guest additions after reboot
|
||||
@@ -39,6 +39,8 @@ Vagrant::Config.run do |config|
|
||||
"echo 'Installation of VBox Guest Additions is proceeding in the background.'; " \
|
||||
"echo '\"vagrant reload\" can be used in about 2 minutes to activate the new guest additions.'; "
|
||||
end
|
||||
# Add vagrant user to the docker group
|
||||
pkg_cmd << "usermod -a -G docker vagrant; "
|
||||
# Activate new kernel
|
||||
pkg_cmd << "shutdown -r +1; "
|
||||
config.vm.provision :shell, :inline => pkg_cmd
|
||||
@@ -78,19 +80,21 @@ Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
config.vm.box = BOX_NAME
|
||||
config.vm.box_url = BOX_URI
|
||||
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
|
||||
vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
|
||||
end
|
||||
end
|
||||
|
||||
if !FORWARD_DOCKER_PORTS.nil?
|
||||
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
|
||||
(49000..49900).each do |port|
|
||||
config.vm.forward_port port, port
|
||||
end
|
||||
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
|
||||
(49000..49900).each do |port|
|
||||
config.vm.forward_port port, port
|
||||
end
|
||||
end
|
||||
|
||||
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
||||
(49000..49900).each do |port|
|
||||
config.vm.network :forwarded_port, :host => port, :guest => port
|
||||
end
|
||||
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
||||
(49000..49900).each do |port|
|
||||
config.vm.network :forwarded_port, :host => port, :guest => port
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
645
api.go
645
api.go
@@ -1,6 +1,8 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
@@ -9,17 +11,24 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const APIVERSION = 1.3
|
||||
const DEFAULTHTTPHOST string = "127.0.0.1"
|
||||
const DEFAULTHTTPPORT int = 4243
|
||||
const (
|
||||
APIVERSION = 1.6
|
||||
DEFAULTHTTPHOST = "127.0.0.1"
|
||||
DEFAULTHTTPPORT = 4243
|
||||
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
|
||||
)
|
||||
|
||||
type HttpApiFunc func(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error
|
||||
|
||||
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
||||
conn, _, err := w.(http.Hijacker).Hijack()
|
||||
@@ -47,26 +56,39 @@ func parseMultipartForm(r *http.Request) error {
|
||||
}
|
||||
|
||||
func httpError(w http.ResponseWriter, err error) {
|
||||
statusCode := http.StatusInternalServerError
|
||||
if strings.HasPrefix(err.Error(), "No such") {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
statusCode = http.StatusNotFound
|
||||
} else if strings.HasPrefix(err.Error(), "Bad parameter") {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
statusCode = http.StatusBadRequest
|
||||
} else if strings.HasPrefix(err.Error(), "Conflict") {
|
||||
http.Error(w, err.Error(), http.StatusConflict)
|
||||
statusCode = http.StatusConflict
|
||||
} else if strings.HasPrefix(err.Error(), "Impossible") {
|
||||
http.Error(w, err.Error(), http.StatusNotAcceptable)
|
||||
statusCode = http.StatusNotAcceptable
|
||||
} else if strings.HasPrefix(err.Error(), "Wrong login/password") {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
statusCode = http.StatusUnauthorized
|
||||
} else if strings.Contains(err.Error(), "hasn't been activated") {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
} else {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
statusCode = http.StatusForbidden
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
|
||||
http.Error(w, err.Error(), statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func writeJSON(w http.ResponseWriter, b []byte) {
|
||||
func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||
b, err := json.Marshal(v)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
w.Write(b)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getBoolParam(value string) (bool, error) {
|
||||
@@ -80,24 +102,12 @@ func getBoolParam(value string) (bool, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version > 1.1 {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return nil
|
||||
}
|
||||
authConfig, err := auth.LoadConfig(srv.runtime.root)
|
||||
func matchesContentType(contentType, expectedType string) bool {
|
||||
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
if err != auth.ErrConfigFileMissing {
|
||||
return err
|
||||
}
|
||||
authConfig = &auth.AuthConfig{}
|
||||
utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
|
||||
}
|
||||
b, err := json.Marshal(&auth.AuthConfig{Username: authConfig.Username, Email: authConfig.Email})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
return err == nil && mimetype == expectedType
|
||||
}
|
||||
|
||||
func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -106,49 +116,19 @@ func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reque
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status := ""
|
||||
if version > 1.1 {
|
||||
status, err = auth.Login(authConfig, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
localAuthConfig, err := auth.LoadConfig(srv.runtime.root)
|
||||
if err != nil {
|
||||
if err != auth.ErrConfigFileMissing {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if authConfig.Username == localAuthConfig.Username {
|
||||
authConfig.Password = localAuthConfig.Password
|
||||
}
|
||||
|
||||
newAuthConfig := auth.NewAuthConfig(authConfig.Username, authConfig.Password, authConfig.Email, srv.runtime.root)
|
||||
status, err = auth.Login(newAuthConfig, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status, err := auth.Login(authConfig, srv.HTTPRequestFactory(nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status != "" {
|
||||
b, err := json.Marshal(&APIAuth{Status: status})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
return writeJSON(w, http.StatusOK, &APIAuth{Status: status})
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
m := srv.DockerVersion()
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
return writeJSON(w, http.StatusOK, srv.DockerVersion())
|
||||
}
|
||||
|
||||
func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -170,7 +150,7 @@ func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r
|
||||
name := vars["name"]
|
||||
|
||||
if err := srv.ContainerExport(name, w); err != nil {
|
||||
utils.Debugf("%s", err.Error())
|
||||
utils.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -191,12 +171,8 @@ func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := json.Marshal(outs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
|
||||
return writeJSON(w, http.StatusOK, outs)
|
||||
}
|
||||
|
||||
func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -207,12 +183,63 @@ 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 {
|
||||
out := srv.DockerInfo()
|
||||
b, err := json.Marshal(out)
|
||||
if err != nil {
|
||||
return writeJSON(w, http.StatusOK, srv.DockerInfo())
|
||||
}
|
||||
|
||||
func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
sendEvent := func(wf *utils.WriteFlusher, event *utils.JSONMessage) error {
|
||||
b, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
return fmt.Errorf("JSON error")
|
||||
}
|
||||
_, err = wf.Write(b)
|
||||
if err != nil {
|
||||
// On error, evict the listener
|
||||
utils.Errorf("%s", err)
|
||||
srv.Lock()
|
||||
delete(srv.listeners, r.RemoteAddr)
|
||||
srv.Unlock()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
listener := make(chan utils.JSONMessage)
|
||||
srv.Lock()
|
||||
srv.listeners[r.RemoteAddr] = listener
|
||||
srv.Unlock()
|
||||
since, err := strconv.ParseInt(r.Form.Get("since"), 10, 0)
|
||||
if err != nil {
|
||||
since = 0
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
wf := utils.NewWriteFlusher(w)
|
||||
if since != 0 {
|
||||
// If since, send previous events that happened after the timestamp
|
||||
for _, event := range srv.events {
|
||||
if event.Time >= since {
|
||||
err := sendEvent(wf, &event)
|
||||
if err != nil && err.Error() == "JSON error" {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for event := range listener {
|
||||
err := sendEvent(wf, &event)
|
||||
if err != nil && err.Error() == "JSON error" {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -225,12 +252,8 @@ func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *ht
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := json.Marshal(outs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
|
||||
return writeJSON(w, http.StatusOK, outs)
|
||||
}
|
||||
|
||||
func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -242,12 +265,28 @@ func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := json.Marshal(changesStr)
|
||||
|
||||
return writeJSON(w, http.StatusOK, changesStr)
|
||||
}
|
||||
|
||||
func getContainersTop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version < 1.4 {
|
||||
return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
name := vars["name"]
|
||||
ps_args := r.Form.Get("ps_args")
|
||||
procsStr, err := srv.ContainerTop(name, ps_args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
|
||||
return writeJSON(w, http.StatusOK, procsStr)
|
||||
}
|
||||
|
||||
func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -270,12 +309,17 @@ func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *h
|
||||
}
|
||||
|
||||
outs := srv.Containers(all, size, n, since, before)
|
||||
b, err := json.Marshal(outs)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
if version < 1.5 {
|
||||
outs2 := []APIContainersOld{}
|
||||
for _, ctnr := range outs {
|
||||
outs2 = append(outs2, ctnr.ToLegacy())
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, outs2)
|
||||
} else {
|
||||
return writeJSON(w, http.StatusOK, outs)
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -305,8 +349,8 @@ func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Req
|
||||
return err
|
||||
}
|
||||
config := &Config{}
|
||||
if err := json.NewDecoder(r.Body).Decode(config); err != nil {
|
||||
utils.Debugf("%s", err.Error())
|
||||
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")
|
||||
@@ -317,13 +361,8 @@ func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Req
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := json.Marshal(&APIID{id})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
|
||||
return writeJSON(w, http.StatusCreated, &APIID{id})
|
||||
}
|
||||
|
||||
// Creates an image from Pull or from Import
|
||||
@@ -337,13 +376,28 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht
|
||||
tag := r.Form.Get("tag")
|
||||
repo := r.Form.Get("repo")
|
||||
|
||||
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||
authConfig := &auth.AuthConfig{}
|
||||
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{}
|
||||
}
|
||||
}
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
if image != "" { //pull
|
||||
registry := r.Form.Get("registry")
|
||||
if err := srv.ImagePull(image, tag, registry, w, sf, &auth.AuthConfig{}); err != nil {
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
if strings.HasPrefix(k, "X-Meta-") {
|
||||
metaHeaders[k] = v
|
||||
}
|
||||
}
|
||||
if err := srv.ImagePull(image, tag, w, sf, authConfig, metaHeaders, version > 1.3); err != nil {
|
||||
if sf.Used() {
|
||||
w.Write(sf.FormatError(err))
|
||||
return nil
|
||||
@@ -372,12 +426,8 @@ func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *htt
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := json.Marshal(outs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
|
||||
return writeJSON(w, http.StatusOK, outs)
|
||||
}
|
||||
|
||||
func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -402,31 +452,37 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
|
||||
return nil
|
||||
}
|
||||
}
|
||||
b, err := json.Marshal(&APIID{ID: imgID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
|
||||
return writeJSON(w, http.StatusOK, &APIID{ID: imgID})
|
||||
}
|
||||
|
||||
func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
authConfig := &auth.AuthConfig{}
|
||||
if version > 1.1 {
|
||||
if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
|
||||
return err
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
if strings.HasPrefix(k, "X-Meta-") {
|
||||
metaHeaders[k] = v
|
||||
}
|
||||
} else {
|
||||
localAuthConfig, err := auth.LoadConfig(srv.runtime.root)
|
||||
if err != nil && err != auth.ErrConfigFileMissing {
|
||||
return err
|
||||
}
|
||||
authConfig = localAuthConfig
|
||||
}
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
registry := r.Form.Get("registry")
|
||||
authConfig := &auth.AuthConfig{}
|
||||
|
||||
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||
if authEncoded != "" {
|
||||
// the new format is to handle the authConfig as a header
|
||||
authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
||||
// to increase compatibility to existing api it is defaulting to be empty
|
||||
authConfig = &auth.AuthConfig{}
|
||||
}
|
||||
} else {
|
||||
// the old format is supported for compatibility if there was no authConfig header
|
||||
if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
@@ -436,7 +492,7 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
if err := srv.ImagePush(name, registry, w, sf, authConfig); err != nil {
|
||||
if err := srv.ImagePush(name, w, sf, authConfig, metaHeaders); err != nil {
|
||||
if sf.Used() {
|
||||
w.Write(sf.FormatError(err))
|
||||
return nil
|
||||
@@ -454,7 +510,12 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
|
||||
return err
|
||||
}
|
||||
|
||||
if len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns() {
|
||||
resolvConf, err := utils.GetResolvConf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
|
||||
out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns))
|
||||
config.Dns = defaultDns
|
||||
}
|
||||
@@ -474,13 +535,12 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
|
||||
out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.")
|
||||
}
|
||||
|
||||
b, err := json.Marshal(out)
|
||||
if err != nil {
|
||||
return err
|
||||
if !config.NetworkDisabled && srv.runtime.capabilities.IPv4ForwardingDisabled {
|
||||
log.Println("Warning: IPv4 forwarding is disabled.")
|
||||
out.Warnings = append(out.Warnings, "IPv4 forwarding is disabled.")
|
||||
}
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
|
||||
return writeJSON(w, http.StatusCreated, out)
|
||||
}
|
||||
|
||||
func postContainersRestart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -535,12 +595,8 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R
|
||||
return err
|
||||
}
|
||||
if imgs != nil {
|
||||
if len(*imgs) != 0 {
|
||||
b, err := json.Marshal(imgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
if len(imgs) != 0 {
|
||||
return writeJSON(w, http.StatusOK, imgs)
|
||||
} else {
|
||||
return fmt.Errorf("Conflict, %s wasn't deleted", name)
|
||||
}
|
||||
@@ -551,12 +607,14 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R
|
||||
}
|
||||
|
||||
func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
hostConfig := &HostConfig{}
|
||||
|
||||
var hostConfig *HostConfig
|
||||
// allow a nil body for backwards compatibility
|
||||
if r.Body != nil {
|
||||
if err := json.NewDecoder(r.Body).Decode(hostConfig); err != nil {
|
||||
return err
|
||||
if matchesContentType(r.Header.Get("Content-Type"), "application/json") {
|
||||
hostConfig = &HostConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(hostConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,12 +659,8 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := json.Marshal(&APIWait{StatusCode: status})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
|
||||
return writeJSON(w, http.StatusOK, &APIWait{StatusCode: status})
|
||||
}
|
||||
|
||||
func postContainersResize(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -661,33 +715,91 @@ func postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
if _, err := srv.ContainerInspect(name); err != nil {
|
||||
c, err := srv.ContainerInspect(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
in, out, err := hijackServer(w)
|
||||
inStream, outStream, err := hijackServer(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if tcpc, ok := in.(*net.TCPConn); ok {
|
||||
if tcpc, ok := inStream.(*net.TCPConn); ok {
|
||||
tcpc.CloseWrite()
|
||||
} else {
|
||||
in.Close()
|
||||
inStream.Close()
|
||||
}
|
||||
}()
|
||||
defer func() {
|
||||
if tcpc, ok := out.(*net.TCPConn); ok {
|
||||
if tcpc, ok := outStream.(*net.TCPConn); ok {
|
||||
tcpc.CloseWrite()
|
||||
} else if closer, ok := out.(io.Closer); ok {
|
||||
} else if closer, ok := outStream.(io.Closer); ok {
|
||||
closer.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
fmt.Fprintf(out, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||
if err := srv.ContainerAttach(name, logs, stream, stdin, stdout, stderr, in, out); err != nil {
|
||||
fmt.Fprintf(out, "Error: %s\n", err)
|
||||
var errStream io.Writer
|
||||
|
||||
fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||
|
||||
if !c.Config.Tty && version >= 1.6 {
|
||||
errStream = utils.NewStdWriter(outStream, utils.Stderr)
|
||||
outStream = utils.NewStdWriter(outStream, utils.Stdout)
|
||||
} else {
|
||||
errStream = outStream
|
||||
}
|
||||
|
||||
if err := srv.ContainerAttach(name, logs, stream, stdin, stdout, stderr, inStream, outStream, errStream); err != nil {
|
||||
fmt.Fprintf(outStream, "Error: %s\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func wsContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
logs, err := getBoolParam(r.Form.Get("logs"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stream, err := getBoolParam(r.Form.Get("stream"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stdin, err := getBoolParam(r.Form.Get("stdin"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stdout, err := getBoolParam(r.Form.Get("stdout"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stderr, err := getBoolParam(r.Form.Get("stderr"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
if _, err := srv.ContainerInspect(name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h := websocket.Handler(func(ws *websocket.Conn) {
|
||||
defer ws.Close()
|
||||
|
||||
if err := srv.ContainerAttach(name, logs, stream, stdin, stdout, stderr, ws, ws, ws); err != nil {
|
||||
utils.Errorf("Error: %s", err)
|
||||
}
|
||||
})
|
||||
h.ServeHTTP(w, r)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -701,12 +813,13 @@ func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := json.Marshal(container)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
_, err = srv.ImageInspect(name)
|
||||
if err == nil {
|
||||
return fmt.Errorf("Conflict between containers and images")
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
|
||||
return writeJSON(w, http.StatusOK, container)
|
||||
}
|
||||
|
||||
func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -719,35 +832,13 @@ func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *htt
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := json.Marshal(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
apiConfig := &APIImageConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
|
||||
return err
|
||||
_, err = srv.ContainerInspect(name)
|
||||
if err == nil {
|
||||
return fmt.Errorf("Conflict between containers and images")
|
||||
}
|
||||
|
||||
image, err := srv.ImageGetCached(apiConfig.ID, apiConfig.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if image == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return nil
|
||||
}
|
||||
apiID := &APIID{ID: image.ID}
|
||||
b, err := json.Marshal(apiID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeJSON(w, b)
|
||||
return nil
|
||||
return writeJSON(w, http.StatusOK, image)
|
||||
}
|
||||
|
||||
func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -756,12 +847,10 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
remoteURL := r.FormValue("remote")
|
||||
repoName := r.FormValue("t")
|
||||
tag := ""
|
||||
if strings.Contains(repoName, ":") {
|
||||
remoteParts := strings.Split(repoName, ":")
|
||||
tag = remoteParts[1]
|
||||
repoName = remoteParts[0]
|
||||
}
|
||||
rawSuppressOutput := r.FormValue("q")
|
||||
rawNoCache := r.FormValue("nocache")
|
||||
rawRm := r.FormValue("rm")
|
||||
repoName, tag := utils.ParseRepositoryTag(repoName)
|
||||
|
||||
var context io.Reader
|
||||
|
||||
@@ -802,11 +891,24 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
context = c
|
||||
}
|
||||
b := NewBuildFile(srv, utils.NewWriteFlusher(w))
|
||||
|
||||
suppressOutput, err := getBoolParam(rawSuppressOutput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
noCache, err := getBoolParam(rawNoCache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rm, err := getBoolParam(rawRm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput, !noCache, rm)
|
||||
id, err := b.Build(context)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "Error build: %s\n", err)
|
||||
return err
|
||||
return fmt.Errorf("Error build: %s", err)
|
||||
}
|
||||
if repoName != "" {
|
||||
srv.runtime.repositories.Set(repoName, tag, id, false)
|
||||
@@ -814,6 +916,36 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
|
||||
return nil
|
||||
}
|
||||
|
||||
func postContainersCopy(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
copyData := &APICopy{}
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
if contentType == "application/json" {
|
||||
if err := json.NewDecoder(r.Body).Decode(copyData); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Content-Type not supported: %s", contentType)
|
||||
}
|
||||
|
||||
if copyData.Resource == "" {
|
||||
return fmt.Errorf("Resource cannot be empty")
|
||||
}
|
||||
if copyData.Resource[0] == '/' {
|
||||
copyData.Resource = copyData.Resource[1:]
|
||||
}
|
||||
|
||||
if err := srv.ContainerCopy(name, copyData.Resource, w); err != nil {
|
||||
utils.Errorf("%s", err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func optionsHandler(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return nil
|
||||
@@ -824,24 +956,61 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
|
||||
}
|
||||
|
||||
func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// log the request
|
||||
utils.Debugf("Calling %s %s", localMethod, localRoute)
|
||||
|
||||
if logging {
|
||||
log.Println(r.Method, r.RequestURI)
|
||||
}
|
||||
|
||||
if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
|
||||
userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
|
||||
if len(userAgent) == 2 && userAgent[1] != VERSION {
|
||||
utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION)
|
||||
}
|
||||
}
|
||||
version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
|
||||
if err != nil {
|
||||
version = APIVERSION
|
||||
}
|
||||
if srv.enableCors {
|
||||
writeCorsHeaders(w, r)
|
||||
}
|
||||
|
||||
if version == 0 || version > APIVERSION {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if err := handlerFunc(srv, version, w, r, mux.Vars(r)); err != nil {
|
||||
utils.Errorf("Error: %s", err)
|
||||
httpError(w, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
||||
r := mux.NewRouter()
|
||||
|
||||
m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{
|
||||
m := map[string]map[string]HttpApiFunc{
|
||||
"GET": {
|
||||
"/auth": getAuth,
|
||||
"/version": getVersion,
|
||||
"/info": getInfo,
|
||||
"/images/json": getImagesJSON,
|
||||
"/images/viz": getImagesViz,
|
||||
"/images/search": getImagesSearch,
|
||||
"/images/{name:.*}/history": getImagesHistory,
|
||||
"/images/{name:.*}/json": getImagesByName,
|
||||
"/containers/ps": getContainersJSON,
|
||||
"/containers/json": getContainersJSON,
|
||||
"/containers/{name:.*}/export": getContainersExport,
|
||||
"/containers/{name:.*}/changes": getContainersChanges,
|
||||
"/containers/{name:.*}/json": getContainersByName,
|
||||
"/events": getEvents,
|
||||
"/info": getInfo,
|
||||
"/version": getVersion,
|
||||
"/images/json": getImagesJSON,
|
||||
"/images/viz": getImagesViz,
|
||||
"/images/search": getImagesSearch,
|
||||
"/images/{name:.*}/history": getImagesHistory,
|
||||
"/images/{name:.*}/json": getImagesByName,
|
||||
"/containers/ps": getContainersJSON,
|
||||
"/containers/json": getContainersJSON,
|
||||
"/containers/{name:.*}/export": getContainersExport,
|
||||
"/containers/{name:.*}/changes": getContainersChanges,
|
||||
"/containers/{name:.*}/json": getContainersByName,
|
||||
"/containers/{name:.*}/top": getContainersTop,
|
||||
"/containers/{name:.*}/attach/ws": wsContainersAttach,
|
||||
},
|
||||
"POST": {
|
||||
"/auth": postAuth,
|
||||
@@ -851,7 +1020,6 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
||||
"/images/{name:.*}/insert": postImagesInsert,
|
||||
"/images/{name:.*}/push": postImagesPush,
|
||||
"/images/{name:.*}/tag": postImagesTag,
|
||||
"/images/getCache": postImagesGetCache,
|
||||
"/containers/create": postContainersCreate,
|
||||
"/containers/{name:.*}/kill": postContainersKill,
|
||||
"/containers/{name:.*}/restart": postContainersRestart,
|
||||
@@ -860,6 +1028,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
||||
"/containers/{name:.*}/wait": postContainersWait,
|
||||
"/containers/{name:.*}/resize": postContainersResize,
|
||||
"/containers/{name:.*}/attach": postContainersAttach,
|
||||
"/containers/{name:.*}/copy": postContainersCopy,
|
||||
},
|
||||
"DELETE": {
|
||||
"/containers/{name:.*}": deleteContainers,
|
||||
@@ -875,37 +1044,13 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
||||
utils.Debugf("Registering %s, %s", method, route)
|
||||
// NOTE: scope issue, make sure the variables are local and won't be changed
|
||||
localRoute := route
|
||||
localMethod := method
|
||||
localFct := fct
|
||||
f := func(w http.ResponseWriter, r *http.Request) {
|
||||
utils.Debugf("Calling %s %s", localMethod, localRoute)
|
||||
localMethod := method
|
||||
|
||||
if logging {
|
||||
log.Println(r.Method, r.RequestURI)
|
||||
}
|
||||
if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
|
||||
userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
|
||||
if len(userAgent) == 2 && userAgent[1] != VERSION {
|
||||
utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION)
|
||||
}
|
||||
}
|
||||
version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
|
||||
if err != nil {
|
||||
version = APIVERSION
|
||||
}
|
||||
if srv.enableCors {
|
||||
writeCorsHeaders(w, r)
|
||||
}
|
||||
if version == 0 || version > APIVERSION {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if err := localFct(srv, version, w, r, mux.Vars(r)); err != nil {
|
||||
httpError(w, err)
|
||||
}
|
||||
}
|
||||
// build the handler function
|
||||
f := makeHttpHandler(srv, logging, localMethod, localRoute, localFct)
|
||||
|
||||
// add the new route
|
||||
if localRoute == "" {
|
||||
r.Methods(localMethod).HandlerFunc(f)
|
||||
} else {
|
||||
@@ -914,6 +1059,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
@@ -928,9 +1074,26 @@ func ListenAndServe(proto, addr string, srv *Server, logging bool) error {
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
//as the daemon is launched as root, change to permission of the socket to allow non-root to connect
|
||||
if proto == "unix" {
|
||||
os.Chmod(addr, 0777)
|
||||
if err := os.Chmod(addr, 0660); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
groups, err := ioutil.ReadFile("/etc/group")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
re := regexp.MustCompile("(^|\n)docker:.*?:([0-9]+)")
|
||||
if gidMatch := re.FindStringSubmatch(string(groups)); gidMatch != nil {
|
||||
gid, err := strconv.Atoi(gidMatch[2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
utils.Debugf("docker group found. gid: %d", gid)
|
||||
if err := os.Chown(addr, 0, gid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
httpSrv := http.Server{Addr: addr, Handler: r}
|
||||
return httpSrv.Serve(l)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package docker
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type APIHistory struct {
|
||||
ID string `json:"Id"`
|
||||
Tags []string `json:",omitempty"`
|
||||
@@ -17,13 +19,23 @@ type APIImages struct {
|
||||
}
|
||||
|
||||
type APIInfo struct {
|
||||
Debug bool
|
||||
Containers int
|
||||
Images int
|
||||
NFd int `json:",omitempty"`
|
||||
NGoroutines int `json:",omitempty"`
|
||||
MemoryLimit bool `json:",omitempty"`
|
||||
SwapLimit bool `json:",omitempty"`
|
||||
Debug bool
|
||||
Containers int
|
||||
Images int
|
||||
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"`
|
||||
}
|
||||
|
||||
type APITop struct {
|
||||
Titles []string
|
||||
Processes [][]string
|
||||
}
|
||||
|
||||
type APIRmi struct {
|
||||
@@ -32,6 +44,30 @@ type APIRmi struct {
|
||||
}
|
||||
|
||||
type APIContainers struct {
|
||||
ID string `json:"Id"`
|
||||
Image string
|
||||
Command string
|
||||
Created int64
|
||||
Status string
|
||||
Ports []APIPort
|
||||
SizeRw int64
|
||||
SizeRootFs int64
|
||||
}
|
||||
|
||||
func (self *APIContainers) ToLegacy() APIContainersOld {
|
||||
return APIContainersOld{
|
||||
ID: self.ID,
|
||||
Image: self.Image,
|
||||
Command: self.Command,
|
||||
Created: self.Created,
|
||||
Status: self.Status,
|
||||
Ports: displayablePorts(self.Ports),
|
||||
SizeRw: self.SizeRw,
|
||||
SizeRootFs: self.SizeRootFs,
|
||||
}
|
||||
}
|
||||
|
||||
type APIContainersOld struct {
|
||||
ID string `json:"Id"`
|
||||
Image string
|
||||
Command string
|
||||
@@ -57,7 +93,17 @@ type APIRun struct {
|
||||
}
|
||||
|
||||
type APIPort struct {
|
||||
Port string
|
||||
PrivatePort int64
|
||||
PublicPort int64
|
||||
Type string
|
||||
}
|
||||
|
||||
func (port *APIPort) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]interface{}{
|
||||
"PrivatePort": port.PrivatePort,
|
||||
"PublicPort": port.PublicPort,
|
||||
"Type": port.Type,
|
||||
})
|
||||
}
|
||||
|
||||
type APIVersion struct {
|
||||
@@ -78,3 +124,8 @@ type APIImageConfig struct {
|
||||
ID string `json:"Id"`
|
||||
*Config
|
||||
}
|
||||
|
||||
type APICopy struct {
|
||||
Resource string
|
||||
HostPath string
|
||||
}
|
||||
|
||||
850
api_test.go
850
api_test.go
File diff suppressed because it is too large
Load Diff
31
archive.go
31
archive.go
@@ -2,9 +2,7 @@ package docker
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
@@ -27,10 +25,6 @@ const (
|
||||
)
|
||||
|
||||
func DetectCompression(source []byte) Compression {
|
||||
for _, c := range source[:10] {
|
||||
utils.Debugf("%x", c)
|
||||
}
|
||||
|
||||
sourceLen := len(source)
|
||||
for compression, m := range map[Compression][]byte{
|
||||
Bzip2: {0x42, 0x5A, 0x68},
|
||||
@@ -104,24 +98,33 @@ func TarFilter(path string, compression Compression, filter []string) (io.Reader
|
||||
|
||||
// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
|
||||
// and unpacks it into the directory at `path`.
|
||||
// The archive may be compressed with one of the following algorithgms:
|
||||
// The archive may be compressed with one of the following algorithms:
|
||||
// identity (uncompressed), gzip, bzip2, xz.
|
||||
// FIXME: specify behavior when target path exists vs. doesn't exist.
|
||||
func Untar(archive io.Reader, path string) error {
|
||||
if archive == nil {
|
||||
return fmt.Errorf("Empty archive")
|
||||
}
|
||||
bufferedArchive := bufio.NewReaderSize(archive, 10)
|
||||
buf, err := bufferedArchive.Peek(10)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
buf := make([]byte, 10)
|
||||
totalN := 0
|
||||
for totalN < 10 {
|
||||
if n, err := archive.Read(buf[totalN:]); err != nil {
|
||||
if err == io.EOF {
|
||||
return fmt.Errorf("Tarball too short")
|
||||
}
|
||||
return err
|
||||
} else {
|
||||
totalN += n
|
||||
utils.Debugf("[tar autodetect] n: %d", n)
|
||||
}
|
||||
}
|
||||
compression := DetectCompression(buf)
|
||||
|
||||
utils.Debugf("Archive compression detected: %s", compression.Extension())
|
||||
|
||||
cmd := exec.Command("tar", "--numeric-owner", "-f", "-", "-C", path, "-x"+compression.Flag())
|
||||
cmd.Stdin = bufferedArchive
|
||||
cmd.Stdin = io.MultiReader(bytes.NewReader(buf), archive)
|
||||
// Hardcode locale environment for predictable outcome regardless of host configuration.
|
||||
// (see https://github.com/dotcloud/docker/issues/355)
|
||||
cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}
|
||||
@@ -170,7 +173,7 @@ func CopyWithTar(src, dst string) error {
|
||||
}
|
||||
// Create dst, copy src's content into it
|
||||
utils.Debugf("Creating dest directory: %s", dst)
|
||||
if err := os.MkdirAll(dst, 0700); err != nil && !os.IsExist(err) {
|
||||
if err := os.MkdirAll(dst, 0755); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
utils.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
||||
@@ -251,7 +254,7 @@ func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
|
||||
}
|
||||
errText := <-errChan
|
||||
if err := cmd.Wait(); err != nil {
|
||||
pipeW.CloseWithError(errors.New(err.Error() + ": " + string(errText)))
|
||||
pipeW.CloseWithError(fmt.Errorf("%s: %s", err, errText))
|
||||
} else {
|
||||
pipeW.Close()
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestCmdStreamLargeStderr(t *testing.T) {
|
||||
cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
|
||||
out, err := CmdStream(cmd)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start command: " + err.Error())
|
||||
t.Fatalf("Failed to start command: %s", err)
|
||||
}
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
@@ -26,7 +26,7 @@ func TestCmdStreamLargeStderr(t *testing.T) {
|
||||
select {
|
||||
case err := <-errCh:
|
||||
if err != nil {
|
||||
t.Fatalf("Command should not have failed (err=%s...)", err.Error()[:100])
|
||||
t.Fatalf("Command should not have failed (err=%.100s...)", err)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatalf("Command did not complete in 5 seconds; probable deadlock")
|
||||
@@ -37,12 +37,12 @@ func TestCmdStreamBad(t *testing.T) {
|
||||
badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
|
||||
out, err := CmdStream(badCmd)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start command: " + err.Error())
|
||||
t.Fatalf("Failed to start command: %s", err)
|
||||
}
|
||||
if output, err := ioutil.ReadAll(out); err == nil {
|
||||
t.Fatalf("Command should have failed")
|
||||
} else if err.Error() != "exit status 1: error couldn't reverse the phase pulser\n" {
|
||||
t.Fatalf("Wrong error value (%s)", err.Error())
|
||||
t.Fatalf("Wrong error value (%s)", err)
|
||||
} else if s := string(output); s != "hello\n" {
|
||||
t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output)
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../registry/MAINTAINERS
|
||||
3
auth/MAINTAINERS
Normal file
3
auth/MAINTAINERS
Normal file
@@ -0,0 +1,3 @@
|
||||
Sam Alba <sam@dotcloud.com> (@samalba)
|
||||
Joffrey Fuhrer <joffrey@dotcloud.com> (@shin-)
|
||||
Ken Cochrane <ken@dotcloud.com> (@kencochrane)
|
||||
219
auth/auth.go
219
auth/auth.go
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -15,35 +16,29 @@ import (
|
||||
// Where we store the config file
|
||||
const CONFIGFILE = ".dockercfg"
|
||||
|
||||
// the registry server we want to login against
|
||||
const INDEXSERVER = "https://index.docker.io/v1"
|
||||
// Only used for user auth + account creation
|
||||
const INDEXSERVER = "https://index.docker.io/v1/"
|
||||
|
||||
//const INDEXSERVER = "http://indexstaging-docker.dotcloud.com/"
|
||||
//const INDEXSERVER = "https://indexstaging-docker.dotcloud.com/v1/"
|
||||
|
||||
var (
|
||||
ErrConfigFileMissing = errors.New("The Auth config file is missing")
|
||||
)
|
||||
|
||||
type AuthConfig struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Auth string `json:"auth"`
|
||||
Email string `json:"email"`
|
||||
ServerAddress string `json:"serveraddress,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigFile struct {
|
||||
Configs map[string]AuthConfig `json:"configs,omitempty"`
|
||||
rootPath string
|
||||
}
|
||||
|
||||
func NewAuthConfig(username, password, email, rootPath string) *AuthConfig {
|
||||
return &AuthConfig{
|
||||
Username: username,
|
||||
Password: password,
|
||||
Email: email,
|
||||
rootPath: rootPath,
|
||||
}
|
||||
}
|
||||
|
||||
func IndexServerAddress() string {
|
||||
if os.Getenv("DOCKER_INDEX_URL") != "" {
|
||||
return os.Getenv("DOCKER_INDEX_URL") + "/v1"
|
||||
}
|
||||
return INDEXSERVER
|
||||
}
|
||||
|
||||
@@ -57,61 +52,97 @@ func encodeAuth(authConfig *AuthConfig) string {
|
||||
}
|
||||
|
||||
// decode the auth string
|
||||
func decodeAuth(authStr string) (*AuthConfig, error) {
|
||||
func decodeAuth(authStr string) (string, string, error) {
|
||||
decLen := base64.StdEncoding.DecodedLen(len(authStr))
|
||||
decoded := make([]byte, decLen)
|
||||
authByte := []byte(authStr)
|
||||
n, err := base64.StdEncoding.Decode(decoded, authByte)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", "", err
|
||||
}
|
||||
if n > decLen {
|
||||
return nil, fmt.Errorf("Something went wrong decoding auth config")
|
||||
return "", "", fmt.Errorf("Something went wrong decoding auth config")
|
||||
}
|
||||
arr := strings.Split(string(decoded), ":")
|
||||
if len(arr) != 2 {
|
||||
return nil, fmt.Errorf("Invalid auth configuration file")
|
||||
return "", "", fmt.Errorf("Invalid auth configuration file")
|
||||
}
|
||||
password := strings.Trim(arr[1], "\x00")
|
||||
return &AuthConfig{Username: arr[0], Password: password}, nil
|
||||
return arr[0], password, nil
|
||||
}
|
||||
|
||||
// load up the auth config information and return values
|
||||
// FIXME: use the internal golang config parser
|
||||
func LoadConfig(rootPath string) (*AuthConfig, error) {
|
||||
func LoadConfig(rootPath string) (*ConfigFile, error) {
|
||||
configFile := ConfigFile{Configs: make(map[string]AuthConfig), rootPath: rootPath}
|
||||
confFile := path.Join(rootPath, CONFIGFILE)
|
||||
if _, err := os.Stat(confFile); err != nil {
|
||||
return &AuthConfig{rootPath: rootPath}, ErrConfigFileMissing
|
||||
return &configFile, nil //missing file is not an error
|
||||
}
|
||||
b, err := ioutil.ReadFile(confFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return &configFile, err
|
||||
}
|
||||
arr := strings.Split(string(b), "\n")
|
||||
if len(arr) < 2 {
|
||||
return nil, fmt.Errorf("The Auth config file is empty")
|
||||
|
||||
if err := json.Unmarshal(b, &configFile.Configs); err != nil {
|
||||
arr := strings.Split(string(b), "\n")
|
||||
if len(arr) < 2 {
|
||||
return &configFile, fmt.Errorf("The Auth config file is empty")
|
||||
}
|
||||
authConfig := AuthConfig{}
|
||||
origAuth := strings.Split(arr[0], " = ")
|
||||
if len(origAuth) != 2 {
|
||||
return &configFile, fmt.Errorf("Invalid Auth config file")
|
||||
}
|
||||
authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
|
||||
if err != nil {
|
||||
return &configFile, err
|
||||
}
|
||||
origEmail := strings.Split(arr[1], " = ")
|
||||
if len(origEmail) != 2 {
|
||||
return &configFile, fmt.Errorf("Invalid Auth config file")
|
||||
}
|
||||
authConfig.Email = origEmail[1]
|
||||
authConfig.ServerAddress = IndexServerAddress()
|
||||
configFile.Configs[IndexServerAddress()] = authConfig
|
||||
} else {
|
||||
for k, authConfig := range configFile.Configs {
|
||||
authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
|
||||
if err != nil {
|
||||
return &configFile, err
|
||||
}
|
||||
authConfig.Auth = ""
|
||||
configFile.Configs[k] = authConfig
|
||||
authConfig.ServerAddress = k
|
||||
}
|
||||
}
|
||||
origAuth := strings.Split(arr[0], " = ")
|
||||
origEmail := strings.Split(arr[1], " = ")
|
||||
authConfig, err := decodeAuth(origAuth[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authConfig.Email = origEmail[1]
|
||||
authConfig.rootPath = rootPath
|
||||
return authConfig, nil
|
||||
return &configFile, nil
|
||||
}
|
||||
|
||||
// save the auth config
|
||||
func SaveConfig(authConfig *AuthConfig) error {
|
||||
confFile := path.Join(authConfig.rootPath, CONFIGFILE)
|
||||
if len(authConfig.Email) == 0 {
|
||||
func SaveConfig(configFile *ConfigFile) error {
|
||||
confFile := path.Join(configFile.rootPath, CONFIGFILE)
|
||||
if len(configFile.Configs) == 0 {
|
||||
os.Remove(confFile)
|
||||
return nil
|
||||
}
|
||||
lines := "auth = " + encodeAuth(authConfig) + "\n" + "email = " + authConfig.Email + "\n"
|
||||
b := []byte(lines)
|
||||
err := ioutil.WriteFile(confFile, b, 0600)
|
||||
|
||||
configs := make(map[string]AuthConfig, len(configFile.Configs))
|
||||
for k, authConfig := range configFile.Configs {
|
||||
authCopy := authConfig
|
||||
|
||||
authCopy.Auth = encodeAuth(&authCopy)
|
||||
authCopy.Username = ""
|
||||
authCopy.Password = ""
|
||||
authCopy.ServerAddress = ""
|
||||
configs[k] = authCopy
|
||||
}
|
||||
|
||||
b, err := json.Marshal(configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(confFile, b, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -119,20 +150,31 @@ func SaveConfig(authConfig *AuthConfig) error {
|
||||
}
|
||||
|
||||
// try to register/login to the registry server
|
||||
func Login(authConfig *AuthConfig, store bool) (string, error) {
|
||||
storeConfig := false
|
||||
func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, error) {
|
||||
client := &http.Client{}
|
||||
reqStatusCode := 0
|
||||
var status string
|
||||
var reqBody []byte
|
||||
jsonBody, err := json.Marshal(authConfig)
|
||||
|
||||
serverAddress := authConfig.ServerAddress
|
||||
if serverAddress == "" {
|
||||
serverAddress = IndexServerAddress()
|
||||
}
|
||||
|
||||
loginAgainstOfficialIndex := serverAddress == IndexServerAddress()
|
||||
|
||||
// to avoid sending the server address to the server it should be removed before marshalled
|
||||
authCopy := *authConfig
|
||||
authCopy.ServerAddress = ""
|
||||
|
||||
jsonBody, err := json.Marshal(authCopy)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Config Error: %s", err)
|
||||
}
|
||||
|
||||
// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
|
||||
b := strings.NewReader(string(jsonBody))
|
||||
req1, err := http.Post(IndexServerAddress()+"/users/", "application/json; charset=utf-8", b)
|
||||
req1, err := http.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Server Error: %s", err)
|
||||
}
|
||||
@@ -144,15 +186,23 @@ func Login(authConfig *AuthConfig, store bool) (string, error) {
|
||||
}
|
||||
|
||||
if reqStatusCode == 201 {
|
||||
status = "Account created. Please use the confirmation link we sent" +
|
||||
" to your e-mail to activate it."
|
||||
storeConfig = true
|
||||
if loginAgainstOfficialIndex {
|
||||
status = "Account created. Please use the confirmation link we sent" +
|
||||
" to your e-mail to activate it."
|
||||
} else {
|
||||
status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it."
|
||||
}
|
||||
} else if reqStatusCode == 403 {
|
||||
return "", fmt.Errorf("Login: Your account hasn't been activated. " +
|
||||
"Please check your e-mail for a confirmation link.")
|
||||
if loginAgainstOfficialIndex {
|
||||
return "", fmt.Errorf("Login: Your account hasn't been activated. " +
|
||||
"Please check your e-mail for a confirmation link.")
|
||||
} else {
|
||||
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 := http.NewRequest("GET", IndexServerAddress()+"/users/", nil)
|
||||
req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
|
||||
req.SetBasicAuth(authConfig.Username, authConfig.Password)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
@@ -165,14 +215,7 @@ func Login(authConfig *AuthConfig, store bool) (string, error) {
|
||||
}
|
||||
if resp.StatusCode == 200 {
|
||||
status = "Login Succeeded"
|
||||
storeConfig = true
|
||||
} else if resp.StatusCode == 401 {
|
||||
if store {
|
||||
authConfig.Email = ""
|
||||
if err := SaveConfig(authConfig); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("Wrong login/password, please try again")
|
||||
} else {
|
||||
return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
|
||||
@@ -184,10 +227,54 @@ func Login(authConfig *AuthConfig, store bool) (string, error) {
|
||||
} else {
|
||||
return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody)
|
||||
}
|
||||
if storeConfig && store {
|
||||
if err := SaveConfig(authConfig); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// this method matches a auth configuration to a server address or a url
|
||||
func (config *ConfigFile) ResolveAuthConfig(registry string) AuthConfig {
|
||||
if registry == IndexServerAddress() || len(registry) == 0 {
|
||||
// default to the index server
|
||||
return config.Configs[IndexServerAddress()]
|
||||
}
|
||||
// if its 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
|
||||
//
|
||||
// as there is only one auth entry which is fully qualified we need to start
|
||||
// parsing and matching
|
||||
|
||||
swapProtocol := func(url string) string {
|
||||
if strings.HasPrefix(url, "http:") {
|
||||
return strings.Replace(url, "http:", "https:", 1)
|
||||
}
|
||||
if strings.HasPrefix(url, "https:") {
|
||||
return strings.Replace(url, "https:", "http:", 1)
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
resolveIgnoringProtocol := func(url string) AuthConfig {
|
||||
if c, found := config.Configs[url]; found {
|
||||
return c
|
||||
}
|
||||
registrySwappedProtocol := swapProtocol(url)
|
||||
// now try to match with the different protocol
|
||||
if c, found := config.Configs[registrySwappedProtocol]; found {
|
||||
return c
|
||||
}
|
||||
return AuthConfig{}
|
||||
}
|
||||
|
||||
// match both protocols as it could also be a server name like httpfoo
|
||||
if strings.HasPrefix(registry, "http:") || strings.HasPrefix(registry, "https:") {
|
||||
return resolveIgnoringProtocol(registry)
|
||||
}
|
||||
|
||||
url := "https://" + registry
|
||||
if !strings.Contains(registry, "/") {
|
||||
url = url + "/v1/"
|
||||
}
|
||||
return resolveIgnoringProtocol(url)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package auth
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -11,7 +12,9 @@ import (
|
||||
func TestEncodeAuth(t *testing.T) {
|
||||
newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
|
||||
authStr := encodeAuth(newAuthConfig)
|
||||
decAuthConfig, err := decodeAuth(authStr)
|
||||
decAuthConfig := &AuthConfig{}
|
||||
var err error
|
||||
decAuthConfig.Username, decAuthConfig.Password, err = decodeAuth(authStr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -29,8 +32,8 @@ func TestEncodeAuth(t *testing.T) {
|
||||
func TestLogin(t *testing.T) {
|
||||
os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com")
|
||||
defer os.Setenv("DOCKER_INDEX_URL", "")
|
||||
authConfig := NewAuthConfig("unittester", "surlautrerivejetattendrai", "noise+unittester@dotcloud.com", "/tmp")
|
||||
status, err := Login(authConfig, false)
|
||||
authConfig := &AuthConfig{Username: "unittester", Password: "surlautrerivejetattendrai", Email: "noise+unittester@dotcloud.com"}
|
||||
status, err := Login(authConfig, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -49,8 +52,8 @@ func TestCreateAccount(t *testing.T) {
|
||||
}
|
||||
token := hex.EncodeToString(tokenBuffer)[:12]
|
||||
username := "ut" + token
|
||||
authConfig := NewAuthConfig(username, "test42", "docker-ut+"+token+"@example.com", "/tmp")
|
||||
status, err := Login(authConfig, false)
|
||||
authConfig := &AuthConfig{Username: username, Password: "test42", Email: "docker-ut+" + token + "@example.com"}
|
||||
status, err := Login(authConfig, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -60,7 +63,7 @@ func TestCreateAccount(t *testing.T) {
|
||||
t.Fatalf("Expected status: \"%s\", found \"%s\" instead.", expectedStatus, status)
|
||||
}
|
||||
|
||||
status, err = Login(authConfig, false)
|
||||
status, err = Login(authConfig, nil)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found nil instead")
|
||||
}
|
||||
@@ -68,6 +71,118 @@ func TestCreateAccount(t *testing.T) {
|
||||
expectedError := "Login: Account is not Active"
|
||||
|
||||
if !strings.Contains(err.Error(), expectedError) {
|
||||
t.Fatalf("Expected message \"%s\" but found \"%s\" instead", expectedError, err.Error())
|
||||
t.Fatalf("Expected message \"%s\" but found \"%s\" instead", expectedError, err)
|
||||
}
|
||||
}
|
||||
|
||||
func setupTempConfigFile() (*ConfigFile, error) {
|
||||
root, err := ioutil.TempDir("", "docker-test")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configFile := &ConfigFile{
|
||||
rootPath: root,
|
||||
Configs: make(map[string]AuthConfig),
|
||||
}
|
||||
|
||||
for _, registry := range []string{"testIndex", IndexServerAddress()} {
|
||||
configFile.Configs[registry] = AuthConfig{
|
||||
Username: "docker-user",
|
||||
Password: "docker-pass",
|
||||
Email: "docker@docker.io",
|
||||
}
|
||||
}
|
||||
|
||||
return configFile, nil
|
||||
}
|
||||
|
||||
func TestSameAuthDataPostSave(t *testing.T) {
|
||||
configFile, err := setupTempConfigFile()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = SaveConfig(configFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
authConfig := configFile.Configs["testIndex"]
|
||||
if authConfig.Username != "docker-user" {
|
||||
t.Fail()
|
||||
}
|
||||
if authConfig.Password != "docker-pass" {
|
||||
t.Fail()
|
||||
}
|
||||
if authConfig.Email != "docker@docker.io" {
|
||||
t.Fail()
|
||||
}
|
||||
if authConfig.Auth != "" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveAuthConfigIndexServer(t *testing.T) {
|
||||
configFile, err := setupTempConfigFile()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, registry := range []string{"", IndexServerAddress()} {
|
||||
resolved := configFile.ResolveAuthConfig(registry)
|
||||
if resolved != configFile.Configs[IndexServerAddress()] {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveAuthConfigFullURL(t *testing.T) {
|
||||
configFile, err := setupTempConfigFile()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
registryAuth := AuthConfig{
|
||||
Username: "foo-user",
|
||||
Password: "foo-pass",
|
||||
Email: "foo@example.com",
|
||||
}
|
||||
localAuth := AuthConfig{
|
||||
Username: "bar-user",
|
||||
Password: "bar-pass",
|
||||
Email: "bar@example.com",
|
||||
}
|
||||
configFile.Configs["https://registry.example.com/v1/"] = registryAuth
|
||||
configFile.Configs["http://localhost:8000/v1/"] = localAuth
|
||||
|
||||
validRegistries := map[string][]string{
|
||||
"https://registry.example.com/v1/": {
|
||||
"https://registry.example.com/v1/",
|
||||
"http://registry.example.com/v1/",
|
||||
"registry.example.com",
|
||||
"registry.example.com/v1/",
|
||||
},
|
||||
"http://localhost:8000/v1/": {
|
||||
"https://localhost:8000/v1/",
|
||||
"http://localhost:8000/v1/",
|
||||
"localhost:8000",
|
||||
"localhost:8000/v1/",
|
||||
},
|
||||
}
|
||||
|
||||
for configKey, registries := range validRegistries {
|
||||
for _, registry := range registries {
|
||||
var (
|
||||
configured AuthConfig
|
||||
ok bool
|
||||
)
|
||||
resolved := configFile.ResolveAuthConfig(registry)
|
||||
if configured, ok = configFile.Configs[configKey]; !ok {
|
||||
t.Fail()
|
||||
}
|
||||
if resolved.Email != configured.Email {
|
||||
t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
132
builder.go
132
builder.go
@@ -1,132 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
var defaultDns = []string{"8.8.8.8", "8.8.4.4"}
|
||||
|
||||
type Builder struct {
|
||||
runtime *Runtime
|
||||
repositories *TagStore
|
||||
graph *Graph
|
||||
|
||||
config *Config
|
||||
image *Image
|
||||
}
|
||||
|
||||
func NewBuilder(runtime *Runtime) *Builder {
|
||||
return &Builder{
|
||||
runtime: runtime,
|
||||
graph: runtime.graph,
|
||||
repositories: runtime.repositories,
|
||||
}
|
||||
}
|
||||
|
||||
func (builder *Builder) Create(config *Config) (*Container, error) {
|
||||
// Lookup image
|
||||
img, err := builder.repositories.LookupImage(config.Image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if img.Config != nil {
|
||||
MergeConfig(config, img.Config)
|
||||
}
|
||||
|
||||
if config.Cmd == nil || len(config.Cmd) == 0 {
|
||||
return nil, fmt.Errorf("No command specified")
|
||||
}
|
||||
|
||||
// Generate id
|
||||
id := GenerateID()
|
||||
// Generate default hostname
|
||||
// FIXME: the lxc template no longer needs to set a default hostname
|
||||
if config.Hostname == "" {
|
||||
config.Hostname = id[:12]
|
||||
}
|
||||
|
||||
container := &Container{
|
||||
// FIXME: we should generate the ID here instead of receiving it as an argument
|
||||
ID: id,
|
||||
Created: time.Now(),
|
||||
Path: config.Cmd[0],
|
||||
Args: config.Cmd[1:], //FIXME: de-duplicate from config
|
||||
Config: config,
|
||||
Image: img.ID, // Always use the resolved image id
|
||||
NetworkSettings: &NetworkSettings{},
|
||||
// FIXME: do we need to store this in the container?
|
||||
SysInitPath: sysInitPath,
|
||||
}
|
||||
container.root = builder.runtime.containerRoot(container.ID)
|
||||
// Step 1: create the container directory.
|
||||
// This doubles as a barrier to avoid race conditions.
|
||||
if err := os.Mkdir(container.root, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(config.Dns) == 0 && len(builder.runtime.Dns) == 0 && utils.CheckLocalDns() {
|
||||
//"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns
|
||||
builder.runtime.Dns = defaultDns
|
||||
}
|
||||
|
||||
// If custom dns exists, then create a resolv.conf for the container
|
||||
if len(config.Dns) > 0 || len(builder.runtime.Dns) > 0 {
|
||||
var dns []string
|
||||
if len(config.Dns) > 0 {
|
||||
dns = config.Dns
|
||||
} else {
|
||||
dns = builder.runtime.Dns
|
||||
}
|
||||
container.ResolvConfPath = path.Join(container.root, "resolv.conf")
|
||||
f, err := os.Create(container.ResolvConfPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
for _, dns := range dns {
|
||||
if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
container.ResolvConfPath = "/etc/resolv.conf"
|
||||
}
|
||||
|
||||
// Step 2: save the container json
|
||||
if err := container.ToDisk(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Step 3: register the container
|
||||
if err := builder.runtime.Register(container); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return container, nil
|
||||
}
|
||||
|
||||
// Commit creates a new filesystem image from the current state of a container.
|
||||
// The image can optionally be tagged into a repository
|
||||
func (builder *Builder) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) {
|
||||
// FIXME: freeze the container before copying it to avoid data corruption?
|
||||
// FIXME: this shouldn't be in commands.
|
||||
rwTar, err := container.ExportRw()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Create a new image from the container's base layers + a new layer from container changes
|
||||
img, err := builder.graph.Create(rwTar, container, comment, author, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Register the image if needed
|
||||
if repository != "" {
|
||||
if err := builder.repositories.Set(repository, tag, img.ID, true); err != nil {
|
||||
return img, err
|
||||
}
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
292
buildfile.go
292
buildfile.go
@@ -1,15 +1,16 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -21,13 +22,15 @@ type BuildFile interface {
|
||||
|
||||
type buildFile struct {
|
||||
runtime *Runtime
|
||||
builder *Builder
|
||||
srv *Server
|
||||
|
||||
image string
|
||||
maintainer string
|
||||
config *Config
|
||||
context string
|
||||
image string
|
||||
maintainer string
|
||||
config *Config
|
||||
context string
|
||||
verbose bool
|
||||
utilizeCache bool
|
||||
rm bool
|
||||
|
||||
tmpContainers map[string]struct{}
|
||||
tmpImages map[string]struct{}
|
||||
@@ -35,15 +38,11 @@ type buildFile struct {
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func (b *buildFile) clearTmp(containers, images map[string]struct{}) {
|
||||
func (b *buildFile) clearTmp(containers map[string]struct{}) {
|
||||
for c := range containers {
|
||||
tmp := b.runtime.Get(c)
|
||||
b.runtime.Destroy(tmp)
|
||||
utils.Debugf("Removing container %s", c)
|
||||
}
|
||||
for i := range images {
|
||||
b.runtime.graph.Delete(i)
|
||||
utils.Debugf("Removing image %s", i)
|
||||
fmt.Fprintf(b.out, "Removing intermediate container %s\n", utils.TruncateID(c))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,20 +50,10 @@ func (b *buildFile) CmdFrom(name string) error {
|
||||
image, err := b.runtime.repositories.LookupImage(name)
|
||||
if err != nil {
|
||||
if b.runtime.graph.IsNotExist(err) {
|
||||
|
||||
var tag, remote string
|
||||
if strings.Contains(name, ":") {
|
||||
remoteParts := strings.Split(name, ":")
|
||||
tag = remoteParts[1]
|
||||
remote = remoteParts[0]
|
||||
} else {
|
||||
remote = name
|
||||
}
|
||||
|
||||
if err := b.srv.ImagePull(remote, tag, "", b.out, utils.NewStreamFormatter(false), nil); err != nil {
|
||||
remote, tag := utils.ParseRepositoryTag(name)
|
||||
if err := b.srv.ImagePull(remote, tag, b.out, utils.NewStreamFormatter(false), nil, nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
image, err = b.runtime.repositories.LookupImage(name)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -75,6 +64,9 @@ func (b *buildFile) CmdFrom(name string) error {
|
||||
}
|
||||
b.image = image.ID
|
||||
b.config = &Config{}
|
||||
if b.config.Env == nil || len(b.config.Env) == 0 {
|
||||
b.config.Env = append(b.config.Env, "HOME=/", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -96,17 +88,21 @@ func (b *buildFile) CmdRun(args string) error {
|
||||
b.config.Cmd = nil
|
||||
MergeConfig(b.config, config)
|
||||
|
||||
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
|
||||
|
||||
utils.Debugf("Command to be executed: %v", b.config.Cmd)
|
||||
|
||||
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
|
||||
return err
|
||||
} else if cache != nil {
|
||||
fmt.Fprintf(b.out, " ---> Using cache\n")
|
||||
utils.Debugf("[BUILDER] Use cached version")
|
||||
b.image = cache.ID
|
||||
return nil
|
||||
} else {
|
||||
utils.Debugf("[BUILDER] Cache miss")
|
||||
if b.utilizeCache {
|
||||
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
|
||||
return err
|
||||
} else if cache != nil {
|
||||
fmt.Fprintf(b.out, " ---> Using cache\n")
|
||||
utils.Debugf("[BUILDER] Use cached version")
|
||||
b.image = cache.ID
|
||||
return nil
|
||||
} else {
|
||||
utils.Debugf("[BUILDER] Cache miss")
|
||||
}
|
||||
}
|
||||
|
||||
cid, err := b.run()
|
||||
@@ -116,10 +112,44 @@ func (b *buildFile) CmdRun(args string) error {
|
||||
if err := b.commit(cid, cmd, "run"); err != nil {
|
||||
return err
|
||||
}
|
||||
b.config.Cmd = cmd
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) FindEnvKey(key string) int {
|
||||
for k, envVar := range b.config.Env {
|
||||
envParts := strings.SplitN(envVar, "=", 2)
|
||||
if key == envParts[0] {
|
||||
return k
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (b *buildFile) ReplaceEnvMatches(value string) (string, error) {
|
||||
exp, err := regexp.Compile("(\\\\\\\\+|[^\\\\]|\\b|\\A)\\$({?)([[:alnum:]_]+)(}?)")
|
||||
if err != nil {
|
||||
return value, err
|
||||
}
|
||||
matches := exp.FindAllString(value, -1)
|
||||
for _, match := range matches {
|
||||
match = match[strings.Index(match, "$"):]
|
||||
matchKey := strings.Trim(match, "${}")
|
||||
|
||||
for _, envVar := range b.config.Env {
|
||||
envParts := strings.SplitN(envVar, "=", 2)
|
||||
envKey := envParts[0]
|
||||
envValue := envParts[1]
|
||||
|
||||
if envKey == matchKey {
|
||||
value = strings.Replace(value, match, envValue, -1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdEnv(args string) error {
|
||||
tmp := strings.SplitN(args, " ", 2)
|
||||
if len(tmp) != 2 {
|
||||
@@ -128,20 +158,25 @@ func (b *buildFile) CmdEnv(args string) error {
|
||||
key := strings.Trim(tmp[0], " \t")
|
||||
value := strings.Trim(tmp[1], " \t")
|
||||
|
||||
for i, elem := range b.config.Env {
|
||||
if strings.HasPrefix(elem, key+"=") {
|
||||
b.config.Env[i] = key + "=" + value
|
||||
return nil
|
||||
}
|
||||
envKey := b.FindEnvKey(key)
|
||||
replacedValue, err := b.ReplaceEnvMatches(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.config.Env = append(b.config.Env, key+"="+value)
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("ENV %s=%s", key, value))
|
||||
replacedVar := fmt.Sprintf("%s=%s", key, replacedValue)
|
||||
|
||||
if envKey >= 0 {
|
||||
b.config.Env[envKey] = replacedVar
|
||||
} else {
|
||||
b.config.Env = append(b.config.Env, replacedVar)
|
||||
}
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("ENV %s", replacedVar))
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdCmd(args string) error {
|
||||
var cmd []string
|
||||
if err := json.Unmarshal([]byte(args), &cmd); err != nil {
|
||||
utils.Debugf("Error unmarshalling: %s, using /bin/sh -c", err)
|
||||
utils.Debugf("Error unmarshalling: %s, setting cmd to /bin/sh -c", err)
|
||||
cmd = []string{"/bin/sh", "-c", args}
|
||||
}
|
||||
if err := b.commit("", cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
|
||||
@@ -157,6 +192,11 @@ func (b *buildFile) CmdExpose(args string) error {
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdUser(args string) error {
|
||||
b.config.User = args
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("USER %v", args))
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdInsert(args string) error {
|
||||
return fmt.Errorf("INSERT has been deprecated. Please use ADD instead")
|
||||
}
|
||||
@@ -165,6 +205,49 @@ func (b *buildFile) CmdCopy(args string) error {
|
||||
return fmt.Errorf("COPY has been deprecated. Please use ADD instead")
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdEntrypoint(args string) error {
|
||||
if args == "" {
|
||||
return fmt.Errorf("Entrypoint cannot be empty")
|
||||
}
|
||||
|
||||
var entrypoint []string
|
||||
if err := json.Unmarshal([]byte(args), &entrypoint); err != nil {
|
||||
b.config.Entrypoint = []string{"/bin/sh", "-c", args}
|
||||
} else {
|
||||
b.config.Entrypoint = entrypoint
|
||||
}
|
||||
if err := b.commit("", b.config.Cmd, fmt.Sprintf("ENTRYPOINT %s", args)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdWorkdir(workdir string) error {
|
||||
b.config.WorkingDir = workdir
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
|
||||
}
|
||||
|
||||
func (b *buildFile) CmdVolume(args string) error {
|
||||
if args == "" {
|
||||
return fmt.Errorf("Volume cannot be empty")
|
||||
}
|
||||
|
||||
var volume []string
|
||||
if err := json.Unmarshal([]byte(args), &volume); err != nil {
|
||||
volume = []string{args}
|
||||
}
|
||||
if b.config.Volumes == nil {
|
||||
b.config.Volumes = NewPathOpts()
|
||||
}
|
||||
for _, v := range volume {
|
||||
b.config.Volumes[v] = struct{}{}
|
||||
}
|
||||
if err := b.commit("", b.config.Cmd, fmt.Sprintf("VOLUME %s", args)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) addRemote(container *Container, orig, dest string) error {
|
||||
file, err := utils.Download(orig, ioutil.Discard)
|
||||
if err != nil {
|
||||
@@ -172,6 +255,24 @@ func (b *buildFile) addRemote(container *Container, orig, dest string) error {
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -179,12 +280,15 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error {
|
||||
origPath := path.Join(b.context, orig)
|
||||
destPath := path.Join(container.RootfsPath(), dest)
|
||||
// Preserve the trailing '/'
|
||||
if dest[len(dest)-1] == '/' {
|
||||
if strings.HasSuffix(dest, "/") {
|
||||
destPath = destPath + "/"
|
||||
}
|
||||
if !strings.HasPrefix(origPath, b.context) {
|
||||
return fmt.Errorf("Forbidden path: %s", origPath)
|
||||
}
|
||||
fi, err := os.Stat(origPath)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("%s: no such file or directory", orig)
|
||||
}
|
||||
if fi.IsDir() {
|
||||
if err := CopyWithTar(origPath, destPath); err != nil {
|
||||
@@ -194,7 +298,7 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error {
|
||||
} else if err := UntarPath(origPath, destPath); err != nil {
|
||||
utils.Debugf("Couldn't untar %s to %s: %s", origPath, destPath, err)
|
||||
// If that fails, just copy it as a regular file
|
||||
if err := os.MkdirAll(path.Dir(destPath), 0700); err != nil {
|
||||
if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := CopyWithTar(origPath, destPath); err != nil {
|
||||
@@ -212,15 +316,23 @@ func (b *buildFile) CmdAdd(args string) error {
|
||||
if len(tmp) != 2 {
|
||||
return fmt.Errorf("Invalid ADD format")
|
||||
}
|
||||
orig := strings.Trim(tmp[0], " \t")
|
||||
dest := strings.Trim(tmp[1], " \t")
|
||||
|
||||
orig, err := b.ReplaceEnvMatches(strings.Trim(tmp[0], " \t"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dest, err := b.ReplaceEnvMatches(strings.Trim(tmp[1], " \t"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
// Create the container and start it
|
||||
container, err := b.builder.Create(b.config)
|
||||
container, err := b.runtime.Create(b.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -255,19 +367,30 @@ func (b *buildFile) run() (string, error) {
|
||||
b.config.Image = b.image
|
||||
|
||||
// Create the container and start it
|
||||
c, err := b.builder.Create(b.config)
|
||||
c, err := b.runtime.Create(b.config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b.tmpContainers[c.ID] = struct{}{}
|
||||
fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(c.ID))
|
||||
|
||||
// override the entry point that may have been picked up from the base image
|
||||
c.Path = b.config.Cmd[0]
|
||||
c.Args = b.config.Cmd[1:]
|
||||
|
||||
//start the container
|
||||
hostConfig := &HostConfig{}
|
||||
if err := c.Start(hostConfig); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if b.verbose {
|
||||
err = <-c.Attach(nil, nil, b.out, b.out)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for it to finish
|
||||
if ret := c.Wait(); ret != 0 {
|
||||
return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret)
|
||||
@@ -287,17 +410,20 @@ 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 cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
|
||||
return err
|
||||
} else if cache != nil {
|
||||
fmt.Fprintf(b.out, " ---> Using cache\n")
|
||||
utils.Debugf("[BUILDER] Use cached version")
|
||||
b.image = cache.ID
|
||||
return nil
|
||||
} else {
|
||||
utils.Debugf("[BUILDER] Cache miss")
|
||||
if b.utilizeCache {
|
||||
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
|
||||
return err
|
||||
} else if cache != nil {
|
||||
fmt.Fprintf(b.out, " ---> Using cache\n")
|
||||
utils.Debugf("[BUILDER] Use cached version")
|
||||
b.image = cache.ID
|
||||
return nil
|
||||
} else {
|
||||
utils.Debugf("[BUILDER] Cache miss")
|
||||
}
|
||||
}
|
||||
container, err := b.builder.Create(b.config)
|
||||
|
||||
container, err := b.runtime.Create(b.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -319,7 +445,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
|
||||
autoConfig := *b.config
|
||||
autoConfig.Cmd = autoCmd
|
||||
// Commit the container
|
||||
image, err := b.builder.Commit(container, "", "", "", b.maintainer, &autoConfig)
|
||||
image, err := b.runtime.Commit(container, "", "", "", b.maintainer, &autoConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -328,10 +454,12 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Long lines can be split with a backslash
|
||||
var lineContinuation = regexp.MustCompile(`\s*\\\s*\n`)
|
||||
|
||||
func (b *buildFile) Build(context io.Reader) (string, error) {
|
||||
// FIXME: @creack any reason for using /tmp instead of ""?
|
||||
// FIXME: @creack "name" is a terrible variable name
|
||||
name, err := ioutil.TempDir("/tmp", "docker-build")
|
||||
name, err := ioutil.TempDir("", "docker-build")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -340,22 +468,18 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
|
||||
}
|
||||
defer os.RemoveAll(name)
|
||||
b.context = name
|
||||
dockerfile, err := os.Open(path.Join(name, "Dockerfile"))
|
||||
if err != nil {
|
||||
filename := path.Join(name, "Dockerfile")
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("Can't build a directory with no Dockerfile")
|
||||
}
|
||||
// FIXME: "file" is also a terrible variable name ;)
|
||||
file := bufio.NewReader(dockerfile)
|
||||
fileBytes, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dockerfile := string(fileBytes)
|
||||
dockerfile = lineContinuation.ReplaceAllString(dockerfile, "")
|
||||
stepN := 0
|
||||
for {
|
||||
line, err := file.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF && line == "" {
|
||||
break
|
||||
} else if err != io.EOF {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
for _, line := range strings.Split(dockerfile, "\n") {
|
||||
line = strings.Trim(strings.Replace(line, "\t", " ", -1), " \t\r\n")
|
||||
// Skip comments and empty line
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
@@ -367,15 +491,16 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
|
||||
}
|
||||
instruction := strings.ToLower(strings.Trim(tmp[0], " "))
|
||||
arguments := strings.Trim(tmp[1], " ")
|
||||
stepN += 1
|
||||
// FIXME: only count known instructions as build steps
|
||||
fmt.Fprintf(b.out, "Step %d : %s %s\n", stepN, strings.ToUpper(instruction), arguments)
|
||||
|
||||
method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
|
||||
if !exists {
|
||||
fmt.Fprintf(b.out, "# Skipping unknown instruction %s\n", strings.ToUpper(instruction))
|
||||
continue
|
||||
}
|
||||
|
||||
stepN += 1
|
||||
fmt.Fprintf(b.out, "Step %d : %s %s\n", stepN, strings.ToUpper(instruction), arguments)
|
||||
|
||||
ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
|
||||
if ret != nil {
|
||||
return "", ret.(error)
|
||||
@@ -385,19 +510,24 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
|
||||
}
|
||||
if b.image != "" {
|
||||
fmt.Fprintf(b.out, "Successfully built %s\n", utils.TruncateID(b.image))
|
||||
if b.rm {
|
||||
b.clearTmp(b.tmpContainers)
|
||||
}
|
||||
return b.image, nil
|
||||
}
|
||||
return "", fmt.Errorf("An error occured during the build\n")
|
||||
return "", fmt.Errorf("An error occurred during the build\n")
|
||||
}
|
||||
|
||||
func NewBuildFile(srv *Server, out io.Writer) BuildFile {
|
||||
func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache, rm bool) BuildFile {
|
||||
return &buildFile{
|
||||
builder: NewBuilder(srv.runtime),
|
||||
runtime: srv.runtime,
|
||||
srv: srv,
|
||||
config: &Config{},
|
||||
out: out,
|
||||
tmpContainers: make(map[string]struct{}),
|
||||
tmpImages: make(map[string]struct{}),
|
||||
verbose: verbose,
|
||||
utilizeCache: utilizeCache,
|
||||
rm: rm,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
"testing"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// mkTestContext generates a build context from the contents of the provided dockerfile.
|
||||
// This context is suitable for use as an argument to BuildFile.Build()
|
||||
func mkTestContext(dockerfile string, files [][2]string, t *testing.T) Archive {
|
||||
context, err := mkBuildContext(fmt.Sprintf(dockerfile, unitTestImageId), files)
|
||||
context, err := mkBuildContext(dockerfile, files)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -23,34 +26,68 @@ type testContextTemplate struct {
|
||||
dockerfile string
|
||||
// Additional files in the context, eg [][2]string{"./passwd", "gordon"}
|
||||
files [][2]string
|
||||
// Additional remote files to host on a local HTTP server.
|
||||
remoteFiles [][2]string
|
||||
}
|
||||
|
||||
// A table of all the contexts to build and test.
|
||||
// A new docker runtime will be created and torn down for each context.
|
||||
var testContexts []testContextTemplate = []testContextTemplate{
|
||||
var testContexts = []testContextTemplate{
|
||||
{
|
||||
`
|
||||
from %s
|
||||
from {IMAGE}
|
||||
run sh -c 'echo root:testpass > /tmp/passwd'
|
||||
run mkdir -p /var/run/sshd
|
||||
run [ "$(cat /tmp/passwd)" = "root:testpass" ]
|
||||
run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
|
||||
`,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
|
||||
// Exactly the same as above, except uses a line split with a \ to test
|
||||
// multiline support.
|
||||
{
|
||||
`
|
||||
from %s
|
||||
add foo /usr/lib/bla/bar
|
||||
run [ "$(cat /usr/lib/bla/bar)" = 'hello world!' ]
|
||||
from {IMAGE}
|
||||
run sh -c 'echo root:testpass \
|
||||
> /tmp/passwd'
|
||||
run mkdir -p /var/run/sshd
|
||||
run [ "$(cat /tmp/passwd)" = "root:testpass" ]
|
||||
run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
|
||||
`,
|
||||
[][2]string{{"foo", "hello world!"}},
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
|
||||
// Line containing literal "\n"
|
||||
{
|
||||
`
|
||||
from {IMAGE}
|
||||
run sh -c 'echo root:testpass > /tmp/passwd'
|
||||
run echo "foo \n bar"; echo "baz"
|
||||
run mkdir -p /var/run/sshd
|
||||
run [ "$(cat /tmp/passwd)" = "root:testpass" ]
|
||||
run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
|
||||
`,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`
|
||||
from {IMAGE}
|
||||
add foo /usr/lib/bla/bar
|
||||
run [ "$(cat /usr/lib/bla/bar)" = 'hello' ]
|
||||
add http://{SERVERADDR}/baz /usr/lib/baz/quux
|
||||
run [ "$(cat /usr/lib/baz/quux)" = 'world!' ]
|
||||
`,
|
||||
[][2]string{{"foo", "hello"}},
|
||||
[][2]string{{"/baz", "world!"}},
|
||||
},
|
||||
|
||||
{
|
||||
`
|
||||
from %s
|
||||
from {IMAGE}
|
||||
add f /
|
||||
run [ "$(cat /f)" = "hello" ]
|
||||
add f /abc
|
||||
@@ -72,38 +109,453 @@ run [ "$(cat /somewheeeere/over/the/rainbooow/ga)" = "bu" ]
|
||||
{"f", "hello"},
|
||||
{"d/ga", "bu"},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
||||
{
|
||||
`
|
||||
from %s
|
||||
from {IMAGE}
|
||||
add http://{SERVERADDR}/x /a/b/c
|
||||
run [ "$(cat /a/b/c)" = "hello" ]
|
||||
add http://{SERVERADDR}/x?foo=bar /
|
||||
run [ "$(cat /x)" = "hello" ]
|
||||
add http://{SERVERADDR}/x /d/
|
||||
run [ "$(cat /d/x)" = "hello" ]
|
||||
add http://{SERVERADDR} /e
|
||||
run [ "$(cat /e)" = "blah" ]
|
||||
`,
|
||||
nil,
|
||||
[][2]string{{"/x", "hello"}, {"/", "blah"}},
|
||||
},
|
||||
|
||||
{
|
||||
`
|
||||
from {IMAGE}
|
||||
env FOO BAR
|
||||
run [ "$FOO" = "BAR" ]
|
||||
`,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
|
||||
{
|
||||
`
|
||||
from {IMAGE}
|
||||
ENTRYPOINT /bin/echo
|
||||
CMD Hello world
|
||||
`,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
|
||||
{
|
||||
`
|
||||
from {IMAGE}
|
||||
VOLUME /test
|
||||
CMD Hello world
|
||||
`,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
|
||||
{
|
||||
`
|
||||
from {IMAGE}
|
||||
env FOO /foo/baz
|
||||
env BAR /bar
|
||||
env BAZ $BAR
|
||||
env FOOPATH $PATH:$FOO
|
||||
run [ "$BAR" = "$BAZ" ]
|
||||
run [ "$FOOPATH" = "$PATH:/foo/baz" ]
|
||||
`,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
|
||||
{
|
||||
`
|
||||
from {IMAGE}
|
||||
env FOO /bar
|
||||
env TEST testdir
|
||||
env BAZ /foobar
|
||||
add testfile $BAZ/
|
||||
add $TEST $FOO
|
||||
run [ "$(cat /foobar/testfile)" = "test1" ]
|
||||
run [ "$(cat /bar/withfile)" = "test2" ]
|
||||
`,
|
||||
[][2]string{
|
||||
{"testfile", "test1"},
|
||||
{"testdir/withfile", "test2"},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
// FIXME: test building with 2 successive overlapping ADD commands
|
||||
|
||||
func constructDockerfile(template string, ip net.IP, port string) string {
|
||||
serverAddr := fmt.Sprintf("%s:%s", ip, port)
|
||||
replacer := strings.NewReplacer("{IMAGE}", unitTestImageID, "{SERVERADDR}", serverAddr)
|
||||
return replacer.Replace(template)
|
||||
}
|
||||
|
||||
func mkTestingFileServer(files [][2]string) (*httptest.Server, error) {
|
||||
mux := http.NewServeMux()
|
||||
for _, file := range files {
|
||||
name, contents := file[0], file[1]
|
||||
mux.HandleFunc(name, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(contents))
|
||||
})
|
||||
}
|
||||
|
||||
// This is how httptest.NewServer sets up a net.Listener, except that our listener must accept remote
|
||||
// connections (from the container).
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := httptest.NewUnstartedServer(mux)
|
||||
s.Listener = listener
|
||||
s.Start()
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func TestBuild(t *testing.T) {
|
||||
for _, ctx := range testContexts {
|
||||
buildImage(ctx, t, nil, true)
|
||||
}
|
||||
}
|
||||
|
||||
func buildImage(context testContextTemplate, t *testing.T, srv *Server, useCache bool) *Image {
|
||||
if srv == nil {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{
|
||||
runtime: runtime,
|
||||
lock: &sync.Mutex{},
|
||||
srv = &Server{
|
||||
runtime: runtime,
|
||||
pullingPool: make(map[string]struct{}),
|
||||
pushingPool: make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
buildfile := NewBuildFile(srv, ioutil.Discard)
|
||||
if _, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t)); err != nil {
|
||||
t.Fatal(err)
|
||||
httpServer, err := mkTestingFileServer(context.remoteFiles)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer httpServer.Close()
|
||||
|
||||
idx := strings.LastIndex(httpServer.URL, ":")
|
||||
if idx < 0 {
|
||||
t.Fatalf("could not get port from test http server address %s", httpServer.URL)
|
||||
}
|
||||
port := httpServer.URL[idx+1:]
|
||||
|
||||
ip := srv.runtime.networkManager.bridgeNetwork.IP
|
||||
dockerfile := constructDockerfile(context.dockerfile, ip, port)
|
||||
|
||||
buildfile := NewBuildFile(srv, ioutil.Discard, false, useCache, false)
|
||||
id, err := buildfile.Build(mkTestContext(dockerfile, context.files, t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
img, err := srv.ImageInspect(id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return img
|
||||
}
|
||||
|
||||
func TestVolume(t *testing.T) {
|
||||
img := buildImage(testContextTemplate{`
|
||||
from {IMAGE}
|
||||
volume /test
|
||||
cmd Hello world
|
||||
`, nil, nil}, t, nil, true)
|
||||
|
||||
if len(img.Config.Volumes) == 0 {
|
||||
t.Fail()
|
||||
}
|
||||
for key := range img.Config.Volumes {
|
||||
if key != "/test" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildMaintainer(t *testing.T) {
|
||||
img := buildImage(testContextTemplate{`
|
||||
from {IMAGE}
|
||||
maintainer dockerio
|
||||
`, nil, nil}, t, nil, true)
|
||||
|
||||
if img.Author != "dockerio" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildUser(t *testing.T) {
|
||||
img := buildImage(testContextTemplate{`
|
||||
from {IMAGE}
|
||||
user dockerio
|
||||
`, nil, nil}, t, nil, true)
|
||||
|
||||
if img.Config.User != "dockerio" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildEnv(t *testing.T) {
|
||||
img := buildImage(testContextTemplate{`
|
||||
from {IMAGE}
|
||||
env port 4243
|
||||
`,
|
||||
nil, nil}, t, nil, true)
|
||||
hasEnv := false
|
||||
for _, envVar := range img.Config.Env {
|
||||
if envVar == "port=4243" {
|
||||
hasEnv = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasEnv {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildCmd(t *testing.T) {
|
||||
img := buildImage(testContextTemplate{`
|
||||
from {IMAGE}
|
||||
cmd ["/bin/echo", "Hello World"]
|
||||
`,
|
||||
nil, nil}, t, nil, true)
|
||||
|
||||
if img.Config.Cmd[0] != "/bin/echo" {
|
||||
t.Log(img.Config.Cmd[0])
|
||||
t.Fail()
|
||||
}
|
||||
if img.Config.Cmd[1] != "Hello World" {
|
||||
t.Log(img.Config.Cmd[1])
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildExpose(t *testing.T) {
|
||||
img := buildImage(testContextTemplate{`
|
||||
from {IMAGE}
|
||||
expose 4243
|
||||
`,
|
||||
nil, nil}, t, nil, true)
|
||||
|
||||
if img.Config.PortSpecs[0] != "4243" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildEntrypoint(t *testing.T) {
|
||||
img := buildImage(testContextTemplate{`
|
||||
from {IMAGE}
|
||||
entrypoint ["/bin/echo"]
|
||||
`,
|
||||
nil, nil}, t, nil, true)
|
||||
|
||||
if img.Config.Entrypoint[0] != "/bin/echo" {
|
||||
}
|
||||
}
|
||||
|
||||
// testing #1405 - config.Cmd does not get cleaned up if
|
||||
// utilizing cache
|
||||
func TestBuildEntrypointRunCleanup(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{
|
||||
runtime: runtime,
|
||||
pullingPool: make(map[string]struct{}),
|
||||
pushingPool: make(map[string]struct{}),
|
||||
}
|
||||
|
||||
img := buildImage(testContextTemplate{`
|
||||
from {IMAGE}
|
||||
run echo "hello"
|
||||
`,
|
||||
nil, nil}, t, srv, true)
|
||||
|
||||
img = buildImage(testContextTemplate{`
|
||||
from {IMAGE}
|
||||
run echo "hello"
|
||||
add foo /foo
|
||||
entrypoint ["/bin/echo"]
|
||||
`,
|
||||
[][2]string{{"foo", "HEYO"}}, nil}, t, srv, true)
|
||||
|
||||
if len(img.Config.Cmd) != 0 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildImageWithCache(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{
|
||||
runtime: runtime,
|
||||
pullingPool: make(map[string]struct{}),
|
||||
pushingPool: make(map[string]struct{}),
|
||||
}
|
||||
|
||||
template := testContextTemplate{`
|
||||
from {IMAGE}
|
||||
maintainer dockerio
|
||||
`,
|
||||
nil, nil}
|
||||
|
||||
img := buildImage(template, t, srv, true)
|
||||
imageId := img.ID
|
||||
|
||||
img = nil
|
||||
img = buildImage(template, t, srv, true)
|
||||
|
||||
if imageId != img.ID {
|
||||
t.Logf("Image ids should match: %s != %s", imageId, img.ID)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildImageWithoutCache(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{
|
||||
runtime: runtime,
|
||||
pullingPool: make(map[string]struct{}),
|
||||
pushingPool: make(map[string]struct{}),
|
||||
}
|
||||
|
||||
template := testContextTemplate{`
|
||||
from {IMAGE}
|
||||
maintainer dockerio
|
||||
`,
|
||||
nil, nil}
|
||||
|
||||
img := buildImage(template, t, srv, true)
|
||||
imageId := img.ID
|
||||
|
||||
img = nil
|
||||
img = buildImage(template, t, srv, false)
|
||||
|
||||
if imageId == img.ID {
|
||||
t.Logf("Image ids should not match: %s == %s", imageId, img.ID)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestForbiddenContextPath(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{
|
||||
runtime: runtime,
|
||||
pullingPool: make(map[string]struct{}),
|
||||
pushingPool: make(map[string]struct{}),
|
||||
}
|
||||
|
||||
context := testContextTemplate{`
|
||||
from {IMAGE}
|
||||
maintainer dockerio
|
||||
add ../../ test/
|
||||
`,
|
||||
[][2]string{{"test.txt", "test1"}, {"other.txt", "other"}}, nil}
|
||||
|
||||
httpServer, err := mkTestingFileServer(context.remoteFiles)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer httpServer.Close()
|
||||
|
||||
idx := strings.LastIndex(httpServer.URL, ":")
|
||||
if idx < 0 {
|
||||
t.Fatalf("could not get port from test http server address %s", httpServer.URL)
|
||||
}
|
||||
port := httpServer.URL[idx+1:]
|
||||
|
||||
ip := srv.runtime.networkManager.bridgeNetwork.IP
|
||||
dockerfile := constructDockerfile(context.dockerfile, ip, port)
|
||||
|
||||
buildfile := NewBuildFile(srv, ioutil.Discard, false, true, false)
|
||||
_, err = buildfile.Build(mkTestContext(dockerfile, context.files, t))
|
||||
|
||||
if err == nil {
|
||||
t.Log("Error should not be nil")
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if err.Error() != "Forbidden path: /" {
|
||||
t.Logf("Error message is not expected: %s", err.Error())
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildADDFileNotFound(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{
|
||||
runtime: runtime,
|
||||
pullingPool: make(map[string]struct{}),
|
||||
pushingPool: make(map[string]struct{}),
|
||||
}
|
||||
|
||||
context := testContextTemplate{`
|
||||
from {IMAGE}
|
||||
add foo /usr/local/bar
|
||||
`,
|
||||
nil, nil}
|
||||
|
||||
httpServer, err := mkTestingFileServer(context.remoteFiles)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer httpServer.Close()
|
||||
|
||||
idx := strings.LastIndex(httpServer.URL, ":")
|
||||
if idx < 0 {
|
||||
t.Fatalf("could not get port from test http server address %s", httpServer.URL)
|
||||
}
|
||||
port := httpServer.URL[idx+1:]
|
||||
|
||||
ip := srv.runtime.networkManager.bridgeNetwork.IP
|
||||
dockerfile := constructDockerfile(context.dockerfile, ip, port)
|
||||
|
||||
buildfile := NewBuildFile(srv, ioutil.Discard, false, true, false)
|
||||
_, err = buildfile.Build(mkTestContext(dockerfile, context.files, t))
|
||||
|
||||
if err == nil {
|
||||
t.Log("Error should not be nil")
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if err.Error() != "foo: no such file or directory" {
|
||||
t.Logf("Error message is not expected: %s", err.Error())
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func Changes(layers []string, rw string) ([]Change, error) {
|
||||
changes = append(changes, change)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
return changes, nil
|
||||
|
||||
816
commands.go
816
commands.go
File diff suppressed because it is too large
Load Diff
443
commands_test.go
443
commands_test.go
@@ -38,7 +38,7 @@ func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
|
||||
f()
|
||||
c <- false
|
||||
}()
|
||||
if <-c {
|
||||
if <-c && msg != "" {
|
||||
t.Fatal(msg)
|
||||
}
|
||||
}
|
||||
@@ -59,94 +59,21 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error
|
||||
return nil
|
||||
}
|
||||
|
||||
/*TODO
|
||||
func cmdImages(srv *Server, args ...string) (string, error) {
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
go func() {
|
||||
if err := srv.CmdImages(nil, stdoutPipe, args...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// force the pipe closed, so that the code below gets an EOF
|
||||
stdoutPipe.Close()
|
||||
}()
|
||||
|
||||
output, err := ioutil.ReadAll(stdout)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Cleanup pipes
|
||||
return string(output), closeWrap(stdout, stdoutPipe)
|
||||
}
|
||||
|
||||
// TestImages checks that 'docker images' displays information correctly
|
||||
func TestImages(t *testing.T) {
|
||||
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
output, err := cmdImages(srv)
|
||||
|
||||
if !strings.Contains(output, "REPOSITORY") {
|
||||
t.Fatal("'images' should have a header")
|
||||
}
|
||||
if !strings.Contains(output, "docker-ut") {
|
||||
t.Fatal("'images' should show the docker-ut image")
|
||||
}
|
||||
if !strings.Contains(output, "e9aa60c60128") {
|
||||
t.Fatal("'images' should show the docker-ut image id")
|
||||
}
|
||||
|
||||
output, err = cmdImages(srv, "-q")
|
||||
|
||||
if strings.Contains(output, "REPOSITORY") {
|
||||
t.Fatal("'images -q' should not have a header")
|
||||
}
|
||||
if strings.Contains(output, "docker-ut") {
|
||||
t.Fatal("'images' should not show the docker-ut image name")
|
||||
}
|
||||
if !strings.Contains(output, "e9aa60c60128") {
|
||||
t.Fatal("'images' should show the docker-ut image id")
|
||||
}
|
||||
|
||||
output, err = cmdImages(srv, "-viz")
|
||||
|
||||
if !strings.HasPrefix(output, "digraph docker {") {
|
||||
t.Fatal("'images -v' should start with the dot header")
|
||||
}
|
||||
if !strings.HasSuffix(output, "}\n") {
|
||||
t.Fatal("'images -v' should end with a '}'")
|
||||
}
|
||||
if !strings.Contains(output, "base -> \"e9aa60c60128\" [style=invis]") {
|
||||
t.Fatal("'images -v' should have the docker-ut image id node")
|
||||
}
|
||||
|
||||
// todo: add checks for -a
|
||||
}
|
||||
|
||||
*/
|
||||
// TestRunHostname checks that 'docker run -h' correctly sets a custom hostname
|
||||
func TestRunHostname(t *testing.T) {
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(nil, stdoutPipe, nil, testDaemonProto, testDaemonAddr)
|
||||
cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
if err := cli.CmdRun("-h", "foobar", unitTestImageId, "hostname"); err != nil {
|
||||
if err := cli.CmdRun("-h", "foobar", unitTestImageID, "hostname"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
utils.Debugf("--")
|
||||
|
||||
setTimeout(t, "Reading command output time out", 2*time.Second, func() {
|
||||
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
|
||||
if err != nil {
|
||||
@@ -163,21 +90,78 @@ func TestRunHostname(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
// TestRunWorkdir checks that 'docker run -w' correctly sets a custom working directory
|
||||
func TestRunWorkdir(t *testing.T) {
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
if err := cli.CmdRun("-w", "/foo/bar", unitTestImageID, "pwd"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
setTimeout(t, "Reading command output time out", 2*time.Second, func() {
|
||||
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cmdOutput != "/foo/bar\n" {
|
||||
t.Fatalf("'pwd' should display '%s', not '%s'", "/foo/bar\n", cmdOutput)
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
|
||||
<-c
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// TestRunWorkdirExists checks that 'docker run -w' correctly sets a custom working directory, even if it exists
|
||||
func TestRunWorkdirExists(t *testing.T) {
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
if err := cli.CmdRun("-w", "/proc", unitTestImageID, "pwd"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
setTimeout(t, "Reading command output time out", 2*time.Second, func() {
|
||||
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cmdOutput != "/proc\n" {
|
||||
t.Fatalf("'pwd' should display '%s', not '%s'", "/proc\n", cmdOutput)
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
|
||||
<-c
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestRunExit(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
c1 := make(chan struct{})
|
||||
go func() {
|
||||
srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", GetTestImage(runtime).Id, "/bin/cat")
|
||||
cli.CmdRun("-i", unitTestImageID, "/bin/cat")
|
||||
close(c1)
|
||||
}()
|
||||
|
||||
@@ -187,21 +171,24 @@ func TestRunExit(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
container := runtime.List()[0]
|
||||
container := globalRuntime.List()[0]
|
||||
|
||||
// Closing /bin/cat stdin, expect it to exit
|
||||
p, err := container.StdinPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := p.Close(); err != nil {
|
||||
if err := stdin.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// as the process exited, CmdRun must finish and unblock. Wait for it
|
||||
setTimeout(t, "Waiting for CmdRun timed out", 2*time.Second, func() {
|
||||
setTimeout(t, "Waiting for CmdRun timed out", 10*time.Second, func() {
|
||||
<-c1
|
||||
cmdWait(srv, container)
|
||||
|
||||
go func() {
|
||||
cli.CmdWait(container.ID)
|
||||
}()
|
||||
|
||||
if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
// Make sure that the client has been disconnected
|
||||
@@ -218,21 +205,18 @@ func TestRunExit(t *testing.T) {
|
||||
|
||||
// Expected behaviour: the process dies when the client disconnects
|
||||
func TestRunDisconnect(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
c1 := make(chan struct{})
|
||||
go func() {
|
||||
// We're simulating a disconnect so the return value doesn't matter. What matters is the
|
||||
// fact that CmdRun returns.
|
||||
srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", GetTestImage(runtime).Id, "/bin/cat")
|
||||
cli.CmdRun("-i", unitTestImageID, "/bin/cat")
|
||||
close(c1)
|
||||
}()
|
||||
|
||||
@@ -256,7 +240,7 @@ func TestRunDisconnect(t *testing.T) {
|
||||
// Client disconnect after run -i should cause stdin to be closed, which should
|
||||
// cause /bin/cat to exit.
|
||||
setTimeout(t, "Waiting for /bin/cat to exit timed out", 2*time.Second, func() {
|
||||
container := runtime.List()[0]
|
||||
container := globalRuntime.List()[0]
|
||||
container.Wait()
|
||||
if container.State.Running {
|
||||
t.Fatalf("/bin/cat is still running after closing stdin")
|
||||
@@ -266,40 +250,39 @@ func TestRunDisconnect(t *testing.T) {
|
||||
|
||||
// Expected behaviour: the process dies when the client disconnects
|
||||
func TestRunDisconnectTty(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
c1 := make(chan struct{})
|
||||
go func() {
|
||||
// We're simulating a disconnect so the return value doesn't matter. What matters is the
|
||||
// fact that CmdRun returns.
|
||||
srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", "-t", GetTestImage(runtime).Id, "/bin/cat")
|
||||
if err := cli.CmdRun("-i", "-t", unitTestImageID, "/bin/cat"); err != nil {
|
||||
utils.Debugf("Error CmdRun: %s\n", err)
|
||||
}
|
||||
|
||||
close(c1)
|
||||
}()
|
||||
|
||||
setTimeout(t, "Waiting for the container to be started timed out", 2*time.Second, func() {
|
||||
setTimeout(t, "Waiting for the container to be started timed out", 10*time.Second, func() {
|
||||
for {
|
||||
// Client disconnect after run -i should keep stdin out in TTY mode
|
||||
l := runtime.List()
|
||||
l := globalRuntime.List()
|
||||
if len(l) == 1 && l[0].State.Running {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
})
|
||||
|
||||
// Client disconnect after run -i should keep stdin out in TTY mode
|
||||
container := runtime.List()[0]
|
||||
container := globalRuntime.List()[0]
|
||||
|
||||
setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
|
||||
setTimeout(t, "Read/Write assertion timed out", 2000*time.Second, func() {
|
||||
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -319,7 +302,6 @@ func TestRunDisconnectTty(t *testing.T) {
|
||||
t.Fatalf("/bin/cat should still be running after closing stdin (tty mode)")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// TestAttachStdin checks attaching to stdin without stdout and stderr.
|
||||
// 'docker run -i -a stdin' should sends the client's stdin to the command,
|
||||
@@ -329,13 +311,13 @@ func TestRunAttachStdin(t *testing.T) {
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(stdin, stdoutPipe, nil, testDaemonProto, testDaemonAddr)
|
||||
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
defer close(ch)
|
||||
cli.CmdRun("-i", "-a", "stdin", unitTestImageId, "sh", "-c", "echo hello && cat")
|
||||
cli.CmdRun("-i", "-a", "stdin", unitTestImageID, "sh", "-c", "echo hello && cat && sleep 5")
|
||||
}()
|
||||
|
||||
// Send input to the command, close stdin
|
||||
@@ -363,69 +345,173 @@ func TestRunAttachStdin(t *testing.T) {
|
||||
|
||||
// wait for CmdRun to return
|
||||
setTimeout(t, "Waiting for CmdRun timed out", 5*time.Second, func() {
|
||||
// Unblock hijack end
|
||||
stdout.Read([]byte{})
|
||||
<-ch
|
||||
})
|
||||
|
||||
setTimeout(t, "Waiting for command to exit timed out", 5*time.Second, func() {
|
||||
setTimeout(t, "Waiting for command to exit timed out", 10*time.Second, func() {
|
||||
container.Wait()
|
||||
})
|
||||
|
||||
// Check logs
|
||||
if cmdLogs, err := container.ReadLog("stdout"); err != nil {
|
||||
if cmdLogs, err := container.ReadLog("json"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if output, err := ioutil.ReadAll(cmdLogs); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
expectedLog := "hello\nhi there\n"
|
||||
if string(output) != expectedLog {
|
||||
t.Fatalf("Unexpected logs: should be '%s', not '%s'\n", expectedLog, output)
|
||||
expectedLogs := []string{"{\"log\":\"hello\\n\",\"stream\":\"stdout\"", "{\"log\":\"hi there\\n\",\"stream\":\"stdout\""}
|
||||
for _, expectedLog := range expectedLogs {
|
||||
if !strings.Contains(string(output), expectedLog) {
|
||||
t.Fatalf("Unexpected logs: should contains '%s', it is not '%s'\n", expectedLog, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Expected behaviour, the process stays alive when the client disconnects
|
||||
func TestAttachDisconnect(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, err := NewBuilder(runtime).Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).Id,
|
||||
CpuShares: 1000,
|
||||
Memory: 33554432,
|
||||
Cmd: []string{"/bin/cat"},
|
||||
OpenStdin: true,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer runtime.Destroy(container)
|
||||
|
||||
// Start the process
|
||||
if err := container.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// TestRunDetach checks attaching and detaching with the escape sequence.
|
||||
func TestRunDetach(t *testing.T) {
|
||||
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
defer close(ch)
|
||||
cli.CmdRun("-i", "-t", unitTestImageID, "cat")
|
||||
}()
|
||||
|
||||
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
|
||||
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
container := globalRuntime.List()[0]
|
||||
|
||||
setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
|
||||
stdinPipe.Write([]byte{'', ''})
|
||||
if err := stdinPipe.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
// wait for CmdRun to return
|
||||
setTimeout(t, "Waiting for CmdRun timed out", 15*time.Second, func() {
|
||||
<-ch
|
||||
})
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if !container.State.Running {
|
||||
t.Fatal("The detached container should be still running")
|
||||
}
|
||||
|
||||
setTimeout(t, "Waiting for container to die timed out", 20*time.Second, func() {
|
||||
container.Kill()
|
||||
container.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
// TestAttachDetach checks that attach in tty mode can be detached
|
||||
func TestAttachDetach(t *testing.T) {
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
go stdout.Read(make([]byte, 1024))
|
||||
setTimeout(t, "Starting container timed out", 2*time.Second, func() {
|
||||
if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
container := globalRuntime.List()[0]
|
||||
|
||||
stdin, stdinPipe = io.Pipe()
|
||||
stdout, stdoutPipe = io.Pipe()
|
||||
cli = NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
defer close(ch)
|
||||
if err := cli.CmdAttach(container.ShortID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
|
||||
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
|
||||
stdinPipe.Write([]byte{'', ''})
|
||||
if err := stdinPipe.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
// wait for CmdRun to return
|
||||
setTimeout(t, "Waiting for CmdAttach timed out", 15*time.Second, func() {
|
||||
<-ch
|
||||
})
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if !container.State.Running {
|
||||
t.Fatal("The detached container should be still running")
|
||||
}
|
||||
|
||||
setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() {
|
||||
container.Kill()
|
||||
container.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
// Expected behaviour, the process stays alive when the client disconnects
|
||||
func TestAttachDisconnect(t *testing.T) {
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
go func() {
|
||||
// Start a process in daemon mode
|
||||
if err := cli.CmdRun("-d", "-i", unitTestImageID, "/bin/cat"); err != nil {
|
||||
utils.Debugf("Error CmdRun: %s\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
setTimeout(t, "Waiting for CmdRun timed out", 10*time.Second, func() {
|
||||
if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(t, "Waiting for the container to be started timed out", 10*time.Second, func() {
|
||||
for {
|
||||
l := globalRuntime.List()
|
||||
if len(l) == 1 && l[0].State.Running {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
})
|
||||
|
||||
container := globalRuntime.List()[0]
|
||||
|
||||
// Attach to it
|
||||
c1 := make(chan struct{})
|
||||
go func() {
|
||||
// We're simulating a disconnect so the return value doesn't matter. What matters is the
|
||||
// fact that CmdAttach returns.
|
||||
srv.CmdAttach(stdin, rcli.NewDockerLocalConn(stdoutPipe), container.Id)
|
||||
cli.CmdAttach(container.ID)
|
||||
close(c1)
|
||||
}()
|
||||
|
||||
@@ -446,14 +532,51 @@ func TestAttachDisconnect(t *testing.T) {
|
||||
|
||||
// We closed stdin, expect /bin/cat to still be running
|
||||
// Wait a little bit to make sure container.monitor() did his thing
|
||||
err = container.WaitTimeout(500 * time.Millisecond)
|
||||
err := container.WaitTimeout(500 * time.Millisecond)
|
||||
if err == nil || !container.State.Running {
|
||||
t.Fatalf("/bin/cat is not running after closing stdin")
|
||||
}
|
||||
|
||||
// Try to avoid the timeoout in destroy. Best effort, don't check error
|
||||
// Try to avoid the timeout in destroy. Best effort, don't check error
|
||||
cStdin, _ := container.StdinPipe()
|
||||
cStdin.Close()
|
||||
container.Wait()
|
||||
}
|
||||
*/
|
||||
|
||||
// Expected behaviour: container gets deleted automatically after exit
|
||||
func TestRunAutoRemove(t *testing.T) {
|
||||
t.Skip("Fixme. Skipping test for now, race condition")
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
if err := cli.CmdRun("-rm", unitTestImageID, "hostname"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
var temporaryContainerID string
|
||||
setTimeout(t, "Reading command output time out", 2*time.Second, func() {
|
||||
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
temporaryContainerID = cmdOutput
|
||||
if err := closeWrap(stdout, stdoutPipe); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
|
||||
<-c
|
||||
})
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
if len(globalRuntime.List()) > 0 {
|
||||
t.Fatalf("failed to remove container automatically: container %s still exists", temporaryContainerID)
|
||||
}
|
||||
}
|
||||
|
||||
652
container.go
652
container.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
23
contrib/Dockerfile.tmLanguage/Dockerfile.YAML-tmLanguage
Normal file
23
contrib/Dockerfile.tmLanguage/Dockerfile.YAML-tmLanguage
Normal file
@@ -0,0 +1,23 @@
|
||||
# [PackageDev] target_format: plist, ext: tmLanguage
|
||||
---
|
||||
name: Dockerfile
|
||||
scopeName: source.dockerfile
|
||||
uuid: a39d8795-59d2-49af-aa00-fe74ee29576e
|
||||
|
||||
patterns:
|
||||
# Keywords
|
||||
- name: keyword.control.dockerfile
|
||||
match: ^\s*(FROM|MAINTAINER|RUN|CMD|EXPOSE|ENV|ADD)\s
|
||||
- name: keyword.operator.dockerfile
|
||||
match: ^\s*(ENTRYPOINT|VOLUME|USER|WORKDIR)\s
|
||||
# String
|
||||
- name: string.quoted.double.dockerfile
|
||||
begin: "\""
|
||||
end: "\""
|
||||
patterns:
|
||||
- name: constant.character.escaped.dockerfile
|
||||
match: \\.
|
||||
# Comment
|
||||
- name: comment.block.dockerfile
|
||||
match: ^\s*#.*$
|
||||
...
|
||||
50
contrib/Dockerfile.tmLanguage/Dockerfile.tmLanguage
Normal file
50
contrib/Dockerfile.tmLanguage/Dockerfile.tmLanguage
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Dockerfile</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*(FROM|MAINTAINER|RUN|CMD|EXPOSE|ENV|ADD)\s</string>
|
||||
<key>name</key>
|
||||
<string>keyword.control.dockerfile</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*(ENTRYPOINT|VOLUME|USER|WORKDIR)\s</string>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.dockerfile</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>"</string>
|
||||
<key>end</key>
|
||||
<string>"</string>
|
||||
<key>name</key>
|
||||
<string>string.quoted.double.dockerfile</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\.</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escaped.dockerfile</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*#.*$</string>
|
||||
<key>name</key>
|
||||
<string>comment.block.dockerfile</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>scopeName</key>
|
||||
<string>source.dockerfile</string>
|
||||
<key>uuid</key>
|
||||
<string>a39d8795-59d2-49af-aa00-fe74ee29576e</string>
|
||||
</dict>
|
||||
</plist>
|
||||
1
contrib/Dockerfile.tmLanguage/MAINTAINERS
Normal file
1
contrib/Dockerfile.tmLanguage/MAINTAINERS
Normal file
@@ -0,0 +1 @@
|
||||
Asbjorn Enge <asbjorn@hanafjedle.net> (@asbjornenge)
|
||||
9
contrib/Dockerfile.tmLanguage/README.md
Normal file
9
contrib/Dockerfile.tmLanguage/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Dockerfile.tmLanguage
|
||||
|
||||
Pretty basic Dockerfile.tmLanguage for Sublime Text syntax highlighting.
|
||||
|
||||
PR's with syntax updates, suggestions etc. are all very much appreciated!
|
||||
|
||||
I'll get to making this installable via Package Control soon!
|
||||
|
||||
enjoy.
|
||||
@@ -1 +1 @@
|
||||
# Maintainer wanted! Enroll on #docker@freenode
|
||||
Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
|
||||
1
contrib/brew/.gitignore
vendored
Normal file
1
contrib/brew/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.pyc
|
||||
78
contrib/brew/README.md
Normal file
78
contrib/brew/README.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# docker-brew
|
||||
|
||||
docker-brew is a command-line tool used to build the docker standard library.
|
||||
|
||||
## Install instructions
|
||||
|
||||
1. Install python if it isn't already available on your OS of choice
|
||||
1. Install the easy_install tool (`sudo apt-get install python-setuptools`
|
||||
for Debian)
|
||||
1. Install the python package manager, `pip` (`easy_install pip`)
|
||||
1. Run the following command: `sudo pip install -r requirements.txt`
|
||||
1. You should now be able to use the `docker-brew` script as such.
|
||||
|
||||
## Basics
|
||||
|
||||
./docker-brew -h
|
||||
|
||||
Display usage and help.
|
||||
|
||||
./docker-brew
|
||||
|
||||
Default build from the default repo/branch. Images will be created under the
|
||||
`library/` namespace. Does not perform a remote push.
|
||||
|
||||
./docker-brew -n mycorp.com -b stable --push git://github.com/mycorp/docker
|
||||
|
||||
Will fetch the library definition files in the `stable` branch of the
|
||||
`git://github.com/mycorp/docker` repository and create images under the
|
||||
`mycorp.com` namespace (e.g. `mycorp.com/ubuntu`). Created images will then
|
||||
be pushed to the official docker repository (pending: support for private
|
||||
repositories)
|
||||
|
||||
## Library definition files
|
||||
|
||||
The library definition files are plain text files found in the `library/`
|
||||
subfolder of the docker repository.
|
||||
|
||||
### File names
|
||||
|
||||
The name of a definition file will determine the name of the image(s) it
|
||||
creates. For example, the `library/ubuntu` file will create images in the
|
||||
`<namespace>/ubuntu` repository. If multiple instructions are present in
|
||||
a single file, all images are expected to be created under a different tag.
|
||||
|
||||
### Instruction format
|
||||
|
||||
Each line represents a build instruction.
|
||||
There are different formats that `docker-brew` is able to parse.
|
||||
|
||||
<git-url>
|
||||
git://github.com/dotcloud/hipache
|
||||
https://github.com/dotcloud/docker.git
|
||||
|
||||
The simplest format. `docker-brew` will fetch data from the provided git
|
||||
repository from the `HEAD`of its `master` branch. Generated image will be
|
||||
tagged as `latest`. Use of this format is discouraged because there is no
|
||||
way to ensure stability.
|
||||
|
||||
<docker-tag> <git-url>
|
||||
bleeding-edge git://github.com/dotcloud/docker
|
||||
unstable https://github.com/dotcloud/docker-redis.git
|
||||
|
||||
A more advanced format. `docker-brew` will fetch data from the provided git
|
||||
repository from the `HEAD`of its `master` branch. Generated image will be
|
||||
tagged as `<docker-tag>`. Recommended if we always want to provide a snapshot
|
||||
of the latest development. Again, no way to ensure stability.
|
||||
|
||||
<docker-tag> <git-url> T:<git-tag>
|
||||
2.4.0 git://github.com/dotcloud/docker-redis T:2.4.0
|
||||
<docker-tag> <git-url> B:<git-branch>
|
||||
zfs git://github.com/dotcloud/docker B:zfs-support
|
||||
<docker-tag> <git-url> C:<git-commit-id>
|
||||
2.2.0 https://github.com/dotcloud/docker-redis.git C:a4bf8923ee4ec566d3ddc212
|
||||
|
||||
The most complete format. `docker-brew` will fetch data from the provided git
|
||||
repository from the provided reference (if it's a branch, brew will fetch its
|
||||
`HEAD`). Generated image will be tagged as `<docker-tag>`. Recommended whenever
|
||||
possible.
|
||||
1
contrib/brew/brew/__init__.py
Normal file
1
contrib/brew/brew/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from brew import build_library, DEFAULT_REPOSITORY, DEFAULT_BRANCH
|
||||
185
contrib/brew/brew/brew.py
Normal file
185
contrib/brew/brew/brew.py
Normal file
@@ -0,0 +1,185 @@
|
||||
import os
|
||||
import logging
|
||||
from shutil import rmtree
|
||||
|
||||
import docker
|
||||
|
||||
import git
|
||||
|
||||
DEFAULT_REPOSITORY = 'git://github.com/dotcloud/docker'
|
||||
DEFAULT_BRANCH = 'master'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
|
||||
level='INFO')
|
||||
client = docker.Client()
|
||||
processed = {}
|
||||
processed_folders = []
|
||||
|
||||
|
||||
def build_library(repository=None, branch=None, namespace=None, push=False,
|
||||
debug=False, prefill=True, registry=None):
|
||||
dst_folder = None
|
||||
summary = Summary()
|
||||
if repository is None:
|
||||
repository = DEFAULT_REPOSITORY
|
||||
if branch is None:
|
||||
branch = DEFAULT_BRANCH
|
||||
if debug:
|
||||
logger.setLevel('DEBUG')
|
||||
|
||||
if not (repository.startswith('https://') or repository.startswith('git://')):
|
||||
logger.info('Repository provided assumed to be a local path')
|
||||
dst_folder = repository
|
||||
|
||||
try:
|
||||
client.version()
|
||||
except Exception as e:
|
||||
logger.error('Could not reach the docker daemon. Please make sure it '
|
||||
'is running.')
|
||||
logger.warning('Also make sure you have access to the docker UNIX '
|
||||
'socket (use sudo)')
|
||||
return
|
||||
|
||||
#FIXME: set destination folder and only pull latest changes instead of
|
||||
# cloning the whole repo everytime
|
||||
if not dst_folder:
|
||||
logger.info('Cloning docker repo from {0}, branch: {1}'.format(
|
||||
repository, branch))
|
||||
try:
|
||||
rep, dst_folder = git.clone_branch(repository, branch)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
logger.error('Source repository could not be fetched. Check '
|
||||
'that the address is correct and the branch exists.')
|
||||
return
|
||||
try:
|
||||
dirlist = os.listdir(os.path.join(dst_folder, 'library'))
|
||||
except OSError as e:
|
||||
logger.error('The path provided ({0}) could not be found or didn\'t'
|
||||
'contain a library/ folder.'.format(dst_folder))
|
||||
return
|
||||
for buildfile in dirlist:
|
||||
if buildfile == 'MAINTAINERS':
|
||||
continue
|
||||
f = open(os.path.join(dst_folder, 'library', buildfile))
|
||||
linecnt = 0
|
||||
for line in f:
|
||||
linecnt = linecnt + 1
|
||||
logger.debug('{0} ---> {1}'.format(buildfile, line))
|
||||
args = line.split()
|
||||
try:
|
||||
if len(args) > 3:
|
||||
raise RuntimeError('Incorrect line format, '
|
||||
'please refer to the docs')
|
||||
|
||||
url = None
|
||||
ref = 'refs/heads/master'
|
||||
tag = None
|
||||
if len(args) == 1: # Just a URL, simple mode
|
||||
url = args[0]
|
||||
elif len(args) == 2 or len(args) == 3: # docker-tag url
|
||||
url = args[1]
|
||||
tag = args[0]
|
||||
|
||||
if len(args) == 3: # docker-tag url B:branch or T:tag
|
||||
ref = None
|
||||
if args[2].startswith('B:'):
|
||||
ref = 'refs/heads/' + args[2][2:]
|
||||
elif args[2].startswith('T:'):
|
||||
ref = 'refs/tags/' + args[2][2:]
|
||||
elif args[2].startswith('C:'):
|
||||
ref = args[2][2:]
|
||||
else:
|
||||
raise RuntimeError('Incorrect line format, '
|
||||
'please refer to the docs')
|
||||
if prefill:
|
||||
logger.debug('Pulling {0} from official repository (cache '
|
||||
'fill)'.format(buildfile))
|
||||
client.pull(buildfile)
|
||||
img = build_repo(url, ref, buildfile, tag, namespace, push,
|
||||
registry)
|
||||
summary.add_success(buildfile, (linecnt, line), img)
|
||||
processed['{0}@{1}'.format(url, ref)] = img
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
summary.add_exception(buildfile, (linecnt, line), e)
|
||||
|
||||
f.close()
|
||||
if dst_folder != repository:
|
||||
rmtree(dst_folder, True)
|
||||
for d in processed_folders:
|
||||
rmtree(d, True)
|
||||
summary.print_summary(logger)
|
||||
|
||||
|
||||
def build_repo(repository, ref, docker_repo, docker_tag, namespace, push, registry):
|
||||
docker_repo = '{0}/{1}'.format(namespace or 'library', docker_repo)
|
||||
img_id = None
|
||||
dst_folder = None
|
||||
if '{0}@{1}'.format(repository, ref) not in processed.keys():
|
||||
logger.info('Cloning {0} (ref: {1})'.format(repository, ref))
|
||||
if repository not in processed:
|
||||
rep, dst_folder = git.clone(repository, ref)
|
||||
processed[repository] = rep
|
||||
processed_folders.append(dst_folder)
|
||||
else:
|
||||
dst_folder = git.checkout(processed[repository], ref)
|
||||
if not 'Dockerfile' in os.listdir(dst_folder):
|
||||
raise RuntimeError('Dockerfile not found in cloned repository')
|
||||
logger.info('Building using dockerfile...')
|
||||
img_id, logs = client.build(path=dst_folder, quiet=True)
|
||||
else:
|
||||
img_id = processed['{0}@{1}'.format(repository, ref)]
|
||||
logger.info('Committing to {0}:{1}'.format(docker_repo,
|
||||
docker_tag or 'latest'))
|
||||
client.tag(img_id, docker_repo, docker_tag)
|
||||
if push:
|
||||
logger.info('Pushing result to registry {0}'.format(
|
||||
registry or "default"))
|
||||
if registry is not None:
|
||||
docker_repo = '{0}/{1}'.format(registry, docker_repo)
|
||||
logger.info('Also tagging {0}'.format(docker_repo))
|
||||
client.tag(img_id, docker_repo, docker_tag)
|
||||
client.push(docker_repo)
|
||||
return img_id
|
||||
|
||||
|
||||
class Summary(object):
|
||||
def __init__(self):
|
||||
self._summary = {}
|
||||
self._has_exc = False
|
||||
|
||||
def _add_data(self, image, linestr, data):
|
||||
if image not in self._summary:
|
||||
self._summary[image] = { linestr: data }
|
||||
else:
|
||||
self._summary[image][linestr] = data
|
||||
|
||||
def add_exception(self, image, line, exc):
|
||||
lineno, linestr = line
|
||||
self._add_data(image, linestr, { 'line': lineno, 'exc': str(exc) })
|
||||
self._has_exc = True
|
||||
|
||||
def add_success(self, image, line, img_id):
|
||||
lineno, linestr = line
|
||||
self._add_data(image, linestr, { 'line': lineno, 'id': img_id })
|
||||
|
||||
def print_summary(self, logger=None):
|
||||
linesep = ''.center(61, '-') + '\n'
|
||||
s = 'BREW BUILD SUMMARY\n' + linesep
|
||||
success = 'OVERALL SUCCESS: {}\n'.format(not self._has_exc)
|
||||
details = linesep
|
||||
for image, lines in self._summary.iteritems():
|
||||
details = details + '{}\n{}'.format(image, linesep)
|
||||
for linestr, data in lines.iteritems():
|
||||
details = details + '{0:2} | {1} | {2:50}\n'.format(
|
||||
data['line'],
|
||||
'KO' if 'exc' in data else 'OK',
|
||||
data['exc'] if 'exc' in data else data['id']
|
||||
)
|
||||
details = details + linesep
|
||||
if logger:
|
||||
logger.info(s + success + details)
|
||||
else:
|
||||
print s, success, details
|
||||
63
contrib/brew/brew/git.py
Normal file
63
contrib/brew/brew/git.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import tempfile
|
||||
import logging
|
||||
|
||||
from dulwich import index
|
||||
from dulwich.client import get_transport_and_path
|
||||
from dulwich.repo import Repo
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def clone_branch(repo_url, branch="master", folder=None):
|
||||
return clone(repo_url, 'refs/heads/' + branch, folder)
|
||||
|
||||
|
||||
def clone_tag(repo_url, tag, folder=None):
|
||||
return clone(repo_url, 'refs/tags/' + tag, folder)
|
||||
|
||||
|
||||
def checkout(rep, ref=None):
|
||||
is_commit = False
|
||||
if ref is None:
|
||||
ref = 'refs/heads/master'
|
||||
elif not ref.startswith('refs/'):
|
||||
is_commit = True
|
||||
if is_commit:
|
||||
rep['HEAD'] = rep.commit(ref)
|
||||
else:
|
||||
rep['HEAD'] = rep.refs[ref]
|
||||
indexfile = rep.index_path()
|
||||
tree = rep["HEAD"].tree
|
||||
index.build_index_from_tree(rep.path, indexfile, rep.object_store, tree)
|
||||
return rep.path
|
||||
|
||||
def clone(repo_url, ref=None, folder=None):
|
||||
is_commit = False
|
||||
if ref is None:
|
||||
ref = 'refs/heads/master'
|
||||
elif not ref.startswith('refs/'):
|
||||
is_commit = True
|
||||
logger.debug("clone repo_url={0}, ref={1}".format(repo_url, ref))
|
||||
if folder is None:
|
||||
folder = tempfile.mkdtemp()
|
||||
logger.debug("folder = {0}".format(folder))
|
||||
rep = Repo.init(folder)
|
||||
client, relative_path = get_transport_and_path(repo_url)
|
||||
logger.debug("client={0}".format(client))
|
||||
|
||||
remote_refs = client.fetch(relative_path, rep)
|
||||
for k, v in remote_refs.iteritems():
|
||||
try:
|
||||
rep.refs.add_if_new(k, v)
|
||||
except:
|
||||
pass
|
||||
|
||||
if is_commit:
|
||||
rep['HEAD'] = rep.commit(ref)
|
||||
else:
|
||||
rep['HEAD'] = remote_refs[ref]
|
||||
indexfile = rep.index_path()
|
||||
tree = rep["HEAD"].tree
|
||||
index.build_index_from_tree(rep.path, indexfile, rep.object_store, tree)
|
||||
logger.debug("done")
|
||||
return rep, folder
|
||||
35
contrib/brew/docker-brew
Executable file
35
contrib/brew/docker-brew
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
try:
|
||||
import brew
|
||||
except ImportError as e:
|
||||
print str(e)
|
||||
print 'Please install the required dependencies first'
|
||||
print 'sudo pip install -r requirements.txt'
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser('Build the docker standard library')
|
||||
parser.add_argument('--push', action='store_true', default=False,
|
||||
help='Push generated repositories')
|
||||
parser.add_argument('--debug', default=False, action='store_true',
|
||||
help='Enable debugging output')
|
||||
parser.add_argument('--noprefill', default=True, action='store_false',
|
||||
dest='prefill', help='Disable cache prefill')
|
||||
parser.add_argument('-n', metavar='NAMESPACE', default='library',
|
||||
help='Namespace used for generated repositories.'
|
||||
' Default is library')
|
||||
parser.add_argument('-b', metavar='BRANCH', default=brew.DEFAULT_BRANCH,
|
||||
help='Branch in the repository where the library definition'
|
||||
' files will be fetched. Default is ' + brew.DEFAULT_BRANCH)
|
||||
parser.add_argument('repository', default=brew.DEFAULT_REPOSITORY,
|
||||
nargs='?', help='git repository containing the library definition'
|
||||
' files. Default is ' + brew.DEFAULT_REPOSITORY)
|
||||
parser.add_argument('--reg', default=None, help='Registry address to'
|
||||
' push build results to. Also sets push to true.')
|
||||
args = parser.parse_args()
|
||||
brew.build_library(args.repository, args.b, args.n,
|
||||
args.push or args.reg is not None, args.debug, args.prefill, args.reg)
|
||||
2
contrib/brew/requirements.txt
Normal file
2
contrib/brew/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
dulwich==0.9.0
|
||||
-e git://github.com/dotcloud/docker-py.git#egg=docker-py
|
||||
22
contrib/brew/setup.py
Normal file
22
contrib/brew/setup.py
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
from setuptools import setup
|
||||
|
||||
ROOT_DIR = os.path.dirname(__file__)
|
||||
SOURCE_DIR = os.path.join(ROOT_DIR)
|
||||
|
||||
test_requirements = []
|
||||
setup(
|
||||
name="docker-brew",
|
||||
version='0.0.1',
|
||||
description="-",
|
||||
packages=['dockerbrew'],
|
||||
install_requires=['dulwich', 'docker'] + test_requirements,
|
||||
zip_safe=False,
|
||||
classifiers=['Development Status :: 3 - Alpha',
|
||||
'Environment :: Other Environment',
|
||||
'Intended Audience :: Developers',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Utilities'],
|
||||
)
|
||||
543
contrib/completion/bash/docker
Executable file
543
contrib/completion/bash/docker
Executable file
@@ -0,0 +1,543 @@
|
||||
#!bash
|
||||
#
|
||||
# bash completion file for core docker commands
|
||||
#
|
||||
# This script provides supports completion of:
|
||||
# - commands and their options
|
||||
# - container ids
|
||||
# - image repos and tags
|
||||
# - filepaths
|
||||
#
|
||||
# To enable the completions either:
|
||||
# - place this file in /etc/bash_completion.d
|
||||
# or
|
||||
# - copy this file and add the line below to your .bashrc after
|
||||
# bash completion features are loaded
|
||||
# . docker.bash
|
||||
#
|
||||
# Note:
|
||||
# Currently, the completions will not work if the docker daemon is not
|
||||
# bound to the default communication port/socket
|
||||
# If the docker daemon is using a unix socket for communication your user
|
||||
# must have access to the socket for the completions to function correctly
|
||||
|
||||
__docker_containers_all()
|
||||
{
|
||||
local containers
|
||||
containers="$( docker ps -a -q )"
|
||||
COMPREPLY=( $( compgen -W "$containers" -- "$cur" ) )
|
||||
}
|
||||
|
||||
__docker_containers_running()
|
||||
{
|
||||
local containers
|
||||
containers="$( docker ps -q )"
|
||||
COMPREPLY=( $( compgen -W "$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" ) )
|
||||
}
|
||||
|
||||
__docker_image_repos()
|
||||
{
|
||||
local repos
|
||||
repos="$( docker images | awk 'NR>1{print $1}' )"
|
||||
COMPREPLY=( $( compgen -W "$repos" -- "$cur" ) )
|
||||
}
|
||||
|
||||
__docker_images()
|
||||
{
|
||||
local images
|
||||
images="$( docker images | awk 'NR>1{print $1":"$2}' )"
|
||||
COMPREPLY=( $( compgen -W "$images" -- "$cur" ) )
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
__docker_image_repos_and_tags()
|
||||
{
|
||||
local repos images
|
||||
repos="$( docker images | awk 'NR>1{print $1}' )"
|
||||
images="$( docker images | awk 'NR>1{print $1":"$2}' )"
|
||||
COMPREPLY=( $( compgen -W "$repos $images" -- "$cur" ) )
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
__docker_containers_and_images()
|
||||
{
|
||||
local containers images
|
||||
containers="$( docker ps -a -q )"
|
||||
images="$( docker images | awk 'NR>1{print $1":"$2}' )"
|
||||
COMPREPLY=( $( compgen -W "$images $containers" -- "$cur" ) )
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
_docker_docker()
|
||||
{
|
||||
case "$prev" in
|
||||
-H)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-H" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=( $( compgen -W "$commands help" -- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_attach()
|
||||
{
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_running
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_build()
|
||||
{
|
||||
case "$prev" in
|
||||
-t)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-no-cache -t -q -rm" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_commit()
|
||||
{
|
||||
case "$prev" in
|
||||
-author|-m|-run)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-author -m -run" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$cpos
|
||||
while [ $counter -le $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
-author|-m|-run)
|
||||
(( counter++ ))
|
||||
;;
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
(( counter++ ))
|
||||
done
|
||||
|
||||
if [ $counter -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_cp()
|
||||
{
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
else
|
||||
_filedir
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_diff()
|
||||
{
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_events()
|
||||
{
|
||||
case "$prev" in
|
||||
-since)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-since" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_export()
|
||||
{
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_help()
|
||||
{
|
||||
if [ $cpos -eq $cword ]; then
|
||||
COMPREPLY=( $( compgen -W "$commands" -- "$cur" ) )
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_history()
|
||||
{
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_image_repos_and_tags
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_images()
|
||||
{
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-a -notrunc -q -viz" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$cpos
|
||||
while [ $counter -le $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
(( counter++ ))
|
||||
done
|
||||
|
||||
if [ $counter -eq $cword ]; then
|
||||
__docker_image_repos
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_import()
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
_docker_info()
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
_docker_insert()
|
||||
{
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_image_repos_and_tags
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_inspect()
|
||||
{
|
||||
__docker_containers_and_images
|
||||
}
|
||||
|
||||
_docker_kill()
|
||||
{
|
||||
__docker_containers_running
|
||||
}
|
||||
|
||||
_docker_login()
|
||||
{
|
||||
case "$prev" in
|
||||
-e|-p|-u)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-e -p -u" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_logs()
|
||||
{
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_port()
|
||||
{
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_all
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_ps()
|
||||
{
|
||||
case "$prev" in
|
||||
-beforeId|-n|-sinceId)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-a -beforeId -l -n -notrunc -q -s -sinceId" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_pull()
|
||||
{
|
||||
case "$prev" in
|
||||
-t)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-t" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_push()
|
||||
{
|
||||
__docker_image_repos
|
||||
}
|
||||
|
||||
_docker_restart()
|
||||
{
|
||||
case "$prev" in
|
||||
-t)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-t" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_containers_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_rm()
|
||||
{
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-v" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_containers_stopped
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_rmi()
|
||||
{
|
||||
__docker_image_repos_and_tags
|
||||
}
|
||||
|
||||
_docker_run()
|
||||
{
|
||||
case "$prev" in
|
||||
-cidfile)
|
||||
_filedir
|
||||
;;
|
||||
-volumes-from)
|
||||
__docker_containers_all
|
||||
;;
|
||||
-a|-c|-dns|-e|-entrypoint|-h|-lxc-conf|-m|-p|-u|-v|-w)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-a -c -cidfile -d -dns -e -entrypoint -h -i -lxc-conf -m -n -p -privileged -t -u -v -volumes-from -w" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$cpos
|
||||
while [ $counter -le $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
-a|-c|-cidfile|-dns|-e|-entrypoint|-h|-lxc-conf|-m|-p|-u|-v|-volumes-from|-w)
|
||||
(( counter++ ))
|
||||
;;
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
(( counter++ ))
|
||||
done
|
||||
|
||||
if [ $counter -eq $cword ]; then
|
||||
__docker_image_repos_and_tags
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_search()
|
||||
{
|
||||
COMPREPLY=( $( compgen -W "-notrunc" -- "$cur" ) )
|
||||
}
|
||||
|
||||
_docker_start()
|
||||
{
|
||||
__docker_containers_stopped
|
||||
}
|
||||
|
||||
_docker_stop()
|
||||
{
|
||||
case "$prev" in
|
||||
-t)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-t" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_containers_running
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_tag()
|
||||
{
|
||||
COMPREPLY=( $( compgen -W "-f" -- "$cur" ) )
|
||||
}
|
||||
|
||||
_docker_top()
|
||||
{
|
||||
if [ $cpos -eq $cword ]; then
|
||||
__docker_containers_running
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_version()
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
_docker_wait()
|
||||
{
|
||||
__docker_containers_all
|
||||
}
|
||||
|
||||
_docker()
|
||||
{
|
||||
local cur prev words cword command="docker" counter=1 word cpos
|
||||
local commands="
|
||||
attach
|
||||
build
|
||||
commit
|
||||
cp
|
||||
diff
|
||||
events
|
||||
export
|
||||
history
|
||||
images
|
||||
import
|
||||
info
|
||||
insert
|
||||
inspect
|
||||
kill
|
||||
login
|
||||
logs
|
||||
port
|
||||
ps
|
||||
pull
|
||||
push
|
||||
restart
|
||||
rm
|
||||
rmi
|
||||
run
|
||||
search
|
||||
start
|
||||
stop
|
||||
tag
|
||||
top
|
||||
version
|
||||
wait
|
||||
"
|
||||
|
||||
COMPREPLY=()
|
||||
_get_comp_words_by_ref -n : cur prev words cword
|
||||
|
||||
while [ $counter -lt $cword ]; do
|
||||
word="${words[$counter]}"
|
||||
case "$word" in
|
||||
-H)
|
||||
(( counter++ ))
|
||||
;;
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
command="$word"
|
||||
cpos=$counter
|
||||
(( cpos++ ))
|
||||
break
|
||||
;;
|
||||
esac
|
||||
(( counter++ ))
|
||||
done
|
||||
|
||||
local completions_func=_docker_${command}
|
||||
declare -F $completions_func >/dev/null && $completions_func
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
complete -F _docker docker
|
||||
242
contrib/completion/zsh/_docker
Executable file
242
contrib/completion/zsh/_docker
Executable file
@@ -0,0 +1,242 @@
|
||||
#compdef docker
|
||||
#
|
||||
# zsh completion for docker (http://docker.io)
|
||||
#
|
||||
# version: 0.2.2
|
||||
# author: Felix Riedel
|
||||
# license: BSD License
|
||||
# github: https://github.com/felixr/docker-zsh-completion
|
||||
#
|
||||
|
||||
__parse_docker_list() {
|
||||
sed -e '/^ID/d' -e 's/[ ]\{2,\}/|/g' -e 's/ \([hdwm]\)\(inutes\|ays\|ours\|eeks\)/\1/' | awk ' BEGIN {FS="|"} { printf("%s:%7s, %s\n", $1, $4, $2)}'
|
||||
}
|
||||
|
||||
__docker_stoppedcontainers() {
|
||||
local expl
|
||||
declare -a stoppedcontainers
|
||||
stoppedcontainers=(${(f)"$(docker ps -a | grep --color=never 'Exit' | __parse_docker_list )"})
|
||||
_describe -t containers-stopped "Stopped Containers" stoppedcontainers
|
||||
}
|
||||
|
||||
__docker_runningcontainers() {
|
||||
local expl
|
||||
declare -a containers
|
||||
|
||||
containers=(${(f)"$(docker ps | __parse_docker_list)"})
|
||||
_describe -t containers-active "Running Containers" containers
|
||||
}
|
||||
|
||||
__docker_containers () {
|
||||
__docker_stoppedcontainers
|
||||
__docker_runningcontainers
|
||||
}
|
||||
|
||||
__docker_images () {
|
||||
local expl
|
||||
declare -a images
|
||||
images=(${(f)"$(docker images | awk '(NR > 1){printf("%s\\:%s\n", $1,$2)}')"})
|
||||
images=($images ${(f)"$(docker images | awk '(NR > 1){printf("%s:%-15s in %s\n", $3,$2,$1)}')"})
|
||||
_describe -t docker-images "Images" images
|
||||
}
|
||||
|
||||
__docker_tags() {
|
||||
local expl
|
||||
declare -a tags
|
||||
tags=(${(f)"$(docker images | awk '(NR>1){print $2}'| sort | uniq)"})
|
||||
_describe -t docker-tags "tags" tags
|
||||
}
|
||||
|
||||
__docker_search() {
|
||||
# declare -a dockersearch
|
||||
local cache_policy
|
||||
zstyle -s ":completion:${curcontext}:" cache-policy cache_policy
|
||||
if [[ -z "$cache_policy" ]]; then
|
||||
zstyle ":completion:${curcontext}:" cache-policy __docker_caching_policy
|
||||
fi
|
||||
|
||||
local searchterm cachename
|
||||
searchterm="${words[$CURRENT]%/}"
|
||||
cachename=_docker-search-$searchterm
|
||||
|
||||
local expl
|
||||
local -a result
|
||||
if ( [[ ${(P)+cachename} -eq 0 ]] || _cache_invalid ${cachename#_} ) \
|
||||
&& ! _retrieve_cache ${cachename#_}; then
|
||||
_message "Searching for ${searchterm}..."
|
||||
result=(${(f)"$(docker search ${searchterm} | awk '(NR>2){print $1}')"})
|
||||
_store_cache ${cachename#_} result
|
||||
fi
|
||||
_wanted dockersearch expl 'Available images' compadd -a result
|
||||
}
|
||||
|
||||
__docker_caching_policy()
|
||||
{
|
||||
# oldp=( "$1"(Nmh+24) ) # 24 hour
|
||||
oldp=( "$1"(Nmh+1) ) # 24 hour
|
||||
(( $#oldp ))
|
||||
}
|
||||
|
||||
|
||||
__docker_repositories () {
|
||||
local expl
|
||||
declare -a repos
|
||||
repos=(${(f)"$(docker images | sed -e '1d' -e 's/[ ].*//' | sort | uniq)"})
|
||||
_describe -t docker-repos "Repositories" repos
|
||||
}
|
||||
|
||||
__docker_commands () {
|
||||
# local -a _docker_subcommands
|
||||
local cache_policy
|
||||
|
||||
zstyle -s ":completion:${curcontext}:" cache-policy cache_policy
|
||||
if [[ -z "$cache_policy" ]]; then
|
||||
zstyle ":completion:${curcontext}:" cache-policy __docker_caching_policy
|
||||
fi
|
||||
|
||||
if ( [[ ${+_docker_subcommands} -eq 0 ]] || _cache_invalid docker_subcommands) \
|
||||
&& ! _retrieve_cache docker_subcommands;
|
||||
then
|
||||
_docker_subcommands=(${${(f)"$(_call_program commands
|
||||
docker 2>&1 | sed -e '1,6d' -e '/^[ ]*$/d' -e 's/[ ]*\([^ ]\+\)\s*\([^ ].*\)/\1:\2/' )"}})
|
||||
_docker_subcommands=($_docker_subcommands 'help:Show help for a command')
|
||||
_store_cache docker_subcommands _docker_subcommands
|
||||
fi
|
||||
_describe -t docker-commands "docker command" _docker_subcommands
|
||||
}
|
||||
|
||||
__docker_subcommand () {
|
||||
local -a _command_args
|
||||
case "$words[1]" in
|
||||
(attach|wait)
|
||||
_arguments ':containers:__docker_runningcontainers'
|
||||
;;
|
||||
(build)
|
||||
_arguments \
|
||||
'-t=-:repository:__docker_repositories' \
|
||||
':path or URL:_directories'
|
||||
;;
|
||||
(commit)
|
||||
_arguments \
|
||||
':container:__docker_containers' \
|
||||
':repository:__docker_repositories' \
|
||||
':tag: '
|
||||
;;
|
||||
(diff|export|logs)
|
||||
_arguments '*:containers:__docker_containers'
|
||||
;;
|
||||
(history)
|
||||
_arguments '*:images:__docker_images'
|
||||
;;
|
||||
(images)
|
||||
_arguments \
|
||||
'-a[Show all images]' \
|
||||
':repository:__docker_repositories'
|
||||
;;
|
||||
(inspect)
|
||||
_arguments '*:containers:__docker_containers'
|
||||
;;
|
||||
(history)
|
||||
_arguments ':images:__docker_images'
|
||||
;;
|
||||
(insert)
|
||||
_arguments '1:containers:__docker_containers' \
|
||||
'2:URL:(http:// file://)' \
|
||||
'3:file:_files'
|
||||
;;
|
||||
(kill)
|
||||
_arguments '*:containers:__docker_runningcontainers'
|
||||
;;
|
||||
(port)
|
||||
_arguments '1:containers:__docker_runningcontainers'
|
||||
;;
|
||||
(start)
|
||||
_arguments '*:containers:__docker_stoppedcontainers'
|
||||
;;
|
||||
(rm)
|
||||
_arguments '-v[Remove the volumes associated to the container]' \
|
||||
'*:containers:__docker_stoppedcontainers'
|
||||
;;
|
||||
(rmi)
|
||||
_arguments '-v[Remove the volumes associated to the container]' \
|
||||
'*:images:__docker_images'
|
||||
;;
|
||||
(top)
|
||||
_arguments '1:containers:__docker_runningcontainers'
|
||||
;;
|
||||
(restart|stop)
|
||||
_arguments '-t=-[Number of seconds to try to stop for before killing the container]:seconds to before killing:(1 5 10 30 60)' \
|
||||
'*:containers:__docker_runningcontainers'
|
||||
;;
|
||||
(top)
|
||||
_arguments ':containers:__docker_runningcontainers'
|
||||
;;
|
||||
(ps)
|
||||
_arguments '-a[Show all containers. Only running containers are shown by default]' \
|
||||
'-h[Show help]' \
|
||||
'-beforeId=-[Show only container created before Id, include non-running one]:containers:__docker_containers' \
|
||||
'-n=-[Show n last created containers, include non-running one]:n:(1 5 10 25 50)'
|
||||
;;
|
||||
(tag)
|
||||
_arguments \
|
||||
'-f[force]'\
|
||||
':image:__docker_images'\
|
||||
':repository:__docker_repositories' \
|
||||
':tag:__docker_tags'
|
||||
;;
|
||||
(run)
|
||||
_arguments \
|
||||
'-a=-[Attach to stdin, stdout or stderr]:toggle:(true false)' \
|
||||
'-c=-[CPU shares (relative weight)]:CPU shares: ' \
|
||||
'-d[Detached mode: leave the container running in the background]' \
|
||||
'*-dns=[Set custom dns servers]:dns server: ' \
|
||||
'*-e=[Set environment variables]:environment variable: ' \
|
||||
'-entrypoint=-[Overwrite the default entrypoint of the image]:entry point: ' \
|
||||
'-h=-[Container host name]:hostname:_hosts' \
|
||||
'-i[Keep stdin open even if not attached]' \
|
||||
'-m=-[Memory limit (in bytes)]:limit: ' \
|
||||
'*-p=-[Expose a container''s port to the host]:port:_ports' \
|
||||
'-t=-[Allocate a pseudo-tty]:toggle:(true false)' \
|
||||
'-u=-[Username or UID]:user:_users' \
|
||||
'*-v=-[Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)]:volume: '\
|
||||
'-volumes-from=-[Mount volumes from the specified container]:volume: ' \
|
||||
'(-):images:__docker_images' \
|
||||
'(-):command: _command_names -e' \
|
||||
'*::arguments: _normal'
|
||||
;;
|
||||
(pull|search)
|
||||
_arguments ':name:__docker_search'
|
||||
;;
|
||||
(help)
|
||||
_arguments ':subcommand:__docker_commands'
|
||||
;;
|
||||
(*)
|
||||
_message 'Unknown sub command'
|
||||
esac
|
||||
|
||||
}
|
||||
|
||||
_docker () {
|
||||
local curcontext="$curcontext" state line
|
||||
typeset -A opt_args
|
||||
|
||||
_arguments -C \
|
||||
'-H=-[tcp://host:port to bind/connect to]:socket: ' \
|
||||
'(-): :->command' \
|
||||
'(-)*:: :->option-or-argument'
|
||||
|
||||
if (( CURRENT == 1 )); then
|
||||
|
||||
fi
|
||||
case $state in
|
||||
(command)
|
||||
__docker_commands
|
||||
;;
|
||||
(option-or-argument)
|
||||
curcontext=${curcontext%:*:*}:docker-$words[1]:
|
||||
__docker_subcommand
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker "$@"
|
||||
@@ -77,7 +77,7 @@ func crashTest() error {
|
||||
stop = false
|
||||
for i := 0; i < 100 && !stop; {
|
||||
func() error {
|
||||
cmd := exec.Command(DOCKERPATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
|
||||
cmd := exec.Command(DOCKERPATH, "run", "ubuntu", "echo", fmt.Sprintf("%d", totalTestCount))
|
||||
i++
|
||||
totalTestCount++
|
||||
outPipe, err := cmd.StdoutPipe()
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/bin/sh
|
||||
# This script is meant for quick & easy install via 'curl URL-OF-SCRIPT | sh'
|
||||
# Original version by Jeff Lindsay <progrium@gmail.com>
|
||||
# Revamped by Jerome Petazzoni <jerome@dotcloud.com>
|
||||
#
|
||||
# This script canonical location is http://get.docker.io/; to update it, run:
|
||||
# s3cmd put -m text/x-shellscript -P install.sh s3://get.docker.io/index
|
||||
|
||||
echo "Ensuring basic dependencies are installed..."
|
||||
apt-get -qq update
|
||||
apt-get -qq install lxc wget
|
||||
|
||||
echo "Looking in /proc/filesystems to see if we have AUFS support..."
|
||||
if grep -q aufs /proc/filesystems
|
||||
then
|
||||
echo "Found."
|
||||
else
|
||||
echo "Ahem, it looks like the current kernel does not support AUFS."
|
||||
echo "Let's see if we can load the AUFS module with modprobe..."
|
||||
if modprobe aufs
|
||||
then
|
||||
echo "Module loaded."
|
||||
else
|
||||
echo "Ahem, things didn't turn out as expected."
|
||||
KPKG=linux-image-extra-$(uname -r)
|
||||
echo "Trying to install $KPKG..."
|
||||
if apt-get -qq install $KPKG
|
||||
then
|
||||
echo "Installed."
|
||||
else
|
||||
echo "Oops, we couldn't install the -extra kernel."
|
||||
echo "Are you sure you are running a supported version of Ubuntu?"
|
||||
echo "Proceeding anyway, but Docker will probably NOT WORK!"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Downloading docker binary and uncompressing into /usr/local/bin..."
|
||||
curl -s http://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-latest.tgz |
|
||||
tar -C /usr/local/bin --strip-components=1 -zxf- \
|
||||
docker-latest/docker
|
||||
|
||||
if [ -f /etc/init/dockerd.conf ]
|
||||
then
|
||||
echo "Upstart script already exists."
|
||||
else
|
||||
echo "Creating /etc/init/dockerd.conf..."
|
||||
echo "exec env LANG=\"en_US.UTF-8\" /usr/local/bin/docker -d" > /etc/init/dockerd.conf
|
||||
fi
|
||||
|
||||
echo "Starting dockerd..."
|
||||
start dockerd > /dev/null
|
||||
|
||||
echo "Done."
|
||||
echo
|
||||
65
contrib/mkimage-arch.sh
Executable file
65
contrib/mkimage-arch.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
# Generate a minimal filesystem for archlinux and load it into the local
|
||||
# docker as "archlinux"
|
||||
# requires root
|
||||
set -e
|
||||
|
||||
PACSTRAP=$(which pacstrap)
|
||||
[ "$PACSTRAP" ] || {
|
||||
echo "Could not find pacstrap. Run pacman -S arch-install-scripts"
|
||||
exit 1
|
||||
}
|
||||
EXPECT=$(which expect)
|
||||
[ "$EXPECT" ] || {
|
||||
echo "Could not find expect. Run pacman -S expect"
|
||||
exit 1
|
||||
}
|
||||
|
||||
ROOTFS=~/rootfs-arch-$$-$RANDOM
|
||||
mkdir $ROOTFS
|
||||
|
||||
#packages to ignore for space savings
|
||||
PKGIGNORE=linux,jfsutils,lvm2,cryptsetup,groff,man-db,man-pages,mdadm,pciutils,pcmciautils,reiserfsprogs,s-nail,xfsprogs
|
||||
|
||||
expect -c "
|
||||
set timeout 60
|
||||
spawn pacstrap -c -d -G -i $ROOTFS base haveged --ignore $PKGIGNORE
|
||||
expect {
|
||||
\"Install anyway\" { send "n\\r"; exp_continue }
|
||||
\"(default=all)\" { send "\\r"; exp_continue }
|
||||
\"Proceed with installation?\" { send "\\r"; exp_continue }
|
||||
\"skip the above package\" {send "y\\r"; exp_continue }
|
||||
\"checking\" { exp_continue }
|
||||
\"loading\" { exp_continue }
|
||||
\"installing\" { exp_continue }
|
||||
}"
|
||||
|
||||
arch-chroot $ROOTFS /bin/sh -c "haveged -w 1024; pacman-key --init; pkill haveged; pacman -Rs --noconfirm haveged; pacman-key --populate archlinux"
|
||||
arch-chroot $ROOTFS /bin/sh -c "ln -s /usr/share/zoneinfo/UTC /etc/localtime"
|
||||
cat > $ROOTFS/etc/locale.gen <<DELIM
|
||||
en_US.UTF-8 UTF-8
|
||||
en_US ISO-8859-1
|
||||
DELIM
|
||||
arch-chroot $ROOTFS locale-gen
|
||||
arch-chroot $ROOTFS /bin/sh -c 'echo "Server = http://mirrors.kernel.org/archlinux/\$repo/os/\$arch" > /etc/pacman.d/mirrorlist'
|
||||
|
||||
# udev doesn't work in containers, rebuild /dev
|
||||
DEV=${ROOTFS}/dev
|
||||
mv ${DEV} ${DEV}.old
|
||||
mkdir -p ${DEV}
|
||||
mknod -m 666 ${DEV}/null c 1 3
|
||||
mknod -m 666 ${DEV}/zero c 1 5
|
||||
mknod -m 666 ${DEV}/random c 1 8
|
||||
mknod -m 666 ${DEV}/urandom c 1 9
|
||||
mkdir -m 755 ${DEV}/pts
|
||||
mkdir -m 1777 ${DEV}/shm
|
||||
mknod -m 666 ${DEV}/tty c 5 0
|
||||
mknod -m 600 ${DEV}/console c 5 1
|
||||
mknod -m 666 ${DEV}/tty0 c 4 0
|
||||
mknod -m 666 ${DEV}/full c 1 7
|
||||
mknod -m 600 ${DEV}/initctl p
|
||||
mknod -m 666 ${DEV}/ptmx c 5 2
|
||||
|
||||
tar -C $ROOTFS -c . | docker import - archlinux
|
||||
docker run -i -t archlinux echo Success.
|
||||
rm -rf $ROOTFS
|
||||
15
contrib/mkimage-centos.sh
Executable file
15
contrib/mkimage-centos.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# Create a CentOS base image for Docker
|
||||
# From unclejack https://github.com/dotcloud/docker/issues/290
|
||||
set -e
|
||||
|
||||
MIRROR_URL="http://centos.netnitco.net/6.4/os/x86_64/"
|
||||
MIRROR_URL_UPDATES="http://centos.netnitco.net/6.4/updates/x86_64/"
|
||||
|
||||
yum install -y febootstrap xz
|
||||
|
||||
febootstrap -i bash -i coreutils -i tar -i bzip2 -i gzip -i vim-minimal -i wget -i patch -i diffutils -i iproute -i yum centos centos64 $MIRROR_URL -u $MIRROR_URL_UPDATES
|
||||
touch centos64/etc/resolv.conf
|
||||
touch centos64/sbin/init
|
||||
|
||||
tar --numeric-owner -Jcpf centos-64.tar.xz -C centos64 .
|
||||
@@ -10,12 +10,18 @@ variant='minbase'
|
||||
include='iproute,iputils-ping'
|
||||
|
||||
repo="$1"
|
||||
suite="${2:-$stableSuite}"
|
||||
suite="$2"
|
||||
mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
|
||||
|
||||
if [ ! "$repo" ]; then
|
||||
echo >&2 "usage: $0 repo [suite [mirror]]"
|
||||
if [ ! "$repo" ] || [ ! "$suite" ]; then
|
||||
echo >&2 "usage: $0 repo suite [mirror]"
|
||||
echo >&2
|
||||
echo >&2 " ie: $0 tianon/debian squeeze"
|
||||
echo >&2 " $0 tianon/debian squeeze http://ftp.uk.debian.org/debian/"
|
||||
echo >&2
|
||||
echo >&2 " ie: $0 tianon/ubuntu precise"
|
||||
echo >&2 " $0 tianon/ubuntu precise http://mirrors.melbourne.co.uk/ubuntu/"
|
||||
echo >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -32,22 +38,44 @@ sudo debootstrap --verbose --variant="$variant" --include="$include" "$suite" "$
|
||||
|
||||
cd "$target"
|
||||
|
||||
# create the image
|
||||
img=$(sudo tar -c . | docker import -)
|
||||
# prevent init scripts from running during install/update
|
||||
# policy-rc.d (for most scripts)
|
||||
echo $'#!/bin/sh\nexit 101' | sudo tee usr/sbin/policy-rc.d > /dev/null
|
||||
sudo chmod +x usr/sbin/policy-rc.d
|
||||
# initctl (for some pesky upstart scripts)
|
||||
sudo chroot . dpkg-divert --local --rename --add /sbin/initctl
|
||||
sudo ln -sf /bin/true sbin/initctl
|
||||
# see https://github.com/dotcloud/docker/issues/446#issuecomment-16953173
|
||||
|
||||
# tag suite
|
||||
docker tag $img $repo $suite
|
||||
# shrink the image, since apt makes us fat (wheezy: ~157.5MB vs ~120MB)
|
||||
sudo chroot . apt-get clean
|
||||
|
||||
# while we're at it, apt is unnecessarily slow inside containers
|
||||
# this forces dpkg not to call sync() after package extraction and speeds up install
|
||||
# the benefit is huge on spinning disks, and the penalty is nonexistent on SSD or decent server virtualization
|
||||
echo 'force-unsafe-io' | sudo tee etc/dpkg/dpkg.cfg.d/02apt-speedup > /dev/null
|
||||
# we want to effectively run "apt-get clean" after every install to keep images small
|
||||
echo 'DPkg::Post-Invoke {"/bin/rm -f /var/cache/apt/archives/*.deb || true";};' | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null
|
||||
|
||||
# helpful undo lines for each the above tweaks (for lack of a better home to keep track of them):
|
||||
# rm /usr/sbin/policy-rc.d
|
||||
# rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl
|
||||
# rm /etc/dpkg/dpkg.cfg.d/02apt-speedup
|
||||
# rm /etc/apt/apt.conf.d/no-cache
|
||||
|
||||
# create the image (and tag $repo:$suite)
|
||||
sudo tar -c . | docker import - $repo $suite
|
||||
|
||||
# test the image
|
||||
docker run -i -t $repo:$suite echo success
|
||||
|
||||
if [ "$suite" = "$stableSuite" -o "$suite" = 'stable' ]; then
|
||||
# tag latest
|
||||
docker tag $img $repo latest
|
||||
docker tag $repo:$suite $repo latest
|
||||
|
||||
# tag the specific debian release version
|
||||
ver=$(docker run $repo:$suite cat /etc/debian_version)
|
||||
docker tag $img $repo $ver
|
||||
docker tag $repo:$suite $repo $ver
|
||||
fi
|
||||
|
||||
# cleanup
|
||||
|
||||
@@ -23,7 +23,9 @@ mkdir bin etc dev dev/pts lib proc sys tmp
|
||||
touch etc/resolv.conf
|
||||
cp /etc/nsswitch.conf etc/nsswitch.conf
|
||||
echo root:x:0:0:root:/:/bin/sh > etc/passwd
|
||||
echo daemon:x:1:1:daemon:/usr/sbin:/bin/sh >> etc/passwd
|
||||
echo root:x:0: > etc/group
|
||||
echo daemon:x:1: >> etc/group
|
||||
ln -s lib lib64
|
||||
ln -s bin sbin
|
||||
cp $BUSYBOX $SOCAT bin
|
||||
@@ -41,6 +43,7 @@ do
|
||||
cp -a /dev/$X dev
|
||||
done
|
||||
|
||||
chmod 0755 $ROOTFS # See #486
|
||||
tar -cf- . | docker import - docker-ut
|
||||
docker run -i -u root docker-ut /bin/echo Success.
|
||||
rm -rf $ROOTFS
|
||||
|
||||
22
contrib/vim-syntax/LICENSE
Normal file
22
contrib/vim-syntax/LICENSE
Normal file
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2013 Honza Pokorny
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
23
contrib/vim-syntax/README.md
Normal file
23
contrib/vim-syntax/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
dockerfile.vim
|
||||
==============
|
||||
|
||||
Syntax highlighting for Dockerfiles
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Via pathogen, the usual way...
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
The syntax highlighting includes:
|
||||
|
||||
* The directives (e.g. `FROM`)
|
||||
* Strings
|
||||
* Comments
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD, short and sweet
|
||||
18
contrib/vim-syntax/doc/dockerfile.txt
Normal file
18
contrib/vim-syntax/doc/dockerfile.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
*dockerfile.txt* Syntax highlighting for Dockerfiles
|
||||
|
||||
Author: Honza Pokorny <http://honza.ca>
|
||||
License: BSD
|
||||
|
||||
INSTALLATION *installation*
|
||||
|
||||
Drop it on your Pathogen path and you're all set.
|
||||
|
||||
FEATURES *features*
|
||||
|
||||
The syntax highlighting includes:
|
||||
|
||||
* The directives (e.g. FROM)
|
||||
* Strings
|
||||
* Comments
|
||||
|
||||
vim:tw=78:et:ft=help:norl:
|
||||
1
contrib/vim-syntax/ftdetect/dockerfile.vim
Normal file
1
contrib/vim-syntax/ftdetect/dockerfile.vim
Normal file
@@ -0,0 +1 @@
|
||||
au BufNewFile,BufRead Dockerfile set filetype=dockerfile
|
||||
22
contrib/vim-syntax/syntax/dockerfile.vim
Normal file
22
contrib/vim-syntax/syntax/dockerfile.vim
Normal file
@@ -0,0 +1,22 @@
|
||||
" dockerfile.vim - Syntax highlighting for Dockerfiles
|
||||
" Maintainer: Honza Pokorny <http://honza.ca>
|
||||
" Version: 0.5
|
||||
|
||||
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
let b:current_syntax = "dockerfile"
|
||||
|
||||
syntax case ignore
|
||||
|
||||
syntax match dockerfileKeyword /\v^\s*(FROM|MAINTAINER|RUN|CMD|EXPOSE|ENV|ADD)\s/
|
||||
syntax match dockerfileKeyword /\v^\s*(ENTRYPOINT|VOLUME|USER|WORKDIR)\s/
|
||||
highlight link dockerfileKeyword Keyword
|
||||
|
||||
syntax region dockerfileString start=/\v"/ skip=/\v\\./ end=/\v"/
|
||||
highlight link dockerfileString String
|
||||
|
||||
syntax match dockerfileComment "\v^\s*#.*$"
|
||||
highlight link dockerfileComment Comment
|
||||
@@ -16,28 +16,34 @@ import (
|
||||
|
||||
var (
|
||||
GITCOMMIT string
|
||||
VERSION string
|
||||
)
|
||||
|
||||
func main() {
|
||||
if utils.SelfPath() == "/sbin/init" {
|
||||
if selfPath := utils.SelfPath(); selfPath == "/sbin/init" || selfPath == "/.dockerinit" {
|
||||
// Running in init mode
|
||||
docker.SysInit()
|
||||
return
|
||||
}
|
||||
// FIXME: Switch d and D ? (to be more sshd like)
|
||||
flVersion := flag.Bool("v", false, "Print version information and quit")
|
||||
flDaemon := flag.Bool("d", false, "Daemon mode")
|
||||
flDebug := flag.Bool("D", false, "Debug mode")
|
||||
flAutoRestart := flag.Bool("r", false, "Restart previously running containers")
|
||||
bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge")
|
||||
flAutoRestart := flag.Bool("r", true, "Restart previously running containers")
|
||||
bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge. Use 'none' to disable container networking")
|
||||
pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
|
||||
flGraphPath := flag.String("g", "/var/lib/docker", "Path to graph storage base dir.")
|
||||
flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
|
||||
flDns := flag.String("dns", "", "Set custom dns servers")
|
||||
flHosts := docker.ListOpts{fmt.Sprintf("tcp://%s:%d", docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT)}
|
||||
flHosts := docker.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)}
|
||||
flag.Var(&flHosts, "H", "tcp://host:port to bind/connect to or unix://path/to/socket to use")
|
||||
flag.Parse()
|
||||
if *flVersion {
|
||||
showVersion()
|
||||
return
|
||||
}
|
||||
if len(flHosts) > 1 {
|
||||
flHosts = flHosts[1:len(flHosts)] //trick to display a nice defaul value in the usage
|
||||
flHosts = flHosts[1:] //trick to display a nice default value in the usage
|
||||
}
|
||||
for i, flHost := range flHosts {
|
||||
flHosts[i] = utils.ParseHost(docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT, flHost)
|
||||
@@ -52,6 +58,7 @@ func main() {
|
||||
os.Setenv("DEBUG", "1")
|
||||
}
|
||||
docker.GITCOMMIT = GITCOMMIT
|
||||
docker.VERSION = VERSION
|
||||
if *flDaemon {
|
||||
if flag.NArg() != 0 {
|
||||
flag.Usage()
|
||||
@@ -68,12 +75,19 @@ func main() {
|
||||
}
|
||||
protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
|
||||
if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
|
||||
if sterr, ok := err.(*utils.StatusError); ok {
|
||||
os.Exit(sterr.Status)
|
||||
}
|
||||
log.Fatal(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func showVersion() {
|
||||
fmt.Printf("Docker version %s, build %s\n", VERSION, GITCOMMIT)
|
||||
}
|
||||
|
||||
func createPidFile(pidfile string) error {
|
||||
if pidString, err := ioutil.ReadFile(pidfile); err == nil {
|
||||
pid, err := strconv.Atoi(string(pidString))
|
||||
|
||||
15
docs/Dockerfile
Normal file
15
docs/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
from ubuntu:12.04
|
||||
maintainer Nick Stinemates
|
||||
|
||||
run apt-get update
|
||||
run apt-get install -y python-setuptools make
|
||||
run easy_install pip
|
||||
add . /docs
|
||||
run pip install -r /docs/requirements.txt
|
||||
run cd /docs; make docs
|
||||
|
||||
expose 8000
|
||||
|
||||
workdir /docs/_build/html
|
||||
|
||||
entrypoint ["python", "-m", "SimpleHTTPServer"]
|
||||
@@ -1,2 +1,2 @@
|
||||
Andy Rothfusz <andy@dotcloud.com>
|
||||
Ken Cochrane <ken@dotcloud.com>
|
||||
Andy Rothfusz <andy@dotcloud.com> (@metalivedev)
|
||||
Ken Cochrane <ken@dotcloud.com> (@kencochrane)
|
||||
|
||||
115
docs/README.md
115
docs/README.md
@@ -1,53 +1,102 @@
|
||||
Docker documentation and website
|
||||
================================
|
||||
Docker Documentation
|
||||
====================
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
This is your definite place to contribute to the docker documentation. The documentation is generated from the
|
||||
.rst files under sources.
|
||||
Overview
|
||||
--------
|
||||
|
||||
The folder also contains the other files to create the http://docker.io website, but you can generally ignore
|
||||
most of those.
|
||||
The source for Docker documentation is here under ``sources/`` in the
|
||||
form of .rst files. These files use
|
||||
[reStructuredText](http://docutils.sourceforge.net/rst.html)
|
||||
formatting with [Sphinx](http://sphinx-doc.org/) extensions for
|
||||
structure, cross-linking and indexing.
|
||||
|
||||
The HTML files are built and hosted on
|
||||
[readthedocs.org](https://readthedocs.org/projects/docker/), appearing
|
||||
via proxy on https://docs.docker.io. The HTML files update
|
||||
automatically after each change to the master or release branch of the
|
||||
[docker files on GitHub](https://github.com/dotcloud/docker) thanks to
|
||||
post-commit hooks. The "release" branch maps to the "latest"
|
||||
documentation and the "master" branch maps to the "master"
|
||||
documentation.
|
||||
|
||||
Installation
|
||||
------------
|
||||
**Warning**: The "master" documentation may include features not yet
|
||||
part of any official docker release. "Master" docs should be used only
|
||||
for understanding bleeding-edge development and "latest" should be
|
||||
used for the latest official release.
|
||||
|
||||
If you need to manually trigger a build of an existing branch, then
|
||||
you can do that through the [readthedocs
|
||||
interface](https://readthedocs.org/builds/docker/). If you would like
|
||||
to add new build targets, including new branches or tags, then you
|
||||
must contact one of the existing maintainers and get your
|
||||
readthedocs.org account added to the maintainers list, or just file an
|
||||
issue on GitHub describing the branch/tag and why it needs to be added
|
||||
to the docs, and one of the maintainers will add it for you.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
To edit and test the docs, you'll need to install the Sphinx tool and
|
||||
its dependencies. There are two main ways to install this tool:
|
||||
|
||||
Native Installation
|
||||
...................
|
||||
|
||||
* Work in your own fork of the code, we accept pull requests.
|
||||
* Install sphinx: `pip install sphinx`
|
||||
* Mac OS X: `[sudo] pip-2.7 install sphinx`)
|
||||
* Mac OS X: `[sudo] pip-2.7 install sphinx`
|
||||
* Install sphinx httpdomain contrib package: `pip install sphinxcontrib-httpdomain`
|
||||
* Mac OS X: `[sudo] pip-2.7 install sphinxcontrib-httpdomain`
|
||||
* If pip is not available you can probably install it using your favorite package manager as **python-pip**
|
||||
|
||||
Alternative Installation: Docker Container
|
||||
..........................................
|
||||
|
||||
If you're running ``docker`` on your development machine then you may
|
||||
find it easier and cleaner to use the Dockerfile. This installs Sphinx
|
||||
in a container, adds the local ``docs/`` directory and builds the HTML
|
||||
docs inside the container, even starting a simple HTTP server on port
|
||||
8000 so that you can connect and see your changes. Just run ``docker
|
||||
build .`` and run the resulting image. This is the equivalent to
|
||||
``make clean server`` since each container starts clean.
|
||||
|
||||
Usage
|
||||
-----
|
||||
* Change the `.rst` files with your favorite editor to your liking.
|
||||
* Run `make docs` to clean up old files and generate new ones.
|
||||
* Your static website can now be found in the `_build` directory.
|
||||
* To preview what you have generated run `make server` and open <http://localhost:8000/> in your favorite browser.
|
||||
* Follow the contribution guidelines (``../CONTRIBUTING.md``)
|
||||
* Work in your own fork of the code, we accept pull requests.
|
||||
* Change the ``.rst`` files with your favorite editor -- try to keep the
|
||||
lines short and respect RST and Sphinx conventions.
|
||||
* Run ``make clean docs`` to clean up old files and generate new ones,
|
||||
or just ``make docs`` to update after small changes.
|
||||
* Your static website can now be found in the ``_build`` directory.
|
||||
* To preview what you have generated run ``make server`` and open
|
||||
http://localhost:8000/ in your favorite browser.
|
||||
|
||||
``make clean docs`` must complete without any warnings or errors.
|
||||
|
||||
Working using GitHub's file editor
|
||||
----------------------------------
|
||||
Alternatively, for small changes and typo's you might want to use GitHub's built in file editor. It allows
|
||||
you to preview your changes right online. Just be carefull not to create many commits.
|
||||
|
||||
Alternatively, for small changes and typos you might want to use
|
||||
GitHub's built in file editor. It allows you to preview your changes
|
||||
right online (though there can be some differences between GitHub
|
||||
markdown and Sphinx RST). Just be careful not to create many commits.
|
||||
|
||||
Images
|
||||
------
|
||||
When you need to add images, try to make them as small as possible (e.g. as gif).
|
||||
|
||||
When you need to add images, try to make them as small as possible
|
||||
(e.g. as gif). Usually images should go in the same directory as the
|
||||
.rst file which references them, or in a subdirectory if one already
|
||||
exists.
|
||||
|
||||
Notes
|
||||
-----
|
||||
* The index.html and gettingstarted.html files are copied from the source dir to the output dir without modification.
|
||||
So changes to those pages should be made directly in html
|
||||
* For the template the css is compiled from less. When changes are needed they can be compiled using
|
||||
lessc ``lessc main.less`` or watched using watch-lessc ``watch-lessc -i main.less -o main.css``
|
||||
|
||||
|
||||
Guides on using sphinx
|
||||
----------------------
|
||||
* To make links to certain pages create a link target like so:
|
||||
* To make links to certain sections create a link target like so:
|
||||
|
||||
```
|
||||
.. _hello_world:
|
||||
@@ -58,7 +107,10 @@ Guides on using sphinx
|
||||
This is.. (etc.)
|
||||
```
|
||||
|
||||
The ``_hello_world:`` will make it possible to link to this position (page and marker) from all other pages.
|
||||
The ``_hello_world:`` will make it possible to link to this position
|
||||
(page and section heading) from all other pages. See the [Sphinx
|
||||
docs](http://sphinx-doc.org/markup/inline.html#role-ref) for more
|
||||
information and examples.
|
||||
|
||||
* Notes, warnings and alarms
|
||||
|
||||
@@ -74,4 +126,17 @@ Guides on using sphinx
|
||||
|
||||
* Code examples
|
||||
|
||||
Start without $, so it's easy to copy and paste.
|
||||
* Start without $, so it's easy to copy and paste.
|
||||
* Use "sudo" with docker to ensure that your command is runnable
|
||||
even if they haven't [used the *docker*
|
||||
group](http://docs.docker.io/en/latest/use/basics/#why-sudo).
|
||||
|
||||
Manpages
|
||||
--------
|
||||
|
||||
* To make the manpages, run ``make man``. Please note there is a bug
|
||||
in spinx 1.1.3 which makes this fail. Upgrade to the latest version
|
||||
of Sphinx.
|
||||
* Then preview the manpage by running ``man _build/man/docker.1``,
|
||||
where ``_build/man/docker.1`` is the path to the generated manfile
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
Solomon Hykes <solomon@dotcloud.com>
|
||||
Solomon Hykes <solomon@dotcloud.com> (@shykes)
|
||||
|
||||
@@ -2,41 +2,140 @@
|
||||
:description: API Documentation for Docker
|
||||
:keywords: API, Docker, rcli, REST, documentation
|
||||
|
||||
.. COMMENT use http://pythonhosted.org/sphinxcontrib-httpdomain/ to
|
||||
.. document the REST API.
|
||||
|
||||
=================
|
||||
Docker Remote API
|
||||
=================
|
||||
|
||||
.. contents:: Table of Contents
|
||||
|
||||
1. Brief introduction
|
||||
=====================
|
||||
|
||||
- The Remote API is replacing rcli
|
||||
- Default port in the docker deamon is 4243
|
||||
- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
|
||||
- Since API version 1.2, the auth configuration is now handled client side, so the client has to send the authConfig as POST in /images/(name)/push
|
||||
- By default the Docker daemon listens on unix:///var/run/docker.sock and the client must have root access to interact with the daemon
|
||||
- If a group named *docker* exists on your system, docker will apply ownership of the socket to the group
|
||||
- The API tends to be REST, but for some complex commands, like attach
|
||||
or pull, the HTTP connection is hijacked to transport stdout stdin
|
||||
and stderr
|
||||
- Since API version 1.2, the auth configuration is now handled client
|
||||
side, so the client has to send the authConfig as POST in
|
||||
/images/(name)/push
|
||||
|
||||
2. Versions
|
||||
===========
|
||||
|
||||
The current verson of the API is 1.3
|
||||
Calling /images/<name>/insert is the same as calling /v1.3/images/<name>/insert
|
||||
You can still call an old version of the api using /v1.0/images/<name>/insert
|
||||
The current version of the API is 1.6
|
||||
|
||||
:doc:`docker_remote_api_v1.3`
|
||||
*****************************
|
||||
Calling /images/<name>/insert is the same as calling
|
||||
/v1.6/images/<name>/insert
|
||||
|
||||
You can still call an old version of the api using
|
||||
/v1.0/images/<name>/insert
|
||||
|
||||
v1.6
|
||||
****
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.6`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
.. http:post:: /containers/(id)/attach
|
||||
|
||||
**New!** You can now split stderr from stdout. This is done by prefixing
|
||||
a header to each transmition. See :http:post:`/containers/(id)/attach`.
|
||||
The WebSocket attach is unchanged.
|
||||
Note that attach calls on the previous API version didn't change. Stdout and
|
||||
stderr are merged.
|
||||
|
||||
|
||||
v1.5
|
||||
****
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.5`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
.. http:post:: /images/create
|
||||
|
||||
**New!** You can now pass registry credentials (via an AuthConfig object)
|
||||
through the `X-Registry-Auth` header
|
||||
|
||||
.. http:post:: /images/(name)/push
|
||||
|
||||
**New!** The AuthConfig object now needs to be passed through
|
||||
the `X-Registry-Auth` header
|
||||
|
||||
.. http:get:: /containers/json
|
||||
|
||||
**New!** The format of the `Ports` entry has been changed to a list of
|
||||
dicts each containing `PublicPort`, `PrivatePort` and `Type` describing a
|
||||
port mapping.
|
||||
|
||||
v1.4
|
||||
****
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.4`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
.. http:post:: /images/create
|
||||
|
||||
**New!** When pulling a repo, all images are now downloaded in parallel.
|
||||
|
||||
.. http:get:: /containers/(id)/top
|
||||
|
||||
**New!** You can now use ps args with docker top, like `docker top <container_id> aux`
|
||||
|
||||
.. http:get:: /events:
|
||||
|
||||
**New!** Image's name added in the events
|
||||
|
||||
v1.3
|
||||
****
|
||||
|
||||
docker v0.5.0 51f6c4a_
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.3`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
.. http:get:: /containers/(id)/top
|
||||
|
||||
List the processes running inside a container.
|
||||
|
||||
.. http:get:: /events:
|
||||
|
||||
**New!** Monitor docker's events via streaming or via polling
|
||||
|
||||
Builder (/build):
|
||||
|
||||
- Simplify the upload of the build context
|
||||
- Simply stream a tarball instead of multipart upload with 4 intermediary buffers
|
||||
- Simply stream a tarball instead of multipart upload with 4
|
||||
intermediary buffers
|
||||
- Simpler, less memory usage, less disk usage and faster
|
||||
|
||||
.. Note::
|
||||
The /build improvements are not reverse-compatible. Pre 1.3 clients will break on /build.
|
||||
.. Warning::
|
||||
|
||||
The /build improvements are not reverse-compatible. Pre 1.3 clients
|
||||
will break on /build.
|
||||
|
||||
List containers (/containers/json):
|
||||
|
||||
@@ -44,32 +143,54 @@ List containers (/containers/json):
|
||||
|
||||
Start containers (/containers/<id>/start):
|
||||
|
||||
- You can now pass host-specific configuration (e.g. bind mounts) in the POST body for start calls
|
||||
- You can now pass host-specific configuration (e.g. bind mounts) in
|
||||
the POST body for start calls
|
||||
|
||||
:doc:`docker_remote_api_v1.2`
|
||||
*****************************
|
||||
v1.2
|
||||
****
|
||||
|
||||
docker v0.4.2 2e7649b_
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.2`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
The auth configuration is now handled by the client.
|
||||
The client should send it's authConfig as POST on each call of /images/(name)/push
|
||||
|
||||
.. http:get:: /auth is now deprecated
|
||||
.. http:post:: /auth only checks the configuration but doesn't store it on the server
|
||||
The client should send it's authConfig as POST on each call of
|
||||
/images/(name)/push
|
||||
|
||||
Deleting an image is now improved, will only untag the image if it has chidrens and remove all the untagged parents if has any.
|
||||
.. http:get:: /auth
|
||||
|
||||
.. http:post:: /images/<name>/delete now returns a JSON with the list of images deleted/untagged
|
||||
**Deprecated.**
|
||||
|
||||
.. http:post:: /auth
|
||||
|
||||
Only checks the configuration but doesn't store it on the server
|
||||
|
||||
Deleting an image is now improved, will only untag the image if it
|
||||
has children and remove all the untagged parents if has any.
|
||||
|
||||
.. http:post:: /images/<name>/delete
|
||||
|
||||
Now returns a JSON structure with the list of images
|
||||
deleted/untagged.
|
||||
|
||||
|
||||
:doc:`docker_remote_api_v1.1`
|
||||
*****************************
|
||||
v1.1
|
||||
****
|
||||
|
||||
docker v0.4.0 a8ae398_
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.1`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
@@ -77,7 +198,7 @@ What's new
|
||||
.. http:post:: /images/(name)/insert
|
||||
.. http:post:: /images/(name)/push
|
||||
|
||||
Uses json stream instead of HTML hijack, it looks like this:
|
||||
Uses json stream instead of HTML hijack, it looks like this:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
@@ -89,12 +210,16 @@ Uses json stream instead of HTML hijack, it looks like this:
|
||||
{"error":"Invalid..."}
|
||||
...
|
||||
|
||||
|
||||
:doc:`docker_remote_api_v1.0`
|
||||
*****************************
|
||||
v1.0
|
||||
****
|
||||
|
||||
docker v0.3.4 8d73740_
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.0`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
@@ -104,29 +229,4 @@ Initial version
|
||||
.. _a8ae398: https://github.com/dotcloud/docker/commit/a8ae398bf52e97148ee7bd0d5868de2e15bd297f
|
||||
.. _8d73740: https://github.com/dotcloud/docker/commit/8d73740343778651c09160cde9661f5f387b36f4
|
||||
.. _2e7649b: https://github.com/dotcloud/docker/commit/2e7649beda7c820793bd46766cbc2cfeace7b168
|
||||
|
||||
==================================
|
||||
Docker Remote API Client Libraries
|
||||
==================================
|
||||
|
||||
These libraries have been not tested by the Docker Maintainers for
|
||||
compatibility. Please file issues with the library owners. If you
|
||||
find more library implementations, please list them in Docker doc bugs
|
||||
and we will add the libraries here.
|
||||
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Language/Framework | Name | Repository |
|
||||
+======================+================+============================================+
|
||||
| Python | docker-py | https://github.com/dotcloud/docker-py |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Ruby | docker-ruby | https://github.com/ActiveState/docker-ruby |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Ruby | docker-client | https://github.com/geku/docker-client |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Ruby | docker-api | https://github.com/swipely/docker-api |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Javascript | docker-js | https://github.com/dgoujard/docker-js |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Javascript (Angular) | dockerui | https://github.com/crosbymichael/dockerui |
|
||||
| **WebUI** | | |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
.. _51f6c4a: https://github.com/dotcloud/docker/commit/51f6c4a7372450d164c61e0054daf0223ddbd909
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
.. use orphan to suppress "WARNING: document isn't included in any toctree"
|
||||
.. per http://sphinx-doc.org/markup/misc.html#file-wide-metadata
|
||||
|
||||
:orphan:
|
||||
|
||||
:title: Remote API v1.0
|
||||
:description: API Documentation for Docker
|
||||
:keywords: API, Docker, rcli, REST, documentation
|
||||
@@ -12,7 +17,7 @@ Docker Remote API v1.0
|
||||
=====================
|
||||
|
||||
- The Remote API is replacing rcli
|
||||
- Default port in the docker deamon is 4243
|
||||
- Default port in the docker daemon is 4243
|
||||
- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
|
||||
|
||||
2. Endpoints
|
||||
@@ -44,28 +49,28 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0"
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0"
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "centos:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0"
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "fedora:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0"
|
||||
@@ -112,7 +117,7 @@ Create a container
|
||||
"date"
|
||||
],
|
||||
"Dns":null,
|
||||
"Image":"base",
|
||||
"Image":"ubuntu",
|
||||
"Volumes":{},
|
||||
"VolumesFrom":""
|
||||
}
|
||||
@@ -178,7 +183,7 @@ Inspect a container
|
||||
"date"
|
||||
],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": ""
|
||||
},
|
||||
@@ -300,8 +305,8 @@ Start a container
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
Stop a contaier
|
||||
***************
|
||||
Stop a container
|
||||
****************
|
||||
|
||||
.. http:post:: /containers/(id)/stop
|
||||
|
||||
@@ -485,14 +490,14 @@ List Images
|
||||
|
||||
[
|
||||
{
|
||||
"Repository":"base",
|
||||
"Tag":"ubuntu-12.10",
|
||||
"Repository":"ubuntu",
|
||||
"Tag":"precise",
|
||||
"Id":"b750fe79269d",
|
||||
"Created":1364102658
|
||||
},
|
||||
{
|
||||
"Repository":"base",
|
||||
"Tag":"ubuntu-quantal",
|
||||
"Repository":"ubuntu",
|
||||
"Tag":"12.04",
|
||||
"Id":"b750fe79269d",
|
||||
"Created":1364102658
|
||||
}
|
||||
@@ -524,9 +529,9 @@ List Images
|
||||
"d6434d954665" -> "d82cbacda43a"
|
||||
base -> "e9aa60c60128" [style=invis]
|
||||
"074be284591f" -> "f71189fff3de"
|
||||
"b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
base [style=invisible]
|
||||
}
|
||||
|
||||
@@ -547,7 +552,7 @@ Create an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -567,8 +572,8 @@ Create an image
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
Insert a file in a image
|
||||
************************
|
||||
Insert a file in an image
|
||||
*************************
|
||||
|
||||
.. http:post:: /images/(name)/insert
|
||||
|
||||
@@ -603,7 +608,7 @@ Inspect an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/centos/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -633,7 +638,7 @@ Inspect an image
|
||||
"Env":null,
|
||||
"Cmd": ["/bin/bash"]
|
||||
,"Dns":null,
|
||||
"Image":"base",
|
||||
"Image":"centos",
|
||||
"Volumes":null,
|
||||
"VolumesFrom":""
|
||||
}
|
||||
@@ -655,7 +660,7 @@ Get the history of an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/fedora/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -827,7 +832,7 @@ Build an image from Dockerfile via stdin
|
||||
|
||||
{{ STREAM }}
|
||||
|
||||
:query t: tag to be applied to the resulting image in case of success
|
||||
:query t: repository name to be applied to the resulting image in case of success
|
||||
:statuscode 200: no error
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
.. use orphan to suppress "WARNING: document isn't included in any toctree"
|
||||
.. per http://sphinx-doc.org/markup/misc.html#file-wide-metadata
|
||||
|
||||
:orphan:
|
||||
|
||||
:title: Remote API v1.1
|
||||
:description: API Documentation for Docker
|
||||
@@ -13,7 +17,7 @@ Docker Remote API v1.1
|
||||
=====================
|
||||
|
||||
- The Remote API is replacing rcli
|
||||
- Default port in the docker deamon is 4243
|
||||
- Default port in the docker daemon is 4243
|
||||
- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
|
||||
|
||||
2. Endpoints
|
||||
@@ -45,28 +49,28 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0"
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0"
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "centos:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0"
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "fedora:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0"
|
||||
@@ -113,7 +117,7 @@ Create a container
|
||||
"date"
|
||||
],
|
||||
"Dns":null,
|
||||
"Image":"base",
|
||||
"Image":"ubuntu",
|
||||
"Volumes":{},
|
||||
"VolumesFrom":""
|
||||
}
|
||||
@@ -179,7 +183,7 @@ Inspect a container
|
||||
"date"
|
||||
],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": ""
|
||||
},
|
||||
@@ -301,8 +305,8 @@ Start a container
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
Stop a contaier
|
||||
***************
|
||||
Stop a container
|
||||
****************
|
||||
|
||||
.. http:post:: /containers/(id)/stop
|
||||
|
||||
@@ -486,14 +490,14 @@ List Images
|
||||
|
||||
[
|
||||
{
|
||||
"Repository":"base",
|
||||
"Tag":"ubuntu-12.10",
|
||||
"Repository":"ubuntu",
|
||||
"Tag":"precise",
|
||||
"Id":"b750fe79269d",
|
||||
"Created":1364102658
|
||||
},
|
||||
{
|
||||
"Repository":"base",
|
||||
"Tag":"ubuntu-quantal",
|
||||
"Repository":"ubuntu",
|
||||
"Tag":"12.04",
|
||||
"Id":"b750fe79269d",
|
||||
"Created":1364102658
|
||||
}
|
||||
@@ -525,9 +529,9 @@ List Images
|
||||
"d6434d954665" -> "d82cbacda43a"
|
||||
base -> "e9aa60c60128" [style=invis]
|
||||
"074be284591f" -> "f71189fff3de"
|
||||
"b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
base [style=invisible]
|
||||
}
|
||||
|
||||
@@ -548,7 +552,7 @@ Create an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -571,8 +575,8 @@ Create an image
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
Insert a file in a image
|
||||
************************
|
||||
Insert a file in an image
|
||||
*************************
|
||||
|
||||
.. http:post:: /images/(name)/insert
|
||||
|
||||
@@ -611,7 +615,7 @@ Inspect an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/centos/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -641,7 +645,7 @@ Inspect an image
|
||||
"Env":null,
|
||||
"Cmd": ["/bin/bash"]
|
||||
,"Dns":null,
|
||||
"Image":"base",
|
||||
"Image":"centos",
|
||||
"Volumes":null,
|
||||
"VolumesFrom":""
|
||||
}
|
||||
@@ -663,7 +667,7 @@ Get the history of an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/fedora/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
.. use orphan to suppress "WARNING: document isn't included in any toctree"
|
||||
.. per http://sphinx-doc.org/markup/misc.html#file-wide-metadata
|
||||
|
||||
:orphan:
|
||||
|
||||
:title: Remote API v1.2
|
||||
:description: API Documentation for Docker
|
||||
:keywords: API, Docker, rcli, REST, documentation
|
||||
@@ -12,7 +17,7 @@ Docker Remote API v1.2
|
||||
=====================
|
||||
|
||||
- The Remote API is replacing rcli
|
||||
- Default port in the docker deamon is 4243
|
||||
- Default port in the docker daemon is 4243
|
||||
- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
|
||||
|
||||
2. Endpoints
|
||||
@@ -44,7 +49,7 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -54,7 +59,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -64,7 +69,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "centos:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0",
|
||||
@@ -74,7 +79,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "fedora:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0",
|
||||
@@ -124,7 +129,7 @@ Create a container
|
||||
"date"
|
||||
],
|
||||
"Dns":null,
|
||||
"Image":"base",
|
||||
"Image":"ubuntu",
|
||||
"Volumes":{},
|
||||
"VolumesFrom":""
|
||||
}
|
||||
@@ -190,7 +195,7 @@ Inspect a container
|
||||
"date"
|
||||
],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": ""
|
||||
},
|
||||
@@ -312,8 +317,8 @@ Start a container
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
Stop a contaier
|
||||
***************
|
||||
Stop a container
|
||||
****************
|
||||
|
||||
.. http:post:: /containers/(id)/stop
|
||||
|
||||
@@ -497,16 +502,16 @@ List Images
|
||||
|
||||
[
|
||||
{
|
||||
"Repository":"base",
|
||||
"Tag":"ubuntu-12.10",
|
||||
"Repository":"ubuntu",
|
||||
"Tag":"precise",
|
||||
"Id":"b750fe79269d",
|
||||
"Created":1364102658,
|
||||
"Size":24653,
|
||||
"VirtualSize":180116135
|
||||
},
|
||||
{
|
||||
"Repository":"base",
|
||||
"Tag":"ubuntu-quantal",
|
||||
"Repository":"ubuntu",
|
||||
"Tag":"12.04",
|
||||
"Id":"b750fe79269d",
|
||||
"Created":1364102658,
|
||||
"Size":24653,
|
||||
@@ -540,9 +545,9 @@ List Images
|
||||
"d6434d954665" -> "d82cbacda43a"
|
||||
base -> "e9aa60c60128" [style=invis]
|
||||
"074be284591f" -> "f71189fff3de"
|
||||
"b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
base [style=invisible]
|
||||
}
|
||||
|
||||
@@ -563,7 +568,7 @@ Create an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -586,8 +591,8 @@ Create an image
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
Insert a file in a image
|
||||
************************
|
||||
Insert a file in an image
|
||||
*************************
|
||||
|
||||
.. http:post:: /images/(name)/insert
|
||||
|
||||
@@ -626,7 +631,7 @@ Inspect an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/centos/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -656,7 +661,7 @@ Inspect an image
|
||||
"Env":null,
|
||||
"Cmd": ["/bin/bash"]
|
||||
,"Dns":null,
|
||||
"Image":"base",
|
||||
"Image":"centos",
|
||||
"Volumes":null,
|
||||
"VolumesFrom":""
|
||||
},
|
||||
@@ -679,7 +684,7 @@ Get the history of an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/fedora/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -691,7 +696,7 @@ Get the history of an image
|
||||
[
|
||||
{
|
||||
"Id":"b750fe79269d",
|
||||
"Tag":["base:latest"],
|
||||
"Tag":["ubuntu:latest"],
|
||||
"Created":1364102658,
|
||||
"CreatedBy":"/bin/bash"
|
||||
},
|
||||
@@ -865,7 +870,7 @@ Build an image from Dockerfile via stdin
|
||||
|
||||
{{ STREAM }}
|
||||
|
||||
:query t: tag to be applied to the resulting image in case of success
|
||||
:query t: repository name to be applied to the resulting image in case of success
|
||||
:query remote: resource to fetch, as URI
|
||||
:statuscode 200: no error
|
||||
:statuscode 500: server error
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
.. use orphan to suppress "WARNING: document isn't included in any toctree"
|
||||
.. per http://sphinx-doc.org/markup/misc.html#file-wide-metadata
|
||||
|
||||
:orphan:
|
||||
|
||||
:title: Remote API v1.3
|
||||
:description: API Documentation for Docker
|
||||
:keywords: API, Docker, rcli, REST, documentation
|
||||
@@ -12,7 +17,7 @@ Docker Remote API v1.3
|
||||
=====================
|
||||
|
||||
- The Remote API is replacing rcli
|
||||
- Default port in the docker deamon is 4243
|
||||
- Default port in the docker daemon is 4243
|
||||
- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
|
||||
|
||||
2. Endpoints
|
||||
@@ -44,7 +49,7 @@ List containers
|
||||
[
|
||||
{
|
||||
"Id": "8dfafdbc3a40",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 1",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -54,7 +59,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "9cd87474be90",
|
||||
"Image": "base:latest",
|
||||
"Image": "ubuntu:latest",
|
||||
"Command": "echo 222222",
|
||||
"Created": 1367854155,
|
||||
"Status": "Exit 0",
|
||||
@@ -64,7 +69,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "3176a2479c92",
|
||||
"Image": "base:latest",
|
||||
"Image": "centos:latest",
|
||||
"Command": "echo 3333333333333333",
|
||||
"Created": 1367854154,
|
||||
"Status": "Exit 0",
|
||||
@@ -74,7 +79,7 @@ List containers
|
||||
},
|
||||
{
|
||||
"Id": "4cb07b47f9fb",
|
||||
"Image": "base:latest",
|
||||
"Image": "fedora:latest",
|
||||
"Command": "echo 444444444444444444444444444444444",
|
||||
"Created": 1367854152,
|
||||
"Status": "Exit 0",
|
||||
@@ -125,7 +130,7 @@ Create a container
|
||||
"date"
|
||||
],
|
||||
"Dns":null,
|
||||
"Image":"base",
|
||||
"Image":"ubuntu",
|
||||
"Volumes":{},
|
||||
"VolumesFrom":""
|
||||
}
|
||||
@@ -191,7 +196,7 @@ Inspect a container
|
||||
"date"
|
||||
],
|
||||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Image": "ubuntu",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": ""
|
||||
},
|
||||
@@ -220,6 +225,46 @@ Inspect a container
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
List processes running inside a container
|
||||
*****************************************
|
||||
|
||||
.. http:get:: /containers/(id)/top
|
||||
|
||||
List processes running inside the container ``id``
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /containers/4fa6e0f0c678/top HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"PID":"11935",
|
||||
"Tty":"pts/2",
|
||||
"Time":"00:00:00",
|
||||
"Cmd":"sh"
|
||||
},
|
||||
{
|
||||
"PID":"12140",
|
||||
"Tty":"pts/2",
|
||||
"Time":"00:00:00",
|
||||
"Cmd":"sleep"
|
||||
}
|
||||
]
|
||||
|
||||
:statuscode 200: no error
|
||||
:statuscode 404: no such container
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
Inspect changes on a container's filesystem
|
||||
*******************************************
|
||||
|
||||
@@ -320,8 +365,8 @@ Start a container
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
Stop a contaier
|
||||
***************
|
||||
Stop a container
|
||||
****************
|
||||
|
||||
.. http:post:: /containers/(id)/stop
|
||||
|
||||
@@ -505,16 +550,16 @@ List Images
|
||||
|
||||
[
|
||||
{
|
||||
"Repository":"base",
|
||||
"Tag":"ubuntu-12.10",
|
||||
"Repository":"ubuntu",
|
||||
"Tag":"precise",
|
||||
"Id":"b750fe79269d",
|
||||
"Created":1364102658,
|
||||
"Size":24653,
|
||||
"VirtualSize":180116135
|
||||
},
|
||||
{
|
||||
"Repository":"base",
|
||||
"Tag":"ubuntu-quantal",
|
||||
"Repository":"ubuntu",
|
||||
"Tag":"12.04",
|
||||
"Id":"b750fe79269d",
|
||||
"Created":1364102658,
|
||||
"Size":24653,
|
||||
@@ -548,9 +593,9 @@ List Images
|
||||
"d6434d954665" -> "d82cbacda43a"
|
||||
base -> "e9aa60c60128" [style=invis]
|
||||
"074be284591f" -> "f71189fff3de"
|
||||
"b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
"9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
|
||||
base [style=invisible]
|
||||
}
|
||||
|
||||
@@ -571,7 +616,7 @@ Create an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
POST /images/create?fromImage=ubuntu HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -594,8 +639,8 @@ Create an image
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
Insert a file in a image
|
||||
************************
|
||||
Insert a file in an image
|
||||
*************************
|
||||
|
||||
.. http:post:: /images/(name)/insert
|
||||
|
||||
@@ -634,7 +679,7 @@ Inspect an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /images/base/json HTTP/1.1
|
||||
GET /images/centos/json HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -664,7 +709,7 @@ Inspect an image
|
||||
"Env":null,
|
||||
"Cmd": ["/bin/bash"]
|
||||
,"Dns":null,
|
||||
"Image":"base",
|
||||
"Image":"centos",
|
||||
"Volumes":null,
|
||||
"VolumesFrom":""
|
||||
},
|
||||
@@ -687,7 +732,7 @@ Get the history of an image
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /images/base/history HTTP/1.1
|
||||
GET /images/fedora/history HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -800,7 +845,7 @@ Remove an image
|
||||
{"Deleted":"53b4f83ac9"}
|
||||
]
|
||||
|
||||
:statuscode 204: no error
|
||||
:statuscode 200: no error
|
||||
:statuscode 404: no such image
|
||||
:statuscode 409: conflict
|
||||
:statuscode 500: server error
|
||||
@@ -880,7 +925,8 @@ Build an image from Dockerfile via stdin
|
||||
|
||||
The Content-type header should be set to "application/tar".
|
||||
|
||||
:query t: tag to be applied to the resulting image in case of success
|
||||
:query t: repository name (and optionally a tag) to be applied to the resulting image in case of success
|
||||
:query q: suppress verbose build output
|
||||
:statuscode 200: no error
|
||||
:statuscode 500: server error
|
||||
|
||||
@@ -943,7 +989,10 @@ Display system-wide information
|
||||
"NFd": 11,
|
||||
"NGoroutines":21,
|
||||
"MemoryLimit":true,
|
||||
"SwapLimit":false
|
||||
"SwapLimit":false,
|
||||
"EventsListeners":"0",
|
||||
"LXCVersion":"0.7.5",
|
||||
"KernelVersion":"3.8.0-19-generic"
|
||||
}
|
||||
|
||||
:statuscode 200: no error
|
||||
@@ -1013,6 +1062,36 @@ Create a new image from a container's changes
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
Monitor Docker's events
|
||||
***********************
|
||||
|
||||
.. http:get:: /events
|
||||
|
||||
Get events from docker, either in real time via streaming, or via polling (using `since`)
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /events?since=1374067924
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status":"create","id":"dfdf82bd3881","time":1374067924}
|
||||
{"status":"start","id":"dfdf82bd3881","time":1374067924}
|
||||
{"status":"stop","id":"dfdf82bd3881","time":1374067966}
|
||||
{"status":"destroy","id":"dfdf82bd3881","time":1374067970}
|
||||
|
||||
:query since: timestamp used for polling
|
||||
:statuscode 200: no error
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
3. Going further
|
||||
================
|
||||
|
||||
|
||||
1171
docs/sources/api/docker_remote_api_v1.4.rst
Normal file
1171
docs/sources/api/docker_remote_api_v1.4.rst
Normal file
File diff suppressed because it is too large
Load Diff
1139
docs/sources/api/docker_remote_api_v1.5.rst
Normal file
1139
docs/sources/api/docker_remote_api_v1.5.rst
Normal file
File diff suppressed because it is too large
Load Diff
1218
docs/sources/api/docker_remote_api_v1.6.rst
Normal file
1218
docs/sources/api/docker_remote_api_v1.6.rst
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,6 @@
|
||||
Docker Index API
|
||||
=================
|
||||
|
||||
.. contents:: Table of Contents
|
||||
|
||||
1. Brief introduction
|
||||
=====================
|
||||
|
||||
@@ -42,7 +40,7 @@ User Repo
|
||||
Authorization: Basic akmklmasadalkm==
|
||||
X-Docker-Token: true
|
||||
|
||||
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”}]
|
||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"}]
|
||||
|
||||
:parameter namespace: the namespace for the repo
|
||||
:parameter repo_name: the name for the repo
|
||||
@@ -54,7 +52,8 @@ User Repo
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=write
|
||||
WWW-Authenticate: Token signature=123abc,repository="foo/bar",access=write
|
||||
X-Docker-Token: signature=123abc,repository="foo/bar",access=write
|
||||
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
|
||||
|
||||
""
|
||||
@@ -92,7 +91,8 @@ User Repo
|
||||
HTTP/1.1 202
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=delete
|
||||
WWW-Authenticate: Token signature=123abc,repository="foo/bar",access=delete
|
||||
X-Docker-Token: signature=123abc,repository="foo/bar",access=delete
|
||||
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
|
||||
|
||||
""
|
||||
@@ -124,7 +124,7 @@ Library Repo
|
||||
Authorization: Basic akmklmasadalkm==
|
||||
X-Docker-Token: true
|
||||
|
||||
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”}]
|
||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"}]
|
||||
|
||||
:parameter repo_name: the library name for the repo
|
||||
|
||||
@@ -135,7 +135,8 @@ Library Repo
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
WWW-Authenticate: Token signature=123abc,repository=”library/foobar”,access=write
|
||||
WWW-Authenticate: Token signature=123abc,repository="library/foobar",access=write
|
||||
X-Docker-Token: signature=123abc,repository="foo/bar",access=write
|
||||
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
|
||||
|
||||
""
|
||||
@@ -174,7 +175,8 @@ Library Repo
|
||||
HTTP/1.1 202
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
WWW-Authenticate: Token signature=123abc,repository=”library/foobar”,access=delete
|
||||
WWW-Authenticate: Token signature=123abc,repository="library/foobar",access=delete
|
||||
X-Docker-Token: signature=123abc,repository="foo/bar",access=delete
|
||||
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
|
||||
|
||||
""
|
||||
@@ -205,8 +207,8 @@ User Repo Images
|
||||
Content-Type: application/json
|
||||
Authorization: Basic akmklmasadalkm==
|
||||
|
||||
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
|
||||
“checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
|
||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
|
||||
"checksum": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"}]
|
||||
|
||||
:parameter namespace: the namespace for the repo
|
||||
:parameter repo_name: the name for the repo
|
||||
@@ -250,10 +252,10 @@ User Repo Images
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
|
||||
“checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”},
|
||||
{“id”: “ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds”,
|
||||
“checksum”: “34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew”}]
|
||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
|
||||
"checksum": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"},
|
||||
{"id": "ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds",
|
||||
"checksum": "34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew"}]
|
||||
|
||||
:statuscode 200: OK
|
||||
:statuscode 404: Not found
|
||||
@@ -275,8 +277,8 @@ Library Repo Images
|
||||
Content-Type: application/json
|
||||
Authorization: Basic akmklmasadalkm==
|
||||
|
||||
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
|
||||
“checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
|
||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
|
||||
"checksum": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"}]
|
||||
|
||||
:parameter repo_name: the library name for the repo
|
||||
|
||||
@@ -318,10 +320,10 @@ Library Repo Images
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
|
||||
“checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”},
|
||||
{“id”: “ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds”,
|
||||
“checksum”: “34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew”}]
|
||||
[{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
|
||||
"checksum": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"},
|
||||
{"id": "ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds",
|
||||
"checksum": "34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew"}]
|
||||
|
||||
:statuscode 200: OK
|
||||
:statuscode 404: Not found
|
||||
@@ -452,7 +454,7 @@ User Register
|
||||
"username": "foobar"'}
|
||||
|
||||
:jsonparameter email: valid email address, that needs to be confirmed
|
||||
:jsonparameter username: min 4 character, max 30 characters, must match the regular expression [a-z0-9_].
|
||||
:jsonparameter username: min 4 character, max 30 characters, must match the regular expression [a-z0-9\_].
|
||||
:jsonparameter password: min 5 characters
|
||||
|
||||
**Example Response**:
|
||||
@@ -541,10 +543,11 @@ Search
|
||||
Content-Type: application/json
|
||||
|
||||
{"query":"search_term",
|
||||
"num_results": 2,
|
||||
"num_results": 3,
|
||||
"results" : [
|
||||
{"name": "dotcloud/base", "description": "A base ubuntu64 image..."},
|
||||
{"name": "base2", "description": "A base ubuntu64 image..."},
|
||||
{"name": "ubuntu", "description": "An ubuntu image..."},
|
||||
{"name": "centos", "description": "A centos image..."},
|
||||
{"name": "fedora", "description": "A fedora image..."}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
Docker Registry API
|
||||
===================
|
||||
|
||||
.. contents:: Table of Contents
|
||||
|
||||
1. Brief introduction
|
||||
=====================
|
||||
@@ -61,7 +60,7 @@ Layer
|
||||
Host: registry-1.docker.io
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Token akmklmasadalkmsdfgsdgdge33
|
||||
Authorization: Token signature=123abc,repository="foo/bar",access=read
|
||||
|
||||
:parameter image_id: the id for the layer you want to get
|
||||
|
||||
@@ -71,39 +70,10 @@ Layer
|
||||
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
Cookie: (Cookie provided by the Registry)
|
||||
|
||||
{
|
||||
id: "088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c",
|
||||
parent: "aeee6396d62273d180a49c96c62e45438d87c7da4a5cf5d2be6bee4e21bc226f",
|
||||
created: "2013-04-30T17:46:10.843673+03:00",
|
||||
container: "8305672a76cc5e3d168f97221106ced35a76ec7ddbb03209b0f0d96bf74f6ef7",
|
||||
container_config: {
|
||||
Hostname: "host-test",
|
||||
User: "",
|
||||
Memory: 0,
|
||||
MemorySwap: 0,
|
||||
AttachStdin: false,
|
||||
AttachStdout: false,
|
||||
AttachStderr: false,
|
||||
PortSpecs: null,
|
||||
Tty: false,
|
||||
OpenStdin: false,
|
||||
StdinOnce: false,
|
||||
Env: null,
|
||||
Cmd: [
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
"apt-get -q -yy -f install libevent-dev"
|
||||
],
|
||||
Dns: null,
|
||||
Image: "imagename/blah",
|
||||
Volumes: { },
|
||||
VolumesFrom: ""
|
||||
},
|
||||
docker_version: "0.1.7"
|
||||
}
|
||||
{layer binary data stream}
|
||||
|
||||
:statuscode 200: OK
|
||||
:statuscode 401: Requires authorization
|
||||
@@ -120,40 +90,10 @@ Layer
|
||||
|
||||
PUT /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/layer HTTP/1.1
|
||||
Host: registry-1.docker.io
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Token akmklmasadalkmsdfgsdgdge33
|
||||
Transfer-Encoding: chunked
|
||||
Authorization: Token signature=123abc,repository="foo/bar",access=write
|
||||
|
||||
{
|
||||
id: "088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c",
|
||||
parent: "aeee6396d62273d180a49c96c62e45438d87c7da4a5cf5d2be6bee4e21bc226f",
|
||||
created: "2013-04-30T17:46:10.843673+03:00",
|
||||
container: "8305672a76cc5e3d168f97221106ced35a76ec7ddbb03209b0f0d96bf74f6ef7",
|
||||
container_config: {
|
||||
Hostname: "host-test",
|
||||
User: "",
|
||||
Memory: 0,
|
||||
MemorySwap: 0,
|
||||
AttachStdin: false,
|
||||
AttachStdout: false,
|
||||
AttachStderr: false,
|
||||
PortSpecs: null,
|
||||
Tty: false,
|
||||
OpenStdin: false,
|
||||
StdinOnce: false,
|
||||
Env: null,
|
||||
Cmd: [
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
"apt-get -q -yy -f install libevent-dev"
|
||||
],
|
||||
Dns: null,
|
||||
Image: "imagename/blah",
|
||||
Volumes: { },
|
||||
VolumesFrom: ""
|
||||
},
|
||||
docker_version: "0.1.7"
|
||||
}
|
||||
{layer binary data stream}
|
||||
|
||||
:parameter image_id: the id for the layer you want to get
|
||||
|
||||
@@ -165,6 +105,7 @@ Layer
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
|
||||
""
|
||||
|
||||
@@ -191,13 +132,38 @@ Image
|
||||
Cookie: (Cookie provided by the Registry)
|
||||
|
||||
{
|
||||
“id”: “088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c”,
|
||||
“checksum”: “sha256:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
|
||||
}
|
||||
id: "088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c",
|
||||
parent: "aeee6396d62273d180a49c96c62e45438d87c7da4a5cf5d2be6bee4e21bc226f",
|
||||
created: "2013-04-30T17:46:10.843673+03:00",
|
||||
container: "8305672a76cc5e3d168f97221106ced35a76ec7ddbb03209b0f0d96bf74f6ef7",
|
||||
container_config: {
|
||||
Hostname: "host-test",
|
||||
User: "",
|
||||
Memory: 0,
|
||||
MemorySwap: 0,
|
||||
AttachStdin: false,
|
||||
AttachStdout: false,
|
||||
AttachStderr: false,
|
||||
PortSpecs: null,
|
||||
Tty: false,
|
||||
OpenStdin: false,
|
||||
StdinOnce: false,
|
||||
Env: null,
|
||||
Cmd: [
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
"apt-get -q -yy -f install libevent-dev"
|
||||
],
|
||||
Dns: null,
|
||||
Image: "imagename/blah",
|
||||
Volumes: { },
|
||||
VolumesFrom: ""
|
||||
},
|
||||
docker_version: "0.1.7"
|
||||
}
|
||||
|
||||
:parameter image_id: the id for the layer you want to get
|
||||
|
||||
|
||||
**Example Response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
@@ -205,6 +171,7 @@ Image
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
|
||||
""
|
||||
|
||||
@@ -234,11 +201,40 @@ Image
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
X-Docker-Size: 456789
|
||||
X-Docker-Checksum: b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087
|
||||
|
||||
{
|
||||
“id”: “088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c”,
|
||||
“checksum”: “sha256:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
|
||||
}
|
||||
id: "088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c",
|
||||
parent: "aeee6396d62273d180a49c96c62e45438d87c7da4a5cf5d2be6bee4e21bc226f",
|
||||
created: "2013-04-30T17:46:10.843673+03:00",
|
||||
container: "8305672a76cc5e3d168f97221106ced35a76ec7ddbb03209b0f0d96bf74f6ef7",
|
||||
container_config: {
|
||||
Hostname: "host-test",
|
||||
User: "",
|
||||
Memory: 0,
|
||||
MemorySwap: 0,
|
||||
AttachStdin: false,
|
||||
AttachStdout: false,
|
||||
AttachStderr: false,
|
||||
PortSpecs: null,
|
||||
Tty: false,
|
||||
OpenStdin: false,
|
||||
StdinOnce: false,
|
||||
Env: null,
|
||||
Cmd: [
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
"apt-get -q -yy -f install libevent-dev"
|
||||
],
|
||||
Dns: null,
|
||||
Image: "imagename/blah",
|
||||
Volumes: { },
|
||||
VolumesFrom: ""
|
||||
},
|
||||
docker_version: "0.1.7"
|
||||
}
|
||||
|
||||
:statuscode 200: OK
|
||||
:statuscode 401: Requires authorization
|
||||
@@ -271,6 +267,7 @@ Ancestry
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
|
||||
["088b4502f51920fbd9b7c503e87c7a2c05aa3adc3d35e79c031fa126b403200f",
|
||||
"aeee63968d87c7da4a5cf5d2be6bee4e21bc226fd62273d180a49c96c62e4543",
|
||||
@@ -297,6 +294,7 @@ Ancestry
|
||||
Host: registry-1.docker.io
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
Cookie: (Cookie provided by the Registry)
|
||||
|
||||
:parameter namespace: namespace for the repo
|
||||
@@ -309,10 +307,11 @@ Ancestry
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
|
||||
{
|
||||
"latest": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
|
||||
“0.1.1”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
|
||||
"0.1.1": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"
|
||||
}
|
||||
|
||||
:statuscode 200: OK
|
||||
@@ -332,6 +331,7 @@ Ancestry
|
||||
Host: registry-1.docker.io
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
Cookie: (Cookie provided by the Registry)
|
||||
|
||||
:parameter namespace: namespace for the repo
|
||||
@@ -345,6 +345,7 @@ Ancestry
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
|
||||
"9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"
|
||||
|
||||
@@ -377,6 +378,7 @@ Ancestry
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
|
||||
""
|
||||
|
||||
@@ -399,7 +401,7 @@ Ancestry
|
||||
Content-Type: application/json
|
||||
Cookie: (Cookie provided by the Registry)
|
||||
|
||||
“9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”
|
||||
"9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"
|
||||
|
||||
:parameter namespace: namespace for the repo
|
||||
:parameter repository: name for the repo
|
||||
@@ -412,6 +414,7 @@ Ancestry
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
|
||||
""
|
||||
|
||||
@@ -449,6 +452,7 @@ Ancestry
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
|
||||
""
|
||||
|
||||
@@ -456,8 +460,41 @@ Ancestry
|
||||
:statuscode 401: Requires authorization
|
||||
:statuscode 404: Repository not found
|
||||
|
||||
3.0 Authorization
|
||||
=================
|
||||
2.4 Status
|
||||
----------
|
||||
|
||||
.. http:get:: /v1/_ping
|
||||
|
||||
Check status of the registry. This endpoint is also used to determine if
|
||||
the registry supports SSL.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /v1/_ping HTTP/1.1
|
||||
Host: registry-1.docker.io
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
""
|
||||
|
||||
**Example Response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
X-Docker-Registry-Version: 0.6.0
|
||||
|
||||
""
|
||||
|
||||
:statuscode 200: OK
|
||||
|
||||
|
||||
3 Authorization
|
||||
===============
|
||||
This is where we describe the authorization process, including the tokens and cookies.
|
||||
|
||||
TODO: add more info.
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
:description: Documentation for docker Registry and Registry API
|
||||
:keywords: docker, registry, api, index
|
||||
|
||||
.. _registryindexspec:
|
||||
|
||||
=====================
|
||||
Registry & index Spec
|
||||
Registry & Index Spec
|
||||
=====================
|
||||
|
||||
.. contents:: Table of Contents
|
||||
|
||||
1. The 3 roles
|
||||
===============
|
||||
|
||||
@@ -154,7 +153,7 @@ API (pulling repository foo/bar):
|
||||
|
||||
.. note::
|
||||
|
||||
**It’s possible not to use the Index at all!** In this case, a deployed version of the Registry is deployed to store and serve images. Those images are not authentified and the security is not guaranteed.
|
||||
**It’s possible not to use the Index at all!** In this case, a deployed version of the Registry is deployed to store and serve images. Those images are not authenticated and the security is not guaranteed.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -367,7 +366,8 @@ POST /v1/users
|
||||
{"email": "sam@dotcloud.com", "password": "toto42", "username": "foobar"'}
|
||||
|
||||
**Validation**:
|
||||
- **username** : min 4 character, max 30 characters, must match the regular expression [a-z0-9_].
|
||||
- **username**: min 4 character, max 30 characters, must match the regular
|
||||
expression [a-z0-9\_].
|
||||
- **password**: min 5 characters
|
||||
|
||||
**Valid**: return HTTP 200
|
||||
@@ -562,8 +562,8 @@ Next request::
|
||||
Cookie: session="wD/J7LqL5ctqw8haL10vgfhrb2Q=?foo=UydiYXInCnAxCi4=×tamp=RjEzNjYzMTQ5NDcuNDc0NjQzCi4="
|
||||
|
||||
|
||||
7.0 Document Version
|
||||
---------------------
|
||||
7 Document Version
|
||||
====================
|
||||
|
||||
- 1.0 : May 6th 2013 : initial release
|
||||
- 1.1 : June 1st 2013 : Added Delete Repository and way to handle new source namespace.
|
||||
- 1.1 : June 1st 2013 : Added Delete Repository and way to handle new source namespace.
|
||||
|
||||
37
docs/sources/api/remote_api_client_libraries.rst
Normal file
37
docs/sources/api/remote_api_client_libraries.rst
Normal file
@@ -0,0 +1,37 @@
|
||||
:title: Registry API
|
||||
:description: Various client libraries available to use with the Docker remote API
|
||||
:keywords: API, Docker, index, registry, REST, documentation, clients, Python, Ruby, Javascript, Erlang, Go
|
||||
|
||||
|
||||
==================================
|
||||
Docker Remote API Client Libraries
|
||||
==================================
|
||||
|
||||
These libraries have not been tested by the Docker Maintainers for
|
||||
compatibility. Please file issues with the library owners. If you
|
||||
find more library implementations, please list them in Docker doc bugs
|
||||
and we will add the libraries here.
|
||||
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Language/Framework | Name | Repository |
|
||||
+======================+================+============================================+
|
||||
| Python | docker-py | https://github.com/dotcloud/docker-py |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Ruby | docker-client | https://github.com/geku/docker-client |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Ruby | docker-api | https://github.com/swipely/docker-api |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Javascript (NodeJS) | docker.io | https://github.com/appersonlabs/docker.io |
|
||||
| | | Install via NPM: `npm install docker.io` |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Javascript | docker-js | https://github.com/dgoujard/docker-js |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Javascript (Angular) | dockerui | https://github.com/crosbymichael/dockerui |
|
||||
| **WebUI** | | |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Java | docker-java | https://github.com/kpelykh/docker-java |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Erlang | erldocker | https://github.com/proger/erldocker |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
| Go | go-dockerclient| https://github.com/fsouza/go-dockerclient |
|
||||
+----------------------+----------------+--------------------------------------------+
|
||||
@@ -8,49 +8,84 @@ Overview
|
||||
======================
|
||||
|
||||
Docker Usage
|
||||
~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To list available commands, either run ``docker`` with no parameters or execute
|
||||
``docker help``::
|
||||
|
||||
$ docker
|
||||
$ sudo docker
|
||||
Usage: docker [OPTIONS] COMMAND [arg...]
|
||||
-H=[tcp://127.0.0.1:4243]: 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
|
||||
|
||||
A self-sufficient runtime for linux containers.
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
Available Commands
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
.. include:: command/attach.rst
|
||||
|
||||
.. include:: command/build.rst
|
||||
|
||||
.. include:: command/commit.rst
|
||||
|
||||
.. include:: command/cp.rst
|
||||
|
||||
.. include:: command/diff.rst
|
||||
|
||||
.. include:: command/events.rst
|
||||
|
||||
.. include:: command/export.rst
|
||||
|
||||
.. include:: command/history.rst
|
||||
|
||||
.. include:: command/images.rst
|
||||
|
||||
.. include:: command/import.rst
|
||||
|
||||
.. include:: command/info.rst
|
||||
|
||||
.. include:: command/insert.rst
|
||||
|
||||
.. include:: command/inspect.rst
|
||||
|
||||
.. include:: command/kill.rst
|
||||
|
||||
.. include:: command/login.rst
|
||||
|
||||
.. include:: command/logs.rst
|
||||
|
||||
.. include:: command/port.rst
|
||||
|
||||
.. include:: command/ps.rst
|
||||
|
||||
.. include:: command/pull.rst
|
||||
|
||||
.. include:: command/push.rst
|
||||
|
||||
.. include:: command/restart.rst
|
||||
|
||||
.. include:: command/rm.rst
|
||||
|
||||
.. include:: command/rmi.rst
|
||||
|
||||
.. include:: command/run.rst
|
||||
|
||||
.. include:: command/search.rst
|
||||
|
||||
.. include:: command/start.rst
|
||||
|
||||
.. include:: command/stop.rst
|
||||
|
||||
.. include:: command/tag.rst
|
||||
|
||||
.. include:: command/top.rst
|
||||
|
||||
.. include:: command/version.rst
|
||||
|
||||
.. include:: command/wait.rst
|
||||
|
||||
|
||||
command/attach
|
||||
command/build
|
||||
command/commit
|
||||
command/diff
|
||||
command/export
|
||||
command/history
|
||||
command/images
|
||||
command/import
|
||||
command/info
|
||||
command/inspect
|
||||
command/kill
|
||||
command/login
|
||||
command/logs
|
||||
command/port
|
||||
command/ps
|
||||
command/pull
|
||||
command/push
|
||||
command/restart
|
||||
command/rm
|
||||
command/rmi
|
||||
command/run
|
||||
command/search
|
||||
command/start
|
||||
command/stop
|
||||
command/tag
|
||||
command/version
|
||||
command/wait
|
||||
|
||||
@@ -10,4 +10,50 @@
|
||||
|
||||
Usage: docker attach CONTAINER
|
||||
|
||||
Attach to a running container
|
||||
Attach to a running container.
|
||||
|
||||
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.
|
||||
|
||||
To stop a container, use ``docker stop``
|
||||
|
||||
To kill the container, use ``docker kill``
|
||||
|
||||
Examples:
|
||||
---------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ID=$(sudo docker run -d ubuntu /usr/bin/top -b)
|
||||
$ sudo docker attach $ID
|
||||
top - 02:05:52 up 3:05, 0 users, load average: 0.01, 0.02, 0.05
|
||||
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
|
||||
Cpu(s): 0.1%us, 0.2%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
|
||||
Mem: 373572k total, 355560k used, 18012k free, 27872k buffers
|
||||
Swap: 786428k total, 0k used, 786428k free, 221740k cached
|
||||
|
||||
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
||||
1 root 20 0 17200 1116 912 R 0 0.3 0:00.03 top
|
||||
|
||||
top - 02:05:55 up 3:05, 0 users, load average: 0.01, 0.02, 0.05
|
||||
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
|
||||
Cpu(s): 0.0%us, 0.2%sy, 0.0%ni, 99.8%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
|
||||
Mem: 373572k total, 355244k used, 18328k free, 27872k buffers
|
||||
Swap: 786428k total, 0k used, 786428k free, 221776k cached
|
||||
|
||||
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
||||
1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top
|
||||
|
||||
|
||||
top - 02:05:58 up 3:06, 0 users, load average: 0.01, 0.02, 0.05
|
||||
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
|
||||
Cpu(s): 0.2%us, 0.3%sy, 0.0%ni, 99.5%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
|
||||
Mem: 373572k total, 355780k used, 17792k free, 27880k buffers
|
||||
Swap: 786428k total, 0k used, 786428k free, 221776k cached
|
||||
|
||||
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
||||
1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top
|
||||
^C$
|
||||
$ sudo docker stop $ID
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
|
||||
Usage: docker build [OPTIONS] PATH | URL | -
|
||||
Build a new container image from the source code at PATH
|
||||
-t="": Tag to be applied to the resulting image in case of success.
|
||||
-t="": Repository name (and optionally a tag) to be applied to the resulting image in case of success.
|
||||
-q=false: Suppress verbose build output.
|
||||
-no-cache: Do not use the cache when building the image.
|
||||
-rm: Remove intermediate containers after a successful build
|
||||
When a single Dockerfile is given as URL, then no context is set. When a git repository is set as URL, the repository is used as context
|
||||
|
||||
|
||||
@@ -19,25 +22,44 @@ Examples
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker build .
|
||||
sudo docker build .
|
||||
|
||||
| This will read the Dockerfile from the current directory. It will also send any other files and directories found in the current directory to the docker daemon.
|
||||
| The contents of this directory would be used by ADD commands found within the Dockerfile.
|
||||
| This will send a lot of data to the docker daemon if the current directory contains a lot of data.
|
||||
| If the absolute path is provided instead of '.', only the files and directories required by the ADD commands from the Dockerfile will be added to the context and transferred to the docker daemon.
|
||||
|
|
||||
This will read the ``Dockerfile`` from the current directory. It will
|
||||
also send any other files and directories found in the current
|
||||
directory to the ``docker`` daemon.
|
||||
|
||||
The contents of this directory would be used by ``ADD`` commands found
|
||||
within the ``Dockerfile``. This will send a lot of data to the
|
||||
``docker`` daemon if the current directory contains a lot of data. If
|
||||
the absolute path is provided instead of ``.`` then only the files and
|
||||
directories required by the ADD commands from the ``Dockerfile`` will be
|
||||
added to the context and transferred to the ``docker`` daemon.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker build - < Dockerfile
|
||||
sudo docker build -t vieux/apache: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.
|
||||
| ADD doesn't work when running in this mode due to the absence of the context, thus having no source files to copy to the container.
|
||||
This will build like the previous example, but it will then tag the
|
||||
resulting image. The repository name will be ``vieux/apache`` and the
|
||||
tag will be ``2.0``
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker build github.com/creack/docker-firefox
|
||||
sudo docker build - < Dockerfile
|
||||
|
||||
| This will clone the github repository and use it as context. The Dockerfile at the root of the repository is used as Dockerfile.
|
||||
| Note that you can specify an arbitrary git repository by using the 'git://' schema.
|
||||
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. ``ADD`` doesn't work when running in this
|
||||
mode because the absence of the context provides no source files to
|
||||
copy to the container.
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker build github.com/creack/docker-firefox
|
||||
|
||||
This will clone the Github repository and use it as context. The
|
||||
``Dockerfile`` at the root of the repository is used as
|
||||
``Dockerfile``. Note that you can specify an arbitrary git repository
|
||||
by using the ``git://`` schema.
|
||||
|
||||
@@ -14,19 +14,39 @@
|
||||
|
||||
-m="": Commit message
|
||||
-author="": Author (eg. "John Hannibal Smith <hannibal@a-team.com>"
|
||||
-run="": Config automatically applied when the image is run. "+`(ex: {"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')
|
||||
-run="": Configuration to be applied when the image is launched with `docker run`.
|
||||
(ex: '{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')
|
||||
|
||||
Full -run example::
|
||||
Full -run example (multiline is ok within a single quote ``'``)
|
||||
|
||||
{"Hostname": "",
|
||||
"User": "",
|
||||
"CpuShares": 0,
|
||||
"Memory": 0,
|
||||
"MemorySwap": 0,
|
||||
"PortSpecs": ["22", "80", "443"],
|
||||
"Tty": true,
|
||||
"OpenStdin": true,
|
||||
"StdinOnce": true,
|
||||
"Env": ["FOO=BAR", "FOO2=BAR2"],
|
||||
"Cmd": ["cat", "-e", "/etc/resolv.conf"],
|
||||
"Dns": ["8.8.8.8", "8.8.4.4"]}
|
||||
::
|
||||
|
||||
$ sudo docker commit -run='
|
||||
{
|
||||
"Entrypoint" : null,
|
||||
"Privileged" : false,
|
||||
"User" : "",
|
||||
"VolumesFrom" : "",
|
||||
"Cmd" : ["cat", "-e", "/etc/resolv.conf"],
|
||||
"Dns" : ["8.8.8.8", "8.8.4.4"],
|
||||
"MemorySwap" : 0,
|
||||
"AttachStdin" : false,
|
||||
"AttachStderr" : false,
|
||||
"CpuShares" : 0,
|
||||
"OpenStdin" : false,
|
||||
"Volumes" : null,
|
||||
"Hostname" : "122612f45831",
|
||||
"PortSpecs" : ["22", "80", "443"],
|
||||
"Image" : "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
||||
"Tty" : false,
|
||||
"Env" : [
|
||||
"HOME=/",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
],
|
||||
"StdinOnce" : false,
|
||||
"Domainname" : "",
|
||||
"WorkingDir" : "/",
|
||||
"NetworkDisabled" : false,
|
||||
"Memory" : 0,
|
||||
"AttachStdout" : false
|
||||
}' $CONTAINER_ID
|
||||
|
||||
14
docs/sources/commandline/command/cp.rst
Normal file
14
docs/sources/commandline/command/cp.rst
Normal file
@@ -0,0 +1,14 @@
|
||||
:title: Cp Command
|
||||
:description: Copy files/folders from the containers filesystem to the host path
|
||||
:keywords: cp, docker, container, documentation, copy
|
||||
|
||||
============================================================================
|
||||
``cp`` -- Copy files/folders from the containers filesystem to the host path
|
||||
============================================================================
|
||||
|
||||
::
|
||||
|
||||
Usage: docker cp CONTAINER:RESOURCE HOSTPATH
|
||||
|
||||
Copy files/folders from the containers filesystem to the host
|
||||
path. Paths are relative to the root of the filesystem.
|
||||
34
docs/sources/commandline/command/events.rst
Normal file
34
docs/sources/commandline/command/events.rst
Normal file
@@ -0,0 +1,34 @@
|
||||
:title: Events Command
|
||||
:description: Get real time events from the server
|
||||
:keywords: events, docker, documentation
|
||||
|
||||
=================================================================
|
||||
``events`` -- Get real time events from the server
|
||||
=================================================================
|
||||
|
||||
::
|
||||
|
||||
Usage: docker events
|
||||
|
||||
Get real time events from the server
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Starting and stopping a container
|
||||
.................................
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker start 4386fb97867d
|
||||
$ sudo docker stop 4386fb97867d
|
||||
|
||||
In another shell
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker events
|
||||
[2013-09-03 15:49:26 +0200 CEST] 4386fb97867d: (from 12de384bfb10) start
|
||||
[2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) die
|
||||
[2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) stop
|
||||
|
||||
@@ -21,6 +21,6 @@ Displaying images visually
|
||||
|
||||
::
|
||||
|
||||
docker images -viz | dot -Tpng -o docker.png
|
||||
sudo docker images -viz | dot -Tpng -o docker.png
|
||||
|
||||
.. image:: images/docker_images.gif
|
||||
.. image:: https://docs.docker.io/en/latest/_static/docker_images.gif
|
||||
|
||||
@@ -12,9 +12,11 @@
|
||||
|
||||
Create a new filesystem image from the contents of a tarball
|
||||
|
||||
At this time, the URL must start with ``http`` and point to a single file archive (.tar, .tar.gz, .bzip)
|
||||
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.
|
||||
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
|
||||
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.
|
||||
|
||||
Examples
|
||||
--------
|
||||
@@ -22,19 +24,21 @@ Examples
|
||||
Import from a remote location
|
||||
.............................
|
||||
|
||||
``$ docker import http://example.com/exampleimage.tgz exampleimagerepo``
|
||||
``$ sudo docker import http://example.com/exampleimage.tgz exampleimagerepo``
|
||||
|
||||
Import from a local file
|
||||
........................
|
||||
|
||||
Import to docker via pipe and standard in
|
||||
|
||||
``$ cat exampleimage.tgz | docker import - exampleimagelocal``
|
||||
|
||||
``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal``
|
||||
|
||||
Import from a local directory
|
||||
.............................
|
||||
|
||||
``$ 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 sudo) when you tar, then the ownerships might not get preserved.
|
||||
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.
|
||||
|
||||
23
docs/sources/commandline/command/insert.rst
Normal file
23
docs/sources/commandline/command/insert.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
:title: Insert Command
|
||||
:description: Insert a file in an image
|
||||
:keywords: insert, image, docker, documentation
|
||||
|
||||
==========================================================================
|
||||
``insert`` -- Insert a file in an image
|
||||
==========================================================================
|
||||
|
||||
::
|
||||
|
||||
Usage: docker insert IMAGE URL PATH
|
||||
|
||||
Insert a file from URL in the IMAGE at PATH
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Insert file from github
|
||||
.......................
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker insert 8283e18b24bc https://raw.github.com/metalivedev/django/master/postinstall /tmp/postinstall.sh
|
||||
@@ -8,6 +8,17 @@
|
||||
|
||||
::
|
||||
|
||||
Usage: docker login
|
||||
Usage: docker login [OPTIONS] [SERVER]
|
||||
|
||||
Register or Login to the docker registry server
|
||||
|
||||
-e="": email
|
||||
-p="": password
|
||||
-u="": username
|
||||
|
||||
If you want to login to a private registry you can
|
||||
specify this by adding the server name.
|
||||
|
||||
example:
|
||||
docker login localhost:8080
|
||||
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
|
||||
Usage: docker rm [OPTIONS] CONTAINER
|
||||
|
||||
Remove a container
|
||||
Remove one or more containers
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
::
|
||||
|
||||
Usage: docker rmimage [OPTIONS] IMAGE
|
||||
Usage: docker rmi IMAGE [IMAGE...]
|
||||
|
||||
Remove an image
|
||||
Remove one or more images
|
||||
|
||||
@@ -8,21 +8,78 @@
|
||||
|
||||
::
|
||||
|
||||
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
|
||||
Usage: docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...]
|
||||
|
||||
Run a command in a new container
|
||||
|
||||
-a=map[]: Attach to stdin, stdout or stderr.
|
||||
-c=0: CPU shares (relative weight)
|
||||
-d=false: Detached mode: leave the container running in the background
|
||||
-cidfile="": Write the container ID to the file
|
||||
-d=false: Detached mode: Run container in the background, print new container id
|
||||
-e=[]: Set environment variables
|
||||
-h="": Container host name
|
||||
-i=false: Keep stdin open even if not attached
|
||||
-privileged=false: Give extended privileges to this container
|
||||
-m=0: Memory limit (in bytes)
|
||||
-n=true: Enable networking for this container
|
||||
-p=[]: Map a network port to the container
|
||||
-rm=false: Automatically remove the container when it exits (incompatible with -d)
|
||||
-t=false: Allocate a pseudo-tty
|
||||
-u="": Username or UID
|
||||
-d=[]: Set custom dns servers for the container
|
||||
-v=[]: Creates a new volume and mounts it at the specified path.
|
||||
-dns=[]: Set custom dns servers for the container
|
||||
-v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume.
|
||||
-volumes-from="": Mount all volumes from the given container.
|
||||
-b=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]
|
||||
-entrypoint="": Overwrite the default entrypoint set by the image.
|
||||
-w="": Working directory inside the container
|
||||
-lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run mount -t tmpfs none /var/spool/squid
|
||||
|
||||
This will *not* work, because by default, most potentially dangerous
|
||||
kernel capabilities are dropped; including ``cap_sys_admin`` (which is
|
||||
required to mount filesystems). However, the ``-privileged`` flag will
|
||||
allow it to run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -privileged mount -t tmpfs none /var/spool/squid
|
||||
|
||||
The ``-privileged`` flag gives *all* capabilities to the container,
|
||||
and it also lifts all the limitations enforced by the ``device``
|
||||
cgroup controller. In other words, the container can then do almost
|
||||
everything that the host can do. This flag exists to allow special
|
||||
use-cases, like running Docker within Docker.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
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
|
||||
container.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd
|
||||
|
||||
The ``-v`` flag mounts the current working directory into the container.
|
||||
The ``-w`` lets the command being executed inside the current
|
||||
working directory, by changing into the directory to the value
|
||||
returned by ``pwd``. So this combination executes the command
|
||||
using the container, but inside the current working directory.
|
||||
|
||||
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
|
||||
Usage: docker search TERM
|
||||
|
||||
Searches for the TERM parameter on the Docker index and prints out a list of repositories
|
||||
that match.
|
||||
Searches for the TERM parameter on the Docker index and prints out
|
||||
a list of repositories that match.
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
::
|
||||
|
||||
Usage: docker stop [OPTIONS] NAME
|
||||
Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
|
||||
|
||||
Stop a running container
|
||||
|
||||
-t=10: Number of seconds to wait for the container to stop before killing it.
|
||||
|
||||
13
docs/sources/commandline/command/top.rst
Normal file
13
docs/sources/commandline/command/top.rst
Normal file
@@ -0,0 +1,13 @@
|
||||
:title: Top Command
|
||||
:description: Lookup the running processes of a container
|
||||
:keywords: top, docker, container, documentation
|
||||
|
||||
=======================================================
|
||||
``top`` -- Lookup the running processes of a container
|
||||
=======================================================
|
||||
|
||||
::
|
||||
|
||||
Usage: docker top CONTAINER
|
||||
|
||||
Lookup the running processes of a container
|
||||
@@ -15,12 +15,15 @@ Contents:
|
||||
attach <command/attach>
|
||||
build <command/build>
|
||||
commit <command/commit>
|
||||
cp <command/cp>
|
||||
diff <command/diff>
|
||||
events <command/events>
|
||||
export <command/export>
|
||||
history <command/history>
|
||||
images <command/images>
|
||||
import <command/import>
|
||||
info <command/info>
|
||||
insert <command/insert>
|
||||
inspect <command/inspect>
|
||||
kill <command/kill>
|
||||
login <command/login>
|
||||
@@ -37,5 +40,6 @@ Contents:
|
||||
start <command/start>
|
||||
stop <command/stop>
|
||||
tag <command/tag>
|
||||
top <command/top>
|
||||
version <command/version>
|
||||
wait <command/wait>
|
||||
wait <command/wait>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 194 KiB |
@@ -1,16 +0,0 @@
|
||||
:title: Concepts
|
||||
:description: -- todo: change me
|
||||
:keywords: concepts, documentation, docker, containers
|
||||
|
||||
|
||||
|
||||
Concepts
|
||||
========
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
../index
|
||||
|
||||
@@ -51,9 +51,7 @@ source_suffix = '.rst'
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
#disable the parmalinks on headers, I find them really annoying
|
||||
html_add_permalinks = None
|
||||
|
||||
html_add_permalinks = u'¶'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'toctree'
|
||||
@@ -203,7 +201,7 @@ latex_elements = {
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Docker.tex', u'Docker Documentation',
|
||||
('toctree', 'Docker.tex', u'Docker Documentation',
|
||||
u'Team Docker', 'manual'),
|
||||
]
|
||||
|
||||
@@ -233,7 +231,7 @@ latex_documents = [
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'docker', u'Docker Documentation',
|
||||
('toctree', 'docker', u'Docker Documentation',
|
||||
[u'Team Docker'], 1)
|
||||
]
|
||||
|
||||
@@ -247,7 +245,7 @@ man_pages = [
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'Docker', u'Docker Documentation',
|
||||
('toctree', 'Docker', u'Docker Documentation',
|
||||
u'Team Docker', 'Docker', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:title: Contribution Guidelines
|
||||
:description: Contribution guidelines: create issues, convetions, pull requests
|
||||
:description: Contribution guidelines: create issues, conventions, pull requests
|
||||
:keywords: contributing, docker, documentation, help, guideline
|
||||
|
||||
Contributing to Docker
|
||||
|
||||
@@ -3,55 +3,130 @@
|
||||
:keywords: Docker, documentation, developers, contributing, dev environment
|
||||
|
||||
Setting Up a Dev Environment
|
||||
============================
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Instructions that have been verified to work on Ubuntu Precise 12.04 (LTS) (64-bit),
|
||||
To make it easier to contribute to Docker, we provide a standard
|
||||
development environment. It is important that the same environment be
|
||||
used for all tests, builds and releases. The standard development
|
||||
environment defines all build dependencies: system libraries and
|
||||
binaries, go environment, go dependencies, etc.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
Step 1: Install Docker
|
||||
----------------------
|
||||
|
||||
**Linux kernel 3.8**
|
||||
Docker's build environment itself is a Docker container, so the first
|
||||
step is to install Docker on your system.
|
||||
|
||||
Due to a bug in LXC docker works best on the 3.8 kernel. Precise comes with a 3.2 kernel, so we need to upgrade it. The kernel we install comes with AUFS built in.
|
||||
You can follow the `install instructions most relevant to your system
|
||||
<https://docs.docker.io/en/latest/installation/>`_. Make sure you have
|
||||
a working, up-to-date docker installation, then continue to the next
|
||||
step.
|
||||
|
||||
|
||||
Step 2: Check out the Source
|
||||
----------------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# install the backported kernel
|
||||
sudo apt-get update && sudo apt-get install linux-image-generic-lts-raring
|
||||
|
||||
# reboot
|
||||
sudo reboot
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt-get install python-software-properties
|
||||
sudo add-apt-repository ppa:gophers/go
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install lxc xz-utils curl golang-stable git aufs-tools
|
||||
|
||||
export GOPATH=~/go/
|
||||
export PATH=$GOPATH/bin:$PATH
|
||||
|
||||
mkdir -p $GOPATH/src/github.com/dotcloud
|
||||
cd $GOPATH/src/github.com/dotcloud
|
||||
git clone git://github.com/dotcloud/docker.git
|
||||
git clone http://git@github.com/dotcloud/docker
|
||||
cd docker
|
||||
|
||||
go get -v github.com/dotcloud/docker/...
|
||||
go install -v github.com/dotcloud/docker/...
|
||||
To checkout a different revision just use ``git checkout`` with the name of branch or revision number.
|
||||
|
||||
|
||||
Then run the docker daemon,
|
||||
Step 3: Build the Environment
|
||||
-----------------------------
|
||||
|
||||
This following command will build a development environment using the Dockerfile in the current directory. Essentially, it will install all the build and runtime dependencies necessary to build and test Docker. This command will take some time to complete when you first execute it.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo $GOPATH/bin/docker -d
|
||||
sudo docker build -t docker .
|
||||
|
||||
|
||||
Run the ``go install`` command (above) to recompile docker.
|
||||
|
||||
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
|
||||
-------------------------------
|
||||
|
||||
To create the Docker binary, run this command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker run -lxc-conf=lxc.aa_profile=unconfined -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh binary
|
||||
|
||||
This will create the Docker binary in ``./bundles/<version>-dev/binary/``
|
||||
|
||||
|
||||
Step 5: Run the Tests
|
||||
---------------------
|
||||
|
||||
To run the Docker test cases you first need to disable `AppArmor <https://wiki.ubuntu.com/AppArmor>`_ using the following commands
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo /etc/init.d/apparmor stop
|
||||
sudo /etc/init.d/apparmor teardown
|
||||
|
||||
To execute the test cases, run this command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker run -lxc-conf=lxc.aa_profile=unconfined -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh test
|
||||
|
||||
|
||||
If the test are successful then the tail of the output should look something like this
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
--- PASS: TestWriteBroadcaster (0.00 seconds)
|
||||
=== RUN TestRaceWriteBroadcaster
|
||||
--- PASS: TestRaceWriteBroadcaster (0.00 seconds)
|
||||
=== RUN TestTruncIndex
|
||||
--- PASS: TestTruncIndex (0.00 seconds)
|
||||
=== RUN TestCompareKernelVersion
|
||||
--- PASS: TestCompareKernelVersion (0.00 seconds)
|
||||
=== RUN TestHumanSize
|
||||
--- PASS: TestHumanSize (0.00 seconds)
|
||||
=== RUN TestParseHost
|
||||
--- PASS: TestParseHost (0.00 seconds)
|
||||
=== RUN TestParseRepositoryTag
|
||||
--- PASS: TestParseRepositoryTag (0.00 seconds)
|
||||
=== RUN TestGetResolvConf
|
||||
--- PASS: TestGetResolvConf (0.00 seconds)
|
||||
=== RUN TestCheckLocalDns
|
||||
--- PASS: TestCheckLocalDns (0.00 seconds)
|
||||
=== RUN TestParseRelease
|
||||
--- PASS: TestParseRelease (0.00 seconds)
|
||||
=== RUN TestDependencyGraphCircular
|
||||
--- PASS: TestDependencyGraphCircular (0.00 seconds)
|
||||
=== RUN TestDependencyGraph
|
||||
--- PASS: TestDependencyGraph (0.00 seconds)
|
||||
PASS
|
||||
ok github.com/dotcloud/docker/utils 0.017s
|
||||
|
||||
|
||||
|
||||
|
||||
Step 6: Use Docker
|
||||
-------------------
|
||||
|
||||
You can run an interactive session in the newly built container:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker run -privileged -i -t docker bash
|
||||
|
||||
# type 'exit' 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>`_.
|
||||
|
||||
@@ -9,27 +9,29 @@ CouchDB Service
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
Here's an example of using data volumes to share the same data between 2 couchdb containers.
|
||||
This could be used for hot upgrades, testing different versions of couchdb on the same data, etc.
|
||||
Here's an example of using data volumes to share the same data between
|
||||
2 CouchDB containers. This could be used for hot upgrades, testing
|
||||
different versions of CouchDB on the same data, etc.
|
||||
|
||||
Create first database
|
||||
---------------------
|
||||
|
||||
Note that we're marking /var/lib/couchdb as a data volume.
|
||||
Note that we're marking ``/var/lib/couchdb`` as a data volume.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
COUCH1=$(docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03)
|
||||
COUCH1=$(sudo docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03)
|
||||
|
||||
Add data to the first database
|
||||
------------------------------
|
||||
|
||||
We're assuming your docker host is reachable at `localhost`. If not, replace `localhost` with the public IP of your docker host.
|
||||
We're assuming your docker host is reachable at `localhost`. If not,
|
||||
replace `localhost` with the public IP of your docker host.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
HOST=localhost
|
||||
URL="http://$HOST:$(docker port $COUCH1 5984)/_utils/"
|
||||
URL="http://$HOST:$(sudo docker port $COUCH1 5984)/_utils/"
|
||||
echo "Navigate to $URL in your browser, and use the couch interface to add data"
|
||||
|
||||
Create second database
|
||||
@@ -39,7 +41,7 @@ This time, we're requesting shared access to $COUCH1's volumes.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
COUCH2=$(docker run -d -volumes-from $COUCH1) shykes/couchdb:2013-05-03)
|
||||
COUCH2=$(sudo docker run -d -volumes-from $COUCH1 shykes/couchdb:2013-05-03)
|
||||
|
||||
Browse data on the second database
|
||||
----------------------------------
|
||||
@@ -47,7 +49,8 @@ Browse data on the second database
|
||||
.. code-block:: bash
|
||||
|
||||
HOST=localhost
|
||||
URL="http://$HOST:$(docker port $COUCH2 5984)/_utils/"
|
||||
echo "Navigate to $URL in your browser. You should see the same data as in the first database!"
|
||||
URL="http://$HOST:$(sudo docker port $COUCH2 5984)/_utils/"
|
||||
echo "Navigate to $URL in your browser. You should see the same data as in the first database"'!'
|
||||
|
||||
Congratulations, you are running 2 Couchdb containers, completely isolated from each other *except* for their data.
|
||||
Congratulations, you are running 2 Couchdb containers, completely
|
||||
isolated from each other *except* for their data.
|
||||
|
||||
@@ -2,6 +2,33 @@
|
||||
:description: A simple hello world example with Docker
|
||||
:keywords: docker, example, hello world
|
||||
|
||||
.. _examples:
|
||||
|
||||
Hello World
|
||||
-----------
|
||||
|
||||
.. _running_examples:
|
||||
|
||||
Running the Examples
|
||||
====================
|
||||
|
||||
All the examples assume your machine is running the docker daemon. To
|
||||
run the docker daemon in the background, simply type:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker -d &
|
||||
|
||||
Now you can run docker in client mode: by default all commands will be
|
||||
forwarded to the ``docker`` daemon via a protected Unix socket, so you
|
||||
must run as root.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker help
|
||||
|
||||
----
|
||||
|
||||
.. _hello_world:
|
||||
|
||||
Hello World
|
||||
@@ -11,26 +38,28 @@ Hello World
|
||||
|
||||
This is the most basic example available for using Docker.
|
||||
|
||||
Download the base container
|
||||
Download the base image (named "ubuntu"):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Download a base image
|
||||
docker pull base
|
||||
# Download an ubuntu image
|
||||
sudo docker pull ubuntu
|
||||
|
||||
The *base* image is a minimal *ubuntu* based container, alternatively you can select *busybox*, a bare
|
||||
minimal linux system. The images are retrieved from the docker repository.
|
||||
Alternatively to the *ubuntu* image, you can select *busybox*, a bare
|
||||
minimal Linux system. The images are retrieved from the Docker
|
||||
repository.
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
#run a simple echo command, that will echo hello world back to the console over standard out.
|
||||
docker run base /bin/echo hello world
|
||||
sudo docker run ubuntu /bin/echo hello world
|
||||
|
||||
**Explanation:**
|
||||
|
||||
- **"sudo"** execute the following commands as user *root*
|
||||
- **"docker run"** run a command in a new container
|
||||
- **"base"** is the image we want to run the command inside of.
|
||||
- **"ubuntu"** is the image we want to run the command inside of.
|
||||
- **"/bin/echo"** is the command we want to run in the container
|
||||
- **"hello world"** is the input for the echo command
|
||||
|
||||
@@ -47,4 +76,106 @@ See the example in action
|
||||
</div>
|
||||
|
||||
|
||||
Continue to the :ref:`hello_world_daemon` example.
|
||||
----
|
||||
|
||||
.. _hello_world_daemon:
|
||||
|
||||
Hello World Daemon
|
||||
==================
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
And now for the most boring daemon ever written!
|
||||
|
||||
This example assumes you have Docker installed and the Ubuntu
|
||||
image already imported with ``docker pull ubuntu``. We will use the Ubuntu
|
||||
image to run a simple hello world daemon that will just print hello
|
||||
world to standard out every second. It will continue to do this until
|
||||
we stop it.
|
||||
|
||||
**Steps:**
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
CONTAINER_ID=$(sudo docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done")
|
||||
|
||||
We are going to run a simple hello world daemon in a new container
|
||||
made from the *ubuntu* image.
|
||||
|
||||
- **"docker run -d "** run a command in a new container. We pass "-d"
|
||||
so it runs as a daemon.
|
||||
- **"ubuntu"** is the image we want to run the command inside of.
|
||||
- **"/bin/sh -c"** is the command we want to run in the container
|
||||
- **"while true; do echo hello world; sleep 1; done"** is the mini
|
||||
script we want to run, that will just print hello world once a
|
||||
second until we stop it.
|
||||
- **$CONTAINER_ID** the output of the run command will return a
|
||||
container id, we can use in future commands to see what is going on
|
||||
with this process.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker logs $CONTAINER_ID
|
||||
|
||||
Check the logs make sure it is working correctly.
|
||||
|
||||
- **"docker logs**" This will return the logs for a container
|
||||
- **$CONTAINER_ID** The Id of the container we want the logs for.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker attach $CONTAINER_ID
|
||||
|
||||
Attach to the container to see the results in realtime.
|
||||
|
||||
- **"docker attach**" This will allow us to attach to a background
|
||||
process to see what is going on.
|
||||
- **$CONTAINER_ID** The Id of the container we want to attach too.
|
||||
|
||||
Exit from the container attachment by pressing Control-C.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker ps
|
||||
|
||||
Check the process list to make sure it is running.
|
||||
|
||||
- **"docker ps"** this shows all running process managed by docker
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker stop $CONTAINER_ID
|
||||
|
||||
Stop the container, since we don't need it anymore.
|
||||
|
||||
- **"docker stop"** This stops a container
|
||||
- **$CONTAINER_ID** The Id of the container we want to stop.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker ps
|
||||
|
||||
Make sure it is really stopped.
|
||||
|
||||
|
||||
**Video:**
|
||||
|
||||
See the example in action
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div style="margin-top:10px;">
|
||||
<iframe width="560" height="350" src="http://ascii.io/a/2562/raw" frameborder="0"></iframe>
|
||||
</div>
|
||||
|
||||
The next example in the series is a :ref:`python_web_app` example, or
|
||||
you could skip to any of the other examples:
|
||||
|
||||
|
||||
* :ref:`python_web_app`
|
||||
* :ref:`nodejs_web_app`
|
||||
* :ref:`running_redis_service`
|
||||
* :ref:`running_ssh_service`
|
||||
* :ref:`running_couchdb_service`
|
||||
* :ref:`postgresql_service`
|
||||
* :ref:`mongodb`
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
:title: Hello world daemon example
|
||||
:description: A simple hello world daemon example with Docker
|
||||
:keywords: docker, example, hello world, daemon
|
||||
|
||||
.. _hello_world_daemon:
|
||||
|
||||
Hello World Daemon
|
||||
==================
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
The most boring daemon ever written.
|
||||
|
||||
This example assumes you have Docker installed and with the base image already imported ``docker pull base``.
|
||||
We will use the base image to run a simple hello world daemon that will just print hello world to standard
|
||||
out every second. It will continue to do this until we stop it.
|
||||
|
||||
**Steps:**
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
CONTAINER_ID=$(docker run -d base /bin/sh -c "while true; do echo hello world; sleep 1; done")
|
||||
|
||||
We are going to run a simple hello world daemon in a new container made from the base image.
|
||||
|
||||
- **"docker run -d "** run a command in a new container. We pass "-d" so it runs as a daemon.
|
||||
- **"base"** is the image we want to run the command inside of.
|
||||
- **"/bin/sh -c"** is the command we want to run in the container
|
||||
- **"while true; do echo hello world; sleep 1; done"** is the mini script we want to run, that will just print hello world once a second until we stop it.
|
||||
- **$CONTAINER_ID** the output of the run command will return a container id, we can use in future commands to see what is going on with this process.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker logs $CONTAINER_ID
|
||||
|
||||
Check the logs make sure it is working correctly.
|
||||
|
||||
- **"docker logs**" This will return the logs for a container
|
||||
- **$CONTAINER_ID** The Id of the container we want the logs for.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker attach $CONTAINER_ID
|
||||
|
||||
Attach to the container to see the results in realtime.
|
||||
|
||||
- **"docker attach**" This will allow us to attach to a background process to see what is going on.
|
||||
- **$CONTAINER_ID** The Id of the container we want to attach too.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker ps
|
||||
|
||||
Check the process list to make sure it is running.
|
||||
|
||||
- **"docker ps"** this shows all running process managed by docker
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker stop $CONTAINER_ID
|
||||
|
||||
Stop the container, since we don't need it anymore.
|
||||
|
||||
- **"docker stop"** This stops a container
|
||||
- **$CONTAINER_ID** The Id of the container we want to stop.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker ps
|
||||
|
||||
Make sure it is really stopped.
|
||||
|
||||
|
||||
**Video:**
|
||||
|
||||
See the example in action
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div style="margin-top:10px;">
|
||||
<iframe width="560" height="350" src="http://ascii.io/a/2562/raw" frameborder="0"></iframe>
|
||||
</div>
|
||||
|
||||
Continue to the :ref:`python_web_app` example.
|
||||
@@ -1,22 +1,26 @@
|
||||
:title: Docker Examples
|
||||
:description: Examples on how to use Docker
|
||||
:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples
|
||||
:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql
|
||||
|
||||
|
||||
.. _example_list:
|
||||
|
||||
Examples
|
||||
============
|
||||
========
|
||||
|
||||
Contents:
|
||||
Here are some examples of how to use Docker to create running
|
||||
processes, starting from a very simple *Hello World* and progressing
|
||||
to more substantial services like you might find in production.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
running_examples
|
||||
hello_world
|
||||
hello_world_daemon
|
||||
python_web_app
|
||||
nodejs_web_app
|
||||
running_redis_service
|
||||
running_ssh_service
|
||||
couchdb_data_volumes
|
||||
postgresql_service
|
||||
mongodb
|
||||
running_riak_service
|
||||
|
||||
100
docs/sources/examples/mongodb.rst
Normal file
100
docs/sources/examples/mongodb.rst
Normal file
@@ -0,0 +1,100 @@
|
||||
:title: Building a Docker Image with MongoDB
|
||||
:description: How to build a Docker image with MongoDB pre-installed
|
||||
:keywords: docker, example, package installation, networking, mongodb
|
||||
|
||||
.. _mongodb_image:
|
||||
|
||||
Building an Image with MongoDB
|
||||
==============================
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
The goal of this example is to show how you can build your own
|
||||
docker images with MongoDB preinstalled. We will do that by
|
||||
constructing a Dockerfile that downloads a base image, adds an
|
||||
apt source and installs the database software on Ubuntu.
|
||||
|
||||
Creating a ``Dockerfile``
|
||||
+++++++++++++++++++++++++
|
||||
|
||||
Create an empty file called ``Dockerfile``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
touch Dockerfile
|
||||
|
||||
Next, define the parent image you want to use to build your own image on top of.
|
||||
Here, we’ll use `Ubuntu <https://index.docker.io/_/ubuntu/>`_ (tag: ``latest``)
|
||||
available on the `docker index <http://index.docker.io>`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
FROM ubuntu:latest
|
||||
|
||||
Since we want to be running the latest version of MongoDB we'll need to add the
|
||||
10gen repo to our apt sources list.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Add 10gen official apt source to the sources list
|
||||
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
|
||||
RUN echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | tee /etc/apt/sources.list.d/10gen.list
|
||||
|
||||
Then, we don't want Ubuntu to complain about init not being available so we'll
|
||||
divert /sbin/initctl to /bin/true so it thinks everything is working.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Hack for initctl not being available in Ubuntu
|
||||
RUN dpkg-divert --local --rename --add /sbin/initctl
|
||||
RUN ln -s /bin/true /sbin/initctl
|
||||
|
||||
Afterwards we'll be able to update our apt repositories and install MongoDB
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Install MongoDB
|
||||
RUN apt-get update
|
||||
RUN apt-get install mongodb-10gen
|
||||
|
||||
To run MongoDB we'll have to create the default data directory (because we want it to
|
||||
run without needing to provide a special configuration file)
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Create the MongoDB data directory
|
||||
RUN mkdir -p /data/db
|
||||
|
||||
Finally, we'll expose the standard port that MongoDB runs on (27107) as well as
|
||||
define an ENTRYPOINT for the container.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
EXPOSE 27017
|
||||
ENTRYPOINT ["usr/bin/mongod"]
|
||||
|
||||
Now, lets build the image which will go through the ``Dockerfile`` we made and
|
||||
run all of the commands.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker build -t <yourname>/mongodb .
|
||||
|
||||
Now you should be able to run ``mongod`` as a daemon and be able to connect on
|
||||
the local port!
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Regular style
|
||||
MONGO_ID=$(docker run -d <yourname>/mongodb)
|
||||
|
||||
# Lean and mean
|
||||
MONGO_ID=$(docker run -d <yourname>/mongodb --noprealloc --smallfiles)
|
||||
|
||||
# Check the logs out
|
||||
docker logs $MONGO_ID
|
||||
|
||||
# Connect and play around
|
||||
mongo --port <port you get from `docker ps`>
|
||||
|
||||
Sweet!
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user