Compare commits
681 commits
Author | SHA1 | Date | |
---|---|---|---|
|
473c5701cb | ||
|
71e6babfa2 | ||
|
979cb4fd1e | ||
|
5f730ed857 | ||
|
68e914ce62 | ||
|
ce07fb6b0f | ||
|
1c44f35eb1 | ||
|
d1c1b391bc | ||
|
8b1112be3b | ||
|
855eb121aa | ||
|
eb04072245 | ||
|
c9e1a9e16d | ||
|
c9ae02ec59 | ||
|
19460e43f4 | ||
|
2a46799a87 | ||
|
aedc0ca2fd | ||
|
05516452d6 | ||
|
b53cef8425 | ||
|
b975e02386 | ||
|
aaff3d513e | ||
|
d4897c746f | ||
|
9287b8f99e | ||
|
7a02f05068 | ||
|
d4e2280b0f | ||
|
ecfddcf227 | ||
|
b0e4cf1ad2 | ||
|
dffbf0bc73 | ||
|
671a4aa4a4 | ||
|
46af6a6a41 | ||
|
87a53468b2 | ||
|
fe375a64db | ||
|
2e440580bc | ||
|
4122443aa7 | ||
|
e301d956b7 | ||
|
1c47c89d76 | ||
|
7e11df6273 | ||
|
b326920b3e | ||
|
56e99a8791 | ||
|
9e74ea8594 | ||
|
740fbef836 | ||
|
9dcb36dfa2 | ||
|
9f46122c94 | ||
|
951ffc056b | ||
|
ed6e849a1c | ||
|
e7e23df3f6 | ||
|
890f4c7aa5 | ||
|
30a5e20cc0 | ||
|
b3b30b0dcd | ||
|
2b0d7d2d86 | ||
|
2e544b1513 | ||
|
e5a90d46e3 | ||
|
018981111c | ||
|
092cba3727 | ||
|
f26487b568 | ||
|
fc0b5dbedb | ||
|
19d67e6e4a | ||
|
e29c142293 | ||
|
54f71fd84a | ||
|
516792001a | ||
|
14deb7d32c | ||
|
26ec0e71d1 | ||
|
5f696e6d48 | ||
|
0406faf887 | ||
|
f6bf85f99c | ||
|
0220459168 | ||
|
b47fa5a5d1 | ||
|
1a17bc6bc4 | ||
|
96b0320d5c | ||
|
905cf26b43 | ||
|
202df64a3b | ||
|
f709fd4e5b | ||
|
17e6f81948 | ||
|
778f9a86c4 | ||
|
fbc84e24aa | ||
|
553d56d456 | ||
|
6e2e808146 | ||
|
2556b7a70a | ||
|
14153795ba | ||
|
ec0e3fe4e2 | ||
|
d00e249c26 | ||
|
68f9fb9fd7 | ||
|
7b15b4a9b2 | ||
|
5e859cf3c1 | ||
|
96b87362e6 | ||
|
baf9288b78 | ||
|
0311ed7b1c | ||
|
c74f17e91e | ||
|
64856b7a24 | ||
|
c7f27f8c07 | ||
|
171745b261 | ||
|
73b9bdc6cb | ||
|
10d999d7ab | ||
|
ee1b5c5944 | ||
|
c4d731c24c | ||
|
1e0179589d | ||
|
c1c5d30ee4 | ||
|
2527cfcc49 | ||
|
de6c83de28 | ||
|
e84317c735 | ||
|
e25c046488 | ||
|
749f8bfee8 | ||
|
bdc5b347f5 | ||
|
370e8f25d2 | ||
|
e0c79df37d | ||
|
e3441eb64c | ||
|
8d0198c59d | ||
|
ed7aea3533 | ||
|
1b05e7aee7 | ||
|
c5147e6ab5 | ||
|
692c98895f | ||
|
4dc1b6221d | ||
|
1afa22733c | ||
|
c19f50acef | ||
|
6b70c68a7c | ||
|
909014d324 | ||
|
1fed3349fb | ||
|
2f445705f9 | ||
|
118e55df62 | ||
|
e6f89d1af5 | ||
|
819bcf8182 | ||
|
3fc246ded2 | ||
|
426c4cb747 | ||
|
017381aea4 | ||
|
214f42d541 | ||
|
a64c9357ca | ||
|
e7077f7d26 | ||
|
328775ea4f | ||
|
bf43d2a58d | ||
|
6a9670b044 | ||
|
53576a5bf9 | ||
|
6b4a835431 | ||
|
92155e37b3 | ||
|
7f396e8ea3 | ||
|
d9bda3080a | ||
|
007e492eac | ||
|
6bc7bc19df | ||
|
cbe6781501 | ||
|
5bf7901f5d | ||
|
06dae4eae5 | ||
|
a757b9bea9 | ||
|
f147d9b0d1 | ||
|
5537dbde84 | ||
|
732e19892d | ||
|
c9ffd3586c | ||
|
0b27583f82 | ||
|
3fdce9676b | ||
|
f0f020a579 | ||
|
3048e50434 | ||
|
b194252d80 | ||
|
e9751047e9 | ||
|
b875349fef | ||
|
b0970b629b | ||
|
e9779fd7df | ||
|
2dd2ac2c14 | ||
|
b60f411292 | ||
|
482534ff5f | ||
|
25810f3f47 | ||
|
37be2d4e9b | ||
|
880c12df7d | ||
|
ecd95a41d2 | ||
|
b9ee1c6880 | ||
|
0c16980468 | ||
|
1ec7869443 | ||
|
0fa35e2073 | ||
|
0be8ebd2a1 | ||
|
b4fd48ce32 | ||
|
9da299954f | ||
|
d4e6422212 | ||
|
954520f0ef | ||
|
23e9d30cea | ||
|
7bb3740386 | ||
|
ba79dde9d2 | ||
|
d621d74f64 | ||
|
78e4fa2caa | ||
|
f6d70d08a8 | ||
|
660a186e92 | ||
|
bd6c3ebed9 | ||
|
49bf474f9e | ||
|
d4f36676f0 | ||
|
197977d7fd | ||
|
8324f11b5a | ||
|
48a9e53d70 | ||
|
bca3b05950 | ||
|
4f914489e3 | ||
|
e62d984138 | ||
|
f6bc09c380 | ||
|
2f2d055013 | ||
|
fc82c4b391 | ||
|
e0c07ab799 | ||
|
acbad31dcd | ||
|
7e792d6ca9 | ||
|
c8083e8735 | ||
|
18fd3885c6 | ||
|
83b2782ea9 | ||
|
ece7e8ba27 | ||
|
4b2b0eb608 | ||
|
3fc1510dff | ||
|
340f288211 | ||
|
0b2a029e56 | ||
|
b7f412ef44 | ||
|
57c17882a3 | ||
|
25879cfa54 | ||
|
6c2fdb64d1 | ||
|
3fc150171f | ||
|
53ac375422 | ||
|
831eeb38f1 | ||
|
f27a297595 | ||
|
46af2edec2 | ||
|
f2260228a7 | ||
|
b2219f1bb5 | ||
|
7c8511236c | ||
|
b2e12626c7 | ||
|
57a63f1ed2 | ||
|
d3bffd978a | ||
|
40736b1848 | ||
|
462cd90f74 | ||
|
19cf2b3d5f | ||
|
1dded0dee4 | ||
|
9f0e745152 | ||
|
464b205956 | ||
|
3b2982a2bb | ||
|
d2e99882b9 | ||
|
25b967e149 | ||
|
eb5273edd4 | ||
|
46afc8f148 | ||
|
1afd41e4f7 | ||
|
43cc971d76 | ||
|
7f20c11c47 | ||
|
69240476d4 | ||
|
a46dbbded9 | ||
|
4e9dd0e51c | ||
|
84f25c7cb8 | ||
|
6841887132 | ||
|
aa5ac38f41 | ||
|
d01138c162 | ||
|
def75b2c2c | ||
|
ff7934faf2 | ||
|
045e9834a5 | ||
|
a2dc349c74 | ||
|
7cbc4cb7f2 | ||
|
e2b04a7818 | ||
|
ea00b14170 | ||
|
5903ead08e | ||
|
428ae70759 | ||
|
eefb2220c0 | ||
|
8b155a9109 | ||
|
c809c40647 | ||
|
994b3928f6 | ||
|
39b3e39c40 | ||
|
e7960584f2 | ||
|
c25a4762ef | ||
|
2c53989b4b | ||
|
1c5cb46649 | ||
|
97b0bb730e | ||
|
6d6c11dceb | ||
|
cf93b2b1c2 | ||
|
7a89c43a5e | ||
|
8afa73d280 | ||
|
d2a4a53f11 | ||
|
7d58f5fb88 | ||
|
73be1c098a | ||
|
c18f592150 | ||
|
f335200410 | ||
|
c5b5b4ea45 | ||
|
627bc91727 | ||
|
15293063ba | ||
|
b219698e75 | ||
|
1ca25a2e5e | ||
|
36c0b59149 | ||
|
3e0df05ec4 | ||
|
f02d46f050 | ||
|
25bbf8ab7c | ||
|
38d914cc96 | ||
|
bb37c67a90 | ||
|
a55a9b14d1 | ||
|
2039ea6adc | ||
|
05426c80d9 | ||
|
fed1b91bb4 | ||
|
88862e707a | ||
|
f3749c5a9c | ||
|
7d4318c83c | ||
|
05fba4af5f | ||
|
501083e82a | ||
|
4e5129c511 | ||
|
e460a5f45c | ||
|
c9a557765e | ||
|
1a865dd303 | ||
|
0c19045523 | ||
|
ecf7a631a6 | ||
|
f573d8b621 | ||
|
51020f7074 | ||
|
5391ce8ffa | ||
|
29f42b4036 | ||
|
caaee2ebab | ||
|
315c34953d | ||
|
b5d4f87612 | ||
|
dc05dfd30e | ||
|
f68d703a8c | ||
|
ce5ea47cb0 | ||
|
a260417927 | ||
|
a80251e8cd | ||
|
8f724a277a | ||
|
61dc897a30 | ||
|
5e7d2ab3b8 | ||
|
261f9b6243 | ||
|
45b441f4f9 | ||
|
61ce93a6f5 | ||
|
162ef5d158 | ||
|
37ea8104b0 | ||
|
1b521547f0 | ||
|
4d2be03b68 | ||
|
fa9618550a | ||
|
29861173e1 | ||
|
b7316e85c0 | ||
|
efdf7ae8f3 | ||
|
a4af72fc87 | ||
|
20dc482b10 | ||
|
8b31f217ff | ||
|
39c9f1c024 | ||
|
4e08d8ef03 | ||
|
47b838db40 | ||
|
1dd2e2f545 | ||
|
e65660da3d | ||
|
004ec96608 | ||
|
54e52b97f4 | ||
|
4aa72cb5a9 | ||
|
f59aa96e3d | ||
|
89c27499da | ||
|
4c65bf9a10 | ||
|
1149997787 | ||
|
1a86cbb031 | ||
|
80c3ed1c0c | ||
|
20d6f23b55 | ||
|
20a7e39728 | ||
|
09296e053d | ||
|
1f4dc1158c | ||
|
99eb943186 | ||
|
d8e435ab6c | ||
|
09cd31b128 | ||
|
85ae13f6d0 | ||
|
fcb58702bc | ||
|
cc855179cf | ||
|
9a6c5e14be | ||
|
0d575d1304 | ||
|
508d06752d | ||
|
159f4f14d7 | ||
|
de5fba3833 | ||
|
4b7bef26c5 | ||
|
cd7fea22f4 | ||
|
30b458a28c | ||
|
870beb70fb | ||
|
64aac182d6 | ||
|
4fba52a2f7 | ||
|
b18516e6ac | ||
|
6e08e2edcf | ||
|
c027e4a861 | ||
|
1c858abc96 | ||
|
3a571b72fd | ||
|
aa22b96457 | ||
|
d842cf6db6 | ||
|
213ee20706 | ||
|
f081b22a4a | ||
|
c84f43d63f | ||
|
be7d576f5d | ||
|
cca0132fcf | ||
|
9ecbaa77ae | ||
|
edd9c522b7 | ||
|
aca0bdab13 | ||
|
674cf8233d | ||
|
90a24f7bfd | ||
|
6c8bd56007 | ||
|
78f2dc90bf | ||
|
52b88ccc23 | ||
|
c9ec321e56 | ||
|
bec385cff0 | ||
|
0383913b65 | ||
|
51b83ae8fb | ||
|
ad794bba5b | ||
|
540758ee99 | ||
|
0f2364f73c | ||
|
6ce7fb23f5 | ||
|
fd7aee8fe0 | ||
|
e0d8cfaa7d | ||
|
62fdac59bd | ||
|
487fad2a2c | ||
|
e26de82e54 | ||
|
8bed67c368 | ||
|
728296b9ea | ||
|
696130c949 | ||
|
d1d6357beb | ||
|
4d92237de1 | ||
|
c0522e349a | ||
|
3ac2991ff8 | ||
|
69f3cb179e | ||
|
1b195847f4 | ||
|
877c2b5ac0 | ||
|
1b310cd47c | ||
|
3891c1f376 | ||
|
c015a2a1a6 | ||
|
2aed732d3a | ||
|
e509501437 | ||
|
e81d02e4cd | ||
|
8f961397f4 | ||
|
7ec079dbfd | ||
|
c49078c78d | ||
|
94f3ccc31e | ||
|
f1ea64fa07 | ||
|
64b41aa52a | ||
|
6df19c209f | ||
|
ecf889816c | ||
|
018f19f8c9 | ||
|
3477da0cdb | ||
|
90c3c389c4 | ||
|
6ad8328cb8 | ||
|
df71cee489 | ||
|
12aa5ea8b1 | ||
|
32e0ea346b | ||
|
68b88c8749 | ||
|
c31df2f3de | ||
|
f687616995 | ||
|
766e6ab07f | ||
|
fba707ee1f | ||
|
76f4793290 | ||
|
0e1721f09f | ||
|
2d0a198eb8 | ||
|
783e1a94b5 | ||
|
bb55c05ec1 | ||
|
6c466eadb7 | ||
|
f5c429bea2 | ||
|
1638b8f5c5 | ||
|
7c081219ac | ||
|
d55b0802cf | ||
|
d9329742d0 | ||
|
375e970991 | ||
|
30e5e0a781 | ||
|
50bcdc9de2 | ||
|
f8b981574c | ||
|
4609c728e3 | ||
|
5764fbb878 | ||
|
ee7d498364 | ||
|
79f153f49e | ||
|
bf711ea00e | ||
|
f5df634db7 | ||
|
5e5d02b9f5 | ||
|
51f1980e30 | ||
|
68cfaf216d | ||
|
0dd3ae8ed0 | ||
|
1f289dc411 | ||
|
005a5428ee | ||
|
b4567db876 | ||
|
1b187660c2 | ||
|
0669e372d2 | ||
|
a40ae77529 | ||
|
7c030bbd76 | ||
|
98132f7db9 | ||
|
a8c2248e6f | ||
|
97b0fd9fe3 | ||
|
936499e1ff | ||
|
8c56588d04 | ||
|
e3d03adc25 | ||
|
90dab0778c | ||
|
b998f95014 | ||
|
7c74809fe9 | ||
|
0f5d85371a | ||
|
bee66467cd | ||
|
d0fb24d853 | ||
|
4a0b7b215e | ||
|
bdde4d3be5 | ||
|
568a0a83a9 | ||
|
a903471bfc | ||
|
76d2619406 | ||
|
e0654b40b7 | ||
|
555f65587e | ||
|
25cb6a829b | ||
|
877207946a | ||
|
0e386515c8 | ||
|
7d7af6ad14 | ||
|
6622465645 | ||
|
bfbb2fdee9 | ||
|
663ca540ea | ||
|
4b230af83f | ||
|
99e1814731 | ||
|
3d27cb3d6f | ||
|
05a75a567a | ||
|
a2c9e625e6 | ||
|
edbe73670a | ||
|
234a8b9496 | ||
|
86ae5d2895 | ||
|
46cfeaf8ef | ||
|
d981bbc288 | ||
|
bc4219bd0c | ||
|
f70e7f840b | ||
|
35a87689e4 | ||
|
07fa269eee | ||
|
46b8a5fb57 | ||
|
fcc38c048c | ||
|
c75eeb4b7c | ||
|
10d2d8aefd | ||
|
d4d4edec2e | ||
|
833f3b3cf0 | ||
|
bbfd3a518b | ||
|
3decd08d7a | ||
|
1f9b3ef9e9 | ||
|
dba611d0f1 | ||
|
2a80a863ca | ||
|
01807b785f | ||
|
b82be871ab | ||
|
00fd466e00 | ||
|
f9f8a94f42 | ||
|
f531a8f06b | ||
|
44fe761b35 | ||
|
38b0c93954 | ||
|
aa6211e153 | ||
|
fad605e8c5 | ||
|
8738566853 | ||
|
f67b380325 | ||
|
520e601dc2 | ||
|
2ac34851ec | ||
|
a4aabe7286 | ||
|
e8b27e7dc7 | ||
|
54518a214d | ||
|
1053fdf27e | ||
|
43e6479fd4 | ||
|
af08f44640 | ||
|
faab09c1aa | ||
|
561f2b27cb | ||
|
63277b0aff | ||
|
8f66c5e731 | ||
|
a275ca2093 | ||
|
c28c63472a | ||
|
fb3c3d7eea | ||
|
40a56f29e2 | ||
|
9e35dea991 | ||
|
a65a2936ac | ||
|
24628fd7a0 | ||
|
9302b0f59c | ||
|
3168635120 | ||
|
f46b2fd604 | ||
|
702b03b6fc | ||
|
3f0aa9bceb | ||
|
3ab3f953d7 | ||
|
bab6bf1d5e | ||
|
8ebbeda5a8 | ||
|
ca51b2bf2c | ||
|
0403addc5f | ||
|
abc0eea899 | ||
|
b4c0b07d89 | ||
|
2d5f5287c3 | ||
|
41b92e6f36 | ||
|
8079cc8147 | ||
|
309662d03f | ||
|
e285a304db | ||
|
6f03b32968 | ||
|
2fb6810841 | ||
|
7e2522ec9f | ||
|
95398c33cc | ||
|
fcb64b6e08 | ||
|
2ecc8cd4bd | ||
|
bdc378e781 | ||
|
01aab8baa3 | ||
|
0a5732d1cf | ||
|
2737519f9a | ||
|
5c684ba099 | ||
|
ba9726350d | ||
|
05eb544ae2 | ||
|
040d4ebd58 | ||
|
db8991c957 | ||
|
20f3c552e0 | ||
|
a20820b05c | ||
|
68e99239d3 | ||
|
a47957d6f6 | ||
|
5b81d7e8b5 | ||
|
4acf6aa456 | ||
|
c707468415 | ||
|
a6e1ed7a51 | ||
|
2c832d7d66 | ||
|
f44e2dca56 | ||
|
c8e2552827 | ||
|
a0df810219 | ||
|
c050ee1917 | ||
|
cbff9474d2 | ||
|
f7ae8204cb | ||
|
2d632c34ce | ||
|
4edaaeb671 | ||
|
d63582c131 | ||
|
e773e0e654 | ||
|
fe8e347741 | ||
|
0d94260458 | ||
|
cdfde9ecc1 | ||
|
8e21480106 | ||
|
fd75cc462f | ||
|
c4fd597ab3 | ||
|
47ccfcc09f | ||
|
e3c24caf7a | ||
|
d4392659f7 | ||
|
649445a206 | ||
|
df8a5e48de | ||
|
c0ea2c8498 | ||
|
ce22bc39dc | ||
|
3e9dd55695 | ||
|
19d2e68fb8 | ||
|
530b7cb4a0 | ||
|
902774c871 | ||
|
1af49192e4 | ||
|
0be7359265 | ||
|
e013d7c543 | ||
|
5e7e260bac | ||
|
20dd2d1e4e | ||
|
3d43caf6f6 | ||
|
3f6e6b3f2d | ||
|
48c2da3cd5 | ||
|
7ef4feaf38 | ||
|
8b9b8bd930 | ||
|
cd587419d0 | ||
|
10ce88227d | ||
|
9f3c9209d0 | ||
|
f02e4e1900 | ||
|
411e0bdc8e | ||
|
51dc584445 | ||
|
78fe91c31c | ||
|
0b8f8876b9 | ||
|
c125c131d7 | ||
|
0a9521d321 | ||
|
88a45c4a1b | ||
|
22f0055e18 | ||
|
73bf1ec999 | ||
|
0269ac0494 | ||
|
f2eeaed8a3 | ||
|
43f1682cef | ||
|
94c4cfc209 | ||
|
dc71b1425e | ||
|
a51fe6b056 | ||
|
1841986d17 | ||
|
0d815d9bb7 | ||
|
3b93497fdb | ||
|
44bb0bb7f2 | ||
|
896e4f2d75 | ||
|
7154093e59 | ||
|
dbf1900e8c | ||
|
d1b1dea80f | ||
|
2dfa08bc50 | ||
|
f5afe63eb6 | ||
|
7a457451f6 | ||
|
5c69e4fa94 | ||
|
c102ab8711 | ||
|
44b5bcd744 | ||
|
0f254ab8b9 | ||
|
4d850bd49a | ||
|
7346a6aebd | ||
|
846b198dd7 | ||
|
6c3744f4fc | ||
|
6e10c4c8b2 | ||
|
a9bbf92b3f | ||
|
02c7b6ffad | ||
|
d3ae1d7bd6 | ||
|
b1c96a73a3 | ||
|
d114b7dc48 | ||
|
fd04a2db51 | ||
|
2c61e9c3bd | ||
|
9ed0bb2d0a | ||
|
ba5d2331d5 | ||
|
6ec7c7a89c | ||
|
a2575eb741 | ||
|
4b003634bc | ||
|
580343247e | ||
|
261958bfa8 | ||
|
2da971cf06 | ||
|
285915755c | ||
|
9798cdd632 | ||
|
5060ff0d8d | ||
|
4bfd3643da | ||
|
f3d628129e | ||
|
19c16fd95e | ||
|
89c4410799 | ||
|
70e7fcc595 | ||
|
03c8f0cda1 | ||
|
22c09cf902 | ||
|
8ac6b97189 | ||
|
a7a8fa0e6b | ||
|
16d4e6395e | ||
|
75fd88ba89 |
11376 changed files with 799314 additions and 2037128 deletions
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"name": "moby",
|
|
||||||
"build": {
|
|
||||||
"context": "..",
|
|
||||||
"dockerfile": "../Dockerfile",
|
|
||||||
"target": "devcontainer"
|
|
||||||
},
|
|
||||||
"workspaceFolder": "/go/src/github.com/docker/docker",
|
|
||||||
"workspaceMount": "source=${localWorkspaceFolder},target=/go/src/github.com/docker/docker,type=bind,consistency=cached",
|
|
||||||
|
|
||||||
"remoteUser": "root",
|
|
||||||
"runArgs": ["--privileged"],
|
|
||||||
|
|
||||||
"customizations": {
|
|
||||||
"vscode": {
|
|
||||||
"extensions": [
|
|
||||||
"golang.go"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
.git
|
bundles
|
||||||
bundles/
|
.gopath
|
||||||
cli/winresources/**/winres.json
|
vendor/pkg
|
||||||
cli/winresources/**/*.syso
|
.go-pkg-cache
|
||||||
|
|
3
.gitattributes
vendored
3
.gitattributes
vendored
|
@ -1,3 +0,0 @@
|
||||||
Dockerfile* linguist-language=Dockerfile
|
|
||||||
vendor.mod linguist-language=Go-Module
|
|
||||||
vendor.sum linguist-language=Go-Checksums
|
|
13
.github/CODEOWNERS
vendored
13
.github/CODEOWNERS
vendored
|
@ -1,13 +0,0 @@
|
||||||
# GitHub code owners
|
|
||||||
# See https://help.github.com/articles/about-codeowners/
|
|
||||||
#
|
|
||||||
# KEEP THIS FILE SORTED. Order is important. Last match takes precedence.
|
|
||||||
|
|
||||||
builder/** @tonistiigi
|
|
||||||
contrib/mkimage/** @tianon
|
|
||||||
daemon/graphdriver/overlay2/** @dmcgowan
|
|
||||||
daemon/graphdriver/windows/** @johnstep
|
|
||||||
daemon/logger/awslogs/** @samuelkarp
|
|
||||||
hack/** @tianon
|
|
||||||
plugin/** @cpuguy83
|
|
||||||
project/** @thaJeztah
|
|
64
.github/ISSUE_TEMPLATE.md
vendored
Normal file
64
.github/ISSUE_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<!--
|
||||||
|
If you are reporting a new issue, make sure that we do not have any duplicates
|
||||||
|
already open. You can ensure this by searching the issue list for this
|
||||||
|
repository. If there is a duplicate, please close your issue and add a comment
|
||||||
|
to the existing issue instead.
|
||||||
|
|
||||||
|
If you suspect your issue is a bug, please edit your issue description to
|
||||||
|
include the BUG REPORT INFORMATION shown below. If you fail to provide this
|
||||||
|
information within 7 days, we cannot debug your issue and will close it. We
|
||||||
|
will, however, reopen it if you later provide the information.
|
||||||
|
|
||||||
|
For more information about reporting issues, see
|
||||||
|
https://github.com/docker/docker/blob/master/CONTRIBUTING.md#reporting-other-issues
|
||||||
|
|
||||||
|
---------------------------------------------------
|
||||||
|
GENERAL SUPPORT INFORMATION
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
The GitHub issue tracker is for bug reports and feature requests.
|
||||||
|
General support can be found at the following locations:
|
||||||
|
|
||||||
|
- Docker Support Forums - https://forums.docker.com
|
||||||
|
- IRC - irc.freenode.net #docker channel
|
||||||
|
- Post a question on StackOverflow, using the Docker tag
|
||||||
|
|
||||||
|
---------------------------------------------------
|
||||||
|
BUG REPORT INFORMATION
|
||||||
|
---------------------------------------------------
|
||||||
|
Use the commands below to provide key information from your environment:
|
||||||
|
You do NOT have to include this information if this is a FEATURE REQUEST
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Description**
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Briefly describe the problem you are having in a few paragraphs.
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Steps to reproduce the issue:**
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
**Describe the results you received:**
|
||||||
|
|
||||||
|
|
||||||
|
**Describe the results you expected:**
|
||||||
|
|
||||||
|
|
||||||
|
**Additional information you deem important (e.g. issue happens only occasionally):**
|
||||||
|
|
||||||
|
**Output of `docker version`:**
|
||||||
|
|
||||||
|
```
|
||||||
|
(paste your output here)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output of `docker info`:**
|
||||||
|
|
||||||
|
```
|
||||||
|
(paste your output here)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Additional environment details (AWS, VirtualBox, physical, etc.):**
|
146
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
146
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -1,146 +0,0 @@
|
||||||
name: Bug report
|
|
||||||
description: Create a report to help us improve
|
|
||||||
labels:
|
|
||||||
- kind/bug
|
|
||||||
- status/0-triage
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thank you for taking the time to report a bug!
|
|
||||||
If this is a security issue please report it to the [Docker Security team](mailto:security@docker.com).
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Please give a clear and concise description of the bug
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: repro
|
|
||||||
attributes:
|
|
||||||
label: Reproduce
|
|
||||||
description: Steps to reproduce the bug
|
|
||||||
placeholder: |
|
|
||||||
1. docker run ...
|
|
||||||
2. docker kill ...
|
|
||||||
3. docker rm ...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: expected
|
|
||||||
attributes:
|
|
||||||
label: Expected behavior
|
|
||||||
description: What is the expected behavior?
|
|
||||||
placeholder: |
|
|
||||||
E.g. "`docker rm` should remove the container and cleanup all associated data"
|
|
||||||
- type: textarea
|
|
||||||
id: version
|
|
||||||
attributes:
|
|
||||||
label: docker version
|
|
||||||
description: Output of `docker version`
|
|
||||||
render: bash
|
|
||||||
placeholder: |
|
|
||||||
Client:
|
|
||||||
Version: 20.10.17
|
|
||||||
API version: 1.41
|
|
||||||
Go version: go1.17.11
|
|
||||||
Git commit: 100c70180fde3601def79a59cc3e996aa553c9b9
|
|
||||||
Built: Mon Jun 6 21:36:39 UTC 2022
|
|
||||||
OS/Arch: linux/amd64
|
|
||||||
Context: default
|
|
||||||
Experimental: true
|
|
||||||
|
|
||||||
Server:
|
|
||||||
Engine:
|
|
||||||
Version: 20.10.17
|
|
||||||
API version: 1.41 (minimum version 1.12)
|
|
||||||
Go version: go1.17.11
|
|
||||||
Git commit: a89b84221c8560e7a3dee2a653353429e7628424
|
|
||||||
Built: Mon Jun 6 22:32:38 2022
|
|
||||||
OS/Arch: linux/amd64
|
|
||||||
Experimental: true
|
|
||||||
containerd:
|
|
||||||
Version: 1.6.6
|
|
||||||
GitCommit: 10c12954828e7c7c9b6e0ea9b0c02b01407d3ae1
|
|
||||||
runc:
|
|
||||||
Version: 1.1.2
|
|
||||||
GitCommit: a916309fff0f838eb94e928713dbc3c0d0ac7aa4
|
|
||||||
docker-init:
|
|
||||||
Version: 0.19.0
|
|
||||||
GitCommit:
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: info
|
|
||||||
attributes:
|
|
||||||
label: docker info
|
|
||||||
description: Output of `docker info`
|
|
||||||
render: bash
|
|
||||||
placeholder: |
|
|
||||||
Client:
|
|
||||||
Context: default
|
|
||||||
Debug Mode: false
|
|
||||||
Plugins:
|
|
||||||
buildx: Docker Buildx (Docker Inc., 0.8.2)
|
|
||||||
compose: Docker Compose (Docker Inc., 2.6.0)
|
|
||||||
|
|
||||||
Server:
|
|
||||||
Containers: 4
|
|
||||||
Running: 2
|
|
||||||
Paused: 0
|
|
||||||
Stopped: 2
|
|
||||||
Images: 80
|
|
||||||
Server Version: 20.10.17
|
|
||||||
Storage Driver: overlay2
|
|
||||||
Backing Filesystem: xfs
|
|
||||||
Supports d_type: true
|
|
||||||
Native Overlay Diff: false
|
|
||||||
userxattr: false
|
|
||||||
Logging Driver: local
|
|
||||||
Cgroup Driver: cgroupfs
|
|
||||||
Cgroup Version: 1
|
|
||||||
Plugins:
|
|
||||||
Volume: local
|
|
||||||
Network: bridge host ipvlan macvlan null overlay
|
|
||||||
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
|
|
||||||
Swarm: inactive
|
|
||||||
Runtimes: runc io.containerd.runc.v2 io.containerd.runtime.v1.linux
|
|
||||||
Default Runtime: runc
|
|
||||||
Init Binary: docker-init
|
|
||||||
containerd version: 10c12954828e7c7c9b6e0ea9b0c02b01407d3ae1
|
|
||||||
runc version: a916309fff0f838eb94e928713dbc3c0d0ac7aa4
|
|
||||||
init version:
|
|
||||||
Security Options:
|
|
||||||
apparmor
|
|
||||||
seccomp
|
|
||||||
Profile: default
|
|
||||||
Kernel Version: 5.13.0-1031-azure
|
|
||||||
Operating System: Ubuntu 20.04.4 LTS
|
|
||||||
OSType: linux
|
|
||||||
Architecture: x86_64
|
|
||||||
CPUs: 4
|
|
||||||
Total Memory: 15.63GiB
|
|
||||||
Name: dev
|
|
||||||
ID: UC44:2RFL:7NQ5:GGFW:34O5:DYRE:CLOH:VLGZ:64AZ:GFXC:PY6H:SAHY
|
|
||||||
Docker Root Dir: /var/lib/docker
|
|
||||||
Debug Mode: true
|
|
||||||
File Descriptors: 46
|
|
||||||
Goroutines: 134
|
|
||||||
System Time: 2022-07-06T18:07:54.812439392Z
|
|
||||||
EventsListeners: 0
|
|
||||||
Registry: https://index.docker.io/v1/
|
|
||||||
Labels:
|
|
||||||
Experimental: true
|
|
||||||
Insecure Registries:
|
|
||||||
127.0.0.0/8
|
|
||||||
Live Restore Enabled: true
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: additional
|
|
||||||
attributes:
|
|
||||||
label: Additional Info
|
|
||||||
description: Additional info you want to provide such as logs, system info, environment, etc.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1,8 +0,0 @@
|
||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: Security and Vulnerabilities
|
|
||||||
url: https://github.com/moby/moby/blob/master/SECURITY.md
|
|
||||||
about: Please report any security issues or vulnerabilities responsibly to the Docker security team. Please do not use the public issue tracker.
|
|
||||||
- name: Questions and Discussions
|
|
||||||
url: https://github.com/moby/moby/discussions/new
|
|
||||||
about: Use Github Discussions to ask questions and/or open discussion topics.
|
|
13
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
13
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
@ -1,13 +0,0 @@
|
||||||
name: Feature request
|
|
||||||
description: Missing functionality? Come tell us about it!
|
|
||||||
labels:
|
|
||||||
- kind/feature
|
|
||||||
- status/0-triage
|
|
||||||
body:
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: What is the feature you want to see?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -1,6 +1,6 @@
|
||||||
<!--
|
<!--
|
||||||
Please make sure you've read and understood our contributing guidelines;
|
Please make sure you've read and understood our contributing guidelines;
|
||||||
https://github.com/moby/moby/blob/master/CONTRIBUTING.md
|
https://github.com/docker/docker/blob/master/CONTRIBUTING.md
|
||||||
|
|
||||||
** Make sure all your commits include a signature generated with `git commit -s` **
|
** Make sure all your commits include a signature generated with `git commit -s` **
|
||||||
|
|
||||||
|
@ -22,12 +22,9 @@ Please provide the following information:
|
||||||
**- Description for the changelog**
|
**- Description for the changelog**
|
||||||
<!--
|
<!--
|
||||||
Write a short (one line) summary that describes the changes in this
|
Write a short (one line) summary that describes the changes in this
|
||||||
pull request for inclusion in the changelog.
|
pull request for inclusion in the changelog:
|
||||||
It must be placed inside the below triple backticks section:
|
|
||||||
-->
|
-->
|
||||||
```markdown changelog
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
**- A picture of a cute animal (not mandatory but encouraged)**
|
**- A picture of a cute animal (not mandatory but encouraged)**
|
||||||
|
|
||||||
|
|
27
.github/actions/setup-runner/action.yml
vendored
27
.github/actions/setup-runner/action.yml
vendored
|
@ -1,27 +0,0 @@
|
||||||
name: 'Setup Runner'
|
|
||||||
description: 'Composite action to set up the GitHub Runner for jobs in the test.yml workflow'
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
- run: |
|
|
||||||
sudo modprobe ip_vs
|
|
||||||
sudo modprobe ipv6
|
|
||||||
sudo modprobe ip6table_filter
|
|
||||||
sudo modprobe -r overlay
|
|
||||||
sudo modprobe overlay redirect_dir=off
|
|
||||||
shell: bash
|
|
||||||
- run: |
|
|
||||||
if [ ! -e /etc/docker/daemon.json ]; then
|
|
||||||
echo '{}' | sudo tee /etc/docker/daemon.json >/dev/null
|
|
||||||
fi
|
|
||||||
DOCKERD_CONFIG=$(jq '.+{"experimental":true,"live-restore":true,"ipv6":true,"fixed-cidr-v6":"2001:db8:1::/64"}' /etc/docker/daemon.json)
|
|
||||||
sudo tee /etc/docker/daemon.json <<<"$DOCKERD_CONFIG" >/dev/null
|
|
||||||
sudo service docker restart
|
|
||||||
shell: bash
|
|
||||||
- run: |
|
|
||||||
./contrib/check-config.sh || true
|
|
||||||
shell: bash
|
|
||||||
- run: |
|
|
||||||
docker info
|
|
||||||
shell: bash
|
|
14
.github/actions/setup-tracing/action.yml
vendored
14
.github/actions/setup-tracing/action.yml
vendored
|
@ -1,14 +0,0 @@
|
||||||
name: 'Setup Tracing'
|
|
||||||
description: 'Composite action to set up the tracing for test jobs'
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
- run: |
|
|
||||||
set -e
|
|
||||||
# Jaeger is set up on Windows through an inline run step. If you update Jaeger here, don't forget to update
|
|
||||||
# the version set in .github/workflows/.windows.yml.
|
|
||||||
docker run -d --net=host --name jaeger -e COLLECTOR_OTLP_ENABLED=true jaegertracing/all-in-one:1.46
|
|
||||||
docker0_ip="$(ip -f inet addr show docker0 | grep -Po 'inet \K[\d.]+')"
|
|
||||||
echo "OTEL_EXPORTER_OTLP_ENDPOINT=http://${docker0_ip}:4318" >> "${GITHUB_ENV}"
|
|
||||||
shell: bash
|
|
48
.github/workflows/.dco.yml
vendored
48
.github/workflows/.dco.yml
vendored
|
@ -1,48 +0,0 @@
|
||||||
# reusable workflow
|
|
||||||
name: .dco
|
|
||||||
|
|
||||||
# TODO: hide reusable workflow from the UI. Tracked in https://github.com/community/community/discussions/12025
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
|
|
||||||
env:
|
|
||||||
ALPINE_VERSION: 3.16
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
-
|
|
||||||
name: Dump context
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
console.log(JSON.stringify(context, null, 2));
|
|
||||||
-
|
|
||||||
name: Get base ref
|
|
||||||
id: base-ref
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
result-encoding: string
|
|
||||||
script: |
|
|
||||||
if (/^refs\/pull\//.test(context.ref) && context.payload?.pull_request?.base?.ref != undefined) {
|
|
||||||
return context.payload.pull_request.base.ref;
|
|
||||||
}
|
|
||||||
return context.ref.replace(/^refs\/heads\//g, '');
|
|
||||||
-
|
|
||||||
name: Validate
|
|
||||||
run: |
|
|
||||||
docker run --rm \
|
|
||||||
-v "$(pwd):/workspace" \
|
|
||||||
-e VALIDATE_REPO \
|
|
||||||
-e VALIDATE_BRANCH \
|
|
||||||
alpine:${{ env.ALPINE_VERSION }} sh -c 'apk add --no-cache -q bash git openssh-client && git config --system --add safe.directory /workspace && cd /workspace && hack/validate/dco'
|
|
||||||
env:
|
|
||||||
VALIDATE_REPO: ${{ github.server_url }}/${{ github.repository }}.git
|
|
||||||
VALIDATE_BRANCH: ${{ steps.base-ref.outputs.result }}
|
|
35
.github/workflows/.test-prepare.yml
vendored
35
.github/workflows/.test-prepare.yml
vendored
|
@ -1,35 +0,0 @@
|
||||||
# reusable workflow
|
|
||||||
name: .test-prepare
|
|
||||||
|
|
||||||
# TODO: hide reusable workflow from the UI. Tracked in https://github.com/community/community/discussions/12025
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
outputs:
|
|
||||||
matrix:
|
|
||||||
description: Test matrix
|
|
||||||
value: ${{ jobs.run.outputs.matrix }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
outputs:
|
|
||||||
matrix: ${{ steps.set.outputs.matrix }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Create matrix
|
|
||||||
id: set
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
let matrix = ['graphdriver'];
|
|
||||||
if ("${{ contains(github.event.pull_request.labels.*.name, 'containerd-integration') || github.event_name != 'pull_request' }}" == "true") {
|
|
||||||
matrix.push('snapshotter');
|
|
||||||
}
|
|
||||||
await core.group(`Set matrix`, async () => {
|
|
||||||
core.info(`matrix: ${JSON.stringify(matrix)}`);
|
|
||||||
core.setOutput('matrix', JSON.stringify(matrix));
|
|
||||||
});
|
|
445
.github/workflows/.test.yml
vendored
445
.github/workflows/.test.yml
vendored
|
@ -1,445 +0,0 @@
|
||||||
# reusable workflow
|
|
||||||
name: .test
|
|
||||||
|
|
||||||
# TODO: hide reusable workflow from the UI. Tracked in https://github.com/community/community/discussions/12025
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
storage:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
default: "graphdriver"
|
|
||||||
|
|
||||||
env:
|
|
||||||
GO_VERSION: "1.21.9"
|
|
||||||
GOTESTLIST_VERSION: v0.3.1
|
|
||||||
TESTSTAT_VERSION: v0.1.25
|
|
||||||
ITG_CLI_MATRIX_SIZE: 6
|
|
||||||
DOCKER_EXPERIMENTAL: 1
|
|
||||||
DOCKER_GRAPHDRIVER: ${{ inputs.storage == 'snapshotter' && 'overlayfs' || 'overlay2' }}
|
|
||||||
TEST_INTEGRATION_USE_SNAPSHOTTER: ${{ inputs.storage == 'snapshotter' && '1' || '' }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
unit:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
continue-on-error: ${{ github.event_name != 'pull_request' }}
|
|
||||||
timeout-minutes: 120
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Set up runner
|
|
||||||
uses: ./.github/actions/setup-runner
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Build dev image
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
targets: dev
|
|
||||||
set: |
|
|
||||||
dev.cache-from=type=gha,scope=dev
|
|
||||||
-
|
|
||||||
name: Test
|
|
||||||
run: |
|
|
||||||
make -o build test-unit
|
|
||||||
-
|
|
||||||
name: Prepare reports
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
mkdir -p bundles /tmp/reports
|
|
||||||
find bundles -path '*/root/*overlay2' -prune -o -type f \( -name '*-report.json' -o -name '*.log' -o -name '*.out' -o -name '*.prof' -o -name '*-report.xml' \) -print | xargs sudo tar -czf /tmp/reports.tar.gz
|
|
||||||
tar -xzf /tmp/reports.tar.gz -C /tmp/reports
|
|
||||||
sudo chown -R $(id -u):$(id -g) /tmp/reports
|
|
||||||
tree -nh /tmp/reports
|
|
||||||
-
|
|
||||||
name: Send to Codecov
|
|
||||||
uses: codecov/codecov-action@v4
|
|
||||||
with:
|
|
||||||
directory: ./bundles
|
|
||||||
env_vars: RUNNER_OS
|
|
||||||
flags: unit
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }} # used to upload coverage reports: https://github.com/moby/buildkit/pull/4660#issue-2142122533
|
|
||||||
-
|
|
||||||
name: Upload reports
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: test-reports-unit-${{ inputs.storage }}
|
|
||||||
path: /tmp/reports/*
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
unit-report:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
continue-on-error: ${{ github.event_name != 'pull_request' }}
|
|
||||||
timeout-minutes: 10
|
|
||||||
if: always()
|
|
||||||
needs:
|
|
||||||
- unit
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
-
|
|
||||||
name: Download reports
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: test-reports-unit-${{ inputs.storage }}
|
|
||||||
path: /tmp/reports
|
|
||||||
-
|
|
||||||
name: Install teststat
|
|
||||||
run: |
|
|
||||||
go install github.com/vearutop/teststat@${{ env.TESTSTAT_VERSION }}
|
|
||||||
-
|
|
||||||
name: Create summary
|
|
||||||
run: |
|
|
||||||
find /tmp/reports -type f -name '*-go-test-report.json' -exec teststat -markdown {} \+ >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
docker-py:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
continue-on-error: ${{ github.event_name != 'pull_request' }}
|
|
||||||
timeout-minutes: 120
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Set up runner
|
|
||||||
uses: ./.github/actions/setup-runner
|
|
||||||
-
|
|
||||||
name: Set up tracing
|
|
||||||
uses: ./.github/actions/setup-tracing
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Build dev image
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
targets: dev
|
|
||||||
set: |
|
|
||||||
dev.cache-from=type=gha,scope=dev
|
|
||||||
-
|
|
||||||
name: Test
|
|
||||||
run: |
|
|
||||||
make -o build test-docker-py
|
|
||||||
-
|
|
||||||
name: Prepare reports
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
mkdir -p bundles /tmp/reports
|
|
||||||
find bundles -path '*/root/*overlay2' -prune -o -type f \( -name '*-report.json' -o -name '*.log' -o -name '*.out' -o -name '*.prof' -o -name '*-report.xml' \) -print | xargs sudo tar -czf /tmp/reports.tar.gz
|
|
||||||
tar -xzf /tmp/reports.tar.gz -C /tmp/reports
|
|
||||||
sudo chown -R $(id -u):$(id -g) /tmp/reports
|
|
||||||
tree -nh /tmp/reports
|
|
||||||
|
|
||||||
curl -sSLf localhost:16686/api/traces?service=integration-test-client > /tmp/reports/jaeger-trace.json
|
|
||||||
-
|
|
||||||
name: Test daemon logs
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
cat bundles/test-docker-py/docker.log
|
|
||||||
-
|
|
||||||
name: Upload reports
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: test-reports-docker-py-${{ inputs.storage }}
|
|
||||||
path: /tmp/reports/*
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
integration-flaky:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
continue-on-error: ${{ github.event_name != 'pull_request' }}
|
|
||||||
timeout-minutes: 120
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Set up runner
|
|
||||||
uses: ./.github/actions/setup-runner
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Build dev image
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
targets: dev
|
|
||||||
set: |
|
|
||||||
dev.cache-from=type=gha,scope=dev
|
|
||||||
-
|
|
||||||
name: Test
|
|
||||||
run: |
|
|
||||||
make -o build test-integration-flaky
|
|
||||||
env:
|
|
||||||
TEST_SKIP_INTEGRATION_CLI: 1
|
|
||||||
|
|
||||||
integration:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
continue-on-error: ${{ github.event_name != 'pull_request' }}
|
|
||||||
timeout-minutes: 120
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-20.04
|
|
||||||
- ubuntu-22.04
|
|
||||||
mode:
|
|
||||||
- ""
|
|
||||||
- rootless
|
|
||||||
- systemd
|
|
||||||
#- rootless-systemd FIXME: https://github.com/moby/moby/issues/44084
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Set up runner
|
|
||||||
uses: ./.github/actions/setup-runner
|
|
||||||
-
|
|
||||||
name: Set up tracing
|
|
||||||
uses: ./.github/actions/setup-tracing
|
|
||||||
-
|
|
||||||
name: Prepare
|
|
||||||
run: |
|
|
||||||
CACHE_DEV_SCOPE=dev
|
|
||||||
if [[ "${{ matrix.mode }}" == *"rootless"* ]]; then
|
|
||||||
echo "DOCKER_ROOTLESS=1" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
if [[ "${{ matrix.mode }}" == *"systemd"* ]]; then
|
|
||||||
echo "SYSTEMD=true" >> $GITHUB_ENV
|
|
||||||
CACHE_DEV_SCOPE="${CACHE_DEV_SCOPE}systemd"
|
|
||||||
fi
|
|
||||||
echo "CACHE_DEV_SCOPE=${CACHE_DEV_SCOPE}" >> $GITHUB_ENV
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Build dev image
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
targets: dev
|
|
||||||
set: |
|
|
||||||
dev.cache-from=type=gha,scope=${{ env.CACHE_DEV_SCOPE }}
|
|
||||||
-
|
|
||||||
name: Test
|
|
||||||
run: |
|
|
||||||
make -o build test-integration
|
|
||||||
env:
|
|
||||||
TEST_SKIP_INTEGRATION_CLI: 1
|
|
||||||
TESTCOVERAGE: 1
|
|
||||||
-
|
|
||||||
name: Prepare reports
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
reportsName=${{ matrix.os }}
|
|
||||||
if [ -n "${{ matrix.mode }}" ]; then
|
|
||||||
reportsName="$reportsName-${{ matrix.mode }}"
|
|
||||||
fi
|
|
||||||
reportsPath="/tmp/reports/$reportsName"
|
|
||||||
echo "TESTREPORTS_NAME=$reportsName" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
mkdir -p bundles $reportsPath
|
|
||||||
find bundles -path '*/root/*overlay2' -prune -o -type f \( -name '*-report.json' -o -name '*.log' -o -name '*.out' -o -name '*.prof' -o -name '*-report.xml' \) -print | xargs sudo tar -czf /tmp/reports.tar.gz
|
|
||||||
tar -xzf /tmp/reports.tar.gz -C $reportsPath
|
|
||||||
sudo chown -R $(id -u):$(id -g) $reportsPath
|
|
||||||
tree -nh $reportsPath
|
|
||||||
|
|
||||||
curl -sSLf localhost:16686/api/traces?service=integration-test-client > $reportsPath/jaeger-trace.json
|
|
||||||
-
|
|
||||||
name: Send to Codecov
|
|
||||||
uses: codecov/codecov-action@v4
|
|
||||||
with:
|
|
||||||
directory: ./bundles/test-integration
|
|
||||||
env_vars: RUNNER_OS
|
|
||||||
flags: integration,${{ matrix.mode }}
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }} # used to upload coverage reports: https://github.com/moby/buildkit/pull/4660#issue-2142122533
|
|
||||||
-
|
|
||||||
name: Test daemon logs
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
cat bundles/test-integration/docker.log
|
|
||||||
-
|
|
||||||
name: Upload reports
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: test-reports-integration-${{ inputs.storage }}-${{ env.TESTREPORTS_NAME }}
|
|
||||||
path: /tmp/reports/*
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
integration-report:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
continue-on-error: ${{ github.event_name != 'pull_request' }}
|
|
||||||
timeout-minutes: 10
|
|
||||||
if: always()
|
|
||||||
needs:
|
|
||||||
- integration
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
-
|
|
||||||
name: Download reports
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: /tmp/reports
|
|
||||||
pattern: test-reports-integration-${{ inputs.storage }}-*
|
|
||||||
merge-multiple: true
|
|
||||||
-
|
|
||||||
name: Install teststat
|
|
||||||
run: |
|
|
||||||
go install github.com/vearutop/teststat@${{ env.TESTSTAT_VERSION }}
|
|
||||||
-
|
|
||||||
name: Create summary
|
|
||||||
run: |
|
|
||||||
find /tmp/reports -type f -name '*-go-test-report.json' -exec teststat -markdown {} \+ >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
integration-cli-prepare:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
continue-on-error: ${{ github.event_name != 'pull_request' }}
|
|
||||||
outputs:
|
|
||||||
matrix: ${{ steps.tests.outputs.matrix }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
-
|
|
||||||
name: Install gotestlist
|
|
||||||
run:
|
|
||||||
go install github.com/crazy-max/gotestlist/cmd/gotestlist@${{ env.GOTESTLIST_VERSION }}
|
|
||||||
-
|
|
||||||
name: Create matrix
|
|
||||||
id: tests
|
|
||||||
working-directory: ./integration-cli
|
|
||||||
run: |
|
|
||||||
# This step creates a matrix for integration-cli tests. Tests suites
|
|
||||||
# are distributed in integration-cli job through a matrix. There is
|
|
||||||
# also overrides being added to the matrix like "./..." to run
|
|
||||||
# "Test integration" step exclusively and specific tests suites that
|
|
||||||
# take a long time to run.
|
|
||||||
matrix="$(gotestlist -d ${{ env.ITG_CLI_MATRIX_SIZE }} -o "./..." -o "DockerSwarmSuite" -o "DockerNetworkSuite|DockerExternalVolumeSuite" ./...)"
|
|
||||||
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Show matrix
|
|
||||||
run: |
|
|
||||||
echo ${{ steps.tests.outputs.matrix }}
|
|
||||||
|
|
||||||
integration-cli:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
continue-on-error: ${{ github.event_name != 'pull_request' }}
|
|
||||||
timeout-minutes: 120
|
|
||||||
needs:
|
|
||||||
- integration-cli-prepare
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
test: ${{ fromJson(needs.integration-cli-prepare.outputs.matrix) }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Set up runner
|
|
||||||
uses: ./.github/actions/setup-runner
|
|
||||||
-
|
|
||||||
name: Set up tracing
|
|
||||||
uses: ./.github/actions/setup-tracing
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Build dev image
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
targets: dev
|
|
||||||
set: |
|
|
||||||
dev.cache-from=type=gha,scope=dev
|
|
||||||
-
|
|
||||||
name: Test
|
|
||||||
run: |
|
|
||||||
make -o build test-integration
|
|
||||||
env:
|
|
||||||
TEST_SKIP_INTEGRATION: 1
|
|
||||||
TESTCOVERAGE: 1
|
|
||||||
TESTFLAGS: "-test.run (${{ matrix.test }})/"
|
|
||||||
-
|
|
||||||
name: Prepare reports
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
reportsName=$(echo -n "${{ matrix.test }}" | sha256sum | cut -d " " -f 1)
|
|
||||||
reportsPath=/tmp/reports/$reportsName
|
|
||||||
echo "TESTREPORTS_NAME=$reportsName" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
mkdir -p bundles $reportsPath
|
|
||||||
echo "${{ matrix.test }}" | tr -s '|' '\n' | tee -a "$reportsPath/tests.txt"
|
|
||||||
find bundles -path '*/root/*overlay2' -prune -o -type f \( -name '*-report.json' -o -name '*.log' -o -name '*.out' -o -name '*.prof' -o -name '*-report.xml' \) -print | xargs sudo tar -czf /tmp/reports.tar.gz
|
|
||||||
tar -xzf /tmp/reports.tar.gz -C $reportsPath
|
|
||||||
sudo chown -R $(id -u):$(id -g) $reportsPath
|
|
||||||
tree -nh $reportsPath
|
|
||||||
|
|
||||||
curl -sSLf localhost:16686/api/traces?service=integration-test-client > $reportsPath/jaeger-trace.json
|
|
||||||
-
|
|
||||||
name: Send to Codecov
|
|
||||||
uses: codecov/codecov-action@v4
|
|
||||||
with:
|
|
||||||
directory: ./bundles/test-integration
|
|
||||||
env_vars: RUNNER_OS
|
|
||||||
flags: integration-cli
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }} # used to upload coverage reports: https://github.com/moby/buildkit/pull/4660#issue-2142122533
|
|
||||||
-
|
|
||||||
name: Test daemon logs
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
cat bundles/test-integration/docker.log
|
|
||||||
-
|
|
||||||
name: Upload reports
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: test-reports-integration-cli-${{ inputs.storage }}-${{ env.TESTREPORTS_NAME }}
|
|
||||||
path: /tmp/reports/*
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
integration-cli-report:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
continue-on-error: ${{ github.event_name != 'pull_request' }}
|
|
||||||
timeout-minutes: 10
|
|
||||||
if: always()
|
|
||||||
needs:
|
|
||||||
- integration-cli
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
-
|
|
||||||
name: Download reports
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: /tmp/reports
|
|
||||||
pattern: test-reports-integration-cli-${{ inputs.storage }}-*
|
|
||||||
merge-multiple: true
|
|
||||||
-
|
|
||||||
name: Install teststat
|
|
||||||
run: |
|
|
||||||
go install github.com/vearutop/teststat@${{ env.TESTSTAT_VERSION }}
|
|
||||||
-
|
|
||||||
name: Create summary
|
|
||||||
run: |
|
|
||||||
find /tmp/reports -type f -name '*-go-test-report.json' -exec teststat -markdown {} \+ >> $GITHUB_STEP_SUMMARY
|
|
551
.github/workflows/.windows.yml
vendored
551
.github/workflows/.windows.yml
vendored
|
@ -1,551 +0,0 @@
|
||||||
# reusable workflow
|
|
||||||
name: .windows
|
|
||||||
|
|
||||||
# TODO: hide reusable workflow from the UI. Tracked in https://github.com/community/community/discussions/12025
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
os:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
storage:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
default: "graphdriver"
|
|
||||||
send_coverage:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
|
|
||||||
env:
|
|
||||||
GO_VERSION: "1.21.9"
|
|
||||||
GOTESTLIST_VERSION: v0.3.1
|
|
||||||
TESTSTAT_VERSION: v0.1.25
|
|
||||||
WINDOWS_BASE_IMAGE: mcr.microsoft.com/windows/servercore
|
|
||||||
WINDOWS_BASE_TAG_2019: ltsc2019
|
|
||||||
WINDOWS_BASE_TAG_2022: ltsc2022
|
|
||||||
TEST_IMAGE_NAME: moby:test
|
|
||||||
TEST_CTN_NAME: moby
|
|
||||||
DOCKER_BUILDKIT: 0
|
|
||||||
ITG_CLI_MATRIX_SIZE: 6
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ${{ inputs.os }}
|
|
||||||
env:
|
|
||||||
GOPATH: ${{ github.workspace }}\go
|
|
||||||
GOBIN: ${{ github.workspace }}\go\bin
|
|
||||||
BIN_OUT: ${{ github.workspace }}\out
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ${{ env.GOPATH }}/src/github.com/docker/docker
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.GOPATH }}/src/github.com/docker/docker
|
|
||||||
-
|
|
||||||
name: Env
|
|
||||||
run: |
|
|
||||||
Get-ChildItem Env: | Out-String
|
|
||||||
-
|
|
||||||
name: Init
|
|
||||||
run: |
|
|
||||||
New-Item -ItemType "directory" -Path "${{ github.workspace }}\go-build"
|
|
||||||
New-Item -ItemType "directory" -Path "${{ github.workspace }}\go\pkg\mod"
|
|
||||||
If ("${{ inputs.os }}" -eq "windows-2019") {
|
|
||||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2019 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
|
||||||
} ElseIf ("${{ inputs.os }}" -eq "windows-2022") {
|
|
||||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2022 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
|
||||||
}
|
|
||||||
-
|
|
||||||
name: Cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~\AppData\Local\go-build
|
|
||||||
~\go\pkg\mod
|
|
||||||
${{ github.workspace }}\go-build
|
|
||||||
${{ env.GOPATH }}\pkg\mod
|
|
||||||
key: ${{ inputs.os }}-${{ github.job }}-${{ hashFiles('**/vendor.sum') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ inputs.os }}-${{ github.job }}-
|
|
||||||
-
|
|
||||||
name: Docker info
|
|
||||||
run: |
|
|
||||||
docker info
|
|
||||||
-
|
|
||||||
name: Build base image
|
|
||||||
run: |
|
|
||||||
& docker build `
|
|
||||||
--build-arg WINDOWS_BASE_IMAGE `
|
|
||||||
--build-arg WINDOWS_BASE_IMAGE_TAG `
|
|
||||||
--build-arg GO_VERSION `
|
|
||||||
-t ${{ env.TEST_IMAGE_NAME }} `
|
|
||||||
-f Dockerfile.windows .
|
|
||||||
-
|
|
||||||
name: Build binaries
|
|
||||||
run: |
|
|
||||||
& docker run --name ${{ env.TEST_CTN_NAME }} -e "DOCKER_GITCOMMIT=${{ github.sha }}" `
|
|
||||||
-v "${{ github.workspace }}\go-build:C:\Users\ContainerAdministrator\AppData\Local\go-build" `
|
|
||||||
-v "${{ github.workspace }}\go\pkg\mod:C:\gopath\pkg\mod" `
|
|
||||||
${{ env.TEST_IMAGE_NAME }} hack\make.ps1 -Daemon -Client
|
|
||||||
-
|
|
||||||
name: Copy artifacts
|
|
||||||
run: |
|
|
||||||
New-Item -ItemType "directory" -Path "${{ env.BIN_OUT }}"
|
|
||||||
docker cp "${{ env.TEST_CTN_NAME }}`:c`:\gopath\src\github.com\docker\docker\bundles\docker.exe" ${{ env.BIN_OUT }}\
|
|
||||||
docker cp "${{ env.TEST_CTN_NAME }}`:c`:\gopath\src\github.com\docker\docker\bundles\dockerd.exe" ${{ env.BIN_OUT }}\
|
|
||||||
docker cp "${{ env.TEST_CTN_NAME }}`:c`:\gopath\bin\gotestsum.exe" ${{ env.BIN_OUT }}\
|
|
||||||
docker cp "${{ env.TEST_CTN_NAME }}`:c`:\containerd\bin\containerd.exe" ${{ env.BIN_OUT }}\
|
|
||||||
docker cp "${{ env.TEST_CTN_NAME }}`:c`:\containerd\bin\containerd-shim-runhcs-v1.exe" ${{ env.BIN_OUT }}\
|
|
||||||
-
|
|
||||||
name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: build-${{ inputs.storage }}-${{ inputs.os }}
|
|
||||||
path: ${{ env.BIN_OUT }}/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 2
|
|
||||||
|
|
||||||
unit-test:
|
|
||||||
runs-on: ${{ inputs.os }}
|
|
||||||
timeout-minutes: 120
|
|
||||||
env:
|
|
||||||
GOPATH: ${{ github.workspace }}\go
|
|
||||||
GOBIN: ${{ github.workspace }}\go\bin
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ${{ env.GOPATH }}/src/github.com/docker/docker
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.GOPATH }}/src/github.com/docker/docker
|
|
||||||
-
|
|
||||||
name: Env
|
|
||||||
run: |
|
|
||||||
Get-ChildItem Env: | Out-String
|
|
||||||
-
|
|
||||||
name: Init
|
|
||||||
run: |
|
|
||||||
New-Item -ItemType "directory" -Path "${{ github.workspace }}\go-build"
|
|
||||||
New-Item -ItemType "directory" -Path "${{ github.workspace }}\go\pkg\mod"
|
|
||||||
New-Item -ItemType "directory" -Path "bundles"
|
|
||||||
If ("${{ inputs.os }}" -eq "windows-2019") {
|
|
||||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2019 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
|
||||||
} ElseIf ("${{ inputs.os }}" -eq "windows-2022") {
|
|
||||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2022 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
|
||||||
}
|
|
||||||
-
|
|
||||||
name: Cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~\AppData\Local\go-build
|
|
||||||
~\go\pkg\mod
|
|
||||||
${{ github.workspace }}\go-build
|
|
||||||
${{ env.GOPATH }}\pkg\mod
|
|
||||||
key: ${{ inputs.os }}-${{ github.job }}-${{ hashFiles('**/vendor.sum') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ inputs.os }}-${{ github.job }}-
|
|
||||||
-
|
|
||||||
name: Docker info
|
|
||||||
run: |
|
|
||||||
docker info
|
|
||||||
-
|
|
||||||
name: Build base image
|
|
||||||
run: |
|
|
||||||
& docker build `
|
|
||||||
--build-arg WINDOWS_BASE_IMAGE `
|
|
||||||
--build-arg WINDOWS_BASE_IMAGE_TAG `
|
|
||||||
--build-arg GO_VERSION `
|
|
||||||
-t ${{ env.TEST_IMAGE_NAME }} `
|
|
||||||
-f Dockerfile.windows .
|
|
||||||
-
|
|
||||||
name: Test
|
|
||||||
run: |
|
|
||||||
& docker run --name ${{ env.TEST_CTN_NAME }} -e "DOCKER_GITCOMMIT=${{ github.sha }}" `
|
|
||||||
-v "${{ github.workspace }}\go-build:C:\Users\ContainerAdministrator\AppData\Local\go-build" `
|
|
||||||
-v "${{ github.workspace }}\go\pkg\mod:C:\gopath\pkg\mod" `
|
|
||||||
-v "${{ env.GOPATH }}\src\github.com\docker\docker\bundles:C:\gopath\src\github.com\docker\docker\bundles" `
|
|
||||||
${{ env.TEST_IMAGE_NAME }} hack\make.ps1 -TestUnit
|
|
||||||
-
|
|
||||||
name: Send to Codecov
|
|
||||||
if: inputs.send_coverage
|
|
||||||
uses: codecov/codecov-action@v4
|
|
||||||
with:
|
|
||||||
working-directory: ${{ env.GOPATH }}\src\github.com\docker\docker
|
|
||||||
directory: bundles
|
|
||||||
env_vars: RUNNER_OS
|
|
||||||
flags: unit
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }} # used to upload coverage reports: https://github.com/moby/buildkit/pull/4660#issue-2142122533
|
|
||||||
-
|
|
||||||
name: Upload reports
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.os }}-${{ inputs.storage }}-unit-reports
|
|
||||||
path: ${{ env.GOPATH }}\src\github.com\docker\docker\bundles\*
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
unit-test-report:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: always()
|
|
||||||
needs:
|
|
||||||
- unit-test
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
-
|
|
||||||
name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.os }}-${{ inputs.storage }}-unit-reports
|
|
||||||
path: /tmp/artifacts
|
|
||||||
-
|
|
||||||
name: Install teststat
|
|
||||||
run: |
|
|
||||||
go install github.com/vearutop/teststat@${{ env.TESTSTAT_VERSION }}
|
|
||||||
-
|
|
||||||
name: Create summary
|
|
||||||
run: |
|
|
||||||
find /tmp/artifacts -type f -name '*-go-test-report.json' -exec teststat -markdown {} \+ >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
integration-test-prepare:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
matrix: ${{ steps.tests.outputs.matrix }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
-
|
|
||||||
name: Install gotestlist
|
|
||||||
run:
|
|
||||||
go install github.com/crazy-max/gotestlist/cmd/gotestlist@${{ env.GOTESTLIST_VERSION }}
|
|
||||||
-
|
|
||||||
name: Create matrix
|
|
||||||
id: tests
|
|
||||||
working-directory: ./integration-cli
|
|
||||||
run: |
|
|
||||||
# This step creates a matrix for integration-cli tests. Tests suites
|
|
||||||
# are distributed in integration-test job through a matrix. There is
|
|
||||||
# also an override being added to the matrix like "./..." to run
|
|
||||||
# "Test integration" step exclusively.
|
|
||||||
matrix="$(gotestlist -d ${{ env.ITG_CLI_MATRIX_SIZE }} -o "./..." ./...)"
|
|
||||||
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Show matrix
|
|
||||||
run: |
|
|
||||||
echo ${{ steps.tests.outputs.matrix }}
|
|
||||||
|
|
||||||
integration-test:
|
|
||||||
runs-on: ${{ inputs.os }}
|
|
||||||
continue-on-error: ${{ inputs.storage == 'snapshotter' && github.event_name != 'pull_request' }}
|
|
||||||
timeout-minutes: 120
|
|
||||||
needs:
|
|
||||||
- build
|
|
||||||
- integration-test-prepare
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
storage:
|
|
||||||
- ${{ inputs.storage }}
|
|
||||||
runtime:
|
|
||||||
- builtin
|
|
||||||
- containerd
|
|
||||||
test: ${{ fromJson(needs.integration-test-prepare.outputs.matrix) }}
|
|
||||||
exclude:
|
|
||||||
- storage: snapshotter
|
|
||||||
runtime: builtin
|
|
||||||
env:
|
|
||||||
GOPATH: ${{ github.workspace }}\go
|
|
||||||
GOBIN: ${{ github.workspace }}\go\bin
|
|
||||||
BIN_OUT: ${{ github.workspace }}\out
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ${{ env.GOPATH }}/src/github.com/docker/docker
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.GOPATH }}/src/github.com/docker/docker
|
|
||||||
-
|
|
||||||
name: Set up Jaeger
|
|
||||||
run: |
|
|
||||||
# Jaeger is set up on Linux through the setup-tracing action. If you update Jaeger here, don't forget to
|
|
||||||
# update the version set in .github/actions/setup-tracing/action.yml.
|
|
||||||
Invoke-WebRequest -Uri "https://github.com/jaegertracing/jaeger/releases/download/v1.46.0/jaeger-1.46.0-windows-amd64.tar.gz" -OutFile ".\jaeger-1.46.0-windows-amd64.tar.gz"
|
|
||||||
tar -zxvf ".\jaeger-1.46.0-windows-amd64.tar.gz"
|
|
||||||
Start-Process '.\jaeger-1.46.0-windows-amd64\jaeger-all-in-one.exe'
|
|
||||||
echo "OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
|
||||||
shell: pwsh
|
|
||||||
-
|
|
||||||
name: Env
|
|
||||||
run: |
|
|
||||||
Get-ChildItem Env: | Out-String
|
|
||||||
-
|
|
||||||
name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: build-${{ inputs.storage }}-${{ inputs.os }}
|
|
||||||
path: ${{ env.BIN_OUT }}
|
|
||||||
-
|
|
||||||
name: Init
|
|
||||||
run: |
|
|
||||||
New-Item -ItemType "directory" -Path "bundles"
|
|
||||||
If ("${{ inputs.os }}" -eq "windows-2019") {
|
|
||||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2019 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
|
||||||
} ElseIf ("${{ inputs.os }}" -eq "windows-2022") {
|
|
||||||
echo "WINDOWS_BASE_IMAGE_TAG=${{ env.WINDOWS_BASE_TAG_2022 }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
|
||||||
}
|
|
||||||
Write-Output "${{ env.BIN_OUT }}" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
|
||||||
|
|
||||||
$testName = ([System.BitConverter]::ToString((New-Object System.Security.Cryptography.SHA256Managed).ComputeHash([System.Text.Encoding]::UTF8.GetBytes("${{ matrix.test }}"))) -replace '-').ToLower()
|
|
||||||
echo "TESTREPORTS_NAME=$testName" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
|
||||||
-
|
|
||||||
# removes docker service that is currently installed on the runner. we
|
|
||||||
# could use Uninstall-Package but not yet available on Windows runners.
|
|
||||||
# more info: https://github.com/actions/virtual-environments/blob/d3a5bad25f3b4326c5666bab0011ac7f1beec95e/images/win/scripts/Installers/Install-Docker.ps1#L11
|
|
||||||
name: Removing current daemon
|
|
||||||
run: |
|
|
||||||
if (Get-Service docker -ErrorAction SilentlyContinue) {
|
|
||||||
$dockerVersion = (docker version -f "{{.Server.Version}}")
|
|
||||||
Write-Host "Current installed Docker version: $dockerVersion"
|
|
||||||
# remove service
|
|
||||||
Stop-Service -Force -Name docker
|
|
||||||
Remove-Service -Name docker
|
|
||||||
# removes event log entry. we could use "Remove-EventLog -LogName -Source docker"
|
|
||||||
# but this cmd is not available atm
|
|
||||||
$ErrorActionPreference = "SilentlyContinue"
|
|
||||||
& reg delete "HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Application\docker" /f 2>&1 | Out-Null
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
Write-Host "Service removed"
|
|
||||||
}
|
|
||||||
-
|
|
||||||
name: Starting containerd
|
|
||||||
if: matrix.runtime == 'containerd'
|
|
||||||
run: |
|
|
||||||
Write-Host "Generating config"
|
|
||||||
& "${{ env.BIN_OUT }}\containerd.exe" config default | Out-File "$env:TEMP\ctn.toml" -Encoding ascii
|
|
||||||
Write-Host "Creating service"
|
|
||||||
New-Item -ItemType Directory "$env:TEMP\ctn-root" -ErrorAction SilentlyContinue | Out-Null
|
|
||||||
New-Item -ItemType Directory "$env:TEMP\ctn-state" -ErrorAction SilentlyContinue | Out-Null
|
|
||||||
Start-Process -Wait "${{ env.BIN_OUT }}\containerd.exe" `
|
|
||||||
-ArgumentList "--log-level=debug", `
|
|
||||||
"--config=$env:TEMP\ctn.toml", `
|
|
||||||
"--address=\\.\pipe\containerd-containerd", `
|
|
||||||
"--root=$env:TEMP\ctn-root", `
|
|
||||||
"--state=$env:TEMP\ctn-state", `
|
|
||||||
"--log-file=$env:TEMP\ctn.log", `
|
|
||||||
"--register-service"
|
|
||||||
Write-Host "Starting service"
|
|
||||||
Start-Service -Name containerd
|
|
||||||
Start-Sleep -Seconds 5
|
|
||||||
Write-Host "Service started successfully!"
|
|
||||||
-
|
|
||||||
name: Starting test daemon
|
|
||||||
run: |
|
|
||||||
Write-Host "Creating service"
|
|
||||||
If ("${{ matrix.runtime }}" -eq "containerd") {
|
|
||||||
$runtimeArg="--containerd=\\.\pipe\containerd-containerd"
|
|
||||||
echo "DOCKER_WINDOWS_CONTAINERD_RUNTIME=1" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
|
||||||
}
|
|
||||||
New-Item -ItemType Directory "$env:TEMP\moby-root" -ErrorAction SilentlyContinue | Out-Null
|
|
||||||
New-Item -ItemType Directory "$env:TEMP\moby-exec" -ErrorAction SilentlyContinue | Out-Null
|
|
||||||
Start-Process -Wait -NoNewWindow "${{ env.BIN_OUT }}\dockerd" `
|
|
||||||
-ArgumentList $runtimeArg, "--debug", `
|
|
||||||
"--host=npipe:////./pipe/docker_engine", `
|
|
||||||
"--data-root=$env:TEMP\moby-root", `
|
|
||||||
"--exec-root=$env:TEMP\moby-exec", `
|
|
||||||
"--pidfile=$env:TEMP\docker.pid", `
|
|
||||||
"--register-service"
|
|
||||||
If ("${{ inputs.storage }}" -eq "snapshotter") {
|
|
||||||
# Make the env-var visible to the service-managed dockerd, as there's no CLI flag for this option.
|
|
||||||
& reg add "HKLM\SYSTEM\CurrentControlSet\Services\docker" /v Environment /t REG_MULTI_SZ /s '@' /d TEST_INTEGRATION_USE_SNAPSHOTTER=1
|
|
||||||
echo "TEST_INTEGRATION_USE_SNAPSHOTTER=1" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
|
||||||
}
|
|
||||||
Write-Host "Starting service"
|
|
||||||
Start-Service -Name docker
|
|
||||||
Write-Host "Service started successfully!"
|
|
||||||
-
|
|
||||||
name: Waiting for test daemon to start
|
|
||||||
run: |
|
|
||||||
$tries=20
|
|
||||||
Write-Host "Waiting for the test daemon to start..."
|
|
||||||
While ($true) {
|
|
||||||
$ErrorActionPreference = "SilentlyContinue"
|
|
||||||
& "${{ env.BIN_OUT }}\docker" version
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
If ($LastExitCode -eq 0) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
$tries--
|
|
||||||
If ($tries -le 0) {
|
|
||||||
Throw "Failed to get a response from the daemon"
|
|
||||||
}
|
|
||||||
Write-Host -NoNewline "."
|
|
||||||
Start-Sleep -Seconds 1
|
|
||||||
}
|
|
||||||
Write-Host "Test daemon started and replied!"
|
|
||||||
env:
|
|
||||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
|
||||||
-
|
|
||||||
name: Docker info
|
|
||||||
run: |
|
|
||||||
& "${{ env.BIN_OUT }}\docker" info
|
|
||||||
env:
|
|
||||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
|
||||||
-
|
|
||||||
name: Building contrib/busybox
|
|
||||||
run: |
|
|
||||||
& "${{ env.BIN_OUT }}\docker" build -t busybox `
|
|
||||||
--build-arg WINDOWS_BASE_IMAGE `
|
|
||||||
--build-arg WINDOWS_BASE_IMAGE_TAG `
|
|
||||||
.\contrib\busybox\
|
|
||||||
env:
|
|
||||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
|
||||||
-
|
|
||||||
name: List images
|
|
||||||
run: |
|
|
||||||
& "${{ env.BIN_OUT }}\docker" images
|
|
||||||
env:
|
|
||||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
|
||||||
-
|
|
||||||
name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
-
|
|
||||||
name: Test integration
|
|
||||||
if: matrix.test == './...'
|
|
||||||
run: |
|
|
||||||
.\hack\make.ps1 -TestIntegration
|
|
||||||
env:
|
|
||||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
|
||||||
GO111MODULE: "off"
|
|
||||||
TEST_CLIENT_BINARY: ${{ env.BIN_OUT }}\docker
|
|
||||||
-
|
|
||||||
name: Test integration-cli
|
|
||||||
if: matrix.test != './...'
|
|
||||||
run: |
|
|
||||||
.\hack\make.ps1 -TestIntegrationCli
|
|
||||||
env:
|
|
||||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
|
||||||
GO111MODULE: "off"
|
|
||||||
TEST_CLIENT_BINARY: ${{ env.BIN_OUT }}\docker
|
|
||||||
INTEGRATION_TESTRUN: ${{ matrix.test }}
|
|
||||||
-
|
|
||||||
name: Send to Codecov
|
|
||||||
if: inputs.send_coverage
|
|
||||||
uses: codecov/codecov-action@v4
|
|
||||||
with:
|
|
||||||
working-directory: ${{ env.GOPATH }}\src\github.com\docker\docker
|
|
||||||
directory: bundles
|
|
||||||
env_vars: RUNNER_OS
|
|
||||||
flags: integration,${{ matrix.runtime }}
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }} # used to upload coverage reports: https://github.com/moby/buildkit/pull/4660#issue-2142122533
|
|
||||||
-
|
|
||||||
name: Docker info
|
|
||||||
run: |
|
|
||||||
& "${{ env.BIN_OUT }}\docker" info
|
|
||||||
env:
|
|
||||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
|
||||||
-
|
|
||||||
name: Stop containerd
|
|
||||||
if: always() && matrix.runtime == 'containerd'
|
|
||||||
run: |
|
|
||||||
$ErrorActionPreference = "SilentlyContinue"
|
|
||||||
Stop-Service -Force -Name containerd
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
-
|
|
||||||
name: Containerd logs
|
|
||||||
if: always() && matrix.runtime == 'containerd'
|
|
||||||
run: |
|
|
||||||
Copy-Item "$env:TEMP\ctn.log" -Destination ".\bundles\containerd.log"
|
|
||||||
Get-Content "$env:TEMP\ctn.log" | Out-Host
|
|
||||||
-
|
|
||||||
name: Stop daemon
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
$ErrorActionPreference = "SilentlyContinue"
|
|
||||||
Stop-Service -Force -Name docker
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
-
|
|
||||||
# as the daemon is registered as a service we have to check the event
|
|
||||||
# logs against the docker provider.
|
|
||||||
name: Daemon event logs
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
Get-WinEvent -ea SilentlyContinue `
|
|
||||||
-FilterHashtable @{ProviderName= "docker"; LogName = "application"} |
|
|
||||||
Sort-Object @{Expression="TimeCreated";Descending=$false} |
|
|
||||||
ForEach-Object {"$($_.TimeCreated.ToUniversalTime().ToString("o")) [$($_.LevelDisplayName)] $($_.Message)"} |
|
|
||||||
Tee-Object -file ".\bundles\daemon.log"
|
|
||||||
-
|
|
||||||
name: Download Jaeger traces
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
Invoke-WebRequest `
|
|
||||||
-Uri "http://127.0.0.1:16686/api/traces?service=integration-test-client" `
|
|
||||||
-OutFile ".\bundles\jaeger-trace.json"
|
|
||||||
-
|
|
||||||
name: Upload reports
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.os }}-${{ inputs.storage }}-integration-reports-${{ matrix.runtime }}-${{ env.TESTREPORTS_NAME }}
|
|
||||||
path: ${{ env.GOPATH }}\src\github.com\docker\docker\bundles\*
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
integration-test-report:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
continue-on-error: ${{ inputs.storage == 'snapshotter' && github.event_name != 'pull_request' }}
|
|
||||||
if: always()
|
|
||||||
needs:
|
|
||||||
- integration-test
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
storage:
|
|
||||||
- ${{ inputs.storage }}
|
|
||||||
runtime:
|
|
||||||
- builtin
|
|
||||||
- containerd
|
|
||||||
exclude:
|
|
||||||
- storage: snapshotter
|
|
||||||
runtime: builtin
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
-
|
|
||||||
name: Download reports
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: /tmp/reports
|
|
||||||
pattern: ${{ inputs.os }}-${{ inputs.storage }}-integration-reports-${{ matrix.runtime }}-*
|
|
||||||
merge-multiple: true
|
|
||||||
-
|
|
||||||
name: Install teststat
|
|
||||||
run: |
|
|
||||||
go install github.com/vearutop/teststat@${{ env.TESTSTAT_VERSION }}
|
|
||||||
-
|
|
||||||
name: Create summary
|
|
||||||
run: |
|
|
||||||
find /tmp/reports -type f -name '*-go-test-report.json' -exec teststat -markdown {} \+ >> $GITHUB_STEP_SUMMARY
|
|
191
.github/workflows/bin-image.yml
vendored
191
.github/workflows/bin-image.yml
vendored
|
@ -1,191 +0,0 @@
|
||||||
name: bin-image
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
- '[0-9]+.[0-9]+'
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
env:
|
|
||||||
MOBYBIN_REPO_SLUG: moby/moby-bin
|
|
||||||
DOCKER_GITCOMMIT: ${{ github.sha }}
|
|
||||||
VERSION: ${{ github.ref }}
|
|
||||||
PLATFORM: Moby Engine - Nightly
|
|
||||||
PRODUCT: moby-bin
|
|
||||||
PACKAGER_NAME: The Moby Project
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate-dco:
|
|
||||||
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
|
||||||
uses: ./.github/workflows/.dco.yml
|
|
||||||
|
|
||||||
prepare:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
outputs:
|
|
||||||
platforms: ${{ steps.platforms.outputs.matrix }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
${{ env.MOBYBIN_REPO_SLUG }}
|
|
||||||
### versioning strategy
|
|
||||||
## push semver tag v23.0.0
|
|
||||||
# moby/moby-bin:23.0.0
|
|
||||||
# moby/moby-bin:latest
|
|
||||||
## push semver prelease tag v23.0.0-beta.1
|
|
||||||
# moby/moby-bin:23.0.0-beta.1
|
|
||||||
## push on master
|
|
||||||
# moby/moby-bin:master
|
|
||||||
## push on 23.0 branch
|
|
||||||
# moby/moby-bin:23.0
|
|
||||||
## any push
|
|
||||||
# moby/moby-bin:sha-ad132f5
|
|
||||||
tags: |
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=pr
|
|
||||||
type=sha
|
|
||||||
-
|
|
||||||
name: Rename meta bake definition file
|
|
||||||
# see https://github.com/docker/metadata-action/issues/381#issuecomment-1918607161
|
|
||||||
run: |
|
|
||||||
bakeFile="${{ steps.meta.outputs.bake-file }}"
|
|
||||||
mv "${bakeFile#cwd://}" "/tmp/bake-meta.json"
|
|
||||||
-
|
|
||||||
name: Upload meta bake definition
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: bake-meta
|
|
||||||
path: /tmp/bake-meta.json
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
-
|
|
||||||
name: Create platforms matrix
|
|
||||||
id: platforms
|
|
||||||
run: |
|
|
||||||
echo "matrix=$(docker buildx bake bin-image-cross --print | jq -cr '.target."bin-image-cross".platforms')" >>${GITHUB_OUTPUT}
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
needs:
|
|
||||||
- validate-dco
|
|
||||||
- prepare
|
|
||||||
if: always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled')
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
platform: ${{ fromJson(needs.prepare.outputs.platforms) }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
-
|
|
||||||
name: Download meta bake definition
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: bake-meta
|
|
||||||
path: /tmp
|
|
||||||
-
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Login to Docker Hub
|
|
||||||
if: github.event_name != 'pull_request' && github.repository == 'moby/moby'
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_MOBYBIN_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_MOBYBIN_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Build
|
|
||||||
id: bake
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
files: |
|
|
||||||
./docker-bake.hcl
|
|
||||||
/tmp/bake-meta.json
|
|
||||||
targets: bin-image
|
|
||||||
set: |
|
|
||||||
*.platform=${{ matrix.platform }}
|
|
||||||
*.output=type=image,name=${{ env.MOBYBIN_REPO_SLUG }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' && github.repository == 'moby/moby' }}
|
|
||||||
*.tags=
|
|
||||||
-
|
|
||||||
name: Export digest
|
|
||||||
if: github.event_name != 'pull_request' && github.repository == 'moby/moby'
|
|
||||||
run: |
|
|
||||||
mkdir -p /tmp/digests
|
|
||||||
digest="${{ fromJSON(steps.bake.outputs.metadata)['bin-image']['containerimage.digest'] }}"
|
|
||||||
touch "/tmp/digests/${digest#sha256:}"
|
|
||||||
-
|
|
||||||
name: Upload digest
|
|
||||||
if: github.event_name != 'pull_request' && github.repository == 'moby/moby'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: digests-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: /tmp/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
merge:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
needs:
|
|
||||||
- build
|
|
||||||
if: always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && github.event_name != 'pull_request' && github.repository == 'moby/moby'
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Download meta bake definition
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: bake-meta
|
|
||||||
path: /tmp
|
|
||||||
-
|
|
||||||
name: Download digests
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: /tmp/digests
|
|
||||||
pattern: digests-*
|
|
||||||
merge-multiple: true
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_MOBYBIN_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_MOBYBIN_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Create manifest list and push
|
|
||||||
working-directory: /tmp/digests
|
|
||||||
run: |
|
|
||||||
set -x
|
|
||||||
docker buildx imagetools create $(jq -cr '.target."docker-metadata-action".tags | map("-t " + .) | join(" ")' /tmp/bake-meta.json) \
|
|
||||||
$(printf '${{ env.MOBYBIN_REPO_SLUG }}@sha256:%s ' *)
|
|
||||||
-
|
|
||||||
name: Inspect image
|
|
||||||
run: |
|
|
||||||
set -x
|
|
||||||
docker buildx imagetools inspect ${{ env.MOBYBIN_REPO_SLUG }}:$(jq -cr '.target."docker-metadata-action".args.DOCKER_META_VERSION' /tmp/bake-meta.json)
|
|
139
.github/workflows/buildkit.yml
vendored
139
.github/workflows/buildkit.yml
vendored
|
@ -1,139 +0,0 @@
|
||||||
name: buildkit
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
- '[0-9]+.[0-9]+'
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
env:
|
|
||||||
GO_VERSION: "1.21.9"
|
|
||||||
DESTDIR: ./build
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate-dco:
|
|
||||||
uses: ./.github/workflows/.dco.yml
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
needs:
|
|
||||||
- validate-dco
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Build
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
targets: binary
|
|
||||||
-
|
|
||||||
name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: binary
|
|
||||||
path: ${{ env.DESTDIR }}
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
timeout-minutes: 120
|
|
||||||
needs:
|
|
||||||
- build
|
|
||||||
env:
|
|
||||||
TEST_IMAGE_BUILD: "0"
|
|
||||||
TEST_IMAGE_ID: "buildkit-tests"
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
worker:
|
|
||||||
- dockerd
|
|
||||||
- dockerd-containerd
|
|
||||||
pkg:
|
|
||||||
- client
|
|
||||||
- cmd/buildctl
|
|
||||||
- solver
|
|
||||||
- frontend
|
|
||||||
- frontend/dockerfile
|
|
||||||
typ:
|
|
||||||
- integration
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Prepare
|
|
||||||
run: |
|
|
||||||
disabledFeatures="cache_backend_azblob,cache_backend_s3"
|
|
||||||
if [ "${{ matrix.worker }}" = "dockerd" ]; then
|
|
||||||
disabledFeatures="${disabledFeatures},merge_diff"
|
|
||||||
fi
|
|
||||||
echo "BUILDKIT_TEST_DISABLE_FEATURES=${disabledFeatures}" >> $GITHUB_ENV
|
|
||||||
# Expose `ACTIONS_RUNTIME_TOKEN` and `ACTIONS_CACHE_URL`, which is used
|
|
||||||
# in BuildKit's test suite to skip/unskip cache exporters:
|
|
||||||
# https://github.com/moby/buildkit/blob/567a99433ca23402d5e9b9f9124005d2e59b8861/client/client_test.go#L5407-L5411
|
|
||||||
-
|
|
||||||
name: Expose GitHub Runtime
|
|
||||||
uses: crazy-max/ghaction-github-runtime@v3
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
path: moby
|
|
||||||
-
|
|
||||||
name: BuildKit ref
|
|
||||||
run: |
|
|
||||||
echo "$(./hack/buildkit-ref)" >> $GITHUB_ENV
|
|
||||||
working-directory: moby
|
|
||||||
-
|
|
||||||
name: Checkout BuildKit ${{ env.BUILDKIT_REF }}
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: ${{ env.BUILDKIT_REPO }}
|
|
||||||
ref: ${{ env.BUILDKIT_REF }}
|
|
||||||
path: buildkit
|
|
||||||
-
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Download binary artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: binary
|
|
||||||
path: ./buildkit/build/moby/
|
|
||||||
-
|
|
||||||
name: Update daemon.json
|
|
||||||
run: |
|
|
||||||
sudo rm -f /etc/docker/daemon.json
|
|
||||||
sudo service docker restart
|
|
||||||
docker version
|
|
||||||
docker info
|
|
||||||
-
|
|
||||||
name: Build test image
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
workdir: ./buildkit
|
|
||||||
targets: integration-tests
|
|
||||||
set: |
|
|
||||||
*.output=type=docker,name=${{ env.TEST_IMAGE_ID }}
|
|
||||||
-
|
|
||||||
name: Test
|
|
||||||
run: |
|
|
||||||
./hack/test ${{ matrix.typ }}
|
|
||||||
env:
|
|
||||||
CONTEXT: "."
|
|
||||||
TEST_DOCKERD: "1"
|
|
||||||
TEST_DOCKERD_BINARY: "./build/moby/dockerd"
|
|
||||||
TESTPKGS: "./${{ matrix.pkg }}"
|
|
||||||
TESTFLAGS: "-v --parallel=1 --timeout=30m --run=//worker=${{ matrix.worker }}$"
|
|
||||||
working-directory: buildkit
|
|
113
.github/workflows/ci.yml
vendored
113
.github/workflows/ci.yml
vendored
|
@ -1,113 +0,0 @@
|
||||||
name: ci
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
- '[0-9]+.[0-9]+'
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
env:
|
|
||||||
DESTDIR: ./build
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate-dco:
|
|
||||||
uses: ./.github/workflows/.dco.yml
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
needs:
|
|
||||||
- validate-dco
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
target:
|
|
||||||
- binary
|
|
||||||
- dynbinary
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Build
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
targets: ${{ matrix.target }}
|
|
||||||
-
|
|
||||||
name: List artifacts
|
|
||||||
run: |
|
|
||||||
tree -nh ${{ env.DESTDIR }}
|
|
||||||
-
|
|
||||||
name: Check artifacts
|
|
||||||
run: |
|
|
||||||
find ${{ env.DESTDIR }} -type f -exec file -e ascii -- {} +
|
|
||||||
|
|
||||||
prepare-cross:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- validate-dco
|
|
||||||
outputs:
|
|
||||||
matrix: ${{ steps.platforms.outputs.matrix }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Create matrix
|
|
||||||
id: platforms
|
|
||||||
run: |
|
|
||||||
matrix="$(docker buildx bake binary-cross --print | jq -cr '.target."binary-cross".platforms')"
|
|
||||||
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Show matrix
|
|
||||||
run: |
|
|
||||||
echo ${{ steps.platforms.outputs.matrix }}
|
|
||||||
|
|
||||||
cross:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
needs:
|
|
||||||
- validate-dco
|
|
||||||
- prepare-cross
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
platform: ${{ fromJson(needs.prepare-cross.outputs.matrix) }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
-
|
|
||||||
name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Build
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
targets: all
|
|
||||||
set: |
|
|
||||||
*.platform=${{ matrix.platform }}
|
|
||||||
-
|
|
||||||
name: List artifacts
|
|
||||||
run: |
|
|
||||||
tree -nh ${{ env.DESTDIR }}
|
|
||||||
-
|
|
||||||
name: Check artifacts
|
|
||||||
run: |
|
|
||||||
find ${{ env.DESTDIR }} -type f -exec file -e ascii -- {} +
|
|
177
.github/workflows/test.yml
vendored
177
.github/workflows/test.yml
vendored
|
@ -1,177 +0,0 @@
|
||||||
name: test
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
- '[0-9]+.[0-9]+'
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
env:
|
|
||||||
GO_VERSION: "1.21.9"
|
|
||||||
GIT_PAGER: "cat"
|
|
||||||
PAGER: "cat"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate-dco:
|
|
||||||
uses: ./.github/workflows/.dco.yml
|
|
||||||
|
|
||||||
build-dev:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
needs:
|
|
||||||
- validate-dco
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
mode:
|
|
||||||
- ""
|
|
||||||
- systemd
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Prepare
|
|
||||||
run: |
|
|
||||||
if [ "${{ matrix.mode }}" = "systemd" ]; then
|
|
||||||
echo "SYSTEMD=true" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Build dev image
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
targets: dev
|
|
||||||
set: |
|
|
||||||
*.cache-from=type=gha,scope=dev${{ matrix.mode }}
|
|
||||||
*.cache-to=type=gha,scope=dev${{ matrix.mode }},mode=max
|
|
||||||
*.output=type=cacheonly
|
|
||||||
|
|
||||||
test:
|
|
||||||
needs:
|
|
||||||
- build-dev
|
|
||||||
- validate-dco
|
|
||||||
uses: ./.github/workflows/.test.yml
|
|
||||||
secrets: inherit
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
storage:
|
|
||||||
- graphdriver
|
|
||||||
- snapshotter
|
|
||||||
with:
|
|
||||||
storage: ${{ matrix.storage }}
|
|
||||||
|
|
||||||
validate-prepare:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
needs:
|
|
||||||
- validate-dco
|
|
||||||
outputs:
|
|
||||||
matrix: ${{ steps.scripts.outputs.matrix }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Create matrix
|
|
||||||
id: scripts
|
|
||||||
run: |
|
|
||||||
scripts=$(cd ./hack/validate && jq -nc '$ARGS.positional - ["all", "default", "dco"] | map(select(test("[.]")|not)) + ["generate-files"]' --args *)
|
|
||||||
echo "matrix=$scripts" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Show matrix
|
|
||||||
run: |
|
|
||||||
echo ${{ steps.scripts.outputs.matrix }}
|
|
||||||
|
|
||||||
validate:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
timeout-minutes: 120
|
|
||||||
needs:
|
|
||||||
- validate-prepare
|
|
||||||
- build-dev
|
|
||||||
strategy:
|
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
|
||||||
script: ${{ fromJson(needs.validate-prepare.outputs.matrix) }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
-
|
|
||||||
name: Set up runner
|
|
||||||
uses: ./.github/actions/setup-runner
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Build dev image
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
targets: dev
|
|
||||||
set: |
|
|
||||||
dev.cache-from=type=gha,scope=dev
|
|
||||||
-
|
|
||||||
name: Validate
|
|
||||||
run: |
|
|
||||||
make -o build validate-${{ matrix.script }}
|
|
||||||
|
|
||||||
smoke-prepare:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
needs:
|
|
||||||
- validate-dco
|
|
||||||
outputs:
|
|
||||||
matrix: ${{ steps.platforms.outputs.matrix }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Create matrix
|
|
||||||
id: platforms
|
|
||||||
run: |
|
|
||||||
matrix="$(docker buildx bake binary-smoketest --print | jq -cr '.target."binary-smoketest".platforms')"
|
|
||||||
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Show matrix
|
|
||||||
run: |
|
|
||||||
echo ${{ steps.platforms.outputs.matrix }}
|
|
||||||
|
|
||||||
smoke:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
needs:
|
|
||||||
- smoke-prepare
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
platform: ${{ fromJson(needs.smoke-prepare.outputs.matrix) }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
-
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Test
|
|
||||||
uses: docker/bake-action@v4
|
|
||||||
with:
|
|
||||||
targets: binary-smoketest
|
|
||||||
set: |
|
|
||||||
*.platform=${{ matrix.platform }}
|
|
62
.github/workflows/validate-pr.yml
vendored
62
.github/workflows/validate-pr.yml
vendored
|
@ -1,62 +0,0 @@
|
||||||
name: validate-pr
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, edited, labeled, unlabeled]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check-area-label:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- name: Missing `area/` label
|
|
||||||
if: contains(join(github.event.pull_request.labels.*.name, ','), 'impact/') && !contains(join(github.event.pull_request.labels.*.name, ','), 'area/')
|
|
||||||
run: |
|
|
||||||
echo "::error::Every PR with an 'impact/*' label should also have an 'area/*' label"
|
|
||||||
exit 1
|
|
||||||
- name: OK
|
|
||||||
run: exit 0
|
|
||||||
|
|
||||||
check-changelog:
|
|
||||||
if: contains(join(github.event.pull_request.labels.*.name, ','), 'impact/')
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
env:
|
|
||||||
PR_BODY: |
|
|
||||||
${{ github.event.pull_request.body }}
|
|
||||||
steps:
|
|
||||||
- name: Check changelog description
|
|
||||||
run: |
|
|
||||||
# Extract the `markdown changelog` note code block
|
|
||||||
block=$(echo -n "$PR_BODY" | tr -d '\r' | awk '/^```markdown changelog$/{flag=1;next}/^```$/{flag=0}flag')
|
|
||||||
|
|
||||||
# Strip empty lines
|
|
||||||
desc=$(echo "$block" | awk NF)
|
|
||||||
|
|
||||||
if [ -z "$desc" ]; then
|
|
||||||
echo "::error::Changelog section is empty. Please provide a description for the changelog."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
len=$(echo -n "$desc" | wc -c)
|
|
||||||
if [[ $len -le 6 ]]; then
|
|
||||||
echo "::error::Description looks too short: $desc"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "This PR will be included in the release notes with the following note:"
|
|
||||||
echo "$desc"
|
|
||||||
|
|
||||||
check-pr-branch:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
env:
|
|
||||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
||||||
steps:
|
|
||||||
# Backports or PR that target a release branch directly should mention the target branch in the title, for example:
|
|
||||||
# [X.Y backport] Some change that needs backporting to X.Y
|
|
||||||
# [X.Y] Change directly targeting the X.Y branch
|
|
||||||
- name: Get branch from PR title
|
|
||||||
id: title_branch
|
|
||||||
run: echo "$PR_TITLE" | sed -n 's/^\[\([0-9]*\.[0-9]*\)[^]]*\].*/branch=\1/p' >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Check release branch
|
|
||||||
if: github.event.pull_request.base.ref != steps.title_branch.outputs.branch && !(github.event.pull_request.base.ref == 'master' && steps.title_branch.outputs.branch == '')
|
|
||||||
run: echo "::error::PR title suggests targetting the ${{ steps.title_branch.outputs.branch }} branch, but is opened against ${{ github.event.pull_request.base.ref }}" && exit 1
|
|
33
.github/workflows/windows-2019.yml
vendored
33
.github/workflows/windows-2019.yml
vendored
|
@ -1,33 +0,0 @@
|
||||||
name: windows-2019
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 10 * * *'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate-dco:
|
|
||||||
uses: ./.github/workflows/.dco.yml
|
|
||||||
|
|
||||||
test-prepare:
|
|
||||||
uses: ./.github/workflows/.test-prepare.yml
|
|
||||||
needs:
|
|
||||||
- validate-dco
|
|
||||||
|
|
||||||
run:
|
|
||||||
needs:
|
|
||||||
- test-prepare
|
|
||||||
uses: ./.github/workflows/.windows.yml
|
|
||||||
secrets: inherit
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
storage: ${{ fromJson(needs.test-prepare.outputs.matrix) }}
|
|
||||||
with:
|
|
||||||
os: windows-2019
|
|
||||||
storage: ${{ matrix.storage }}
|
|
||||||
send_coverage: false
|
|
36
.github/workflows/windows-2022.yml
vendored
36
.github/workflows/windows-2022.yml
vendored
|
@ -1,36 +0,0 @@
|
||||||
name: windows-2022
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
- '[0-9]+.[0-9]+'
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate-dco:
|
|
||||||
uses: ./.github/workflows/.dco.yml
|
|
||||||
|
|
||||||
test-prepare:
|
|
||||||
uses: ./.github/workflows/.test-prepare.yml
|
|
||||||
needs:
|
|
||||||
- validate-dco
|
|
||||||
|
|
||||||
run:
|
|
||||||
needs:
|
|
||||||
- test-prepare
|
|
||||||
uses: ./.github/workflows/.windows.yml
|
|
||||||
secrets: inherit
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
storage: ${{ fromJson(needs.test-prepare.outputs.matrix) }}
|
|
||||||
with:
|
|
||||||
os: windows-2022
|
|
||||||
storage: ${{ matrix.storage }}
|
|
||||||
send_coverage: true
|
|
49
.gitignore
vendored
49
.gitignore
vendored
|
@ -1,28 +1,33 @@
|
||||||
# If you want to ignore files created by your editor/tools, please consider a
|
# Docker project generated files to ignore
|
||||||
# [global .gitignore](https://help.github.com/articles/ignoring-files).
|
# if you want to ignore files created by your editor/tools,
|
||||||
|
# please consider a global .gitignore https://help.github.com/articles/ignoring-files
|
||||||
*~
|
*.exe
|
||||||
*.bak
|
*.exe~
|
||||||
*.orig
|
*.orig
|
||||||
|
*.test
|
||||||
.*.swp
|
.*.swp
|
||||||
.DS_Store
|
.DS_Store
|
||||||
thumbs.db
|
# a .bashrc may be added to customize the build environment
|
||||||
|
|
||||||
# local repository customization
|
|
||||||
.envrc
|
|
||||||
.bashrc
|
.bashrc
|
||||||
.editorconfig
|
.editorconfig
|
||||||
|
.gopath/
|
||||||
# build artifacts
|
.go-pkg-cache/
|
||||||
|
autogen/
|
||||||
bundles/
|
bundles/
|
||||||
cli/winresources/*/*.syso
|
cmd/dockerd/dockerd
|
||||||
cli/winresources/*/winres.json
|
cmd/docker/docker
|
||||||
contrib/builder/rpm/*/changelog
|
dockerversion/version_autogen.go
|
||||||
|
dockerversion/version_autogen_unix.go
|
||||||
# ci artifacts
|
docs/AWS_S3_BUCKET
|
||||||
*.exe
|
docs/GITCOMMIT
|
||||||
*.gz
|
docs/GIT_BRANCH
|
||||||
go-test-report.json
|
docs/VERSION
|
||||||
junit-report.xml
|
docs/_build
|
||||||
profile.out
|
docs/_static
|
||||||
test.main
|
docs/_templates
|
||||||
|
docs/changed-files
|
||||||
|
# generated by man/md2man-all.sh
|
||||||
|
man/man1
|
||||||
|
man/man5
|
||||||
|
man/man8
|
||||||
|
vendor/pkg/
|
||||||
|
|
137
.golangci.yml
137
.golangci.yml
|
@ -1,137 +0,0 @@
|
||||||
linters:
|
|
||||||
enable:
|
|
||||||
- depguard
|
|
||||||
- dupword # Checks for duplicate words in the source code.
|
|
||||||
- goimports
|
|
||||||
- gosec
|
|
||||||
- gosimple
|
|
||||||
- govet
|
|
||||||
- importas
|
|
||||||
- ineffassign
|
|
||||||
- misspell
|
|
||||||
- revive
|
|
||||||
- staticcheck
|
|
||||||
- typecheck
|
|
||||||
- unconvert
|
|
||||||
- unused
|
|
||||||
|
|
||||||
disable:
|
|
||||||
- errcheck
|
|
||||||
|
|
||||||
run:
|
|
||||||
concurrency: 2
|
|
||||||
modules-download-mode: vendor
|
|
||||||
|
|
||||||
skip-dirs:
|
|
||||||
- docs
|
|
||||||
|
|
||||||
linters-settings:
|
|
||||||
dupword:
|
|
||||||
ignore:
|
|
||||||
- "true" # some tests use this as expected output
|
|
||||||
- "false" # some tests use this as expected output
|
|
||||||
- "root" # for tests using "ls" output with files owned by "root:root"
|
|
||||||
importas:
|
|
||||||
# Do not allow unaliased imports of aliased packages.
|
|
||||||
no-unaliased: true
|
|
||||||
|
|
||||||
alias:
|
|
||||||
# Enforce alias to prevent it accidentally being used instead of our
|
|
||||||
# own errdefs package (or vice-versa).
|
|
||||||
- pkg: github.com/containerd/containerd/errdefs
|
|
||||||
alias: cerrdefs
|
|
||||||
- pkg: github.com/opencontainers/image-spec/specs-go/v1
|
|
||||||
alias: ocispec
|
|
||||||
|
|
||||||
govet:
|
|
||||||
check-shadowing: false
|
|
||||||
depguard:
|
|
||||||
rules:
|
|
||||||
main:
|
|
||||||
deny:
|
|
||||||
- pkg: io/ioutil
|
|
||||||
desc: The io/ioutil package has been deprecated, see https://go.dev/doc/go1.16#ioutil
|
|
||||||
- pkg: "github.com/stretchr/testify/assert"
|
|
||||||
desc: Use "gotest.tools/v3/assert" instead
|
|
||||||
- pkg: "github.com/stretchr/testify/require"
|
|
||||||
desc: Use "gotest.tools/v3/assert" instead
|
|
||||||
- pkg: "github.com/stretchr/testify/suite"
|
|
||||||
desc: Do not use
|
|
||||||
revive:
|
|
||||||
rules:
|
|
||||||
# FIXME make sure all packages have a description. Currently, there's many packages without.
|
|
||||||
- name: package-comments
|
|
||||||
disabled: true
|
|
||||||
issues:
|
|
||||||
# The default exclusion rules are a bit too permissive, so copying the relevant ones below
|
|
||||||
exclude-use-default: false
|
|
||||||
|
|
||||||
exclude-rules:
|
|
||||||
# We prefer to use an "exclude-list" so that new "default" exclusions are not
|
|
||||||
# automatically inherited. We can decide whether or not to follow upstream
|
|
||||||
# defaults when updating golang-ci-lint versions.
|
|
||||||
# Unfortunately, this means we have to copy the whole exclusion pattern, as
|
|
||||||
# (unlike the "include" option), the "exclude" option does not take exclusion
|
|
||||||
# ID's.
|
|
||||||
#
|
|
||||||
# These exclusion patterns are copied from the default excluses at:
|
|
||||||
# https://github.com/golangci/golangci-lint/blob/v1.46.2/pkg/config/issues.go#L10-L104
|
|
||||||
|
|
||||||
# EXC0001
|
|
||||||
- text: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*print(f|ln)?|os\\.(Un)?Setenv). is not checked"
|
|
||||||
linters:
|
|
||||||
- errcheck
|
|
||||||
# EXC0006
|
|
||||||
- text: "Use of unsafe calls should be audited"
|
|
||||||
linters:
|
|
||||||
- gosec
|
|
||||||
# EXC0007
|
|
||||||
- text: "Subprocess launch(ed with variable|ing should be audited)"
|
|
||||||
linters:
|
|
||||||
- gosec
|
|
||||||
# EXC0008
|
|
||||||
# TODO: evaluate these and fix where needed: G307: Deferring unsafe method "*os.File" on type "Close" (gosec)
|
|
||||||
- text: "(G104|G307)"
|
|
||||||
linters:
|
|
||||||
- gosec
|
|
||||||
# EXC0009
|
|
||||||
- text: "(Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)"
|
|
||||||
linters:
|
|
||||||
- gosec
|
|
||||||
# EXC0010
|
|
||||||
- text: "Potential file inclusion via variable"
|
|
||||||
linters:
|
|
||||||
- gosec
|
|
||||||
|
|
||||||
# Looks like the match in "EXC0007" above doesn't catch this one
|
|
||||||
# TODO: consider upstreaming this to golangci-lint's default exclusion rules
|
|
||||||
- text: "G204: Subprocess launched with a potential tainted input or cmd arguments"
|
|
||||||
linters:
|
|
||||||
- gosec
|
|
||||||
# Looks like the match in "EXC0009" above doesn't catch this one
|
|
||||||
# TODO: consider upstreaming this to golangci-lint's default exclusion rules
|
|
||||||
- text: "G306: Expect WriteFile permissions to be 0600 or less"
|
|
||||||
linters:
|
|
||||||
- gosec
|
|
||||||
|
|
||||||
# Exclude some linters from running on tests files.
|
|
||||||
- path: _test\.go
|
|
||||||
linters:
|
|
||||||
- errcheck
|
|
||||||
- gosec
|
|
||||||
|
|
||||||
# Suppress golint complaining about generated types in api/types/
|
|
||||||
- text: "type name will be used as (container|volume)\\.(Container|Volume).* by other packages, and that stutters; consider calling this"
|
|
||||||
path: "api/types/(volume|container)/"
|
|
||||||
linters:
|
|
||||||
- revive
|
|
||||||
# FIXME temporarily suppress these (see https://github.com/gotestyourself/gotest.tools/issues/272)
|
|
||||||
- text: "SA1019: (assert|cmp|is)\\.ErrorType is deprecated"
|
|
||||||
linters:
|
|
||||||
- staticcheck
|
|
||||||
|
|
||||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
|
||||||
max-issues-per-linter: 0
|
|
||||||
|
|
||||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
|
||||||
max-same-issues: 0
|
|
3357
CHANGELOG.md
Normal file
3357
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load diff
172
CONTRIBUTING.md
172
CONTRIBUTING.md
|
@ -1,14 +1,14 @@
|
||||||
# Contribute to the Moby Project
|
# Contributing to Docker
|
||||||
|
|
||||||
Want to hack on the Moby Project? Awesome! We have a contributor's guide that explains
|
Want to hack on Docker? Awesome! We have a contributor's guide that explains
|
||||||
[setting up a development environment and the contribution
|
[setting up a Docker development environment and the contribution
|
||||||
process](docs/contributing/).
|
process](https://docs.docker.com/opensource/project/who-written-for/).
|
||||||
|
|
||||||
[![Contributors guide](docs/static_files/contributors.png)](https://docs.docker.com/opensource/project/who-written-for/)
|
[![Contributors guide](docs/static_files/contributors.png)](https://docs.docker.com/opensource/project/who-written-for/)
|
||||||
|
|
||||||
This page contains information about reporting issues as well as some tips and
|
This page contains information about reporting issues as well as some tips and
|
||||||
guidelines useful to experienced open source contributors. Finally, make sure
|
guidelines useful to experienced open source contributors. Finally, make sure
|
||||||
you read our [community guidelines](#moby-community-guidelines) before you
|
you read our [community guidelines](#docker-community-guidelines) before you
|
||||||
start participating.
|
start participating.
|
||||||
|
|
||||||
## Topics
|
## Topics
|
||||||
|
@ -17,20 +17,20 @@ start participating.
|
||||||
* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
|
* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
|
||||||
* [Reporting Issues](#reporting-other-issues)
|
* [Reporting Issues](#reporting-other-issues)
|
||||||
* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
|
* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
|
||||||
* [Community Guidelines](#moby-community-guidelines)
|
* [Community Guidelines](#docker-community-guidelines)
|
||||||
|
|
||||||
## Reporting security issues
|
## Reporting security issues
|
||||||
|
|
||||||
The Moby maintainers take security seriously. If you discover a security
|
The Docker maintainers take security seriously. If you discover a security
|
||||||
issue, please bring it to their attention right away!
|
issue, please bring it to their attention right away!
|
||||||
|
|
||||||
Please **DO NOT** file a public issue, instead send your report privately to
|
Please **DO NOT** file a public issue, instead send your report privately to
|
||||||
[security@docker.com](mailto:security@docker.com).
|
[security@docker.com](mailto:security@docker.com).
|
||||||
|
|
||||||
Security reports are greatly appreciated and we will publicly thank you for it,
|
Security reports are greatly appreciated and we will publicly thank you for it.
|
||||||
although we keep your name confidential if you request it. We also like to send
|
We also like to send gifts—if you're into Docker schwag, make sure to let
|
||||||
gifts—if you're into schwag, make sure to let us know. We currently do not
|
us know. We currently do not offer a paid security bounty program, but are not
|
||||||
offer a paid security bounty program, but are not ruling it out in the future.
|
ruling it out in the future.
|
||||||
|
|
||||||
|
|
||||||
## Reporting other issues
|
## Reporting other issues
|
||||||
|
@ -39,7 +39,7 @@ A great way to contribute to the project is to send a detailed report when you
|
||||||
encounter an issue. We always appreciate a well-written, thorough bug report,
|
encounter an issue. We always appreciate a well-written, thorough bug report,
|
||||||
and will thank you for it!
|
and will thank you for it!
|
||||||
|
|
||||||
Check that [our issue database](https://github.com/moby/moby/issues)
|
Check that [our issue database](https://github.com/docker/docker/issues)
|
||||||
doesn't already include that problem or suggestion before submitting an issue.
|
doesn't already include that problem or suggestion before submitting an issue.
|
||||||
If you find a match, you can use the "subscribe" button to get notified on
|
If you find a match, you can use the "subscribe" button to get notified on
|
||||||
updates. Do *not* leave random "+1" or "I have this too" comments, as they
|
updates. Do *not* leave random "+1" or "I have this too" comments, as they
|
||||||
|
@ -66,13 +66,13 @@ This section gives the experienced contributor some tips and guidelines.
|
||||||
|
|
||||||
Not sure if that typo is worth a pull request? Found a bug and know how to fix
|
Not sure if that typo is worth a pull request? Found a bug and know how to fix
|
||||||
it? Do it! We will appreciate it. Any significant improvement should be
|
it? Do it! We will appreciate it. Any significant improvement should be
|
||||||
documented as [a GitHub issue](https://github.com/moby/moby/issues) before
|
documented as [a GitHub issue](https://github.com/docker/docker/issues) before
|
||||||
anybody starts working on it.
|
anybody starts working on it.
|
||||||
|
|
||||||
We are always thrilled to receive pull requests. We do our best to process them
|
We are always thrilled to receive pull requests. We do our best to process them
|
||||||
quickly. If your pull request is not accepted on the first try,
|
quickly. If your pull request is not accepted on the first try,
|
||||||
don't get discouraged! Our contributor's guide explains [the review process we
|
don't get discouraged! Our contributor's guide explains [the review process we
|
||||||
use for simple changes](https://docs.docker.com/contribute/overview/).
|
use for simple changes](https://docs.docker.com/opensource/workflow/make-a-contribution/).
|
||||||
|
|
||||||
### Design and cleanup proposals
|
### Design and cleanup proposals
|
||||||
|
|
||||||
|
@ -83,7 +83,11 @@ contributions, see [the advanced contribution
|
||||||
section](https://docs.docker.com/opensource/workflow/advanced-contributing/) in
|
section](https://docs.docker.com/opensource/workflow/advanced-contributing/) in
|
||||||
the contributors guide.
|
the contributors guide.
|
||||||
|
|
||||||
### Connect with other Moby Project contributors
|
We try hard to keep Docker lean and focused. Docker can't do everything for
|
||||||
|
everybody. This means that we might decide against incorporating a new feature.
|
||||||
|
However, there might be a way to implement that feature *on top of* Docker.
|
||||||
|
|
||||||
|
### Talking to other Docker users and contributors
|
||||||
|
|
||||||
<table class="tg">
|
<table class="tg">
|
||||||
<col width="45%">
|
<col width="45%">
|
||||||
|
@ -92,28 +96,52 @@ the contributors guide.
|
||||||
<td>Forums</td>
|
<td>Forums</td>
|
||||||
<td>
|
<td>
|
||||||
A public forum for users to discuss questions and explore current design patterns and
|
A public forum for users to discuss questions and explore current design patterns and
|
||||||
best practices about all the Moby projects. To participate, log in with your Github
|
best practices about Docker and related projects in the Docker Ecosystem. To participate,
|
||||||
account or create an account at <a href="https://forums.mobyproject.org" target="_blank">https://forums.mobyproject.org</a>.
|
just log in with your Docker Hub account on <a href="https://forums.docker.com" target="_blank">https://forums.docker.com</a>.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Slack</td>
|
<td>Internet Relay Chat (IRC)</td>
|
||||||
<td>
|
<td>
|
||||||
<p>
|
<p>
|
||||||
Register for the Docker Community Slack at
|
IRC a direct line to our most knowledgeable Docker users; we have
|
||||||
<a href="https://dockr.ly/comm-slack" target="_blank">https://dockr.ly/comm-slack</a>.
|
both the <code>#docker</code> and <code>#docker-dev</code> group on
|
||||||
We use the #moby-project channel for general discussion, and there are separate channels for other Moby projects such as #containerd.
|
<strong>irc.freenode.net</strong>.
|
||||||
|
IRC is a rich chat protocol but it can overwhelm new users. You can search
|
||||||
|
<a href="https://botbot.me/freenode/docker/#" target="_blank">our chat archives</a>.
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
Read our <a href="https://docs.docker.com/opensource/get-help/#irc-quickstart" target="_blank">IRC quickstart guide</a>
|
||||||
|
for an easy way to get started.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Google Group</td>
|
||||||
|
<td>
|
||||||
|
The <a href="https://groups.google.com/forum/#!forum/docker-dev" target="_blank">docker-dev</a>
|
||||||
|
group is for contributors and other people contributing to the Docker project.
|
||||||
|
You can join them without a google account by sending an email to
|
||||||
|
<a href="mailto:docker-dev+subscribe@googlegroups.com">docker-dev+subscribe@googlegroups.com</a>.
|
||||||
|
After receiving the join-request message, you can simply reply to that to confirm the subscription.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Twitter</td>
|
<td>Twitter</td>
|
||||||
<td>
|
<td>
|
||||||
You can follow <a href="https://twitter.com/moby/" target="_blank">Moby Project Twitter feed</a>
|
You can follow <a href="https://twitter.com/docker/" target="_blank">Docker's Twitter feed</a>
|
||||||
to get updates on our products. You can also tweet us questions or just
|
to get updates on our products. You can also tweet us questions or just
|
||||||
share blogs or stories.
|
share blogs or stories.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Stack Overflow</td>
|
||||||
|
<td>
|
||||||
|
Stack Overflow has over 17000 Docker questions listed. We regularly
|
||||||
|
monitor <a href="https://stackoverflow.com/search?tab=newest&q=docker" target="_blank">Docker questions</a>
|
||||||
|
and so do many other knowledgeable Docker users.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,11 +155,10 @@ Fork the repository and make changes on your fork in a feature branch:
|
||||||
your intentions, and name it XXXX-something where XXXX is the number of the
|
your intentions, and name it XXXX-something where XXXX is the number of the
|
||||||
issue.
|
issue.
|
||||||
|
|
||||||
Submit tests for your changes. See [TESTING.md](./TESTING.md) for details.
|
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
|
||||||
If your changes need integration tests, write them against the API. The `cli`
|
suite](https://docs.docker.com/opensource/project/test-and-docs/) on your branch before
|
||||||
integration tests are slowly either migrated to API tests or moved away as unit
|
submitting a pull request.
|
||||||
tests in `docker/cli` and end-to-end tests for Docker.
|
|
||||||
|
|
||||||
Update the documentation when creating or modifying features. Test your
|
Update the documentation when creating or modifying features. Test your
|
||||||
documentation changes for clarity, concision, and correctness, as well as a
|
documentation changes for clarity, concision, and correctness, as well as a
|
||||||
|
@ -146,64 +173,10 @@ committing your changes. Most editors have plug-ins that do this automatically.
|
||||||
Pull request descriptions should be as clear as possible and include a reference
|
Pull request descriptions should be as clear as possible and include a reference
|
||||||
to all the issues that they address.
|
to all the issues that they address.
|
||||||
|
|
||||||
### Successful Changes
|
|
||||||
|
|
||||||
Before contributing large or high impact changes, make the effort to coordinate
|
|
||||||
with the maintainers of the project before submitting a pull request. This
|
|
||||||
prevents you from doing extra work that may or may not be merged.
|
|
||||||
|
|
||||||
Large PRs that are just submitted without any prior communication are unlikely
|
|
||||||
to be successful.
|
|
||||||
|
|
||||||
While pull requests are the methodology for submitting changes to code, changes
|
|
||||||
are much more likely to be accepted if they are accompanied by additional
|
|
||||||
engineering work. While we don't define this explicitly, most of these goals
|
|
||||||
are accomplished through communication of the design goals and subsequent
|
|
||||||
solutions. Often times, it helps to first state the problem before presenting
|
|
||||||
solutions.
|
|
||||||
|
|
||||||
Typically, the best methods of accomplishing this are to submit an issue,
|
|
||||||
stating the problem. This issue can include a problem statement and a
|
|
||||||
checklist with requirements. If solutions are proposed, alternatives should be
|
|
||||||
listed and eliminated. Even if the criteria for elimination of a solution is
|
|
||||||
frivolous, say so.
|
|
||||||
|
|
||||||
Larger changes typically work best with design documents. These are focused on
|
|
||||||
providing context to the design at the time the feature was conceived and can
|
|
||||||
inform future documentation contributions.
|
|
||||||
|
|
||||||
### Commit Messages
|
|
||||||
|
|
||||||
Commit messages must start with a capitalized and short summary (max. 50 chars)
|
Commit messages must start with a capitalized and short summary (max. 50 chars)
|
||||||
written in the imperative, followed by an optional, more detailed explanatory
|
written in the imperative, followed by an optional, more detailed explanatory
|
||||||
text which is separated from the summary by an empty line.
|
text which is separated from the summary by an empty line.
|
||||||
|
|
||||||
Commit messages should follow best practices, including explaining the context
|
|
||||||
of the problem and how it was solved, including in caveats or follow up changes
|
|
||||||
required. They should tell the story of the change and provide readers
|
|
||||||
understanding of what led to it.
|
|
||||||
|
|
||||||
If you're lost about what this even means, please see [How to Write a Git
|
|
||||||
Commit Message](http://chris.beams.io/posts/git-commit/) for a start.
|
|
||||||
|
|
||||||
In practice, the best approach to maintaining a nice commit message is to
|
|
||||||
leverage a `git add -p` and `git commit --amend` to formulate a solid
|
|
||||||
changeset. This allows one to piece together a change, as information becomes
|
|
||||||
available.
|
|
||||||
|
|
||||||
If you squash a series of commits, don't just submit that. Re-write the commit
|
|
||||||
message, as if the series of commits was a single stroke of brilliance.
|
|
||||||
|
|
||||||
That said, there is no requirement to have a single commit for a PR, as long as
|
|
||||||
each commit tells the story. For example, if there is a feature that requires a
|
|
||||||
package, it might make sense to have the package in a separate commit then have
|
|
||||||
a subsequent commit that uses it.
|
|
||||||
|
|
||||||
Remember, you're telling part of the story with the commit message. Don't make
|
|
||||||
your chapter weird.
|
|
||||||
|
|
||||||
### Review
|
|
||||||
|
|
||||||
Code review comments may be added to your pull request. Discuss, then make the
|
Code review comments may be added to your pull request. Discuss, then make the
|
||||||
suggested modifications and push additional commits to your feature branch. Post
|
suggested modifications and push additional commits to your feature branch. Post
|
||||||
a comment after pushing. New commits show up in the pull request automatically,
|
a comment after pushing. New commits show up in the pull request automatically,
|
||||||
|
@ -224,9 +197,10 @@ calling it in another file constitute a single logical unit of work. The very
|
||||||
high majority of submissions should have a single commit, so if in doubt: squash
|
high majority of submissions should have a single commit, so if in doubt: squash
|
||||||
down to one.
|
down to one.
|
||||||
|
|
||||||
After every commit, [make sure the test suite passes](./TESTING.md). Include
|
After every commit, [make sure the test suite passes]
|
||||||
documentation changes in the same pull request so that a revert would remove
|
(https://docs.docker.com/opensource/project/test-and-docs/). Include documentation
|
||||||
all traces of the feature or fix.
|
changes in the same pull request so that a revert would remove all traces of
|
||||||
|
the feature or fix.
|
||||||
|
|
||||||
Include an issue reference like `Closes #XXXX` or `Fixes #XXXX` in commits that
|
Include an issue reference like `Closes #XXXX` or `Fixes #XXXX` in commits that
|
||||||
close an issue. Including references automatically closes the issue on a merge.
|
close an issue. Including references automatically closes the issue on a merge.
|
||||||
|
@ -238,11 +212,15 @@ Please see the [Coding Style](#coding-style) for further guidelines.
|
||||||
|
|
||||||
### Merge approval
|
### Merge approval
|
||||||
|
|
||||||
Moby maintainers use LGTM (Looks Good To Me) in comments on the code review to
|
Docker maintainers use LGTM (Looks Good To Me) in comments on the code review to
|
||||||
indicate acceptance, or use the Github review approval feature.
|
indicate acceptance.
|
||||||
|
|
||||||
For an explanation of the review and approval process see the
|
A change requires LGTMs from an absolute majority of the maintainers of each
|
||||||
[REVIEWING](project/REVIEWING.md) page.
|
component affected. For example, if a change affects `docs/` and `registry/`, it
|
||||||
|
needs an absolute majority from the maintainers of `docs/` AND, separately, an
|
||||||
|
absolute majority of the maintainers of `registry/`.
|
||||||
|
|
||||||
|
For more details, see the [MAINTAINERS](MAINTAINERS) page.
|
||||||
|
|
||||||
### Sign your work
|
### Sign your work
|
||||||
|
|
||||||
|
@ -302,16 +280,17 @@ commit automatically with `git commit -s`.
|
||||||
### How can I become a maintainer?
|
### How can I become a maintainer?
|
||||||
|
|
||||||
The procedures for adding new maintainers are explained in the
|
The procedures for adding new maintainers are explained in the
|
||||||
[/project/GOVERNANCE.md](/project/GOVERNANCE.md)
|
global [MAINTAINERS](https://github.com/docker/opensource/blob/master/MAINTAINERS)
|
||||||
file in this repository.
|
file in the [https://github.com/docker/opensource/](https://github.com/docker/opensource/)
|
||||||
|
repository.
|
||||||
|
|
||||||
Don't forget: being a maintainer is a time investment. Make sure you
|
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
|
will have time to make yourself available. You don't have to be a
|
||||||
maintainer to make a difference on the project!
|
maintainer to make a difference on the project!
|
||||||
|
|
||||||
## Moby community guidelines
|
## Docker community guidelines
|
||||||
|
|
||||||
We want to keep the Moby community awesome, growing and collaborative. We need
|
We want to keep the Docker community awesome, growing and collaborative. We need
|
||||||
your help to keep it that way. To help with this we've come up with some general
|
your help to keep it that way. To help with this we've come up with some general
|
||||||
guidelines for the community as a whole:
|
guidelines for the community as a whole:
|
||||||
|
|
||||||
|
@ -339,11 +318,6 @@ guidelines for the community as a whole:
|
||||||
used to ping maintainers to review a pull request, a proposal or an
|
used to ping maintainers to review a pull request, a proposal or an
|
||||||
issue.
|
issue.
|
||||||
|
|
||||||
The open source governance for this repository is handled via the [Moby Technical Steering Committee (TSC)](https://github.com/moby/tsc)
|
|
||||||
charter. For any concerns with the community process regarding technical contributions,
|
|
||||||
please contact the TSC. More information on project governance is available in
|
|
||||||
our [project/GOVERNANCE.md](/project/GOVERNANCE.md) document.
|
|
||||||
|
|
||||||
### Guideline violations — 3 strikes method
|
### Guideline violations — 3 strikes method
|
||||||
|
|
||||||
The point of this section is not to find opportunities to punish people, but we
|
The point of this section is not to find opportunities to punish people, but we
|
||||||
|
@ -422,6 +396,6 @@ The rules:
|
||||||
guidelines. Since you've read all the rules, you now know that.
|
guidelines. Since you've read all the rules, you now know that.
|
||||||
|
|
||||||
If you are having trouble getting into the mood of idiomatic Go, we recommend
|
If you are having trouble getting into the mood of idiomatic Go, we recommend
|
||||||
reading through [Effective Go](https://go.dev/doc/effective_go). The
|
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
|
||||||
[Go Blog](https://go.dev/blog/) is also a great resource. Drinking the
|
[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
|
||||||
kool-aid is a lot easier than going thirsty.
|
kool-aid is a lot easier than going thirsty.
|
||||||
|
|
877
Dockerfile
877
Dockerfile
|
@ -1,671 +1,246 @@
|
||||||
# syntax=docker/dockerfile:1.7
|
# This file describes the standard way to build Docker, using docker
|
||||||
|
#
|
||||||
ARG GO_VERSION=1.21.9
|
# Usage:
|
||||||
ARG BASE_DEBIAN_DISTRO="bookworm"
|
#
|
||||||
ARG GOLANG_IMAGE="golang:${GO_VERSION}-${BASE_DEBIAN_DISTRO}"
|
# # Assemble the full dev environment. This is slow the first time.
|
||||||
ARG XX_VERSION=1.4.0
|
# docker build -t docker .
|
||||||
|
#
|
||||||
ARG VPNKIT_VERSION=0.5.0
|
# # Mount your source in an interactive container for quick testing:
|
||||||
|
# docker run -v `pwd`:/go/src/github.com/docker/docker --privileged -i -t docker bash
|
||||||
ARG DOCKERCLI_REPOSITORY="https://github.com/docker/cli.git"
|
#
|
||||||
ARG DOCKERCLI_VERSION=v26.0.0
|
# # Run the test suite:
|
||||||
# cli version used for integration-cli tests
|
# docker run --privileged docker hack/make.sh test-unit test-integration-cli test-docker-py
|
||||||
ARG DOCKERCLI_INTEGRATION_REPOSITORY="https://github.com/docker/cli.git"
|
#
|
||||||
ARG DOCKERCLI_INTEGRATION_VERSION=v17.06.2-ce
|
# # Publish a release:
|
||||||
ARG BUILDX_VERSION=0.13.1
|
# docker run --privileged \
|
||||||
ARG COMPOSE_VERSION=v2.25.0
|
# -e AWS_S3_BUCKET=baz \
|
||||||
|
# -e AWS_ACCESS_KEY=foo \
|
||||||
ARG SYSTEMD="false"
|
# -e AWS_SECRET_KEY=bar \
|
||||||
ARG DOCKER_STATIC=1
|
# -e GPG_PASSPHRASE=gloubiboulga \
|
||||||
|
# docker hack/release.sh
|
||||||
# REGISTRY_VERSION specifies the version of the registry to download from
|
#
|
||||||
# https://hub.docker.com/r/distribution/distribution. This version of
|
# Note: AppArmor used to mess with privileged mode, but this is no longer
|
||||||
# the registry is used to test schema 2 manifests. Generally, the version
|
# the case. Therefore, you don't have to disable it anymore.
|
||||||
# specified here should match a current release.
|
|
||||||
ARG REGISTRY_VERSION=2.8.3
|
|
||||||
|
|
||||||
# delve is currently only supported on linux/amd64 and linux/arm64;
|
|
||||||
# https://github.com/go-delve/delve/blob/v1.8.1/pkg/proc/native/support_sentinel.go#L1-L6
|
|
||||||
ARG DELVE_SUPPORTED=${TARGETPLATFORM#linux/amd64} DELVE_SUPPORTED=${DELVE_SUPPORTED#linux/arm64}
|
|
||||||
ARG DELVE_SUPPORTED=${DELVE_SUPPORTED:+"unsupported"}
|
|
||||||
ARG DELVE_SUPPORTED=${DELVE_SUPPORTED:-"supported"}
|
|
||||||
|
|
||||||
# cross compilation helper
|
|
||||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
|
||||||
|
|
||||||
# dummy stage to make sure the image is built for deps that don't support some
|
|
||||||
# architectures
|
|
||||||
FROM --platform=$BUILDPLATFORM busybox AS build-dummy
|
|
||||||
RUN mkdir -p /build
|
|
||||||
FROM scratch AS binary-dummy
|
|
||||||
COPY --from=build-dummy /build /build
|
|
||||||
|
|
||||||
# base
|
|
||||||
FROM --platform=$BUILDPLATFORM ${GOLANG_IMAGE} AS base
|
|
||||||
COPY --from=xx / /
|
|
||||||
RUN echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
|
|
||||||
RUN apt-get update && apt-get install --no-install-recommends -y file
|
|
||||||
ENV GO111MODULE=off
|
|
||||||
ENV GOTOOLCHAIN=local
|
|
||||||
|
|
||||||
FROM base AS criu
|
|
||||||
ADD --chmod=0644 https://download.opensuse.org/repositories/devel:/tools:/criu/Debian_11/Release.key /etc/apt/trusted.gpg.d/criu.gpg.asc
|
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-criu-aptlib,target=/var/lib/apt \
|
|
||||||
--mount=type=cache,sharing=locked,id=moby-criu-aptcache,target=/var/cache/apt \
|
|
||||||
echo 'deb https://download.opensuse.org/repositories/devel:/tools:/criu/Debian_12/ /' > /etc/apt/sources.list.d/criu.list \
|
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y --no-install-recommends criu \
|
|
||||||
&& install -D /usr/sbin/criu /build/criu \
|
|
||||||
&& /build/criu --version
|
|
||||||
|
|
||||||
# registry
|
|
||||||
FROM base AS registry-src
|
|
||||||
WORKDIR /usr/src/registry
|
|
||||||
RUN git init . && git remote add origin "https://github.com/distribution/distribution.git"
|
|
||||||
|
|
||||||
FROM base AS registry
|
|
||||||
WORKDIR /go/src/github.com/docker/distribution
|
|
||||||
|
|
||||||
# REGISTRY_VERSION_SCHEMA1 specifies the version of the registry to build and
|
|
||||||
# install from the https://github.com/docker/distribution repository. This is
|
|
||||||
# an older (pre v2.3.0) version of the registry that only supports schema1
|
|
||||||
# manifests. This version of the registry is not working on arm64, so installation
|
|
||||||
# is skipped on that architecture.
|
|
||||||
ARG REGISTRY_VERSION_SCHEMA1=v2.1.0
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
RUN --mount=from=registry-src,src=/usr/src/registry,rw \
|
|
||||||
--mount=type=cache,target=/root/.cache/go-build,id=registry-build-$TARGETPLATFORM \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod \
|
|
||||||
--mount=type=tmpfs,target=/go/src <<EOT
|
|
||||||
set -ex
|
|
||||||
export GOPATH="/go/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH"
|
|
||||||
# Make the /build directory no matter what so that it doesn't fail on arm64 or
|
|
||||||
# any other platform where we don't build this registry
|
|
||||||
mkdir /build
|
|
||||||
case $TARGETPLATFORM in
|
|
||||||
linux/amd64|linux/arm/v7|linux/ppc64le|linux/s390x)
|
|
||||||
git fetch -q --depth 1 origin "${REGISTRY_VERSION_SCHEMA1}" +refs/tags/*:refs/tags/*
|
|
||||||
git checkout -q FETCH_HEAD
|
|
||||||
CGO_ENABLED=0 xx-go build -o /build/registry-v2-schema1 -v ./cmd/registry
|
|
||||||
xx-verify /build/registry-v2-schema1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
EOT
|
|
||||||
|
|
||||||
FROM distribution/distribution:$REGISTRY_VERSION AS registry-v2
|
|
||||||
RUN mkdir /build && mv /bin/registry /build/registry-v2
|
|
||||||
|
|
||||||
# go-swagger
|
|
||||||
FROM base AS swagger-src
|
|
||||||
WORKDIR /usr/src/swagger
|
|
||||||
# Currently uses a fork from https://github.com/kolyshkin/go-swagger/tree/golang-1.13-fix
|
|
||||||
# TODO: move to under moby/ or fix upstream go-swagger to work for us.
|
|
||||||
RUN git init . && git remote add origin "https://github.com/kolyshkin/go-swagger.git"
|
|
||||||
# GO_SWAGGER_COMMIT specifies the version of the go-swagger binary to build and
|
|
||||||
# install. Go-swagger is used in CI for validating swagger.yaml in hack/validate/swagger-gen
|
|
||||||
ARG GO_SWAGGER_COMMIT=c56166c036004ba7a3a321e5951ba472b9ae298c
|
|
||||||
RUN git fetch -q --depth 1 origin "${GO_SWAGGER_COMMIT}" && git checkout -q FETCH_HEAD
|
|
||||||
|
|
||||||
FROM base AS swagger
|
|
||||||
WORKDIR /go/src/github.com/go-swagger/go-swagger
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
RUN --mount=from=swagger-src,src=/usr/src/swagger,rw \
|
|
||||||
--mount=type=cache,target=/root/.cache/go-build,id=swagger-build-$TARGETPLATFORM \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod \
|
|
||||||
--mount=type=tmpfs,target=/go/src/ <<EOT
|
|
||||||
set -e
|
|
||||||
xx-go build -o /build/swagger ./cmd/swagger
|
|
||||||
xx-verify /build/swagger
|
|
||||||
EOT
|
|
||||||
|
|
||||||
# frozen-images
|
|
||||||
# See also frozenImages in "testutil/environment/protect.go" (which needs to
|
|
||||||
# be updated when adding images to this list)
|
|
||||||
FROM debian:${BASE_DEBIAN_DISTRO} AS frozen-images
|
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-frozen-images-aptlib,target=/var/lib/apt \
|
|
||||||
--mount=type=cache,sharing=locked,id=moby-frozen-images-aptcache,target=/var/cache/apt \
|
|
||||||
apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
ca-certificates \
|
|
||||||
curl \
|
|
||||||
jq
|
|
||||||
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
|
||||||
COPY contrib/download-frozen-image-v2.sh /
|
|
||||||
ARG TARGETARCH
|
|
||||||
ARG TARGETVARIANT
|
|
||||||
RUN /download-frozen-image-v2.sh /build \
|
|
||||||
busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 \
|
|
||||||
busybox:glibc@sha256:1f81263701cddf6402afe9f33fca0266d9fff379e59b1748f33d3072da71ee85 \
|
|
||||||
debian:bookworm-slim@sha256:2bc5c236e9b262645a323e9088dfa3bb1ecb16cc75811daf40a23a824d665be9 \
|
|
||||||
hello-world:latest@sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9 \
|
|
||||||
arm32v7/hello-world:latest@sha256:50b8560ad574c779908da71f7ce370c0a2471c098d44d1c8f6b513c5a55eeeb1
|
|
||||||
|
|
||||||
# delve
|
|
||||||
FROM base AS delve-src
|
|
||||||
WORKDIR /usr/src/delve
|
|
||||||
RUN git init . && git remote add origin "https://github.com/go-delve/delve.git"
|
|
||||||
# DELVE_VERSION specifies the version of the Delve debugger binary
|
|
||||||
# from the https://github.com/go-delve/delve repository.
|
|
||||||
# It can be used to run Docker with a possibility of
|
|
||||||
# attaching debugger to it.
|
|
||||||
ARG DELVE_VERSION=v1.21.1
|
|
||||||
RUN git fetch -q --depth 1 origin "${DELVE_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
|
||||||
|
|
||||||
FROM base AS delve-supported
|
|
||||||
WORKDIR /usr/src/delve
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
RUN --mount=from=delve-src,src=/usr/src/delve,rw \
|
|
||||||
--mount=type=cache,target=/root/.cache/go-build,id=delve-build-$TARGETPLATFORM \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod <<EOT
|
|
||||||
set -e
|
|
||||||
GO111MODULE=on xx-go build -o /build/dlv ./cmd/dlv
|
|
||||||
xx-verify /build/dlv
|
|
||||||
EOT
|
|
||||||
|
|
||||||
FROM binary-dummy AS delve-unsupported
|
|
||||||
FROM delve-${DELVE_SUPPORTED} AS delve
|
|
||||||
|
|
||||||
FROM base AS tomll
|
|
||||||
# GOTOML_VERSION specifies the version of the tomll binary to build and install
|
|
||||||
# from the https://github.com/pelletier/go-toml repository. This binary is used
|
|
||||||
# in CI in the hack/validate/toml script.
|
|
||||||
#
|
#
|
||||||
# When updating this version, consider updating the github.com/pelletier/go-toml
|
|
||||||
# dependency in vendor.mod accordingly.
|
|
||||||
ARG GOTOML_VERSION=v1.8.1
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod \
|
|
||||||
GOBIN=/build/ GO111MODULE=on go install "github.com/pelletier/go-toml/cmd/tomll@${GOTOML_VERSION}" \
|
|
||||||
&& /build/tomll --help
|
|
||||||
|
|
||||||
FROM base AS gowinres
|
FROM debian:jessie
|
||||||
# GOWINRES_VERSION defines go-winres tool version
|
|
||||||
ARG GOWINRES_VERSION=v0.3.1
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod \
|
|
||||||
GOBIN=/build/ GO111MODULE=on go install "github.com/tc-hib/go-winres@${GOWINRES_VERSION}" \
|
|
||||||
&& /build/go-winres --help
|
|
||||||
|
|
||||||
# containerd
|
# allow replacing httpredir or deb mirror
|
||||||
FROM base AS containerd-src
|
ARG APT_MIRROR=deb.debian.org
|
||||||
WORKDIR /usr/src/containerd
|
RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
|
||||||
RUN git init . && git remote add origin "https://github.com/containerd/containerd.git"
|
|
||||||
# CONTAINERD_VERSION is used to build containerd binaries, and used for the
|
|
||||||
# integration tests. The distributed docker .deb and .rpm packages depend on a
|
|
||||||
# separate (containerd.io) package, which may be a different version as is
|
|
||||||
# specified here. The containerd golang package is also pinned in vendor.mod.
|
|
||||||
# When updating the binary version you may also need to update the vendor
|
|
||||||
# version to pick up bug fixes or new APIs, however, usually the Go packages
|
|
||||||
# are built from a commit from the master branch.
|
|
||||||
ARG CONTAINERD_VERSION=v1.7.15
|
|
||||||
RUN git fetch -q --depth 1 origin "${CONTAINERD_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
|
||||||
|
|
||||||
FROM base AS containerd-build
|
# Add zfs ppa
|
||||||
WORKDIR /go/src/github.com/containerd/containerd
|
RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys E871F18B51E0147C77796AC81196BA81F6B0FC61 \
|
||||||
ARG TARGETPLATFORM
|
|| apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys E871F18B51E0147C77796AC81196BA81F6B0FC61
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-containerd-aptlib,target=/var/lib/apt \
|
RUN echo deb http://ppa.launchpad.net/zfs-native/stable/ubuntu trusty main > /etc/apt/sources.list.d/zfs.list
|
||||||
--mount=type=cache,sharing=locked,id=moby-containerd-aptcache,target=/var/cache/apt \
|
|
||||||
apt-get update && xx-apt-get install -y --no-install-recommends \
|
|
||||||
gcc \
|
|
||||||
libbtrfs-dev \
|
|
||||||
libsecret-1-dev \
|
|
||||||
pkg-config
|
|
||||||
ARG DOCKER_STATIC
|
|
||||||
RUN --mount=from=containerd-src,src=/usr/src/containerd,rw \
|
|
||||||
--mount=type=cache,target=/root/.cache/go-build,id=containerd-build-$TARGETPLATFORM <<EOT
|
|
||||||
set -e
|
|
||||||
export CC=$(xx-info)-gcc
|
|
||||||
export CGO_ENABLED=$([ "$DOCKER_STATIC" = "1" ] && echo "0" || echo "1")
|
|
||||||
xx-go --wrap
|
|
||||||
make $([ "$DOCKER_STATIC" = "1" ] && echo "STATIC=1") binaries
|
|
||||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") bin/containerd
|
|
||||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") bin/containerd-shim-runc-v2
|
|
||||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") bin/ctr
|
|
||||||
mkdir /build
|
|
||||||
mv bin/containerd bin/containerd-shim-runc-v2 bin/ctr /build
|
|
||||||
EOT
|
|
||||||
|
|
||||||
FROM containerd-build AS containerd-linux
|
# Packaged dependencies
|
||||||
FROM binary-dummy AS containerd-windows
|
RUN apt-get update && apt-get install -y \
|
||||||
FROM containerd-${TARGETOS} AS containerd
|
apparmor \
|
||||||
|
apt-utils \
|
||||||
FROM base AS golangci_lint
|
aufs-tools \
|
||||||
ARG GOLANGCI_LINT_VERSION=v1.55.2
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod \
|
|
||||||
GOBIN=/build/ GO111MODULE=on go install "github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCI_LINT_VERSION}" \
|
|
||||||
&& /build/golangci-lint --version
|
|
||||||
|
|
||||||
FROM base AS gotestsum
|
|
||||||
ARG GOTESTSUM_VERSION=v1.8.2
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod \
|
|
||||||
GOBIN=/build/ GO111MODULE=on go install "gotest.tools/gotestsum@${GOTESTSUM_VERSION}" \
|
|
||||||
&& /build/gotestsum --version
|
|
||||||
|
|
||||||
FROM base AS shfmt
|
|
||||||
ARG SHFMT_VERSION=v3.8.0
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod \
|
|
||||||
GOBIN=/build/ GO111MODULE=on go install "mvdan.cc/sh/v3/cmd/shfmt@${SHFMT_VERSION}" \
|
|
||||||
&& /build/shfmt --version
|
|
||||||
|
|
||||||
FROM base AS gopls
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod \
|
|
||||||
GOBIN=/build/ GO111MODULE=on go install "golang.org/x/tools/gopls@latest" \
|
|
||||||
&& /build/gopls version
|
|
||||||
|
|
||||||
FROM base AS dockercli
|
|
||||||
WORKDIR /go/src/github.com/docker/cli
|
|
||||||
ARG DOCKERCLI_REPOSITORY
|
|
||||||
ARG DOCKERCLI_VERSION
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
RUN --mount=source=hack/dockerfile/cli.sh,target=/download-or-build-cli.sh \
|
|
||||||
--mount=type=cache,id=dockercli-git-$TARGETPLATFORM,sharing=locked,target=./.git \
|
|
||||||
--mount=type=cache,target=/root/.cache/go-build,id=dockercli-build-$TARGETPLATFORM \
|
|
||||||
rm -f ./.git/*.lock \
|
|
||||||
&& /download-or-build-cli.sh ${DOCKERCLI_VERSION} ${DOCKERCLI_REPOSITORY} /build \
|
|
||||||
&& /build/docker --version
|
|
||||||
|
|
||||||
FROM base AS dockercli-integration
|
|
||||||
WORKDIR /go/src/github.com/docker/cli
|
|
||||||
ARG DOCKERCLI_INTEGRATION_REPOSITORY
|
|
||||||
ARG DOCKERCLI_INTEGRATION_VERSION
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
RUN --mount=source=hack/dockerfile/cli.sh,target=/download-or-build-cli.sh \
|
|
||||||
--mount=type=cache,id=dockercli-git-$TARGETPLATFORM,sharing=locked,target=./.git \
|
|
||||||
--mount=type=cache,target=/root/.cache/go-build,id=dockercli-build-$TARGETPLATFORM \
|
|
||||||
rm -f ./.git/*.lock \
|
|
||||||
&& /download-or-build-cli.sh ${DOCKERCLI_INTEGRATION_VERSION} ${DOCKERCLI_INTEGRATION_REPOSITORY} /build \
|
|
||||||
&& /build/docker --version
|
|
||||||
|
|
||||||
# runc
|
|
||||||
FROM base AS runc-src
|
|
||||||
WORKDIR /usr/src/runc
|
|
||||||
RUN git init . && git remote add origin "https://github.com/opencontainers/runc.git"
|
|
||||||
# RUNC_VERSION should match the version that is used by the containerd version
|
|
||||||
# that is used. If you need to update runc, open a pull request in the containerd
|
|
||||||
# project first, and update both after that is merged. When updating RUNC_VERSION,
|
|
||||||
# consider updating runc in vendor.mod accordingly.
|
|
||||||
ARG RUNC_VERSION=v1.1.12
|
|
||||||
RUN git fetch -q --depth 1 origin "${RUNC_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
|
||||||
|
|
||||||
FROM base AS runc-build
|
|
||||||
WORKDIR /go/src/github.com/opencontainers/runc
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-runc-aptlib,target=/var/lib/apt \
|
|
||||||
--mount=type=cache,sharing=locked,id=moby-runc-aptcache,target=/var/cache/apt \
|
|
||||||
apt-get update && xx-apt-get install -y --no-install-recommends \
|
|
||||||
dpkg-dev \
|
|
||||||
gcc \
|
|
||||||
libc6-dev \
|
|
||||||
libseccomp-dev \
|
|
||||||
pkg-config
|
|
||||||
ARG DOCKER_STATIC
|
|
||||||
RUN --mount=from=runc-src,src=/usr/src/runc,rw \
|
|
||||||
--mount=type=cache,target=/root/.cache/go-build,id=runc-build-$TARGETPLATFORM <<EOT
|
|
||||||
set -e
|
|
||||||
xx-go --wrap
|
|
||||||
CGO_ENABLED=1 make "$([ "$DOCKER_STATIC" = "1" ] && echo "static" || echo "runc")"
|
|
||||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") runc
|
|
||||||
mkdir /build
|
|
||||||
mv runc /build/
|
|
||||||
EOT
|
|
||||||
|
|
||||||
FROM runc-build AS runc-linux
|
|
||||||
FROM binary-dummy AS runc-windows
|
|
||||||
FROM runc-${TARGETOS} AS runc
|
|
||||||
|
|
||||||
# tini
|
|
||||||
FROM base AS tini-src
|
|
||||||
WORKDIR /usr/src/tini
|
|
||||||
RUN git init . && git remote add origin "https://github.com/krallin/tini.git"
|
|
||||||
# TINI_VERSION specifies the version of tini (docker-init) to build. This
|
|
||||||
# binary is used when starting containers with the `--init` option.
|
|
||||||
ARG TINI_VERSION=v0.19.0
|
|
||||||
RUN git fetch -q --depth 1 origin "${TINI_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
|
||||||
|
|
||||||
FROM base AS tini-build
|
|
||||||
WORKDIR /go/src/github.com/krallin/tini
|
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-tini-aptlib,target=/var/lib/apt \
|
|
||||||
--mount=type=cache,sharing=locked,id=moby-tini-aptcache,target=/var/cache/apt \
|
|
||||||
apt-get update && apt-get install -y --no-install-recommends cmake
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-tini-aptlib,target=/var/lib/apt \
|
|
||||||
--mount=type=cache,sharing=locked,id=moby-tini-aptcache,target=/var/cache/apt \
|
|
||||||
xx-apt-get install -y --no-install-recommends \
|
|
||||||
gcc \
|
|
||||||
libc6-dev \
|
|
||||||
pkg-config
|
|
||||||
RUN --mount=from=tini-src,src=/usr/src/tini,rw \
|
|
||||||
--mount=type=cache,target=/root/.cache/go-build,id=tini-build-$TARGETPLATFORM <<EOT
|
|
||||||
set -e
|
|
||||||
CC=$(xx-info)-gcc cmake .
|
|
||||||
make tini-static
|
|
||||||
xx-verify --static tini-static
|
|
||||||
mkdir /build
|
|
||||||
mv tini-static /build/docker-init
|
|
||||||
EOT
|
|
||||||
|
|
||||||
FROM tini-build AS tini-linux
|
|
||||||
FROM binary-dummy AS tini-windows
|
|
||||||
FROM tini-${TARGETOS} AS tini
|
|
||||||
|
|
||||||
# rootlesskit
|
|
||||||
FROM base AS rootlesskit-src
|
|
||||||
WORKDIR /usr/src/rootlesskit
|
|
||||||
RUN git init . && git remote add origin "https://github.com/rootless-containers/rootlesskit.git"
|
|
||||||
# When updating, also update vendor.mod and hack/dockerfile/install/rootlesskit.installer accordingly.
|
|
||||||
ARG ROOTLESSKIT_VERSION=v2.0.2
|
|
||||||
RUN git fetch -q --depth 1 origin "${ROOTLESSKIT_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
|
||||||
|
|
||||||
FROM base AS rootlesskit-build
|
|
||||||
WORKDIR /go/src/github.com/rootless-containers/rootlesskit
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-rootlesskit-aptlib,target=/var/lib/apt \
|
|
||||||
--mount=type=cache,sharing=locked,id=moby-rootlesskit-aptcache,target=/var/cache/apt \
|
|
||||||
apt-get update && xx-apt-get install -y --no-install-recommends \
|
|
||||||
gcc \
|
|
||||||
libc6-dev \
|
|
||||||
pkg-config
|
|
||||||
ENV GO111MODULE=on
|
|
||||||
ARG DOCKER_STATIC
|
|
||||||
RUN --mount=from=rootlesskit-src,src=/usr/src/rootlesskit,rw \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod \
|
|
||||||
--mount=type=cache,target=/root/.cache/go-build,id=rootlesskit-build-$TARGETPLATFORM <<EOT
|
|
||||||
set -e
|
|
||||||
export CGO_ENABLED=$([ "$DOCKER_STATIC" = "1" ] && echo "0" || echo "1")
|
|
||||||
xx-go build -o /build/rootlesskit -ldflags="$([ "$DOCKER_STATIC" != "1" ] && echo "-linkmode=external")" ./cmd/rootlesskit
|
|
||||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") /build/rootlesskit
|
|
||||||
xx-go build -o /build/rootlesskit-docker-proxy -ldflags="$([ "$DOCKER_STATIC" != "1" ] && echo "-linkmode=external")" ./cmd/rootlesskit-docker-proxy
|
|
||||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") /build/rootlesskit-docker-proxy
|
|
||||||
EOT
|
|
||||||
COPY --link ./contrib/dockerd-rootless.sh /build/
|
|
||||||
COPY --link ./contrib/dockerd-rootless-setuptool.sh /build/
|
|
||||||
|
|
||||||
FROM rootlesskit-build AS rootlesskit-linux
|
|
||||||
FROM binary-dummy AS rootlesskit-windows
|
|
||||||
FROM rootlesskit-${TARGETOS} AS rootlesskit
|
|
||||||
|
|
||||||
FROM base AS crun
|
|
||||||
ARG CRUN_VERSION=1.12
|
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-crun-aptlib,target=/var/lib/apt \
|
|
||||||
--mount=type=cache,sharing=locked,id=moby-crun-aptcache,target=/var/cache/apt \
|
|
||||||
apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
autoconf \
|
|
||||||
automake \
|
automake \
|
||||||
|
bash-completion \
|
||||||
|
binutils-mingw-w64 \
|
||||||
|
bsdmainutils \
|
||||||
|
btrfs-tools \
|
||||||
build-essential \
|
build-essential \
|
||||||
|
clang \
|
||||||
|
cmake \
|
||||||
|
createrepo \
|
||||||
|
curl \
|
||||||
|
dpkg-sig \
|
||||||
|
gcc-mingw-w64 \
|
||||||
|
git \
|
||||||
|
iptables \
|
||||||
|
jq \
|
||||||
|
libapparmor-dev \
|
||||||
libcap-dev \
|
libcap-dev \
|
||||||
libprotobuf-c-dev \
|
libltdl-dev \
|
||||||
libseccomp-dev \
|
libnl-3-dev \
|
||||||
libsystemd-dev \
|
libprotobuf-c0-dev \
|
||||||
|
libprotobuf-dev \
|
||||||
|
libsqlite3-dev \
|
||||||
|
libsystemd-journal-dev \
|
||||||
libtool \
|
libtool \
|
||||||
libudev-dev \
|
mercurial \
|
||||||
libyajl-dev \
|
net-tools \
|
||||||
python3 \
|
pkg-config \
|
||||||
;
|
protobuf-compiler \
|
||||||
RUN --mount=type=tmpfs,target=/tmp/crun-build \
|
protobuf-c-compiler \
|
||||||
git clone https://github.com/containers/crun.git /tmp/crun-build && \
|
python-dev \
|
||||||
cd /tmp/crun-build && \
|
python-mock \
|
||||||
git checkout -q "${CRUN_VERSION}" && \
|
python-pip \
|
||||||
./autogen.sh && \
|
python-websocket \
|
||||||
./configure --bindir=/build && \
|
ubuntu-zfs \
|
||||||
make -j install
|
xfsprogs \
|
||||||
|
vim-common \
|
||||||
|
libzfs-dev \
|
||||||
|
tar \
|
||||||
|
zip \
|
||||||
|
--no-install-recommends \
|
||||||
|
&& pip install awscli==1.10.15
|
||||||
|
# Get lvm2 source for compiling statically
|
||||||
|
ENV LVM2_VERSION 2.02.103
|
||||||
|
RUN mkdir -p /usr/local/lvm2 \
|
||||||
|
&& curl -fsSL "https://mirrors.kernel.org/sourceware/lvm2/LVM2.${LVM2_VERSION}.tgz" \
|
||||||
|
| tar -xzC /usr/local/lvm2 --strip-components=1
|
||||||
|
# See https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
||||||
|
|
||||||
# vpnkit
|
# Compile and install lvm2
|
||||||
# use dummy scratch stage to avoid build to fail for unsupported platforms
|
RUN cd /usr/local/lvm2 \
|
||||||
FROM scratch AS vpnkit-windows
|
&& ./configure \
|
||||||
FROM scratch AS vpnkit-linux-386
|
--build="$(gcc -print-multiarch)" \
|
||||||
FROM scratch AS vpnkit-linux-arm
|
--enable-static_link \
|
||||||
FROM scratch AS vpnkit-linux-ppc64le
|
&& make device-mapper \
|
||||||
FROM scratch AS vpnkit-linux-riscv64
|
&& make install_device-mapper
|
||||||
FROM scratch AS vpnkit-linux-s390x
|
# See https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||||
FROM djs55/vpnkit:${VPNKIT_VERSION} AS vpnkit-linux-amd64
|
|
||||||
FROM djs55/vpnkit:${VPNKIT_VERSION} AS vpnkit-linux-arm64
|
|
||||||
FROM vpnkit-linux-${TARGETARCH} AS vpnkit-linux
|
|
||||||
FROM vpnkit-${TARGETOS} AS vpnkit
|
|
||||||
|
|
||||||
# containerutility
|
# Configure the container for OSX cross compilation
|
||||||
FROM base AS containerutil-src
|
ENV OSX_SDK MacOSX10.11.sdk
|
||||||
WORKDIR /usr/src/containerutil
|
ENV OSX_CROSS_COMMIT a9317c18a3a457ca0a657f08cc4d0d43c6cf8953
|
||||||
RUN git init . && git remote add origin "https://github.com/docker-archive/windows-container-utility.git"
|
RUN set -x \
|
||||||
ARG CONTAINERUTILITY_VERSION=aa1ba87e99b68e0113bd27ec26c60b88f9d4ccd9
|
&& export OSXCROSS_PATH="/osxcross" \
|
||||||
RUN git fetch -q --depth 1 origin "${CONTAINERUTILITY_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
&& git clone https://github.com/tpoechtrager/osxcross.git $OSXCROSS_PATH \
|
||||||
|
&& ( cd $OSXCROSS_PATH && git checkout -q $OSX_CROSS_COMMIT) \
|
||||||
|
&& curl -sSL https://s3.dockerproject.org/darwin/v2/${OSX_SDK}.tar.xz -o "${OSXCROSS_PATH}/tarballs/${OSX_SDK}.tar.xz" \
|
||||||
|
&& UNATTENDED=yes OSX_VERSION_MIN=10.6 ${OSXCROSS_PATH}/build.sh
|
||||||
|
ENV PATH /osxcross/target/bin:$PATH
|
||||||
|
|
||||||
FROM base AS containerutil-build
|
# Install seccomp: the version shipped in trusty is too old
|
||||||
WORKDIR /usr/src/containerutil
|
ENV SECCOMP_VERSION 2.3.1
|
||||||
ARG TARGETPLATFORM
|
RUN set -x \
|
||||||
RUN xx-apt-get install -y --no-install-recommends \
|
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||||
gcc \
|
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||||
g++ \
|
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||||
libc6-dev \
|
&& ( \
|
||||||
pkg-config
|
cd "$SECCOMP_PATH" \
|
||||||
RUN --mount=from=containerutil-src,src=/usr/src/containerutil,rw \
|
&& ./configure --prefix=/usr/local \
|
||||||
--mount=type=cache,target=/root/.cache/go-build,id=containerutil-build-$TARGETPLATFORM <<EOT
|
&& make \
|
||||||
set -e
|
&& make install \
|
||||||
CC="$(xx-info)-gcc" CXX="$(xx-info)-g++" make
|
&& ldconfig \
|
||||||
xx-verify --static containerutility.exe
|
) \
|
||||||
mkdir /build
|
&& rm -rf "$SECCOMP_PATH"
|
||||||
mv containerutility.exe /build/
|
|
||||||
EOT
|
|
||||||
|
|
||||||
FROM binary-dummy AS containerutil-linux
|
# Install Go
|
||||||
FROM containerutil-build AS containerutil-windows-amd64
|
# IMPORTANT: If the version of Go is updated, the Windows to Linux CI machines
|
||||||
FROM containerutil-windows-${TARGETARCH} AS containerutil-windows
|
# will need updating, to avoid errors. Ping #docker-maintainers on IRC
|
||||||
FROM containerutil-${TARGETOS} AS containerutil
|
# with a heads-up.
|
||||||
FROM docker/buildx-bin:${BUILDX_VERSION} as buildx
|
ENV GO_VERSION 1.7.5
|
||||||
FROM docker/compose-bin:${COMPOSE_VERSION} as compose
|
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" \
|
||||||
|
| tar -xzC /usr/local
|
||||||
|
|
||||||
FROM base AS dev-systemd-false
|
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||||
COPY --link --from=frozen-images /build/ /docker-frozen-images
|
ENV GOPATH /go
|
||||||
COPY --link --from=swagger /build/ /usr/local/bin/
|
|
||||||
COPY --link --from=delve /build/ /usr/local/bin/
|
|
||||||
COPY --link --from=tomll /build/ /usr/local/bin/
|
|
||||||
COPY --link --from=gowinres /build/ /usr/local/bin/
|
|
||||||
COPY --link --from=tini /build/ /usr/local/bin/
|
|
||||||
COPY --link --from=registry /build/ /usr/local/bin/
|
|
||||||
COPY --link --from=registry-v2 /build/ /usr/local/bin/
|
|
||||||
|
|
||||||
# Skip the CRIU stage for now, as the opensuse package repository is sometimes
|
# Compile Go for cross compilation
|
||||||
# unstable, and we're currently not using it in CI.
|
ENV DOCKER_CROSSPLATFORMS \
|
||||||
#
|
linux/386 linux/arm \
|
||||||
# FIXME(thaJeztah): re-enable this stage when https://github.com/moby/moby/issues/38963 is resolved (see https://github.com/moby/moby/pull/38984)
|
darwin/amd64 \
|
||||||
# COPY --link --from=criu /build/ /usr/local/bin/
|
freebsd/amd64 freebsd/386 freebsd/arm \
|
||||||
COPY --link --from=gotestsum /build/ /usr/local/bin/
|
windows/amd64 windows/386 \
|
||||||
COPY --link --from=golangci_lint /build/ /usr/local/bin/
|
solaris/amd64
|
||||||
COPY --link --from=shfmt /build/ /usr/local/bin/
|
|
||||||
COPY --link --from=runc /build/ /usr/local/bin/
|
# Dependency for golint
|
||||||
COPY --link --from=containerd /build/ /usr/local/bin/
|
ENV GO_TOOLS_COMMIT 823804e1ae08dbb14eb807afc7db9993bc9e3cc3
|
||||||
COPY --link --from=rootlesskit /build/ /usr/local/bin/
|
RUN git clone https://github.com/golang/tools.git /go/src/golang.org/x/tools \
|
||||||
COPY --link --from=vpnkit / /usr/local/bin/
|
&& (cd /go/src/golang.org/x/tools && git checkout -q $GO_TOOLS_COMMIT)
|
||||||
COPY --link --from=containerutil /build/ /usr/local/bin/
|
|
||||||
COPY --link --from=crun /build/ /usr/local/bin/
|
# Grab Go's lint tool
|
||||||
COPY --link hack/dockerfile/etc/docker/ /etc/docker/
|
ENV GO_LINT_COMMIT 32a87160691b3c96046c0c678fe57c5bef761456
|
||||||
COPY --link --from=buildx /buildx /usr/local/libexec/docker/cli-plugins/docker-buildx
|
RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint \
|
||||||
COPY --link --from=compose /docker-compose /usr/libexec/docker/cli-plugins/docker-compose
|
&& (cd /go/src/github.com/golang/lint && git checkout -q $GO_LINT_COMMIT) \
|
||||||
|
&& go install -v github.com/golang/lint/golint
|
||||||
|
|
||||||
|
# Install CRIU for checkpoint/restore support
|
||||||
|
ENV CRIU_VERSION 2.2
|
||||||
|
RUN mkdir -p /usr/src/criu \
|
||||||
|
&& curl -sSL https://github.com/xemul/criu/archive/v${CRIU_VERSION}.tar.gz | tar -v -C /usr/src/criu/ -xz --strip-components=1 \
|
||||||
|
&& cd /usr/src/criu \
|
||||||
|
&& make \
|
||||||
|
&& make install-criu
|
||||||
|
|
||||||
|
# Install two versions of the registry. The first is an older version that
|
||||||
|
# only supports schema1 manifests. The second is a newer version that supports
|
||||||
|
# both. This allows integration-cli tests to cover push/pull with both schema1
|
||||||
|
# and schema2 manifests.
|
||||||
|
ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||||
|
ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
|
||||||
|
RUN set -x \
|
||||||
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
|
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/registry-v2-schema1 github.com/docker/distribution/cmd/registry \
|
||||||
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
|
# Install notary and notary-server
|
||||||
|
ENV NOTARY_VERSION v0.4.2
|
||||||
|
RUN set -x \
|
||||||
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
|
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||||
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
|
# Get the "docker-py" source so we can run their integration tests
|
||||||
|
ENV DOCKER_PY_COMMIT e2655f658408f9ad1f62abdef3eb6ed43c0cf324
|
||||||
|
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||||
|
&& cd /docker-py \
|
||||||
|
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||||
|
&& pip install -r test-requirements.txt
|
||||||
|
|
||||||
|
# Install yamllint for validating swagger.yaml
|
||||||
|
RUN pip install yamllint==1.5.0
|
||||||
|
|
||||||
|
# Install go-swagger for validating swagger.yaml
|
||||||
|
ENV GO_SWAGGER_COMMIT c28258affb0b6251755d92489ef685af8d4ff3eb
|
||||||
|
RUN git clone https://github.com/go-swagger/go-swagger.git /go/src/github.com/go-swagger/go-swagger \
|
||||||
|
&& (cd /go/src/github.com/go-swagger/go-swagger && git checkout -q $GO_SWAGGER_COMMIT) \
|
||||||
|
&& go install -v github.com/go-swagger/go-swagger/cmd/swagger
|
||||||
|
|
||||||
|
# Set user.email so crosbymichael's in-container merge commits go smoothly
|
||||||
|
RUN git config --global user.email 'docker-dummy@example.com'
|
||||||
|
|
||||||
|
# Add an unprivileged user to be used for tests which need it
|
||||||
|
RUN groupadd -r docker
|
||||||
|
RUN useradd --create-home --gid docker unprivilegeduser
|
||||||
|
|
||||||
ENV PATH=/usr/local/cli:$PATH
|
|
||||||
ENV TEST_CLIENT_BINARY=/usr/local/cli-integration/docker
|
|
||||||
ENV CONTAINERD_ADDRESS=/run/docker/containerd/containerd.sock
|
|
||||||
ENV CONTAINERD_NAMESPACE=moby
|
|
||||||
WORKDIR /go/src/github.com/docker/docker
|
|
||||||
VOLUME /var/lib/docker
|
VOLUME /var/lib/docker
|
||||||
VOLUME /home/unprivilegeduser/.local/share/docker
|
WORKDIR /go/src/github.com/docker/docker
|
||||||
|
ENV DOCKER_BUILDTAGS apparmor pkcs11 seccomp selinux
|
||||||
|
|
||||||
|
# Let us use a .bashrc file
|
||||||
|
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||||
|
# Add integration helps to bashrc
|
||||||
|
RUN echo "source $PWD/hack/make/.integration-test-helpers" >> /etc/bash.bashrc
|
||||||
|
|
||||||
|
# Register Docker's bash completion.
|
||||||
|
RUN ln -sv $PWD/contrib/completion/bash/docker /etc/bash_completion.d/docker
|
||||||
|
|
||||||
|
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||||
|
COPY contrib/download-frozen-image-v2.sh /go/src/github.com/docker/docker/contrib/
|
||||||
|
RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||||
|
buildpack-deps:jessie@sha256:25785f89240fbcdd8a74bdaf30dd5599a9523882c6dfc567f2e9ef7cf6f79db6 \
|
||||||
|
busybox:latest@sha256:e4f93f6ed15a0cdd342f5aae387886fba0ab98af0a102da6276eaf24d6e6ade0 \
|
||||||
|
debian:jessie@sha256:f968f10b4b523737e253a97eac59b0d1420b5c19b69928d35801a6373ffe330e \
|
||||||
|
hello-world:latest@sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7
|
||||||
|
# See also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||||
|
|
||||||
|
# Install tomlv, vndr, runc, containerd, tini, docker-proxy
|
||||||
|
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||||
|
COPY hack/dockerfile/binaries-commits /tmp/binaries-commits
|
||||||
|
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||||
|
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy bindata
|
||||||
|
|
||||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||||
ENTRYPOINT ["hack/dind"]
|
ENTRYPOINT ["hack/dind"]
|
||||||
|
|
||||||
FROM dev-systemd-false AS dev-systemd-true
|
# Upload docker source
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \
|
COPY . /go/src/github.com/docker/docker
|
||||||
--mount=type=cache,sharing=locked,id=moby-dev-aptcache,target=/var/cache/apt \
|
|
||||||
apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
dbus \
|
|
||||||
dbus-user-session \
|
|
||||||
systemd \
|
|
||||||
systemd-sysv
|
|
||||||
ENTRYPOINT ["hack/dind-systemd"]
|
|
||||||
|
|
||||||
FROM dev-systemd-${SYSTEMD} AS dev-base
|
|
||||||
RUN groupadd -r docker
|
|
||||||
RUN useradd --create-home --gid docker unprivilegeduser \
|
|
||||||
&& mkdir -p /home/unprivilegeduser/.local/share/docker \
|
|
||||||
&& chown -R unprivilegeduser /home/unprivilegeduser
|
|
||||||
# Let us use a .bashrc file
|
|
||||||
RUN ln -sfv /go/src/github.com/docker/docker/.bashrc ~/.bashrc
|
|
||||||
# Activate bash completion and include Docker's completion if mounted with DOCKER_BASH_COMPLETION_PATH
|
|
||||||
RUN echo "source /usr/share/bash-completion/bash_completion" >> /etc/bash.bashrc
|
|
||||||
RUN ln -s /usr/local/completion/bash/docker /etc/bash_completion.d/docker
|
|
||||||
RUN ldconfig
|
|
||||||
# Set dev environment as safe git directory to prevent "dubious ownership" errors
|
|
||||||
# when bind-mounting the source into the dev-container. See https://github.com/moby/moby/pull/44930
|
|
||||||
RUN git config --global --add safe.directory $GOPATH/src/github.com/docker/docker
|
|
||||||
# This should only install packages that are specifically needed for the dev environment and nothing else
|
|
||||||
# Do you really need to add another package here? Can it be done in a different build stage?
|
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \
|
|
||||||
--mount=type=cache,sharing=locked,id=moby-dev-aptcache,target=/var/cache/apt \
|
|
||||||
apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
apparmor \
|
|
||||||
bash-completion \
|
|
||||||
bzip2 \
|
|
||||||
inetutils-ping \
|
|
||||||
iproute2 \
|
|
||||||
iptables \
|
|
||||||
jq \
|
|
||||||
libcap2-bin \
|
|
||||||
libnet1 \
|
|
||||||
libnl-3-200 \
|
|
||||||
libprotobuf-c1 \
|
|
||||||
libyajl2 \
|
|
||||||
net-tools \
|
|
||||||
patch \
|
|
||||||
pigz \
|
|
||||||
sudo \
|
|
||||||
systemd-journal-remote \
|
|
||||||
thin-provisioning-tools \
|
|
||||||
uidmap \
|
|
||||||
vim \
|
|
||||||
vim-common \
|
|
||||||
xfsprogs \
|
|
||||||
xz-utils \
|
|
||||||
zip \
|
|
||||||
zstd
|
|
||||||
# Switch to use iptables instead of nftables (to match the CI hosts)
|
|
||||||
# TODO use some kind of runtime auto-detection instead if/when nftables is supported (https://github.com/moby/moby/issues/26824)
|
|
||||||
RUN update-alternatives --set iptables /usr/sbin/iptables-legacy || true \
|
|
||||||
&& update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy || true \
|
|
||||||
&& update-alternatives --set arptables /usr/sbin/arptables-legacy || true
|
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \
|
|
||||||
--mount=type=cache,sharing=locked,id=moby-dev-aptcache,target=/var/cache/apt \
|
|
||||||
apt-get update && apt-get install --no-install-recommends -y \
|
|
||||||
gcc \
|
|
||||||
pkg-config \
|
|
||||||
dpkg-dev \
|
|
||||||
libapparmor-dev \
|
|
||||||
libseccomp-dev \
|
|
||||||
libsecret-1-dev \
|
|
||||||
libsystemd-dev \
|
|
||||||
libudev-dev \
|
|
||||||
yamllint
|
|
||||||
COPY --link --from=dockercli /build/ /usr/local/cli
|
|
||||||
COPY --link --from=dockercli-integration /build/ /usr/local/cli-integration
|
|
||||||
|
|
||||||
FROM base AS build
|
|
||||||
COPY --from=gowinres /build/ /usr/local/bin/
|
|
||||||
WORKDIR /go/src/github.com/docker/docker
|
|
||||||
ENV GO111MODULE=off
|
|
||||||
ENV CGO_ENABLED=1
|
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-build-aptlib,target=/var/lib/apt \
|
|
||||||
--mount=type=cache,sharing=locked,id=moby-build-aptcache,target=/var/cache/apt \
|
|
||||||
apt-get update && apt-get install --no-install-recommends -y \
|
|
||||||
clang \
|
|
||||||
lld \
|
|
||||||
llvm
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
RUN --mount=type=cache,sharing=locked,id=moby-build-aptlib,target=/var/lib/apt \
|
|
||||||
--mount=type=cache,sharing=locked,id=moby-build-aptcache,target=/var/cache/apt \
|
|
||||||
xx-apt-get install --no-install-recommends -y \
|
|
||||||
dpkg-dev \
|
|
||||||
gcc \
|
|
||||||
libapparmor-dev \
|
|
||||||
libc6-dev \
|
|
||||||
libseccomp-dev \
|
|
||||||
libsecret-1-dev \
|
|
||||||
libsystemd-dev \
|
|
||||||
libudev-dev \
|
|
||||||
pkg-config
|
|
||||||
ARG DOCKER_BUILDTAGS
|
|
||||||
ARG DOCKER_DEBUG
|
|
||||||
ARG DOCKER_GITCOMMIT=HEAD
|
|
||||||
ARG DOCKER_LDFLAGS
|
|
||||||
ARG DOCKER_STATIC
|
|
||||||
ARG VERSION
|
|
||||||
ARG PLATFORM
|
|
||||||
ARG PRODUCT
|
|
||||||
ARG DEFAULT_PRODUCT_LICENSE
|
|
||||||
ARG PACKAGER_NAME
|
|
||||||
# PREFIX overrides DEST dir in make.sh script otherwise it fails because of
|
|
||||||
# read only mount in current work dir
|
|
||||||
ENV PREFIX=/tmp
|
|
||||||
RUN <<EOT
|
|
||||||
# in bullseye arm64 target does not link with lld so configure it to use ld instead
|
|
||||||
if [ "$(xx-info arch)" = "arm64" ]; then
|
|
||||||
XX_CC_PREFER_LINKER=ld xx-clang --setup-target-triple
|
|
||||||
fi
|
|
||||||
EOT
|
|
||||||
RUN --mount=type=bind,target=.,rw \
|
|
||||||
--mount=type=tmpfs,target=cli/winresources/dockerd \
|
|
||||||
--mount=type=tmpfs,target=cli/winresources/docker-proxy \
|
|
||||||
--mount=type=cache,target=/root/.cache/go-build,id=moby-build-$TARGETPLATFORM <<EOT
|
|
||||||
set -e
|
|
||||||
target=$([ "$DOCKER_STATIC" = "1" ] && echo "binary" || echo "dynbinary")
|
|
||||||
xx-go --wrap
|
|
||||||
PKG_CONFIG=$(xx-go env PKG_CONFIG) ./hack/make.sh $target
|
|
||||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") /tmp/bundles/${target}-daemon/dockerd$([ "$(xx-info os)" = "windows" ] && echo ".exe")
|
|
||||||
xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") /tmp/bundles/${target}-daemon/docker-proxy$([ "$(xx-info os)" = "windows" ] && echo ".exe")
|
|
||||||
mkdir /build
|
|
||||||
mv /tmp/bundles/${target}-daemon/* /build/
|
|
||||||
EOT
|
|
||||||
|
|
||||||
# usage:
|
|
||||||
# > docker buildx bake binary
|
|
||||||
# > DOCKER_STATIC=0 docker buildx bake binary
|
|
||||||
# or
|
|
||||||
# > make binary
|
|
||||||
# > make dynbinary
|
|
||||||
FROM scratch AS binary
|
|
||||||
COPY --from=build /build/ /
|
|
||||||
|
|
||||||
# usage:
|
|
||||||
# > docker buildx bake all
|
|
||||||
FROM scratch AS all
|
|
||||||
COPY --link --from=tini /build/ /
|
|
||||||
COPY --link --from=runc /build/ /
|
|
||||||
COPY --link --from=containerd /build/ /
|
|
||||||
COPY --link --from=rootlesskit /build/ /
|
|
||||||
COPY --link --from=containerutil /build/ /
|
|
||||||
COPY --link --from=vpnkit / /
|
|
||||||
COPY --link --from=build /build /
|
|
||||||
|
|
||||||
# smoke tests
|
|
||||||
# usage:
|
|
||||||
# > docker buildx bake binary-smoketest
|
|
||||||
FROM --platform=$TARGETPLATFORM base AS smoketest
|
|
||||||
WORKDIR /usr/local/bin
|
|
||||||
COPY --from=build /build .
|
|
||||||
RUN <<EOT
|
|
||||||
set -ex
|
|
||||||
file dockerd
|
|
||||||
dockerd --version
|
|
||||||
file docker-proxy
|
|
||||||
docker-proxy --version
|
|
||||||
EOT
|
|
||||||
|
|
||||||
# devcontainer is a stage used by .devcontainer/devcontainer.json
|
|
||||||
FROM dev-base AS devcontainer
|
|
||||||
COPY --link . .
|
|
||||||
COPY --link --from=gopls /build/ /usr/local/bin/
|
|
||||||
|
|
||||||
# usage:
|
|
||||||
# > make shell
|
|
||||||
# > SYSTEMD=true make shell
|
|
||||||
FROM dev-base AS dev
|
|
||||||
COPY --link . .
|
|
||||||
|
|
175
Dockerfile.aarch64
Normal file
175
Dockerfile.aarch64
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
# This file describes the standard way to build Docker on aarch64, using docker
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# # Assemble the full dev environment. This is slow the first time.
|
||||||
|
# docker build -t docker -f Dockerfile.aarch64 .
|
||||||
|
#
|
||||||
|
# # Mount your source in an interactive container for quick testing:
|
||||||
|
# docker run -v `pwd`:/go/src/github.com/docker/docker --privileged -i -t docker bash
|
||||||
|
#
|
||||||
|
# # Run the test suite:
|
||||||
|
# docker run --privileged docker hack/make.sh test-unit test-integration-cli test-docker-py
|
||||||
|
#
|
||||||
|
# Note: AppArmor used to mess with privileged mode, but this is no longer
|
||||||
|
# the case. Therefore, you don't have to disable it anymore.
|
||||||
|
#
|
||||||
|
|
||||||
|
FROM aarch64/ubuntu:wily
|
||||||
|
|
||||||
|
# Packaged dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
apparmor \
|
||||||
|
aufs-tools \
|
||||||
|
automake \
|
||||||
|
bash-completion \
|
||||||
|
btrfs-tools \
|
||||||
|
build-essential \
|
||||||
|
cmake \
|
||||||
|
createrepo \
|
||||||
|
curl \
|
||||||
|
dpkg-sig \
|
||||||
|
g++ \
|
||||||
|
gcc \
|
||||||
|
git \
|
||||||
|
iptables \
|
||||||
|
jq \
|
||||||
|
libapparmor-dev \
|
||||||
|
libc6-dev \
|
||||||
|
libcap-dev \
|
||||||
|
libltdl-dev \
|
||||||
|
libsqlite3-dev \
|
||||||
|
libsystemd-dev \
|
||||||
|
mercurial \
|
||||||
|
net-tools \
|
||||||
|
parallel \
|
||||||
|
pkg-config \
|
||||||
|
python-dev \
|
||||||
|
python-mock \
|
||||||
|
python-pip \
|
||||||
|
python-websocket \
|
||||||
|
gccgo \
|
||||||
|
iproute2 \
|
||||||
|
iputils-ping \
|
||||||
|
vim-common \
|
||||||
|
--no-install-recommends
|
||||||
|
|
||||||
|
# Get lvm2 source for compiling statically
|
||||||
|
ENV LVM2_VERSION 2.02.103
|
||||||
|
RUN mkdir -p /usr/local/lvm2 \
|
||||||
|
&& curl -fsSL "https://mirrors.kernel.org/sourceware/lvm2/LVM2.${LVM2_VERSION}.tgz" \
|
||||||
|
| tar -xzC /usr/local/lvm2 --strip-components=1
|
||||||
|
# See https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
||||||
|
|
||||||
|
# Fix platform enablement in lvm2 to support aarch64 properly
|
||||||
|
RUN set -e \
|
||||||
|
&& for f in config.guess config.sub; do \
|
||||||
|
curl -fsSL -o "/usr/local/lvm2/autoconf/$f" "http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=$f;hb=HEAD"; \
|
||||||
|
done
|
||||||
|
# "arch.c:78:2: error: #error the arch code needs to know about your machine type"
|
||||||
|
|
||||||
|
# Compile and install lvm2
|
||||||
|
RUN cd /usr/local/lvm2 \
|
||||||
|
&& ./configure \
|
||||||
|
--build="$(gcc -print-multiarch)" \
|
||||||
|
--enable-static_link \
|
||||||
|
&& make device-mapper \
|
||||||
|
&& make install_device-mapper
|
||||||
|
# See https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||||
|
|
||||||
|
# Install seccomp: the version shipped in trusty is too old
|
||||||
|
ENV SECCOMP_VERSION 2.3.1
|
||||||
|
RUN set -x \
|
||||||
|
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||||
|
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||||
|
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||||
|
&& ( \
|
||||||
|
cd "$SECCOMP_PATH" \
|
||||||
|
&& ./configure --prefix=/usr/local \
|
||||||
|
&& make \
|
||||||
|
&& make install \
|
||||||
|
&& ldconfig \
|
||||||
|
) \
|
||||||
|
&& rm -rf "$SECCOMP_PATH"
|
||||||
|
|
||||||
|
# Install Go
|
||||||
|
# We don't have official binary tarballs for ARM64, eigher for Go or bootstrap,
|
||||||
|
# so we use gccgo as bootstrap to build Go from source code.
|
||||||
|
# We don't use the official ARMv6 released binaries as a GOROOT_BOOTSTRAP, because
|
||||||
|
# not all ARM64 platforms support 32-bit mode. 32-bit mode is optional for ARMv8.
|
||||||
|
ENV GO_VERSION 1.7.5
|
||||||
|
RUN mkdir /usr/src/go && curl -fsSL https://golang.org/dl/go${GO_VERSION}.src.tar.gz | tar -v -C /usr/src/go -xz --strip-components=1 \
|
||||||
|
&& cd /usr/src/go/src \
|
||||||
|
&& GOOS=linux GOARCH=arm64 GOROOT_BOOTSTRAP="$(go env GOROOT)" ./make.bash
|
||||||
|
|
||||||
|
ENV PATH /usr/src/go/bin:$PATH
|
||||||
|
ENV GOPATH /go
|
||||||
|
|
||||||
|
# Only install one version of the registry, because old version which support
|
||||||
|
# schema1 manifests is not working on ARM64, we should skip integration-cli
|
||||||
|
# tests for schema1 manifests on ARM64.
|
||||||
|
ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
|
||||||
|
RUN set -x \
|
||||||
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
|
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \
|
||||||
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
|
# Install notary and notary-server
|
||||||
|
ENV NOTARY_VERSION v0.4.2
|
||||||
|
RUN set -x \
|
||||||
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
|
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||||
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
|
# Get the "docker-py" source so we can run their integration tests
|
||||||
|
ENV DOCKER_PY_COMMIT e2655f658408f9ad1f62abdef3eb6ed43c0cf324
|
||||||
|
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||||
|
&& cd /docker-py \
|
||||||
|
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||||
|
&& pip install -r test-requirements.txt
|
||||||
|
|
||||||
|
# Set user.email so crosbymichael's in-container merge commits go smoothly
|
||||||
|
RUN git config --global user.email 'docker-dummy@example.com'
|
||||||
|
|
||||||
|
# Add an unprivileged user to be used for tests which need it
|
||||||
|
RUN groupadd -r docker
|
||||||
|
RUN useradd --create-home --gid docker unprivilegeduser
|
||||||
|
|
||||||
|
VOLUME /var/lib/docker
|
||||||
|
WORKDIR /go/src/github.com/docker/docker
|
||||||
|
ENV DOCKER_BUILDTAGS apparmor pkcs11 seccomp selinux
|
||||||
|
|
||||||
|
# Let us use a .bashrc file
|
||||||
|
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||||
|
|
||||||
|
# Register Docker's bash completion.
|
||||||
|
RUN ln -sv $PWD/contrib/completion/bash/docker /etc/bash_completion.d/docker
|
||||||
|
|
||||||
|
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||||
|
COPY contrib/download-frozen-image-v2.sh /go/src/github.com/docker/docker/contrib/
|
||||||
|
RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||||
|
aarch64/buildpack-deps:jessie@sha256:6aa1d6910791b7ac78265fd0798e5abd6cb3f27ae992f6f960f6c303ec9535f2 \
|
||||||
|
aarch64/busybox:latest@sha256:b23a6a37cf269dff6e46d2473b6e227afa42b037e6d23435f1d2bc40fc8c2828 \
|
||||||
|
aarch64/debian:jessie@sha256:4be74a41a7c70ebe887b634b11ffe516cf4fcd56864a54941e56bb49883c3170 \
|
||||||
|
aarch64/hello-world:latest@sha256:65a4a158587b307bb02db4de41b836addb0c35175bdc801367b1ac1ddeb9afda
|
||||||
|
# See also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||||
|
|
||||||
|
# Install tomlv, vndr, runc, containerd, tini, docker-proxy
|
||||||
|
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||||
|
COPY hack/dockerfile/binaries-commits /tmp/binaries-commits
|
||||||
|
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||||
|
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy
|
||||||
|
|
||||||
|
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||||
|
ENTRYPOINT ["hack/dind"]
|
||||||
|
|
||||||
|
# Upload docker source
|
||||||
|
COPY . /go/src/github.com/docker/docker
|
182
Dockerfile.armhf
Normal file
182
Dockerfile.armhf
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
# This file describes the standard way to build Docker on ARMv7, using docker
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# # Assemble the full dev environment. This is slow the first time.
|
||||||
|
# docker build -t docker -f Dockerfile.armhf .
|
||||||
|
#
|
||||||
|
# # Mount your source in an interactive container for quick testing:
|
||||||
|
# docker run -v `pwd`:/go/src/github.com/docker/docker --privileged -i -t docker bash
|
||||||
|
#
|
||||||
|
# # Run the test suite:
|
||||||
|
# docker run --privileged docker hack/make.sh test-unit test-integration-cli test-docker-py
|
||||||
|
#
|
||||||
|
# Note: AppArmor used to mess with privileged mode, but this is no longer
|
||||||
|
# the case. Therefore, you don't have to disable it anymore.
|
||||||
|
#
|
||||||
|
|
||||||
|
FROM armhf/debian:jessie
|
||||||
|
|
||||||
|
# allow replacing httpredir or deb mirror
|
||||||
|
ARG APT_MIRROR=deb.debian.org
|
||||||
|
RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
|
||||||
|
|
||||||
|
# Packaged dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
apparmor \
|
||||||
|
aufs-tools \
|
||||||
|
automake \
|
||||||
|
bash-completion \
|
||||||
|
btrfs-tools \
|
||||||
|
build-essential \
|
||||||
|
createrepo \
|
||||||
|
curl \
|
||||||
|
cmake \
|
||||||
|
dpkg-sig \
|
||||||
|
git \
|
||||||
|
iptables \
|
||||||
|
jq \
|
||||||
|
net-tools \
|
||||||
|
libapparmor-dev \
|
||||||
|
libcap-dev \
|
||||||
|
libltdl-dev \
|
||||||
|
libsqlite3-dev \
|
||||||
|
libsystemd-journal-dev \
|
||||||
|
libtool \
|
||||||
|
mercurial \
|
||||||
|
pkg-config \
|
||||||
|
python-dev \
|
||||||
|
python-mock \
|
||||||
|
python-pip \
|
||||||
|
python-websocket \
|
||||||
|
xfsprogs \
|
||||||
|
tar \
|
||||||
|
vim-common \
|
||||||
|
--no-install-recommends \
|
||||||
|
&& pip install awscli==1.10.15
|
||||||
|
|
||||||
|
# Get lvm2 source for compiling statically
|
||||||
|
ENV LVM2_VERSION 2.02.103
|
||||||
|
RUN mkdir -p /usr/local/lvm2 \
|
||||||
|
&& curl -fsSL "https://mirrors.kernel.org/sourceware/lvm2/LVM2.${LVM2_VERSION}.tgz" \
|
||||||
|
| tar -xzC /usr/local/lvm2 --strip-components=1
|
||||||
|
# See https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
||||||
|
|
||||||
|
# Compile and install lvm2
|
||||||
|
RUN cd /usr/local/lvm2 \
|
||||||
|
&& ./configure \
|
||||||
|
--build="$(gcc -print-multiarch)" \
|
||||||
|
--enable-static_link \
|
||||||
|
&& make device-mapper \
|
||||||
|
&& make install_device-mapper
|
||||||
|
# See https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||||
|
|
||||||
|
# Install Go
|
||||||
|
ENV GO_VERSION 1.7.5
|
||||||
|
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" \
|
||||||
|
| tar -xzC /usr/local
|
||||||
|
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||||
|
ENV GOPATH /go
|
||||||
|
|
||||||
|
# We're building for armhf, which is ARMv7, so let's be explicit about that
|
||||||
|
ENV GOARCH arm
|
||||||
|
ENV GOARM 7
|
||||||
|
|
||||||
|
# Dependency for golint
|
||||||
|
ENV GO_TOOLS_COMMIT 823804e1ae08dbb14eb807afc7db9993bc9e3cc3
|
||||||
|
RUN git clone https://github.com/golang/tools.git /go/src/golang.org/x/tools \
|
||||||
|
&& (cd /go/src/golang.org/x/tools && git checkout -q $GO_TOOLS_COMMIT)
|
||||||
|
|
||||||
|
# Grab Go's lint tool
|
||||||
|
ENV GO_LINT_COMMIT 32a87160691b3c96046c0c678fe57c5bef761456
|
||||||
|
RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint \
|
||||||
|
&& (cd /go/src/github.com/golang/lint && git checkout -q $GO_LINT_COMMIT) \
|
||||||
|
&& go install -v github.com/golang/lint/golint
|
||||||
|
|
||||||
|
# Install seccomp: the version shipped in trusty is too old
|
||||||
|
ENV SECCOMP_VERSION 2.3.1
|
||||||
|
RUN set -x \
|
||||||
|
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||||
|
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||||
|
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||||
|
&& ( \
|
||||||
|
cd "$SECCOMP_PATH" \
|
||||||
|
&& ./configure --prefix=/usr/local \
|
||||||
|
&& make \
|
||||||
|
&& make install \
|
||||||
|
&& ldconfig \
|
||||||
|
) \
|
||||||
|
&& rm -rf "$SECCOMP_PATH"
|
||||||
|
|
||||||
|
# Install two versions of the registry. The first is an older version that
|
||||||
|
# only supports schema1 manifests. The second is a newer version that supports
|
||||||
|
# both. This allows integration-cli tests to cover push/pull with both schema1
|
||||||
|
# and schema2 manifests.
|
||||||
|
ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||||
|
ENV REGISTRY_COMMIT cb08de17d74bef86ce6c5abe8b240e282f5750be
|
||||||
|
RUN set -x \
|
||||||
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
|
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/registry-v2-schema1 github.com/docker/distribution/cmd/registry \
|
||||||
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
|
# Install notary and notary-server
|
||||||
|
ENV NOTARY_VERSION v0.4.2
|
||||||
|
RUN set -x \
|
||||||
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
|
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||||
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
|
# Get the "docker-py" source so we can run their integration tests
|
||||||
|
ENV DOCKER_PY_COMMIT e2655f658408f9ad1f62abdef3eb6ed43c0cf324
|
||||||
|
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||||
|
&& cd /docker-py \
|
||||||
|
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||||
|
&& pip install -r test-requirements.txt
|
||||||
|
|
||||||
|
# Set user.email so crosbymichael's in-container merge commits go smoothly
|
||||||
|
RUN git config --global user.email 'docker-dummy@example.com'
|
||||||
|
|
||||||
|
# Add an unprivileged user to be used for tests which need it
|
||||||
|
RUN groupadd -r docker
|
||||||
|
RUN useradd --create-home --gid docker unprivilegeduser
|
||||||
|
|
||||||
|
VOLUME /var/lib/docker
|
||||||
|
WORKDIR /go/src/github.com/docker/docker
|
||||||
|
ENV DOCKER_BUILDTAGS apparmor pkcs11 seccomp selinux
|
||||||
|
|
||||||
|
# Let us use a .bashrc file
|
||||||
|
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||||
|
|
||||||
|
# Register Docker's bash completion.
|
||||||
|
RUN ln -sv $PWD/contrib/completion/bash/docker /etc/bash_completion.d/docker
|
||||||
|
|
||||||
|
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||||
|
COPY contrib/download-frozen-image-v2.sh /go/src/github.com/docker/docker/contrib/
|
||||||
|
RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||||
|
armhf/buildpack-deps:jessie@sha256:ca6cce8e5bf5c952129889b5cc15cd6aa8d995d77e55e3749bbaadae50e476cb \
|
||||||
|
armhf/busybox:latest@sha256:d98a7343ac750ffe387e3d514f8521ba69846c216778919b01414b8617cfb3d4 \
|
||||||
|
armhf/debian:jessie@sha256:4a2187483f04a84f9830910fe3581d69b3c985cc045d9f01d8e2f3795b28107b \
|
||||||
|
armhf/hello-world:latest@sha256:161dcecea0225975b2ad5f768058212c1e0d39e8211098666ffa1ac74cfb7791
|
||||||
|
# See also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||||
|
|
||||||
|
# Install tomlv, vndr, runc, containerd, tini, docker-proxy
|
||||||
|
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||||
|
COPY hack/dockerfile/binaries-commits /tmp/binaries-commits
|
||||||
|
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||||
|
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy
|
||||||
|
|
||||||
|
ENTRYPOINT ["hack/dind"]
|
||||||
|
|
||||||
|
# Upload docker source
|
||||||
|
COPY . /go/src/github.com/docker/docker
|
188
Dockerfile.ppc64le
Normal file
188
Dockerfile.ppc64le
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
# This file describes the standard way to build Docker on ppc64le, using docker
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# # Assemble the full dev environment. This is slow the first time.
|
||||||
|
# docker build -t docker -f Dockerfile.ppc64le .
|
||||||
|
#
|
||||||
|
# # Mount your source in an interactive container for quick testing:
|
||||||
|
# docker run -v `pwd`:/go/src/github.com/docker/docker --privileged -i -t docker bash
|
||||||
|
#
|
||||||
|
# # Run the test suite:
|
||||||
|
# docker run --privileged docker hack/make.sh test-unit test-integration-cli test-docker-py
|
||||||
|
#
|
||||||
|
# Note: AppArmor used to mess with privileged mode, but this is no longer
|
||||||
|
# the case. Therefore, you don't have to disable it anymore.
|
||||||
|
#
|
||||||
|
|
||||||
|
FROM ppc64le/debian:jessie
|
||||||
|
|
||||||
|
# allow replacing httpredir or deb mirror
|
||||||
|
ARG APT_MIRROR=deb.debian.org
|
||||||
|
RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
|
||||||
|
|
||||||
|
# Packaged dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
apparmor \
|
||||||
|
aufs-tools \
|
||||||
|
automake \
|
||||||
|
bash-completion \
|
||||||
|
btrfs-tools \
|
||||||
|
build-essential \
|
||||||
|
cmake \
|
||||||
|
createrepo \
|
||||||
|
curl \
|
||||||
|
dpkg-sig \
|
||||||
|
git \
|
||||||
|
iptables \
|
||||||
|
jq \
|
||||||
|
net-tools \
|
||||||
|
libapparmor-dev \
|
||||||
|
libcap-dev \
|
||||||
|
libltdl-dev \
|
||||||
|
libsqlite3-dev \
|
||||||
|
libsystemd-journal-dev \
|
||||||
|
libtool \
|
||||||
|
mercurial \
|
||||||
|
pkg-config \
|
||||||
|
python-dev \
|
||||||
|
python-mock \
|
||||||
|
python-pip \
|
||||||
|
python-websocket \
|
||||||
|
xfsprogs \
|
||||||
|
tar \
|
||||||
|
vim-common \
|
||||||
|
--no-install-recommends
|
||||||
|
|
||||||
|
# Get lvm2 source for compiling statically
|
||||||
|
ENV LVM2_VERSION 2.02.103
|
||||||
|
RUN mkdir -p /usr/local/lvm2 \
|
||||||
|
&& curl -fsSL "https://mirrors.kernel.org/sourceware/lvm2/LVM2.${LVM2_VERSION}.tgz" \
|
||||||
|
| tar -xzC /usr/local/lvm2 --strip-components=1
|
||||||
|
# See https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
||||||
|
|
||||||
|
# Fix platform enablement in lvm2 to support ppc64le properly
|
||||||
|
RUN set -e \
|
||||||
|
&& for f in config.guess config.sub; do \
|
||||||
|
curl -fsSL -o "/usr/local/lvm2/autoconf/$f" "http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=$f;hb=HEAD"; \
|
||||||
|
done
|
||||||
|
# "arch.c:78:2: error: #error the arch code needs to know about your machine type"
|
||||||
|
|
||||||
|
# Compile and install lvm2
|
||||||
|
RUN cd /usr/local/lvm2 \
|
||||||
|
&& ./configure \
|
||||||
|
--build="$(gcc -print-multiarch)" \
|
||||||
|
--enable-static_link \
|
||||||
|
&& make device-mapper \
|
||||||
|
&& make install_device-mapper
|
||||||
|
# See https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||||
|
|
||||||
|
# Install seccomp: the version shipped in jessie is too old
|
||||||
|
ENV SECCOMP_VERSION 2.3.1
|
||||||
|
RUN set -x \
|
||||||
|
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||||
|
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||||
|
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||||
|
&& ( \
|
||||||
|
cd "$SECCOMP_PATH" \
|
||||||
|
&& ./configure --prefix=/usr/local \
|
||||||
|
&& make \
|
||||||
|
&& make install \
|
||||||
|
&& ldconfig \
|
||||||
|
) \
|
||||||
|
&& rm -rf "$SECCOMP_PATH"
|
||||||
|
|
||||||
|
|
||||||
|
# Install Go
|
||||||
|
# NOTE: official ppc64le go binaries weren't available until go 1.6.4 and 1.7.4
|
||||||
|
ENV GO_VERSION 1.7.5
|
||||||
|
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" \
|
||||||
|
| tar -xzC /usr/local
|
||||||
|
|
||||||
|
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||||
|
ENV GOPATH /go
|
||||||
|
|
||||||
|
# Dependency for golint
|
||||||
|
ENV GO_TOOLS_COMMIT 823804e1ae08dbb14eb807afc7db9993bc9e3cc3
|
||||||
|
RUN git clone https://github.com/golang/tools.git /go/src/golang.org/x/tools \
|
||||||
|
&& (cd /go/src/golang.org/x/tools && git checkout -q $GO_TOOLS_COMMIT)
|
||||||
|
|
||||||
|
# Grab Go's lint tool
|
||||||
|
ENV GO_LINT_COMMIT 32a87160691b3c96046c0c678fe57c5bef761456
|
||||||
|
RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint \
|
||||||
|
&& (cd /go/src/github.com/golang/lint && git checkout -q $GO_LINT_COMMIT) \
|
||||||
|
&& go install -v github.com/golang/lint/golint
|
||||||
|
|
||||||
|
# Install two versions of the registry. The first is an older version that
|
||||||
|
# only supports schema1 manifests. The second is a newer version that supports
|
||||||
|
# both. This allows integration-cli tests to cover push/pull with both schema1
|
||||||
|
# and schema2 manifests.
|
||||||
|
ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||||
|
ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
|
||||||
|
RUN set -x \
|
||||||
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
|
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/registry-v2-schema1 github.com/docker/distribution/cmd/registry \
|
||||||
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
|
# Install notary and notary-server
|
||||||
|
ENV NOTARY_VERSION v0.4.2
|
||||||
|
RUN set -x \
|
||||||
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
|
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||||
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
|
# Get the "docker-py" source so we can run their integration tests
|
||||||
|
ENV DOCKER_PY_COMMIT e2655f658408f9ad1f62abdef3eb6ed43c0cf324
|
||||||
|
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||||
|
&& cd /docker-py \
|
||||||
|
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||||
|
&& pip install -r test-requirements.txt
|
||||||
|
|
||||||
|
# Set user.email so crosbymichael's in-container merge commits go smoothly
|
||||||
|
RUN git config --global user.email 'docker-dummy@example.com'
|
||||||
|
|
||||||
|
# Add an unprivileged user to be used for tests which need it
|
||||||
|
RUN groupadd -r docker
|
||||||
|
RUN useradd --create-home --gid docker unprivilegeduser
|
||||||
|
|
||||||
|
VOLUME /var/lib/docker
|
||||||
|
WORKDIR /go/src/github.com/docker/docker
|
||||||
|
ENV DOCKER_BUILDTAGS apparmor pkcs11 seccomp selinux
|
||||||
|
|
||||||
|
# Let us use a .bashrc file
|
||||||
|
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||||
|
|
||||||
|
# Register Docker's bash completion.
|
||||||
|
RUN ln -sv $PWD/contrib/completion/bash/docker /etc/bash_completion.d/docker
|
||||||
|
|
||||||
|
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||||
|
COPY contrib/download-frozen-image-v2.sh /go/src/github.com/docker/docker/contrib/
|
||||||
|
RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||||
|
ppc64le/buildpack-deps:jessie@sha256:902bfe4ef1389f94d143d64516dd50a2de75bca2e66d4a44b1d73f63ddf05dda \
|
||||||
|
ppc64le/busybox:latest@sha256:38bb82085248d5a3c24bd7a5dc146f2f2c191e189da0441f1c2ca560e3fc6f1b \
|
||||||
|
ppc64le/debian:jessie@sha256:412845f51b6ab662afba71bc7a716e20fdb9b84f185d180d4c7504f8a75c4f91 \
|
||||||
|
ppc64le/hello-world:latest@sha256:186a40a9a02ca26df0b6c8acdfb8ac2f3ae6678996a838f977e57fac9d963974
|
||||||
|
# See also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||||
|
|
||||||
|
# Install tomlv, vndr, runc, containerd, tini, docker-proxy
|
||||||
|
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||||
|
COPY hack/dockerfile/binaries-commits /tmp/binaries-commits
|
||||||
|
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||||
|
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy
|
||||||
|
|
||||||
|
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||||
|
ENTRYPOINT ["hack/dind"]
|
||||||
|
|
||||||
|
# Upload docker source
|
||||||
|
COPY . /go/src/github.com/docker/docker
|
190
Dockerfile.s390x
Normal file
190
Dockerfile.s390x
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
# This file describes the standard way to build Docker on s390x, using docker
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# # Assemble the full dev environment. This is slow the first time.
|
||||||
|
# docker build -t docker -f Dockerfile.s390x .
|
||||||
|
#
|
||||||
|
# # Mount your source in an interactive container for quick testing:
|
||||||
|
# docker run -v `pwd`:/go/src/github.com/docker/docker --privileged -i -t docker bash
|
||||||
|
#
|
||||||
|
# # Run the test suite:
|
||||||
|
# docker run --privileged docker hack/make.sh test-unit test-integration-cli test-docker-py
|
||||||
|
#
|
||||||
|
# Note: AppArmor used to mess with privileged mode, but this is no longer
|
||||||
|
# the case. Therefore, you don't have to disable it anymore.
|
||||||
|
#
|
||||||
|
|
||||||
|
FROM s390x/gcc:6.1
|
||||||
|
|
||||||
|
# Packaged dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
apparmor \
|
||||||
|
aufs-tools \
|
||||||
|
automake \
|
||||||
|
bash-completion \
|
||||||
|
btrfs-tools \
|
||||||
|
build-essential \
|
||||||
|
cmake \
|
||||||
|
createrepo \
|
||||||
|
curl \
|
||||||
|
dpkg-sig \
|
||||||
|
git \
|
||||||
|
iptables \
|
||||||
|
jq \
|
||||||
|
net-tools \
|
||||||
|
libapparmor-dev \
|
||||||
|
libcap-dev \
|
||||||
|
libltdl-dev \
|
||||||
|
libsqlite3-dev \
|
||||||
|
libsystemd-journal-dev \
|
||||||
|
libtool \
|
||||||
|
mercurial \
|
||||||
|
pkg-config \
|
||||||
|
python-dev \
|
||||||
|
python-mock \
|
||||||
|
python-pip \
|
||||||
|
python-websocket \
|
||||||
|
xfsprogs \
|
||||||
|
tar \
|
||||||
|
vim-common \
|
||||||
|
--no-install-recommends
|
||||||
|
|
||||||
|
# glibc in Debian has a bug specific to s390x that won't be fixed until Debian 8.6 is released
|
||||||
|
# - https://github.com/docker/docker/issues/24748
|
||||||
|
# - https://sourceware.org/git/?p=glibc.git;a=commit;h=890b7a4b33d482b5c768ab47d70758b80227e9bc
|
||||||
|
# - https://sourceware.org/git/?p=glibc.git;a=commit;h=2e807f29595eb5b1e5d0decc6e356a3562ecc58e
|
||||||
|
RUN echo 'deb http://httpredir.debian.org/debian jessie-proposed-updates main' >> /etc/apt/sources.list.d/pu.list \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y libc6 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install seccomp: the version shipped in jessie is too old
|
||||||
|
ENV SECCOMP_VERSION 2.3.1
|
||||||
|
RUN set -x \
|
||||||
|
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||||
|
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||||
|
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||||
|
&& ( \
|
||||||
|
cd "$SECCOMP_PATH" \
|
||||||
|
&& ./configure --prefix=/usr/local \
|
||||||
|
&& make \
|
||||||
|
&& make install \
|
||||||
|
&& ldconfig \
|
||||||
|
) \
|
||||||
|
&& rm -rf "$SECCOMP_PATH"
|
||||||
|
|
||||||
|
# Get lvm2 source for compiling statically
|
||||||
|
ENV LVM2_VERSION 2.02.103
|
||||||
|
RUN mkdir -p /usr/local/lvm2 \
|
||||||
|
&& curl -fsSL "https://mirrors.kernel.org/sourceware/lvm2/LVM2.${LVM2_VERSION}.tgz" \
|
||||||
|
| tar -xzC /usr/local/lvm2 --strip-components=1
|
||||||
|
# See https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
||||||
|
|
||||||
|
# Fix platform enablement in lvm2 to support s390x properly
|
||||||
|
RUN set -e \
|
||||||
|
&& for f in config.guess config.sub; do \
|
||||||
|
curl -fsSL -o "/usr/local/lvm2/autoconf/$f" "http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=$f;hb=HEAD"; \
|
||||||
|
done
|
||||||
|
# "arch.c:78:2: error: #error the arch code needs to know about your machine type"
|
||||||
|
|
||||||
|
# Compile and install lvm2
|
||||||
|
RUN cd /usr/local/lvm2 \
|
||||||
|
&& ./configure \
|
||||||
|
--build="$(gcc -print-multiarch)" \
|
||||||
|
--enable-static_link \
|
||||||
|
&& make device-mapper \
|
||||||
|
&& make install_device-mapper
|
||||||
|
# See https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||||
|
|
||||||
|
ENV GO_VERSION 1.7.5
|
||||||
|
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" \
|
||||||
|
| tar -xzC /usr/local
|
||||||
|
|
||||||
|
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||||
|
ENV GOPATH /go
|
||||||
|
|
||||||
|
# Dependency for golint
|
||||||
|
ENV GO_TOOLS_COMMIT 823804e1ae08dbb14eb807afc7db9993bc9e3cc3
|
||||||
|
RUN git clone https://github.com/golang/tools.git /go/src/golang.org/x/tools \
|
||||||
|
&& (cd /go/src/golang.org/x/tools && git checkout -q $GO_TOOLS_COMMIT)
|
||||||
|
|
||||||
|
# Grab Go's lint tool
|
||||||
|
ENV GO_LINT_COMMIT 32a87160691b3c96046c0c678fe57c5bef761456
|
||||||
|
RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint \
|
||||||
|
&& (cd /go/src/github.com/golang/lint && git checkout -q $GO_LINT_COMMIT) \
|
||||||
|
&& go install -v github.com/golang/lint/golint
|
||||||
|
|
||||||
|
# Install two versions of the registry. The first is an older version that
|
||||||
|
# only supports schema1 manifests. The second is a newer version that supports
|
||||||
|
# both. This allows integration-cli tests to cover push/pull with both schema1
|
||||||
|
# and schema2 manifests.
|
||||||
|
ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||||
|
ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
|
||||||
|
RUN set -x \
|
||||||
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
|
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/registry-v2-schema1 github.com/docker/distribution/cmd/registry \
|
||||||
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
|
# Install notary and notary-server
|
||||||
|
ENV NOTARY_VERSION v0.4.2
|
||||||
|
RUN set -x \
|
||||||
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
|
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||||
|
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||||
|
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||||
|
go build -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||||
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
|
# Get the "docker-py" source so we can run their integration tests
|
||||||
|
ENV DOCKER_PY_COMMIT e2655f658408f9ad1f62abdef3eb6ed43c0cf324
|
||||||
|
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||||
|
&& cd /docker-py \
|
||||||
|
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||||
|
&& pip install -r test-requirements.txt
|
||||||
|
|
||||||
|
# Set user.email so crosbymichael's in-container merge commits go smoothly
|
||||||
|
RUN git config --global user.email 'docker-dummy@example.com'
|
||||||
|
|
||||||
|
# Add an unprivileged user to be used for tests which need it
|
||||||
|
RUN groupadd -r docker
|
||||||
|
RUN useradd --create-home --gid docker unprivilegeduser
|
||||||
|
|
||||||
|
VOLUME /var/lib/docker
|
||||||
|
WORKDIR /go/src/github.com/docker/docker
|
||||||
|
ENV DOCKER_BUILDTAGS apparmor selinux seccomp
|
||||||
|
|
||||||
|
# Let us use a .bashrc file
|
||||||
|
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||||
|
|
||||||
|
# Register Docker's bash completion.
|
||||||
|
RUN ln -sv $PWD/contrib/completion/bash/docker /etc/bash_completion.d/docker
|
||||||
|
|
||||||
|
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||||
|
COPY contrib/download-frozen-image-v2.sh /go/src/github.com/docker/docker/contrib/
|
||||||
|
RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||||
|
s390x/buildpack-deps:jessie@sha256:4d1381224acaca6c4bfe3604de3af6972083a8558a99672cb6989c7541780099 \
|
||||||
|
s390x/busybox:latest@sha256:dd61522c983884a66ed72d60301925889028c6d2d5e0220a8fe1d9b4c6a4f01b \
|
||||||
|
s390x/debian:jessie@sha256:b74c863400909eff3c5e196cac9bfd1f6333ce47aae6a38398d87d5875da170a \
|
||||||
|
s390x/hello-world:latest@sha256:780d80b3a7677c3788c0d5cd9168281320c8d4a6d9183892d8ee5cdd610f5699
|
||||||
|
# See also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||||
|
|
||||||
|
# Install tomlv, vndr, runc, containerd, tini, docker-proxy
|
||||||
|
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||||
|
COPY hack/dockerfile/binaries-commits /tmp/binaries-commits
|
||||||
|
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||||
|
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy
|
||||||
|
|
||||||
|
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||||
|
ENTRYPOINT ["hack/dind"]
|
||||||
|
|
||||||
|
# Upload docker source
|
||||||
|
COPY . /go/src/github.com/docker/docker
|
|
@ -1,48 +1,72 @@
|
||||||
# docker build -t docker:simple -f Dockerfile.simple .
|
# docker build -t docker:simple -f Dockerfile.simple .
|
||||||
# docker run --rm docker:simple hack/make.sh dynbinary
|
# docker run --rm docker:simple hack/make.sh dynbinary
|
||||||
# docker run --rm --privileged docker:simple hack/dind hack/make.sh test-unit
|
# docker run --rm --privileged docker:simple hack/dind hack/make.sh test-unit
|
||||||
# docker run --rm --privileged -v /var/lib/docker docker:simple hack/dind hack/make.sh dynbinary test-integration
|
# docker run --rm --privileged -v /var/lib/docker docker:simple hack/dind hack/make.sh dynbinary test-integration-cli
|
||||||
|
|
||||||
# This represents the bare minimum required to build and test Docker.
|
# This represents the bare minimum required to build and test Docker.
|
||||||
|
|
||||||
ARG GO_VERSION=1.21.9
|
FROM debian:jessie
|
||||||
|
|
||||||
ARG BASE_DEBIAN_DISTRO="bookworm"
|
# allow replacing httpredir or deb mirror
|
||||||
ARG GOLANG_IMAGE="golang:${GO_VERSION}-${BASE_DEBIAN_DISTRO}"
|
ARG APT_MIRROR=deb.debian.org
|
||||||
|
RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
|
||||||
FROM ${GOLANG_IMAGE}
|
|
||||||
ENV GO111MODULE=off
|
|
||||||
ENV GOTOOLCHAIN=local
|
|
||||||
|
|
||||||
# Compile and runtime deps
|
# Compile and runtime deps
|
||||||
# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#build-dependencies
|
# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#build-dependencies
|
||||||
# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#runtime-dependencies
|
# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#runtime-dependencies
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
btrfs-tools \
|
||||||
build-essential \
|
build-essential \
|
||||||
curl \
|
curl \
|
||||||
cmake \
|
cmake \
|
||||||
|
gcc \
|
||||||
git \
|
git \
|
||||||
libapparmor-dev \
|
libapparmor-dev \
|
||||||
libseccomp-dev \
|
libdevmapper-dev \
|
||||||
|
libsqlite3-dev \
|
||||||
|
\
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
e2fsprogs \
|
e2fsprogs \
|
||||||
iptables \
|
iptables \
|
||||||
pkg-config \
|
|
||||||
pigz \
|
|
||||||
procps \
|
procps \
|
||||||
xfsprogs \
|
xfsprogs \
|
||||||
xz-utils \
|
xz-utils \
|
||||||
\
|
\
|
||||||
|
aufs-tools \
|
||||||
vim-common \
|
vim-common \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install seccomp: the version shipped in trusty is too old
|
||||||
|
ENV SECCOMP_VERSION 2.3.1
|
||||||
|
RUN set -x \
|
||||||
|
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||||
|
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||||
|
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||||
|
&& ( \
|
||||||
|
cd "$SECCOMP_PATH" \
|
||||||
|
&& ./configure --prefix=/usr/local \
|
||||||
|
&& make \
|
||||||
|
&& make install \
|
||||||
|
&& ldconfig \
|
||||||
|
) \
|
||||||
|
&& rm -rf "$SECCOMP_PATH"
|
||||||
|
|
||||||
|
# Install Go
|
||||||
|
# IMPORTANT: If the version of Go is updated, the Windows to Linux CI machines
|
||||||
|
# will need updating, to avoid errors. Ping #docker-maintainers on IRC
|
||||||
|
# with a heads-up.
|
||||||
|
ENV GO_VERSION 1.7.5
|
||||||
|
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" \
|
||||||
|
| tar -xzC /usr/local
|
||||||
|
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||||
|
ENV GOPATH /go
|
||||||
|
ENV CGO_LDFLAGS -L/lib
|
||||||
|
|
||||||
# Install runc, containerd, tini and docker-proxy
|
# Install runc, containerd, tini and docker-proxy
|
||||||
# Please edit hack/dockerfile/install/<name>.installer to update them.
|
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||||
COPY hack/dockerfile/install hack/dockerfile/install
|
COPY hack/dockerfile/binaries-commits /tmp/binaries-commits
|
||||||
RUN for i in runc containerd tini proxy dockercli; \
|
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||||
do hack/dockerfile/install/install.sh $i; \
|
RUN /tmp/install-binaries.sh runc containerd tini proxy
|
||||||
done
|
|
||||||
ENV PATH=/usr/local/cli:$PATH
|
|
||||||
|
|
||||||
ENV AUTO_GOPATH 1
|
ENV AUTO_GOPATH 1
|
||||||
WORKDIR /usr/src/docker
|
WORKDIR /usr/src/docker
|
||||||
|
|
20
Dockerfile.solaris
Normal file
20
Dockerfile.solaris
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Defines an image that hosts a native Docker build environment for Solaris
|
||||||
|
# TODO: Improve stub
|
||||||
|
|
||||||
|
FROM solaris:latest
|
||||||
|
|
||||||
|
# compile and runtime deps
|
||||||
|
RUN pkg install --accept \
|
||||||
|
git \
|
||||||
|
gnu-coreutils \
|
||||||
|
gnu-make \
|
||||||
|
gnu-tar \
|
||||||
|
diagnostic/top \
|
||||||
|
golang \
|
||||||
|
library/golang/* \
|
||||||
|
developer/gcc-*
|
||||||
|
|
||||||
|
ENV GOPATH /go/:/usr/lib/gocode/1.5/
|
||||||
|
ENV DOCKER_CROSSPLATFORMS solaris/amd64
|
||||||
|
WORKDIR /go/src/github.com/docker/docker
|
||||||
|
COPY . /go/src/github.com/docker/docker
|
|
@ -45,8 +45,8 @@
|
||||||
#
|
#
|
||||||
# 1. Clone the sources from github.com:
|
# 1. Clone the sources from github.com:
|
||||||
#
|
#
|
||||||
# >> git clone https://github.com/docker/docker.git C:\gopath\src\github.com\docker\docker
|
# >> git clone https://github.com/docker/docker.git C:\go\src\github.com\docker\docker
|
||||||
# >> Cloning into 'C:\gopath\src\github.com\docker\docker'...
|
# >> Cloning into 'C:\go\src\github.com\docker\docker'...
|
||||||
# >> remote: Counting objects: 186216, done.
|
# >> remote: Counting objects: 186216, done.
|
||||||
# >> remote: Compressing objects: 100% (21/21), done.
|
# >> remote: Compressing objects: 100% (21/21), done.
|
||||||
# >> remote: Total 186216 (delta 5), reused 0 (delta 0), pack-reused 186195
|
# >> remote: Total 186216 (delta 5), reused 0 (delta 0), pack-reused 186195
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
#
|
#
|
||||||
# 2. Change directory to the cloned docker sources:
|
# 2. Change directory to the cloned docker sources:
|
||||||
#
|
#
|
||||||
# >> cd C:\gopath\src\github.com\docker\docker
|
# >> cd C:\go\src\github.com\docker\docker
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# 3. Build a docker image with the components required to build the docker binaries from source
|
# 3. Build a docker image with the components required to build the docker binaries from source
|
||||||
|
@ -71,16 +71,15 @@
|
||||||
#
|
#
|
||||||
# 4. Build the docker executable binaries by running one of the following:
|
# 4. Build the docker executable binaries by running one of the following:
|
||||||
#
|
#
|
||||||
# >> $DOCKER_GITCOMMIT=(git rev-parse --short HEAD)
|
# >> docker run --name binaries nativebuildimage hack\make.ps1 -Binary
|
||||||
# >> docker run --name binaries -e DOCKER_GITCOMMIT=$DOCKER_GITCOMMIT nativebuildimage hack\make.ps1 -Binary
|
# >> docker run --name binaries -m 2GB nativebuildimage hack\make.ps1 -Binary (if using Hyper-V containers)
|
||||||
# >> docker run --name binaries -e DOCKER_GITCOMMIT=$DOCKER_GITCOMMIT -m 2GB nativebuildimage hack\make.ps1 -Binary (if using Hyper-V containers)
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# 5. Copy the binaries out of the container, replacing HostPath with an appropriate destination
|
# 5. Copy the binaries out of the container, replacing HostPath with an appropriate destination
|
||||||
# folder on the host system where you want the binaries to be located.
|
# folder on the host system where you want the binaries to be located.
|
||||||
#
|
#
|
||||||
# >> docker cp binaries:C:\gopath\src\github.com\docker\docker\bundles\docker.exe C:\HostPath\docker.exe
|
# >> docker cp binaries:C:\go\src\github.com\docker\docker\bundles\docker.exe C:\HostPath\docker.exe
|
||||||
# >> docker cp binaries:C:\gopath\src\github.com\docker\docker\bundles\dockerd.exe C:\HostPath\dockerd.exe
|
# >> docker cp binaries:C:\go\src\github.com\docker\docker\bundles\dockerd.exe C:\HostPath\dockerd.exe
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# 6. (Optional) Remove the interim container holding the built executable binaries:
|
# 6. (Optional) Remove the interim container holding the built executable binaries:
|
||||||
|
@ -99,14 +98,19 @@
|
||||||
# -----------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# The validation tests can only run directly on the host. This is because they calculate
|
# The validation tests can either run in a container, or directly on the host. To run in a
|
||||||
# information from the git repo, but the .git directory is not passed into the image as
|
# container, ensure you have created the nativebuildimage above. Then run one of the
|
||||||
# it is excluded via .dockerignore. Run the following from a Windows PowerShell prompt
|
# following from an (elevated) Windows PowerShell prompt:
|
||||||
# (elevation is not required): (Note Go must be installed to run these tests)
|
#
|
||||||
|
# >> docker run --rm nativebuildimage hack\make.ps1 -DCO -PkgImports -GoFormat
|
||||||
|
# >> docker run --rm -m 2GB nativebuildimage hack\make.ps1 -DCO -PkgImports -GoFormat (if using Hyper-V containers)
|
||||||
|
|
||||||
|
# To run the validation tests on the host, from the root of the repository, run the
|
||||||
|
# following from a Windows PowerShell prompt (elevation is not required): (Note Go
|
||||||
|
# must be installed to run these tests)
|
||||||
#
|
#
|
||||||
# >> hack\make.ps1 -DCO -PkgImports -GoFormat
|
# >> hack\make.ps1 -DCO -PkgImports -GoFormat
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@ -120,7 +124,7 @@
|
||||||
# -----------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# To run unit tests and binary build, ensure you have created the nativebuildimage above. Then
|
# To run all tests and binary build, ensure you have created the nativebuildimage above. Then
|
||||||
# run one of the following from an (elevated) Windows PowerShell prompt:
|
# run one of the following from an (elevated) Windows PowerShell prompt:
|
||||||
#
|
#
|
||||||
# >> docker run nativebuildimage hack\make.ps1 -All
|
# >> docker run nativebuildimage hack\make.ps1 -All
|
||||||
|
@ -132,7 +136,7 @@
|
||||||
# Important notes:
|
# Important notes:
|
||||||
# ---------------
|
# ---------------
|
||||||
#
|
#
|
||||||
# Don't attempt to use a bind mount to pass a local directory as the bundles target
|
# Don't attempt to use a bind-mount to pass a local directory as the bundles target
|
||||||
# directory. It does not work (golang attempts for follow a mapped folder incorrectly).
|
# directory. It does not work (golang attempts for follow a mapped folder incorrectly).
|
||||||
# Instead, use docker cp as per the example.
|
# Instead, use docker cp as per the example.
|
||||||
#
|
#
|
||||||
|
@ -147,40 +151,29 @@
|
||||||
# The docker integration tests do not currently run in a container on Windows, predominantly
|
# The docker integration tests do not currently run in a container on Windows, predominantly
|
||||||
# due to Windows not supporting privileged mode, so anything using a volume would fail.
|
# due to Windows not supporting privileged mode, so anything using a volume would fail.
|
||||||
# They (along with the rest of the docker CI suite) can be run using
|
# They (along with the rest of the docker CI suite) can be run using
|
||||||
# https://github.com/kevpar/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1.
|
# https://github.com/jhowardmsft/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1.
|
||||||
#
|
#
|
||||||
# -----------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# The number of build steps below are explicitly minimised to improve performance.
|
# The number of build steps below are explicitly minimised to improve performance.
|
||||||
|
FROM microsoft/windowsservercore
|
||||||
ARG WINDOWS_BASE_IMAGE=mcr.microsoft.com/windows/servercore
|
|
||||||
ARG WINDOWS_BASE_IMAGE_TAG=ltsc2022
|
|
||||||
FROM ${WINDOWS_BASE_IMAGE}:${WINDOWS_BASE_IMAGE_TAG}
|
|
||||||
|
|
||||||
# Use PowerShell as the default shell
|
# Use PowerShell as the default shell
|
||||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
SHELL ["powershell", "-command"]
|
||||||
|
|
||||||
ARG GO_VERSION=1.21.9
|
|
||||||
ARG GOTESTSUM_VERSION=v1.8.2
|
|
||||||
ARG GOWINRES_VERSION=v0.3.1
|
|
||||||
ARG CONTAINERD_VERSION=v1.7.15
|
|
||||||
|
|
||||||
# Environment variable notes:
|
# Environment variable notes:
|
||||||
# - GO_VERSION must be consistent with 'Dockerfile' used by Linux.
|
# - GO_VERSION must be consistent with 'Dockerfile' used by Linux.
|
||||||
# - CONTAINERD_VERSION must be consistent with 'hack/dockerfile/install/containerd.installer' used by Linux.
|
|
||||||
# - FROM_DOCKERFILE is used for detection of building within a container.
|
# - FROM_DOCKERFILE is used for detection of building within a container.
|
||||||
ENV GO_VERSION=${GO_VERSION} `
|
ENV GO_VERSION=1.7.5 `
|
||||||
CONTAINERD_VERSION=${CONTAINERD_VERSION} `
|
GIT_VERSION=2.11.0 `
|
||||||
GIT_VERSION=2.11.1 `
|
GOPATH=C:\go `
|
||||||
GOPATH=C:\gopath `
|
FROM_DOCKERFILE=1
|
||||||
GO111MODULE=off `
|
|
||||||
GOTOOLCHAIN=local `
|
|
||||||
FROM_DOCKERFILE=1 `
|
|
||||||
GOTESTSUM_VERSION=${GOTESTSUM_VERSION} `
|
|
||||||
GOWINRES_VERSION=${GOWINRES_VERSION}
|
|
||||||
|
|
||||||
RUN `
|
RUN `
|
||||||
|
$ErrorActionPreference = 'Stop'; `
|
||||||
|
$ProgressPreference = 'SilentlyContinue'; `
|
||||||
|
`
|
||||||
Function Test-Nano() { `
|
Function Test-Nano() { `
|
||||||
$EditionId = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name 'EditionID').EditionId; `
|
$EditionId = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name 'EditionID').EditionId; `
|
||||||
return (($EditionId -eq 'ServerStandardNano') -or ($EditionId -eq 'ServerDataCenterNano') -or ($EditionId -eq 'NanoServer')); `
|
return (($EditionId -eq 'ServerStandardNano') -or ($EditionId -eq 'ServerDataCenterNano') -or ($EditionId -eq 'NanoServer')); `
|
||||||
|
@ -207,36 +200,38 @@ RUN `
|
||||||
Throw ("Failed to download " + $source) `
|
Throw ("Failed to download " + $source) `
|
||||||
}`
|
}`
|
||||||
} else { `
|
} else { `
|
||||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; `
|
|
||||||
$webClient = New-Object System.Net.WebClient; `
|
$webClient = New-Object System.Net.WebClient; `
|
||||||
$webClient.DownloadFile($source, $target); `
|
$webClient.DownloadFile($source, $target); `
|
||||||
} `
|
} `
|
||||||
} `
|
} `
|
||||||
`
|
`
|
||||||
setx /M PATH $('C:\git\cmd;C:\git\usr\bin;'+$Env:PATH+';C:\gcc\bin;C:\go\bin;C:\containerd\bin'); `
|
setx /M PATH $('C:\git\bin;C:\git\usr\bin;'+$Env:PATH+';C:\gcc\bin;C:\go\bin'); `
|
||||||
`
|
`
|
||||||
Write-Host INFO: Downloading git...; `
|
Write-Host INFO: Downloading git...; `
|
||||||
$location='https://www.nuget.org/api/v2/package/GitForWindows/'+$Env:GIT_VERSION; `
|
$location='https://github.com/git-for-windows/git/releases/download/v'+$env:GIT_VERSION+'.windows.1/PortableGit-'+$env:GIT_VERSION+'-64-bit.7z.exe'; `
|
||||||
Download-File $location C:\gitsetup.zip; `
|
Download-File $location C:\gitsetup.7z.exe; `
|
||||||
`
|
`
|
||||||
Write-Host INFO: Downloading go...; `
|
Write-Host INFO: Downloading go...; `
|
||||||
$dlGoVersion=$Env:GO_VERSION; `
|
Download-File $('https://golang.org/dl/go'+$Env:GO_VERSION+'.windows-amd64.zip') C:\go.zip; `
|
||||||
Download-File "https://go.dev/dl/go${dlGoVersion}.windows-amd64.zip" C:\go.zip; `
|
|
||||||
`
|
`
|
||||||
Write-Host INFO: Downloading compiler 1 of 3...; `
|
Write-Host INFO: Downloading compiler 1 of 3...; `
|
||||||
Download-File https://raw.githubusercontent.com/moby/docker-tdmgcc/master/gcc.zip C:\gcc.zip; `
|
Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/gcc.zip C:\gcc.zip; `
|
||||||
`
|
`
|
||||||
Write-Host INFO: Downloading compiler 2 of 3...; `
|
Write-Host INFO: Downloading compiler 2 of 3...; `
|
||||||
Download-File https://raw.githubusercontent.com/moby/docker-tdmgcc/master/runtime.zip C:\runtime.zip; `
|
Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/runtime.zip C:\runtime.zip; `
|
||||||
`
|
`
|
||||||
Write-Host INFO: Downloading compiler 3 of 3...; `
|
Write-Host INFO: Downloading compiler 3 of 3...; `
|
||||||
Download-File https://raw.githubusercontent.com/moby/docker-tdmgcc/master/binutils.zip C:\binutils.zip; `
|
Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/binutils.zip C:\binutils.zip; `
|
||||||
`
|
`
|
||||||
|
Write-Host INFO: Installing PS7Zip package...; `
|
||||||
|
Install-Package PS7Zip -Force | Out-Null; `
|
||||||
|
Write-Host INFO: Importing PS7Zip...; `
|
||||||
|
Import-Module PS7Zip -Force; `
|
||||||
|
New-Item C:\git -ItemType Directory | Out-Null ; `
|
||||||
|
cd C:\git; `
|
||||||
Write-Host INFO: Extracting git...; `
|
Write-Host INFO: Extracting git...; `
|
||||||
Expand-Archive C:\gitsetup.zip C:\git-tmp; `
|
Expand-7Zip C:\gitsetup.7z.exe | Out-Null; `
|
||||||
New-Item -Type Directory C:\git | Out-Null; `
|
cd C:\; `
|
||||||
Move-Item C:\git-tmp\tools\* C:\git\.; `
|
|
||||||
Remove-Item -Recurse -Force C:\git-tmp; `
|
|
||||||
`
|
`
|
||||||
Write-Host INFO: Expanding go...; `
|
Write-Host INFO: Expanding go...; `
|
||||||
Expand-Archive C:\go.zip -DestinationPath C:\; `
|
Expand-Archive C:\go.zip -DestinationPath C:\; `
|
||||||
|
@ -252,63 +247,21 @@ RUN `
|
||||||
Remove-Item C:\gcc.zip; `
|
Remove-Item C:\gcc.zip; `
|
||||||
Remove-Item C:\runtime.zip; `
|
Remove-Item C:\runtime.zip; `
|
||||||
Remove-Item C:\binutils.zip; `
|
Remove-Item C:\binutils.zip; `
|
||||||
Remove-Item C:\gitsetup.zip; `
|
Remove-Item C:\gitsetup.7z.exe; `
|
||||||
`
|
`
|
||||||
Write-Host INFO: Downloading containerd; `
|
Write-Host INFO: Creating source directory...; `
|
||||||
Install-Package -Force 7Zip4PowerShell; `
|
New-Item -ItemType Directory -Path C:\go\src\github.com\docker\docker | Out-Null; `
|
||||||
$location='https://github.com/containerd/containerd/releases/download/'+$Env:CONTAINERD_VERSION+'/containerd-'+$Env:CONTAINERD_VERSION.TrimStart('v')+'-windows-amd64.tar.gz'; `
|
|
||||||
Download-File $location C:\containerd.tar.gz; `
|
|
||||||
New-Item -Path C:\containerd -ItemType Directory; `
|
|
||||||
Expand-7Zip C:\containerd.tar.gz C:\; `
|
|
||||||
Expand-7Zip C:\containerd.tar C:\containerd; `
|
|
||||||
Remove-Item C:\containerd.tar.gz; `
|
|
||||||
Remove-Item C:\containerd.tar; `
|
|
||||||
`
|
|
||||||
# Ensure all directories exist that we will require below....
|
|
||||||
$srcDir = """$Env:GOPATH`\src\github.com\docker\docker\bundles"""; `
|
|
||||||
Write-Host INFO: Ensuring existence of directory $srcDir...; `
|
|
||||||
New-Item -Force -ItemType Directory -Path $srcDir | Out-Null; `
|
|
||||||
`
|
`
|
||||||
Write-Host INFO: Configuring git core.autocrlf...; `
|
Write-Host INFO: Configuring git core.autocrlf...; `
|
||||||
C:\git\cmd\git config --global core.autocrlf true;
|
C:\git\bin\git config --global core.autocrlf true; `
|
||||||
|
|
||||||
RUN `
|
|
||||||
Function Install-GoTestSum() { `
|
|
||||||
$Env:GO111MODULE = 'on'; `
|
|
||||||
$tmpGobin = "${Env:GOBIN_TMP}"; `
|
|
||||||
$Env:GOBIN = """${Env:GOPATH}`\bin"""; `
|
|
||||||
Write-Host "INFO: Installing gotestsum version $Env:GOTESTSUM_VERSION in $Env:GOBIN"; `
|
|
||||||
&go install "gotest.tools/gotestsum@${Env:GOTESTSUM_VERSION}"; `
|
|
||||||
$Env:GOBIN = "${tmpGobin}"; `
|
|
||||||
$Env:GO111MODULE = 'off'; `
|
|
||||||
if ($LASTEXITCODE -ne 0) { `
|
|
||||||
Throw '"gotestsum install failed..."'; `
|
|
||||||
} `
|
|
||||||
} `
|
|
||||||
`
|
`
|
||||||
Install-GoTestSum
|
Write-Host INFO: Completed
|
||||||
|
|
||||||
RUN `
|
|
||||||
Function Install-GoWinres() { `
|
|
||||||
$Env:GO111MODULE = 'on'; `
|
|
||||||
$tmpGobin = "${Env:GOBIN_TMP}"; `
|
|
||||||
$Env:GOBIN = """${Env:GOPATH}`\bin"""; `
|
|
||||||
Write-Host "INFO: Installing go-winres version $Env:GOWINRES_VERSION in $Env:GOBIN"; `
|
|
||||||
&go install "github.com/tc-hib/go-winres@${Env:GOWINRES_VERSION}"; `
|
|
||||||
$Env:GOBIN = "${tmpGobin}"; `
|
|
||||||
$Env:GO111MODULE = 'off'; `
|
|
||||||
if ($LASTEXITCODE -ne 0) { `
|
|
||||||
Throw '"go-winres install failed..."'; `
|
|
||||||
} `
|
|
||||||
} `
|
|
||||||
`
|
|
||||||
Install-GoWinres
|
|
||||||
|
|
||||||
# Make PowerShell the default entrypoint
|
# Make PowerShell the default entrypoint
|
||||||
ENTRYPOINT ["powershell.exe"]
|
ENTRYPOINT ["powershell.exe"]
|
||||||
|
|
||||||
# Set the working directory to the location of the sources
|
# Set the working directory to the location of the sources
|
||||||
WORKDIR ${GOPATH}\src\github.com\docker\docker
|
WORKDIR C:\go\src\github.com\docker\docker
|
||||||
|
|
||||||
# Copy the sources into the container
|
# Copy the sources into the container
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
165
Jenkinsfile
vendored
165
Jenkinsfile
vendored
|
@ -1,165 +0,0 @@
|
||||||
#!groovy
|
|
||||||
pipeline {
|
|
||||||
agent none
|
|
||||||
|
|
||||||
options {
|
|
||||||
buildDiscarder(logRotator(daysToKeepStr: '30'))
|
|
||||||
timeout(time: 2, unit: 'HOURS')
|
|
||||||
timestamps()
|
|
||||||
}
|
|
||||||
parameters {
|
|
||||||
booleanParam(name: 'arm64', defaultValue: true, description: 'ARM (arm64) Build/Test')
|
|
||||||
booleanParam(name: 'dco', defaultValue: true, description: 'Run the DCO check')
|
|
||||||
}
|
|
||||||
environment {
|
|
||||||
DOCKER_BUILDKIT = '1'
|
|
||||||
DOCKER_EXPERIMENTAL = '1'
|
|
||||||
DOCKER_GRAPHDRIVER = 'overlay2'
|
|
||||||
CHECK_CONFIG_COMMIT = '33a3680e08d1007e72c3b3f1454f823d8e9948ee'
|
|
||||||
TESTDEBUG = '0'
|
|
||||||
TIMEOUT = '120m'
|
|
||||||
}
|
|
||||||
stages {
|
|
||||||
stage('pr-hack') {
|
|
||||||
when { changeRequest() }
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
echo "Workaround for PR auto-cancel feature. Borrowed from https://issues.jenkins-ci.org/browse/JENKINS-43353"
|
|
||||||
def buildNumber = env.BUILD_NUMBER as int
|
|
||||||
if (buildNumber > 1) milestone(buildNumber - 1)
|
|
||||||
milestone(buildNumber)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('DCO-check') {
|
|
||||||
when {
|
|
||||||
beforeAgent true
|
|
||||||
expression { params.dco }
|
|
||||||
}
|
|
||||||
agent { label 'arm64 && ubuntu-2004' }
|
|
||||||
steps {
|
|
||||||
sh '''
|
|
||||||
docker run --rm \
|
|
||||||
-v "$WORKSPACE:/workspace" \
|
|
||||||
-e VALIDATE_REPO=${GIT_URL} \
|
|
||||||
-e VALIDATE_BRANCH=${CHANGE_TARGET} \
|
|
||||||
alpine sh -c 'apk add --no-cache -q bash git openssh-client && git config --system --add safe.directory /workspace && cd /workspace && hack/validate/dco'
|
|
||||||
'''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Build') {
|
|
||||||
parallel {
|
|
||||||
stage('arm64') {
|
|
||||||
when {
|
|
||||||
beforeAgent true
|
|
||||||
expression { params.arm64 }
|
|
||||||
}
|
|
||||||
agent { label 'arm64 && ubuntu-2004' }
|
|
||||||
environment {
|
|
||||||
TEST_SKIP_INTEGRATION_CLI = '1'
|
|
||||||
}
|
|
||||||
|
|
||||||
stages {
|
|
||||||
stage("Print info") {
|
|
||||||
steps {
|
|
||||||
sh 'docker version'
|
|
||||||
sh 'docker info'
|
|
||||||
sh '''
|
|
||||||
echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}"
|
|
||||||
curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \
|
|
||||||
&& bash ${WORKSPACE}/check-config.sh || true
|
|
||||||
'''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage("Build dev image") {
|
|
||||||
steps {
|
|
||||||
sh 'docker build --force-rm -t docker:${GIT_COMMIT} .'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage("Unit tests") {
|
|
||||||
steps {
|
|
||||||
sh '''
|
|
||||||
sudo modprobe ip6table_filter
|
|
||||||
'''
|
|
||||||
sh '''
|
|
||||||
docker run --rm -t --privileged \
|
|
||||||
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
|
|
||||||
--name docker-pr$BUILD_NUMBER \
|
|
||||||
-e DOCKER_EXPERIMENTAL \
|
|
||||||
-e DOCKER_GITCOMMIT=${GIT_COMMIT} \
|
|
||||||
-e DOCKER_GRAPHDRIVER \
|
|
||||||
-e VALIDATE_REPO=${GIT_URL} \
|
|
||||||
-e VALIDATE_BRANCH=${CHANGE_TARGET} \
|
|
||||||
docker:${GIT_COMMIT} \
|
|
||||||
hack/test/unit
|
|
||||||
'''
|
|
||||||
}
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
junit testResults: 'bundles/junit-report*.xml', allowEmptyResults: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage("Integration tests") {
|
|
||||||
environment { TEST_SKIP_INTEGRATION_CLI = '1' }
|
|
||||||
steps {
|
|
||||||
sh '''
|
|
||||||
docker run --rm -t --privileged \
|
|
||||||
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
|
|
||||||
--name docker-pr$BUILD_NUMBER \
|
|
||||||
-e DOCKER_EXPERIMENTAL \
|
|
||||||
-e DOCKER_GITCOMMIT=${GIT_COMMIT} \
|
|
||||||
-e DOCKER_GRAPHDRIVER \
|
|
||||||
-e TESTDEBUG \
|
|
||||||
-e TEST_INTEGRATION_USE_SNAPSHOTTER \
|
|
||||||
-e TEST_SKIP_INTEGRATION_CLI \
|
|
||||||
-e TIMEOUT \
|
|
||||||
-e VALIDATE_REPO=${GIT_URL} \
|
|
||||||
-e VALIDATE_BRANCH=${CHANGE_TARGET} \
|
|
||||||
docker:${GIT_COMMIT} \
|
|
||||||
hack/make.sh \
|
|
||||||
dynbinary \
|
|
||||||
test-integration
|
|
||||||
'''
|
|
||||||
}
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
sh '''
|
|
||||||
echo "Ensuring container killed."
|
|
||||||
docker rm -vf docker-pr$BUILD_NUMBER || true
|
|
||||||
'''
|
|
||||||
|
|
||||||
sh '''
|
|
||||||
echo "Chowning /workspace to jenkins user"
|
|
||||||
docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace
|
|
||||||
'''
|
|
||||||
|
|
||||||
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') {
|
|
||||||
sh '''
|
|
||||||
bundleName=arm64-integration
|
|
||||||
echo "Creating ${bundleName}-bundles.tar.gz"
|
|
||||||
# exclude overlay2 directories
|
|
||||||
find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz
|
|
||||||
'''
|
|
||||||
|
|
||||||
archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cleanup {
|
|
||||||
sh 'make clean'
|
|
||||||
deleteDir()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
2
LICENSE
2
LICENSE
|
@ -176,7 +176,7 @@
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
Copyright 2013-2018 Docker, Inc.
|
Copyright 2013-2016 Docker, Inc.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
325
MAINTAINERS
325
MAINTAINERS
|
@ -1,14 +1,12 @@
|
||||||
# Moby maintainers file
|
# Docker maintainers file
|
||||||
#
|
#
|
||||||
# This file describes the maintainer groups within the moby/moby project.
|
# This file describes who runs the docker/docker project and how.
|
||||||
# More detail on Moby project governance is available in the
|
# This is a living document - if you see something out of date or missing, speak up!
|
||||||
# project/GOVERNANCE.md file found in this repository.
|
|
||||||
#
|
#
|
||||||
# It is structured to be consumable by both humans and programs.
|
# It is structured to be consumable by both humans and programs.
|
||||||
# To extract its contents programmatically, use any TOML-compliant
|
# To extract its contents programmatically, use any TOML-compliant
|
||||||
# parser.
|
# parser.
|
||||||
#
|
#
|
||||||
# TODO(estesp): This file should not necessarily depend on docker/opensource
|
|
||||||
# This file is compiled into the MAINTAINERS file in docker/opensource.
|
# This file is compiled into the MAINTAINERS file in docker/opensource.
|
||||||
#
|
#
|
||||||
[Org]
|
[Org]
|
||||||
|
@ -23,34 +21,48 @@
|
||||||
# a subsystem, they are responsible for doing so and holding the
|
# a subsystem, they are responsible for doing so and holding the
|
||||||
# subsystem maintainers accountable. If ownership is unclear, they are the de facto owners.
|
# subsystem maintainers accountable. If ownership is unclear, they are the de facto owners.
|
||||||
|
|
||||||
|
# For each release (including minor releases), a "release captain" is assigned from the
|
||||||
|
# pool of core maintainers. Rotation is encouraged across all maintainers, to ensure
|
||||||
|
# the release process is clear and up-to-date.
|
||||||
|
|
||||||
people = [
|
people = [
|
||||||
"akerouanton",
|
"aaronlehmann",
|
||||||
"akihirosuda",
|
"akihirosuda",
|
||||||
|
"aluzzardi",
|
||||||
"anusha",
|
"anusha",
|
||||||
"coolljt0725",
|
"coolljt0725",
|
||||||
"corhere",
|
|
||||||
"cpuguy83",
|
"cpuguy83",
|
||||||
"crazy-max",
|
"crosbymichael",
|
||||||
|
"dnephin",
|
||||||
|
"duglin",
|
||||||
"estesp",
|
"estesp",
|
||||||
"johnstep",
|
"icecrime",
|
||||||
|
"jhowardmsft",
|
||||||
"justincormack",
|
"justincormack",
|
||||||
"kolyshkin",
|
"lk4d4",
|
||||||
"laurazard",
|
"mavenugo",
|
||||||
"mhbauer",
|
"mhbauer",
|
||||||
"neersighted",
|
"mlaventure",
|
||||||
"rumpl",
|
"mrjana",
|
||||||
"runcom",
|
"runcom",
|
||||||
"samuelkarp",
|
|
||||||
"stevvooe",
|
"stevvooe",
|
||||||
"thajeztah",
|
|
||||||
"tianon",
|
"tianon",
|
||||||
"tibor",
|
"tibor",
|
||||||
"tonistiigi",
|
"tonistiigi",
|
||||||
"unclejack",
|
"unclejack",
|
||||||
"vdemeester",
|
"vdemeester",
|
||||||
"vieux",
|
"vieux"
|
||||||
"vvoland",
|
]
|
||||||
"yongtang"
|
|
||||||
|
[Org."Docs maintainers"]
|
||||||
|
|
||||||
|
# TODO Describe the docs maintainers role.
|
||||||
|
|
||||||
|
people = [
|
||||||
|
"jamtur01",
|
||||||
|
"misty",
|
||||||
|
"sven",
|
||||||
|
"thajeztah"
|
||||||
]
|
]
|
||||||
|
|
||||||
[Org.Curators]
|
[Org.Curators]
|
||||||
|
@ -66,18 +78,11 @@
|
||||||
# - close an issue or pull request when it's inappropriate or off-topic
|
# - close an issue or pull request when it's inappropriate or off-topic
|
||||||
|
|
||||||
people = [
|
people = [
|
||||||
"alexellis",
|
"aboch",
|
||||||
"andrewhsu",
|
"andrewhsu",
|
||||||
"bsousaa",
|
"ehazlett",
|
||||||
"dmcgowan",
|
"mgoelzer",
|
||||||
"fntlnz",
|
|
||||||
"gianarb",
|
|
||||||
"olljanat",
|
|
||||||
"programmerq",
|
"programmerq",
|
||||||
"ripcurld",
|
|
||||||
"robmry",
|
|
||||||
"sam-thibault",
|
|
||||||
"samwhited",
|
|
||||||
"thajeztah"
|
"thajeztah"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -88,22 +93,6 @@
|
||||||
# Thank you!
|
# Thank you!
|
||||||
|
|
||||||
people = [
|
people = [
|
||||||
# Aaron Lehmann was a maintainer for swarmkit, the registry, and the engine,
|
|
||||||
# and contributed many improvements, features, and bugfixes in those areas,
|
|
||||||
# among which "automated service rollbacks", templated secrets and configs,
|
|
||||||
# and resumable image layer downloads.
|
|
||||||
"aaronlehmann",
|
|
||||||
|
|
||||||
# Harald Albers is the mastermind behind the bash completion scripts for the
|
|
||||||
# Docker CLI. The completion scripts moved to the Docker CLI repository, so
|
|
||||||
# you can now find him perform his magic in the https://github.com/docker/cli repository.
|
|
||||||
"albers",
|
|
||||||
|
|
||||||
# Andrea Luzzardi started contributing to the Docker codebase in the "dotCloud"
|
|
||||||
# era, even before it was called "Docker". He is one of the architects of both
|
|
||||||
# Swarm and SwarmKit, and its integration into the Docker engine.
|
|
||||||
"aluzzardi",
|
|
||||||
|
|
||||||
# David Calavera contributed many features to Docker, such as an improved
|
# David Calavera contributed many features to Docker, such as an improved
|
||||||
# event system, dynamic configuration reloading, volume plugins, fancy
|
# event system, dynamic configuration reloading, volume plugins, fancy
|
||||||
# new templating options, and an external client credential store. As a
|
# new templating options, and an external client credential store. As a
|
||||||
|
@ -113,30 +102,6 @@
|
||||||
# and tweets as @calavera.
|
# and tweets as @calavera.
|
||||||
"calavera",
|
"calavera",
|
||||||
|
|
||||||
# Michael Crosby was "chief maintainer" of the Docker project.
|
|
||||||
# During his time as a maintainer, Michael contributed to many
|
|
||||||
# milestones of the project; he was release captain of Docker v1.0.0,
|
|
||||||
# started the development of "libcontainer" (what later became runc)
|
|
||||||
# and containerd, as well as demoing cool hacks such as live migrating
|
|
||||||
# a game server container with checkpoint/restore.
|
|
||||||
#
|
|
||||||
# Michael is currently a maintainer of containerd, but you may see
|
|
||||||
# him around in other projects on GitHub.
|
|
||||||
"crosbymichael",
|
|
||||||
|
|
||||||
# Before becoming a maintainer, Daniel Nephin was a core contributor
|
|
||||||
# to "Fig" (now known as Docker Compose). As a maintainer for both the
|
|
||||||
# Engine and Docker CLI, Daniel contributed many features, among which
|
|
||||||
# the `docker stack` commands, allowing users to deploy their Docker
|
|
||||||
# Compose projects as a Swarm service.
|
|
||||||
"dnephin",
|
|
||||||
|
|
||||||
# Doug Davis contributed many features and fixes for the classic builder,
|
|
||||||
# such as "wildcard" copy, the dockerignore file, custom paths/names
|
|
||||||
# for the Dockerfile, as well as enhancements to the API and documentation.
|
|
||||||
# Follow Doug on Twitter, where he tweets as @duginabox.
|
|
||||||
"duglin",
|
|
||||||
|
|
||||||
# As a maintainer, Erik was responsible for the "builder", and
|
# As a maintainer, Erik was responsible for the "builder", and
|
||||||
# started the first designs for the new networking model in
|
# started the first designs for the new networking model in
|
||||||
# Docker. Erik is now working on all kinds of plugins for Docker
|
# Docker. Erik is now working on all kinds of plugins for Docker
|
||||||
|
@ -145,33 +110,6 @@
|
||||||
# still stumble into him in our issue tracker, or on IRC.
|
# still stumble into him in our issue tracker, or on IRC.
|
||||||
"erikh",
|
"erikh",
|
||||||
|
|
||||||
# Evan Hazlett is the creator of the Shipyard and Interlock open source projects,
|
|
||||||
# and the author of "Orca", which became the foundation of Docker Universal Control
|
|
||||||
# Plane (UCP). As a maintainer, Evan helped integrating SwarmKit (secrets, tasks)
|
|
||||||
# into the Docker engine.
|
|
||||||
"ehazlett",
|
|
||||||
|
|
||||||
# Arnaud Porterie (AKA "icecrime") was in charge of maintaining the maintainers.
|
|
||||||
# As a maintainer, he made life easier for contributors to the Docker open-source
|
|
||||||
# projects, bringing order in the chaos by designing a triage- and review workflow
|
|
||||||
# using labels (see https://icecrime.net/technology/a-structured-approach-to-labeling/),
|
|
||||||
# and automating the hell out of things with his buddies GordonTheTurtle and Poule
|
|
||||||
# (a chicken!).
|
|
||||||
#
|
|
||||||
# A lesser-known fact is that he created the first commit in the libnetwork repository
|
|
||||||
# even though he didn't know anything about it. Some say, he's now selling stuff on
|
|
||||||
# the internet ;-)
|
|
||||||
"icecrime",
|
|
||||||
|
|
||||||
# After a false start with his first PR being rejected, James Turnbull became a frequent
|
|
||||||
# contributor to the documentation, and became a docs maintainer on December 5, 2013. As
|
|
||||||
# a maintainer, James lifted the docs to a higher standard, and introduced the community
|
|
||||||
# guidelines ("three strikes"). James is currently changing the world as CTO of https://www.empatico.org,
|
|
||||||
# meanwhile authoring various books that are worth checking out. You can find him on Twitter,
|
|
||||||
# rambling as @kartar, and although no longer active as a maintainer, he's always "game" to
|
|
||||||
# help out reviewing docs PRs, so you may still see him around in the repository.
|
|
||||||
"jamtur01",
|
|
||||||
|
|
||||||
# Jessica Frazelle, also known as the "Keyser Söze of containers",
|
# Jessica Frazelle, also known as the "Keyser Söze of containers",
|
||||||
# runs *everything* in containers. She started contributing to
|
# runs *everything* in containers. She started contributing to
|
||||||
# Docker with a (fun fun) change involving both iptables and regular
|
# Docker with a (fun fun) change involving both iptables and regular
|
||||||
|
@ -182,41 +120,13 @@
|
||||||
# containers a lot more secure). Besides being a maintainer, she
|
# containers a lot more secure). Besides being a maintainer, she
|
||||||
# set up the CI infrastructure for the project, giving everyone
|
# set up the CI infrastructure for the project, giving everyone
|
||||||
# something to shout at if a PR failed ("noooo Janky!").
|
# something to shout at if a PR failed ("noooo Janky!").
|
||||||
|
# Jess is currently working on the DCOS security team at Mesosphere,
|
||||||
|
# and contributing to various open source projects.
|
||||||
# Be sure you don't miss her talks at a conference near you (a must-see),
|
# Be sure you don't miss her talks at a conference near you (a must-see),
|
||||||
# read her blog at https://blog.jessfraz.com (a must-read), and
|
# read her blog at https://blog.jessfraz.com (a must-read), and
|
||||||
# check out her open source projects on GitHub https://github.com/jessfraz (a must-try).
|
# check out her open source projects on GitHub https://github.com/jessfraz (a must-try).
|
||||||
"jessfraz",
|
"jessfraz",
|
||||||
|
|
||||||
# As a maintainer, John Howard managed to make the impossible possible;
|
|
||||||
# to run Docker on Windows. After facing many challenges, teaching
|
|
||||||
# fellow-maintainers that 'Windows is not Linux', and many changes in
|
|
||||||
# Windows Server to facilitate containers, native Windows containers
|
|
||||||
# saw the light of day in 2015.
|
|
||||||
#
|
|
||||||
# John is now enjoying life without containers: playing piano, painting,
|
|
||||||
# and walking his dogs, but you may occasionally see him drop by on GitHub.
|
|
||||||
"lowenna",
|
|
||||||
|
|
||||||
# Alexander Morozov contributed many features to Docker, worked on the premise of
|
|
||||||
# what later became containerd (and worked on that too), and made a "stupid" Go
|
|
||||||
# vendor tool specifically for docker/docker needs: vndr (https://github.com/LK4D4/vndr).
|
|
||||||
# Not many know that Alexander is a master negotiator, being able to change course
|
|
||||||
# of action with a single "Nope, we're not gonna do that".
|
|
||||||
"lk4d4",
|
|
||||||
|
|
||||||
# Madhu Venugopal was part of the SocketPlane team that joined Docker.
|
|
||||||
# As a maintainer, he was working with Jana for the Container Network
|
|
||||||
# Model (CNM) implemented through libnetwork, and the "routing mesh" powering
|
|
||||||
# Swarm mode networking.
|
|
||||||
"mavenugo",
|
|
||||||
|
|
||||||
# As a maintainer, Kenfe-Mickaël Laventure worked on the container runtime,
|
|
||||||
# integrating containerd 1.0 with the daemon, and adding support for custom
|
|
||||||
# OCI runtimes, as well as implementing the `docker prune` subcommands,
|
|
||||||
# which was a welcome feature to be added. You can keep up with Mickaél on
|
|
||||||
# Twitter (@kmlaventure).
|
|
||||||
"mlaventure",
|
|
||||||
|
|
||||||
# As a docs maintainer, Mary Anthony contributed greatly to the Docker
|
# As a docs maintainer, Mary Anthony contributed greatly to the Docker
|
||||||
# docs. She wrote the Docker Contributor Guide and Getting Started
|
# docs. She wrote the Docker Contributor Guide and Getting Started
|
||||||
# Guides. She helped create a doc build system independent of
|
# Guides. She helped create a doc build system independent of
|
||||||
|
@ -226,30 +136,6 @@
|
||||||
# maryatdocker/docker-whale back in May 2015.
|
# maryatdocker/docker-whale back in May 2015.
|
||||||
"moxiegirl",
|
"moxiegirl",
|
||||||
|
|
||||||
# Jana Radhakrishnan was part of the SocketPlane team that joined Docker.
|
|
||||||
# As a maintainer, he was the lead architect for the Container Network
|
|
||||||
# Model (CNM) implemented through libnetwork, and the "routing mesh" powering
|
|
||||||
# Swarm mode networking.
|
|
||||||
#
|
|
||||||
# Jana started new adventures in networking, but you can find him tweeting as @mrjana,
|
|
||||||
# coding on GitHub https://github.com/mrjana, and he may be hiding on the Docker Community
|
|
||||||
# slack channel :-)
|
|
||||||
"mrjana",
|
|
||||||
|
|
||||||
# Sven Dowideit became a well known person in the Docker ecosphere, building
|
|
||||||
# boot2docker, and became a regular contributor to the project, starting as
|
|
||||||
# early as October 2013 (https://github.com/docker/docker/pull/2119), to become
|
|
||||||
# a maintainer less than two months later (https://github.com/docker/docker/pull/3061).
|
|
||||||
#
|
|
||||||
# As a maintainer, Sven took on the task to convert the documentation from
|
|
||||||
# ReStructuredText to Markdown, migrate to Hugo for generating the docs, and
|
|
||||||
# writing tooling for building, testing, and publishing them.
|
|
||||||
#
|
|
||||||
# If you're not in the occasion to visit "the Australian office", you
|
|
||||||
# can keep up with Sven on Twitter (@SvenDowideit), his blog http://fosiki.com,
|
|
||||||
# and of course on GitHub.
|
|
||||||
"sven",
|
|
||||||
|
|
||||||
# Vincent "vbatts!" Batts made his first contribution to the project
|
# Vincent "vbatts!" Batts made his first contribution to the project
|
||||||
# in November 2013, to become a maintainer a few months later, on
|
# in November 2013, to become a maintainer a few months later, on
|
||||||
# May 10, 2014 (https://github.com/docker/docker/commit/d6e666a87a01a5634c250358a94c814bf26cb778).
|
# May 10, 2014 (https://github.com/docker/docker/commit/d6e666a87a01a5634c250358a94c814bf26cb778).
|
||||||
|
@ -284,19 +170,14 @@
|
||||||
Email = "aaron.lehmann@docker.com"
|
Email = "aaron.lehmann@docker.com"
|
||||||
GitHub = "aaronlehmann"
|
GitHub = "aaronlehmann"
|
||||||
|
|
||||||
[people.akerouanton]
|
[people.aboch]
|
||||||
Name = "Albin Kerouanton"
|
Name = "Alessandro Boch"
|
||||||
Email = "albinker@gmail.com"
|
Email = "aboch@docker.com"
|
||||||
GitHub = "akerouanton"
|
GitHub = "aboch"
|
||||||
|
|
||||||
[people.alexellis]
|
|
||||||
Name = "Alex Ellis"
|
|
||||||
Email = "alexellis2@gmail.com"
|
|
||||||
GitHub = "alexellis"
|
|
||||||
|
|
||||||
[people.akihirosuda]
|
[people.akihirosuda]
|
||||||
Name = "Akihiro Suda"
|
Name = "Akihiro Suda"
|
||||||
Email = "akihiro.suda.cz@hco.ntt.co.jp"
|
Email = "suda.akihiro@lab.ntt.co.jp"
|
||||||
GitHub = "AkihiroSuda"
|
GitHub = "AkihiroSuda"
|
||||||
|
|
||||||
[people.aluzzardi]
|
[people.aluzzardi]
|
||||||
|
@ -304,11 +185,6 @@
|
||||||
Email = "al@docker.com"
|
Email = "al@docker.com"
|
||||||
GitHub = "aluzzardi"
|
GitHub = "aluzzardi"
|
||||||
|
|
||||||
[people.albers]
|
|
||||||
Name = "Harald Albers"
|
|
||||||
Email = "github@albersweb.de"
|
|
||||||
GitHub = "albers"
|
|
||||||
|
|
||||||
[people.andrewhsu]
|
[people.andrewhsu]
|
||||||
Name = "Andrew Hsu"
|
Name = "Andrew Hsu"
|
||||||
Email = "andrewhsu@docker.com"
|
Email = "andrewhsu@docker.com"
|
||||||
|
@ -319,11 +195,6 @@
|
||||||
Email = "anusha@docker.com"
|
Email = "anusha@docker.com"
|
||||||
GitHub = "anusha-ragunathan"
|
GitHub = "anusha-ragunathan"
|
||||||
|
|
||||||
[people.bsousaa]
|
|
||||||
Name = "Bruno de Sousa"
|
|
||||||
Email = "bruno.sousa@docker.com"
|
|
||||||
GitHub = "bsousaa"
|
|
||||||
|
|
||||||
[people.calavera]
|
[people.calavera]
|
||||||
Name = "David Calavera"
|
Name = "David Calavera"
|
||||||
Email = "david.calavera@gmail.com"
|
Email = "david.calavera@gmail.com"
|
||||||
|
@ -334,20 +205,10 @@
|
||||||
Email = "leijitang@huawei.com"
|
Email = "leijitang@huawei.com"
|
||||||
GitHub = "coolljt0725"
|
GitHub = "coolljt0725"
|
||||||
|
|
||||||
[people.corhere]
|
|
||||||
Name = "Cory Snider"
|
|
||||||
Email = "csnider@mirantis.com"
|
|
||||||
GitHub = "corhere"
|
|
||||||
|
|
||||||
[people.cpuguy83]
|
[people.cpuguy83]
|
||||||
Name = "Brian Goff"
|
Name = "Brian Goff"
|
||||||
Email = "cpuguy83@gmail.com"
|
Email = "cpuguy83@gmail.com"
|
||||||
GitHub = "cpuguy83"
|
Github = "cpuguy83"
|
||||||
|
|
||||||
[people.crazy-max]
|
|
||||||
Name = "Kevin Alvarez"
|
|
||||||
Email = "contact@crazymax.dev"
|
|
||||||
GitHub = "crazy-max"
|
|
||||||
|
|
||||||
[people.crosbymichael]
|
[people.crosbymichael]
|
||||||
Name = "Michael Crosby"
|
Name = "Michael Crosby"
|
||||||
|
@ -359,11 +220,6 @@
|
||||||
Email = "dnephin@gmail.com"
|
Email = "dnephin@gmail.com"
|
||||||
GitHub = "dnephin"
|
GitHub = "dnephin"
|
||||||
|
|
||||||
[people.dmcgowan]
|
|
||||||
Name = "Derek McGowan"
|
|
||||||
Email = "derek@mcgstyle.net"
|
|
||||||
GitHub = "dmcgowan"
|
|
||||||
|
|
||||||
[people.duglin]
|
[people.duglin]
|
||||||
Name = "Doug Davis"
|
Name = "Doug Davis"
|
||||||
Email = "dug@us.ibm.com"
|
Email = "dug@us.ibm.com"
|
||||||
|
@ -384,19 +240,9 @@
|
||||||
Email = "estesp@linux.vnet.ibm.com"
|
Email = "estesp@linux.vnet.ibm.com"
|
||||||
GitHub = "estesp"
|
GitHub = "estesp"
|
||||||
|
|
||||||
[people.fntlnz]
|
|
||||||
Name = "Lorenzo Fontana"
|
|
||||||
Email = "fontanalorenz@gmail.com"
|
|
||||||
GitHub = "fntlnz"
|
|
||||||
|
|
||||||
[people.gianarb]
|
|
||||||
Name = "Gianluca Arbezzano"
|
|
||||||
Email = "ga@thumpflow.com"
|
|
||||||
GitHub = "gianarb"
|
|
||||||
|
|
||||||
[people.icecrime]
|
[people.icecrime]
|
||||||
Name = "Arnaud Porterie"
|
Name = "Arnaud Porterie"
|
||||||
Email = "icecrime@gmail.com"
|
Email = "arnaud@docker.com"
|
||||||
GitHub = "icecrime"
|
GitHub = "icecrime"
|
||||||
|
|
||||||
[people.jamtur01]
|
[people.jamtur01]
|
||||||
|
@ -404,54 +250,49 @@
|
||||||
Email = "james@lovedthanlost.net"
|
Email = "james@lovedthanlost.net"
|
||||||
GitHub = "jamtur01"
|
GitHub = "jamtur01"
|
||||||
|
|
||||||
|
[people.jhowardmsft]
|
||||||
|
Name = "John Howard"
|
||||||
|
Email = "jhoward@microsoft.com"
|
||||||
|
GitHub = "jhowardmsft"
|
||||||
|
|
||||||
[people.jessfraz]
|
[people.jessfraz]
|
||||||
Name = "Jessie Frazelle"
|
Name = "Jessie Frazelle"
|
||||||
Email = "jess@linux.com"
|
Email = "jess@linux.com"
|
||||||
GitHub = "jessfraz"
|
GitHub = "jessfraz"
|
||||||
|
|
||||||
[people.johnstep]
|
|
||||||
Name = "John Stephens"
|
|
||||||
Email = "johnstep@docker.com"
|
|
||||||
GitHub = "johnstep"
|
|
||||||
|
|
||||||
[people.justincormack]
|
[people.justincormack]
|
||||||
Name = "Justin Cormack"
|
Name = "Justin Cormack"
|
||||||
Email = "justin.cormack@docker.com"
|
Email = "justin.cormack@docker.com"
|
||||||
GitHub = "justincormack"
|
GitHub = "justincormack"
|
||||||
|
|
||||||
[people.kolyshkin]
|
|
||||||
Name = "Kir Kolyshkin"
|
|
||||||
Email = "kolyshkin@gmail.com"
|
|
||||||
GitHub = "kolyshkin"
|
|
||||||
|
|
||||||
[people.laurazard]
|
|
||||||
Name = "Laura Brehm"
|
|
||||||
Email = "laura.brehm@docker.com"
|
|
||||||
GitHub = "laurazard"
|
|
||||||
|
|
||||||
[people.lk4d4]
|
[people.lk4d4]
|
||||||
Name = "Alexander Morozov"
|
Name = "Alexander Morozov"
|
||||||
Email = "lk4d4@docker.com"
|
Email = "lk4d4@docker.com"
|
||||||
GitHub = "lk4d4"
|
GitHub = "lk4d4"
|
||||||
|
|
||||||
[people.lowenna]
|
|
||||||
Name = "John Howard"
|
|
||||||
Email = "github@lowenna.com"
|
|
||||||
GitHub = "lowenna"
|
|
||||||
|
|
||||||
[people.mavenugo]
|
[people.mavenugo]
|
||||||
Name = "Madhu Venugopal"
|
Name = "Madhu Venugopal"
|
||||||
Email = "madhu@docker.com"
|
Email = "madhu@docker.com"
|
||||||
GitHub = "mavenugo"
|
GitHub = "mavenugo"
|
||||||
|
|
||||||
|
[people.mgoelzer]
|
||||||
|
Name = "Mike Goelzer"
|
||||||
|
Email = "mike.goelzer@docker.com"
|
||||||
|
GitHub = "mgoelzer"
|
||||||
|
|
||||||
[people.mhbauer]
|
[people.mhbauer]
|
||||||
Name = "Morgan Bauer"
|
Name = "Morgan Bauer"
|
||||||
Email = "mbauer@us.ibm.com"
|
Email = "mbauer@us.ibm.com"
|
||||||
GitHub = "mhbauer"
|
GitHub = "mhbauer"
|
||||||
|
|
||||||
|
[people.misty]
|
||||||
|
Name = "Misty Stanley-Jones"
|
||||||
|
Email = "misty@docker.com"
|
||||||
|
GitHub = "mstanleyjones"
|
||||||
|
|
||||||
[people.mlaventure]
|
[people.mlaventure]
|
||||||
Name = "Kenfe-Mickaël Laventure"
|
Name = "Kenfe-Mickaël Laventure"
|
||||||
Email = "mickael.laventure@gmail.com"
|
Email = "mickael.laventure@docker.com"
|
||||||
GitHub = "mlaventure"
|
GitHub = "mlaventure"
|
||||||
|
|
||||||
[people.moxiegirl]
|
[people.moxiegirl]
|
||||||
|
@ -464,56 +305,16 @@
|
||||||
Email = "mrjana@docker.com"
|
Email = "mrjana@docker.com"
|
||||||
GitHub = "mrjana"
|
GitHub = "mrjana"
|
||||||
|
|
||||||
[people.neersighted]
|
|
||||||
Name = "Bjorn Neergaard"
|
|
||||||
Email = "bjorn@neersighted.com"
|
|
||||||
GitHub = "neersighted"
|
|
||||||
|
|
||||||
[people.olljanat]
|
|
||||||
Name = "Olli Janatuinen"
|
|
||||||
Email = "olli.janatuinen@gmail.com"
|
|
||||||
GitHub = "olljanat"
|
|
||||||
|
|
||||||
[people.programmerq]
|
[people.programmerq]
|
||||||
Name = "Jeff Anderson"
|
Name = "Jeff Anderson"
|
||||||
Email = "jeff@docker.com"
|
Email = "jeff@docker.com"
|
||||||
GitHub = "programmerq"
|
GitHub = "programmerq"
|
||||||
|
|
||||||
[people.robmry]
|
|
||||||
Name = "Rob Murray"
|
|
||||||
Email = "rob.murray@docker.com"
|
|
||||||
GitHub = "robmry"
|
|
||||||
|
|
||||||
[people.ripcurld]
|
|
||||||
Name = "Boaz Shuster"
|
|
||||||
Email = "ripcurld.github@gmail.com"
|
|
||||||
GitHub = "ripcurld"
|
|
||||||
|
|
||||||
[people.rumpl]
|
|
||||||
Name = "Djordje Lukic"
|
|
||||||
Email = "djordje.lukic@docker.com"
|
|
||||||
GitHub = "rumpl"
|
|
||||||
|
|
||||||
[people.runcom]
|
[people.runcom]
|
||||||
Name = "Antonio Murdaca"
|
Name = "Antonio Murdaca"
|
||||||
Email = "runcom@redhat.com"
|
Email = "runcom@redhat.com"
|
||||||
GitHub = "runcom"
|
GitHub = "runcom"
|
||||||
|
|
||||||
[people.sam-thibault]
|
|
||||||
Name = "Sam Thibault"
|
|
||||||
Email = "sam.thibault@docker.com"
|
|
||||||
GitHub = "sam-thibault"
|
|
||||||
|
|
||||||
[people.samuelkarp]
|
|
||||||
Name = "Samuel Karp"
|
|
||||||
Email = "me@samuelkarp.com"
|
|
||||||
GitHub = "samuelkarp"
|
|
||||||
|
|
||||||
[people.samwhited]
|
|
||||||
Name = "Sam Whited"
|
|
||||||
Email = "sam@samwhited.com"
|
|
||||||
GitHub = "samwhited"
|
|
||||||
|
|
||||||
[people.shykes]
|
[people.shykes]
|
||||||
Name = "Solomon Hykes"
|
Name = "Solomon Hykes"
|
||||||
Email = "solomon@docker.com"
|
Email = "solomon@docker.com"
|
||||||
|
@ -573,13 +374,3 @@
|
||||||
Name = "Vishnu Kannan"
|
Name = "Vishnu Kannan"
|
||||||
Email = "vishnuk@google.com"
|
Email = "vishnuk@google.com"
|
||||||
GitHub = "vishh"
|
GitHub = "vishh"
|
||||||
|
|
||||||
[people.vvoland]
|
|
||||||
Name = "Paweł Gronowski"
|
|
||||||
Email = "pawel.gronowski@docker.com"
|
|
||||||
GitHub = "vvoland"
|
|
||||||
|
|
||||||
[people.yongtang]
|
|
||||||
Name = "Yong Tang"
|
|
||||||
Email = "yong.tang.github@outlook.com"
|
|
||||||
GitHub = "yongtang"
|
|
||||||
|
|
238
Makefile
238
Makefile
|
@ -1,128 +1,66 @@
|
||||||
.PHONY: all binary dynbinary build cross help install manpages run shell test test-docker-py test-integration test-unit validate validate-% win
|
.PHONY: all binary build cross deb help init-go-pkg-cache install manpages rpm run shell test test-docker-py test-integration-cli test-unit tgz validate win
|
||||||
|
|
||||||
DOCKER ?= docker
|
|
||||||
BUILDX ?= $(DOCKER) buildx
|
|
||||||
|
|
||||||
# set the graph driver as the current graphdriver if not set
|
# set the graph driver as the current graphdriver if not set
|
||||||
DOCKER_GRAPHDRIVER := $(if $(DOCKER_GRAPHDRIVER),$(DOCKER_GRAPHDRIVER),$(shell docker info -f '{{ .Driver }}' 2>&1))
|
DOCKER_GRAPHDRIVER := $(if $(DOCKER_GRAPHDRIVER),$(DOCKER_GRAPHDRIVER),$(shell docker info 2>&1 | grep "Storage Driver" | sed 's/.*: //'))
|
||||||
export DOCKER_GRAPHDRIVER
|
|
||||||
|
|
||||||
DOCKER_GITCOMMIT := $(shell git rev-parse HEAD)
|
# get OS/Arch of docker engine
|
||||||
export DOCKER_GITCOMMIT
|
DOCKER_OSARCH := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $${DOCKER_ENGINE_OSARCH:-$$DOCKER_CLIENT_OSARCH}')
|
||||||
|
DOCKERFILE := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $${DOCKERFILE}')
|
||||||
# allow overriding the repository and branch that validation scripts are running
|
|
||||||
# against these are used in hack/validate/.validate to check what changed in the PR.
|
|
||||||
export VALIDATE_REPO
|
|
||||||
export VALIDATE_BRANCH
|
|
||||||
export VALIDATE_ORIGIN_BRANCH
|
|
||||||
|
|
||||||
export PAGER
|
|
||||||
export GIT_PAGER
|
|
||||||
|
|
||||||
# env vars passed through directly to Docker's build scripts
|
# env vars passed through directly to Docker's build scripts
|
||||||
# to allow things like `make KEEPBUNDLE=1 binary` easily
|
# to allow things like `make KEEPBUNDLE=1 binary` easily
|
||||||
# `project/PACKAGERS.md` have some limited documentation of some of these
|
# `project/PACKAGERS.md` have some limited documentation of some of these
|
||||||
#
|
|
||||||
# DOCKER_LDFLAGS can be used to pass additional parameters to -ldflags
|
|
||||||
# option of "go build". For example, a built-in graphdriver priority list
|
|
||||||
# can be changed during build time like this:
|
|
||||||
#
|
|
||||||
# make DOCKER_LDFLAGS="-X github.com/docker/docker/daemon/graphdriver.priority=overlay2,zfs" dynbinary
|
|
||||||
#
|
|
||||||
DOCKER_ENVS := \
|
DOCKER_ENVS := \
|
||||||
|
-e BUILD_APT_MIRROR \
|
||||||
-e BUILDFLAGS \
|
-e BUILDFLAGS \
|
||||||
-e KEEPBUNDLE \
|
-e KEEPBUNDLE \
|
||||||
-e DOCKER_BUILD_ARGS \
|
-e DOCKER_BUILD_ARGS \
|
||||||
-e DOCKER_BUILD_GOGC \
|
-e DOCKER_BUILD_GOGC \
|
||||||
-e DOCKER_BUILD_OPTS \
|
|
||||||
-e DOCKER_BUILD_PKGS \
|
-e DOCKER_BUILD_PKGS \
|
||||||
-e DOCKER_BUILDKIT \
|
|
||||||
-e DOCKER_BASH_COMPLETION_PATH \
|
|
||||||
-e DOCKER_CLI_PATH \
|
|
||||||
-e DOCKERCLI_VERSION \
|
|
||||||
-e DOCKERCLI_REPOSITORY \
|
|
||||||
-e DOCKERCLI_INTEGRATION_VERSION \
|
|
||||||
-e DOCKERCLI_INTEGRATION_REPOSITORY \
|
|
||||||
-e DOCKER_DEBUG \
|
-e DOCKER_DEBUG \
|
||||||
-e DOCKER_EXPERIMENTAL \
|
-e DOCKER_EXPERIMENTAL \
|
||||||
-e DOCKER_GITCOMMIT \
|
-e DOCKER_GITCOMMIT \
|
||||||
-e DOCKER_GRAPHDRIVER \
|
-e DOCKER_GRAPHDRIVER=$(DOCKER_GRAPHDRIVER) \
|
||||||
-e DOCKER_LDFLAGS \
|
-e DOCKER_INCREMENTAL_BINARY \
|
||||||
-e DOCKER_PORT \
|
-e DOCKER_PORT \
|
||||||
-e DOCKER_REMAP_ROOT \
|
-e DOCKER_REMAP_ROOT \
|
||||||
-e DOCKER_ROOTLESS \
|
|
||||||
-e DOCKER_STORAGE_OPTS \
|
-e DOCKER_STORAGE_OPTS \
|
||||||
-e DOCKER_TEST_HOST \
|
|
||||||
-e DOCKER_USERLANDPROXY \
|
-e DOCKER_USERLANDPROXY \
|
||||||
-e DOCKERD_ARGS \
|
|
||||||
-e DELVE_PORT \
|
|
||||||
-e GITHUB_ACTIONS \
|
|
||||||
-e TEST_FORCE_VALIDATE \
|
|
||||||
-e TEST_INTEGRATION_DIR \
|
|
||||||
-e TEST_INTEGRATION_USE_SNAPSHOTTER \
|
|
||||||
-e TEST_INTEGRATION_FAIL_FAST \
|
|
||||||
-e TEST_SKIP_INTEGRATION \
|
|
||||||
-e TEST_SKIP_INTEGRATION_CLI \
|
|
||||||
-e TEST_IGNORE_CGROUP_CHECK \
|
|
||||||
-e TESTCOVERAGE \
|
|
||||||
-e TESTDEBUG \
|
|
||||||
-e TESTDIRS \
|
-e TESTDIRS \
|
||||||
-e TESTFLAGS \
|
-e TESTFLAGS \
|
||||||
-e TESTFLAGS_INTEGRATION \
|
|
||||||
-e TESTFLAGS_INTEGRATION_CLI \
|
|
||||||
-e TEST_FILTER \
|
|
||||||
-e TIMEOUT \
|
-e TIMEOUT \
|
||||||
-e VALIDATE_REPO \
|
-e HTTP_PROXY \
|
||||||
-e VALIDATE_BRANCH \
|
-e HTTPS_PROXY \
|
||||||
-e VALIDATE_ORIGIN_BRANCH \
|
-e NO_PROXY \
|
||||||
-e VERSION \
|
-e http_proxy \
|
||||||
-e PLATFORM \
|
-e https_proxy \
|
||||||
-e DEFAULT_PRODUCT_LICENSE \
|
-e no_proxy
|
||||||
-e PRODUCT \
|
|
||||||
-e PACKAGER_NAME \
|
|
||||||
-e PAGER \
|
|
||||||
-e GIT_PAGER \
|
|
||||||
-e OTEL_EXPORTER_OTLP_ENDPOINT \
|
|
||||||
-e OTEL_EXPORTER_OTLP_PROTOCOL \
|
|
||||||
-e OTEL_SERVICE_NAME
|
|
||||||
# note: we _cannot_ add "-e DOCKER_BUILDTAGS" here because even if it's unset in the shell, that would shadow the "ENV DOCKER_BUILDTAGS" set in our Dockerfile, which is very important for our official builds
|
# note: we _cannot_ add "-e DOCKER_BUILDTAGS" here because even if it's unset in the shell, that would shadow the "ENV DOCKER_BUILDTAGS" set in our Dockerfile, which is very important for our official builds
|
||||||
|
|
||||||
# to allow `make BIND_DIR=. shell` or `make BIND_DIR= test`
|
# to allow `make BIND_DIR=. shell` or `make BIND_DIR= test`
|
||||||
# (default to no bind mount if DOCKER_HOST is set)
|
# (default to no bind mount if DOCKER_HOST is set)
|
||||||
# note: BINDDIR is supported for backwards-compatibility here
|
# note: BINDDIR is supported for backwards-compatibility here
|
||||||
BIND_DIR := $(if $(BINDDIR),$(BINDDIR),$(if $(DOCKER_HOST),,bundles))
|
BIND_DIR := $(if $(BINDDIR),$(BINDDIR),$(if $(DOCKER_HOST),,bundles))
|
||||||
|
|
||||||
# DOCKER_MOUNT can be overriden, but use at your own risk!
|
|
||||||
ifndef DOCKER_MOUNT
|
|
||||||
DOCKER_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/docker/docker/$(BIND_DIR)")
|
DOCKER_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/docker/docker/$(BIND_DIR)")
|
||||||
DOCKER_MOUNT := $(if $(DOCKER_BINDDIR_MOUNT_OPTS),$(DOCKER_MOUNT):$(DOCKER_BINDDIR_MOUNT_OPTS),$(DOCKER_MOUNT))
|
|
||||||
|
|
||||||
# This allows the test suite to be able to run without worrying about the underlying fs used by the container running the daemon (e.g. aufs-on-aufs), so long as the host running the container is running a supported fs.
|
# This allows the test suite to be able to run without worrying about the underlying fs used by the container running the daemon (e.g. aufs-on-aufs), so long as the host running the container is running a supported fs.
|
||||||
# The volume will be cleaned up when the container is removed due to `--rm`.
|
# The volume will be cleaned up when the container is removed due to `--rm`.
|
||||||
# Note that `BIND_DIR` will already be set to `bundles` if `DOCKER_HOST` is not set (see above BIND_DIR line), in such case this will do nothing since `DOCKER_MOUNT` will already be set.
|
# Note that `BIND_DIR` will already be set to `bundles` if `DOCKER_HOST` is not set (see above BIND_DIR line), in such case this will do nothing since `DOCKER_MOUNT` will already be set.
|
||||||
DOCKER_MOUNT := $(if $(DOCKER_MOUNT),$(DOCKER_MOUNT),-v /go/src/github.com/docker/docker/bundles) -v "$(CURDIR)/.git:/go/src/github.com/docker/docker/.git"
|
DOCKER_MOUNT := $(if $(DOCKER_MOUNT),$(DOCKER_MOUNT),-v /go/src/github.com/docker/docker/bundles)
|
||||||
|
|
||||||
DOCKER_MOUNT_CACHE := -v docker-dev-cache:/root/.cache -v docker-mod-cache:/go/pkg/mod/
|
# enable .go-pkg-cache if DOCKER_INCREMENTAL_BINARY and DOCKER_MOUNT (i.e.DOCKER_HOST) are set
|
||||||
DOCKER_MOUNT_CLI := $(if $(DOCKER_CLI_PATH),-v $(shell dirname $(DOCKER_CLI_PATH)):/usr/local/cli,)
|
PKGCACHE_DIR := $(if $(PKGCACHE_DIR),$(PKGCACHE_DIR),.go-pkg-cache)
|
||||||
DOCKER_MOUNT_BASH_COMPLETION := $(if $(DOCKER_BASH_COMPLETION_PATH),-v $(shell dirname $(DOCKER_BASH_COMPLETION_PATH)):/usr/local/completion/bash,)
|
PKGCACHE_MAP := gopath:/go/pkg goroot-linux_amd64_netgo:/usr/local/go/pkg/linux_amd64_netgo
|
||||||
DOCKER_MOUNT := $(DOCKER_MOUNT) $(DOCKER_MOUNT_CACHE) $(DOCKER_MOUNT_CLI) $(DOCKER_MOUNT_BASH_COMPLETION)
|
DOCKER_MOUNT := $(if $(DOCKER_INCREMENTAL_BINARY),$(DOCKER_MOUNT) $(shell echo $(PKGCACHE_MAP) | sed -E 's@([^ ]*)@-v "$(CURDIR)/$(PKGCACHE_DIR)/\1"@g'),$(DOCKER_MOUNT))
|
||||||
endif # ifndef DOCKER_MOUNT
|
|
||||||
|
|
||||||
# This allows to set the docker-dev container name
|
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||||
DOCKER_CONTAINER_NAME := $(if $(CONTAINER_NAME),--name $(CONTAINER_NAME),)
|
GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g")
|
||||||
|
DOCKER_IMAGE := docker-dev$(if $(GIT_BRANCH_CLEAN),:$(GIT_BRANCH_CLEAN))
|
||||||
DOCKER_IMAGE := docker-dev
|
|
||||||
DOCKER_PORT_FORWARD := $(if $(DOCKER_PORT),-p "$(DOCKER_PORT)",)
|
DOCKER_PORT_FORWARD := $(if $(DOCKER_PORT),-p "$(DOCKER_PORT)",)
|
||||||
DELVE_PORT_FORWARD := $(if $(DELVE_PORT),-p "$(DELVE_PORT)",)
|
|
||||||
|
|
||||||
DOCKER_FLAGS := $(DOCKER) run --rm --privileged $(DOCKER_CONTAINER_NAME) $(DOCKER_ENVS) $(DOCKER_MOUNT) $(DOCKER_PORT_FORWARD) $(DELVE_PORT_FORWARD)
|
DOCKER_FLAGS := docker run --rm -i --privileged $(DOCKER_ENVS) $(DOCKER_MOUNT) $(DOCKER_PORT_FORWARD)
|
||||||
|
BUILD_APT_MIRROR := $(if $(DOCKER_BUILD_APT_MIRROR),--build-arg APT_MIRROR=$(DOCKER_BUILD_APT_MIRROR))
|
||||||
SWAGGER_DOCS_PORT ?= 9000
|
export BUILD_APT_MIRROR
|
||||||
|
|
||||||
define \n
|
|
||||||
|
|
||||||
|
|
||||||
endef
|
|
||||||
|
|
||||||
# if this session isn't interactive, then we don't want to allocate a
|
# if this session isn't interactive, then we don't want to allocate a
|
||||||
# TTY, which would fail, but if it is interactive, we do want to attach
|
# TTY, which would fail, but if it is interactive, we do want to attach
|
||||||
|
@ -132,109 +70,76 @@ ifeq ($(INTERACTIVE), 1)
|
||||||
DOCKER_FLAGS += -t
|
DOCKER_FLAGS += -t
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# on GitHub Runners input device is not a TTY but we allocate a pseudo-one,
|
|
||||||
# otherwise keep STDIN open even if not attached if not a GitHub Runner.
|
|
||||||
ifeq ($(GITHUB_ACTIONS),true)
|
|
||||||
DOCKER_FLAGS += -t
|
|
||||||
else
|
|
||||||
DOCKER_FLAGS += -i
|
|
||||||
endif
|
|
||||||
|
|
||||||
DOCKER_RUN_DOCKER := $(DOCKER_FLAGS) "$(DOCKER_IMAGE)"
|
DOCKER_RUN_DOCKER := $(DOCKER_FLAGS) "$(DOCKER_IMAGE)"
|
||||||
|
|
||||||
DOCKER_BUILD_ARGS += --build-arg=GO_VERSION
|
|
||||||
DOCKER_BUILD_ARGS += --build-arg=DOCKERCLI_VERSION
|
|
||||||
DOCKER_BUILD_ARGS += --build-arg=DOCKERCLI_REPOSITORY
|
|
||||||
DOCKER_BUILD_ARGS += --build-arg=DOCKERCLI_INTEGRATION_VERSION
|
|
||||||
DOCKER_BUILD_ARGS += --build-arg=DOCKERCLI_INTEGRATION_REPOSITORY
|
|
||||||
ifdef DOCKER_SYSTEMD
|
|
||||||
DOCKER_BUILD_ARGS += --build-arg=SYSTEMD=true
|
|
||||||
endif
|
|
||||||
|
|
||||||
BUILD_OPTS := ${DOCKER_BUILD_ARGS} ${DOCKER_BUILD_OPTS}
|
|
||||||
BUILD_CMD := $(BUILDX) build
|
|
||||||
BAKE_CMD := $(BUILDX) bake
|
|
||||||
|
|
||||||
default: binary
|
default: binary
|
||||||
|
|
||||||
all: build ## validate all checks, build linux binaries, run all tests,\ncross build non-linux binaries, and generate archives
|
all: build ## validate all checks, build linux binaries, run all tests\ncross build non-linux binaries and generate archives
|
||||||
$(DOCKER_RUN_DOCKER) bash -c 'hack/validate/default && hack/make.sh'
|
$(DOCKER_RUN_DOCKER) bash -c 'hack/validate/default && hack/make.sh'
|
||||||
|
|
||||||
binary: bundles ## build statically linked linux binaries
|
binary: build ## build the linux binaries
|
||||||
$(BAKE_CMD) binary
|
$(DOCKER_RUN_DOCKER) hack/make.sh binary
|
||||||
|
|
||||||
dynbinary: bundles ## build dynamically linked linux binaries
|
build: bundles init-go-pkg-cache
|
||||||
$(BAKE_CMD) dynbinary
|
docker build ${BUILD_APT_MIRROR} ${DOCKER_BUILD_ARGS} -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" .
|
||||||
|
|
||||||
cross: bundles ## cross build the binaries
|
|
||||||
$(BAKE_CMD) binary-cross
|
|
||||||
|
|
||||||
bundles:
|
bundles:
|
||||||
mkdir bundles
|
mkdir bundles
|
||||||
|
|
||||||
.PHONY: clean
|
cross: build ## cross build the binaries for darwin, freebsd and\nwindows
|
||||||
clean: clean-cache
|
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross
|
||||||
|
|
||||||
|
deb: build ## build the deb packages
|
||||||
|
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary build-deb
|
||||||
|
|
||||||
.PHONY: clean-cache
|
|
||||||
clean-cache: ## remove the docker volumes that are used for caching in the dev-container
|
|
||||||
docker volume rm -f docker-dev-cache docker-mod-cache
|
|
||||||
|
|
||||||
help: ## this help
|
help: ## this help
|
||||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {gsub("\\\\n",sprintf("\n%22c",""), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||||
|
|
||||||
|
init-go-pkg-cache:
|
||||||
|
mkdir -p $(shell echo $(PKGCACHE_MAP) | sed -E 's@([^: ]*):[^ ]*@$(PKGCACHE_DIR)/\1@g')
|
||||||
|
|
||||||
install: ## install the linux binaries
|
install: ## install the linux binaries
|
||||||
KEEPBUNDLE=1 hack/make.sh install-binary
|
KEEPBUNDLE=1 hack/make.sh install-binary
|
||||||
|
|
||||||
|
manpages: ## Generate man pages from go source and markdown
|
||||||
|
docker build -t docker-manpage-dev -f "man/$(DOCKERFILE)" ./man
|
||||||
|
docker run --rm \
|
||||||
|
-v $(PWD):/go/src/github.com/docker/docker/ \
|
||||||
|
docker-manpage-dev
|
||||||
|
|
||||||
|
rpm: build ## build the rpm packages
|
||||||
|
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary build-rpm
|
||||||
|
|
||||||
run: build ## run the docker daemon in a container
|
run: build ## run the docker daemon in a container
|
||||||
$(DOCKER_RUN_DOCKER) sh -c "KEEPBUNDLE=1 hack/make.sh install-binary run"
|
$(DOCKER_RUN_DOCKER) sh -c "KEEPBUNDLE=1 hack/make.sh install-binary run"
|
||||||
|
|
||||||
.PHONY: build
|
|
||||||
ifeq ($(BIND_DIR), .)
|
|
||||||
build: shell_target := --target=dev-base
|
|
||||||
else
|
|
||||||
build: shell_target := --target=dev
|
|
||||||
endif
|
|
||||||
build: bundles
|
|
||||||
$(BUILD_CMD) $(BUILD_OPTS) $(shell_target) --load -t "$(DOCKER_IMAGE)" .
|
|
||||||
|
|
||||||
shell: build ## start a shell inside the build env
|
shell: build ## start a shell inside the build env
|
||||||
$(DOCKER_RUN_DOCKER) bash
|
$(DOCKER_RUN_DOCKER) bash
|
||||||
|
|
||||||
test: build test-unit ## run the unit, integration and docker-py tests
|
yaml-docs-gen: build ## generate documentation YAML files consumed by docs repo
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration test-docker-py
|
$(DOCKER_RUN_DOCKER) sh -c 'hack/make.sh yaml-docs-generator && ( root=$$(pwd); cd bundles/latest/yaml-docs-generator; mkdir docs; ./yaml-docs-generator --root $${root} --target $$(pwd)/docs )'
|
||||||
|
|
||||||
|
test: build ## run the unit, integration and docker-py tests
|
||||||
|
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary cross test-unit test-integration-cli test-docker-py
|
||||||
|
|
||||||
test-docker-py: build ## run the docker-py tests
|
test-docker-py: build ## run the docker-py tests
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-docker-py
|
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-docker-py
|
||||||
|
|
||||||
test-integration-cli: test-integration ## (DEPRECATED) use test-integration
|
test-integration-cli: build ## run the integration tests
|
||||||
|
$(DOCKER_RUN_DOCKER) hack/make.sh build-integration-test-binary dynbinary test-integration-cli
|
||||||
ifneq ($(and $(TEST_SKIP_INTEGRATION),$(TEST_SKIP_INTEGRATION_CLI)),)
|
|
||||||
test-integration:
|
|
||||||
@echo Both integrations suites skipped per environment variables
|
|
||||||
else
|
|
||||||
test-integration: build ## run the integration tests
|
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration
|
|
||||||
endif
|
|
||||||
|
|
||||||
test-integration-flaky: build ## run the stress test for all new integration tests
|
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration-flaky
|
|
||||||
|
|
||||||
test-unit: build ## run the unit tests
|
test-unit: build ## run the unit tests
|
||||||
$(DOCKER_RUN_DOCKER) hack/test/unit
|
$(DOCKER_RUN_DOCKER) hack/make.sh test-unit
|
||||||
|
|
||||||
|
tgz: build ## build the archives (.zip on windows and .tgz\notherwise) containing the binaries
|
||||||
|
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross tgz
|
||||||
|
|
||||||
validate: build ## validate DCO, Seccomp profile generation, gofmt,\n./pkg/ isolation, golint, tests, tomls, go vet and vendor
|
validate: build ## validate DCO, Seccomp profile generation, gofmt,\n./pkg/ isolation, golint, tests, tomls, go vet and vendor
|
||||||
$(DOCKER_RUN_DOCKER) hack/validate/all
|
$(DOCKER_RUN_DOCKER) hack/validate/all
|
||||||
|
|
||||||
validate-generate-files:
|
win: build ## cross build the binary for windows
|
||||||
$(BUILD_CMD) --target "validate" \
|
$(DOCKER_RUN_DOCKER) hack/make.sh win
|
||||||
--output "type=cacheonly" \
|
|
||||||
--file "./hack/dockerfiles/generate-files.Dockerfile" .
|
|
||||||
|
|
||||||
validate-%: build ## validate specific check
|
|
||||||
$(DOCKER_RUN_DOCKER) hack/validate/$*
|
|
||||||
|
|
||||||
win: bundles ## cross build the binary for windows
|
|
||||||
$(BAKE_CMD) --set *.platform=windows/amd64 binary
|
|
||||||
|
|
||||||
.PHONY: swagger-gen
|
.PHONY: swagger-gen
|
||||||
swagger-gen:
|
swagger-gen:
|
||||||
|
@ -243,24 +148,3 @@ swagger-gen:
|
||||||
--entrypoint hack/generate-swagger-api.sh \
|
--entrypoint hack/generate-swagger-api.sh \
|
||||||
-e GOPATH=/go \
|
-e GOPATH=/go \
|
||||||
quay.io/goswagger/swagger:0.7.4
|
quay.io/goswagger/swagger:0.7.4
|
||||||
|
|
||||||
.PHONY: swagger-docs
|
|
||||||
swagger-docs: ## preview the API documentation
|
|
||||||
@echo "API docs preview will be running at http://localhost:$(SWAGGER_DOCS_PORT)"
|
|
||||||
@docker run --rm -v $(PWD)/api/swagger.yaml:/usr/share/nginx/html/swagger.yaml \
|
|
||||||
-e 'REDOC_OPTIONS=hide-hostname="true" lazy-rendering' \
|
|
||||||
-p $(SWAGGER_DOCS_PORT):80 \
|
|
||||||
bfirsh/redoc:1.14.0
|
|
||||||
|
|
||||||
.PHONY: generate-files
|
|
||||||
generate-files:
|
|
||||||
$(eval $@_TMP_OUT := $(shell mktemp -d -t moby-output.XXXXXXXXXX))
|
|
||||||
@if [ -z "$($@_TMP_OUT)" ]; then \
|
|
||||||
echo "Temp dir is not set"; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
$(BUILD_CMD) --target "update" \
|
|
||||||
--output "type=local,dest=$($@_TMP_OUT)" \
|
|
||||||
--file "./hack/dockerfiles/generate-files.Dockerfile" .
|
|
||||||
cp -R "$($@_TMP_OUT)"/. .
|
|
||||||
rm -rf "$($@_TMP_OUT)"/*
|
|
||||||
|
|
4
NOTICE
4
NOTICE
|
@ -1,9 +1,9 @@
|
||||||
Docker
|
Docker
|
||||||
Copyright 2012-2017 Docker, Inc.
|
Copyright 2012-2016 Docker, Inc.
|
||||||
|
|
||||||
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
||||||
|
|
||||||
This product contains software (https://github.com/creack/pty) developed
|
This product contains software (https://github.com/kr/pty) developed
|
||||||
by Keith Rarick, licensed under the MIT License.
|
by Keith Rarick, licensed under the MIT License.
|
||||||
|
|
||||||
The following is courtesy of our legal counsel:
|
The following is courtesy of our legal counsel:
|
||||||
|
|
309
README.md
309
README.md
|
@ -1,48 +1,270 @@
|
||||||
The Moby Project
|
Docker: the container engine [![Release](https://img.shields.io/github/release/docker/docker.svg)](https://github.com/docker/docker/releases/latest)
|
||||||
================
|
============================
|
||||||
|
|
||||||
![Moby Project logo](docs/static_files/moby-project-logo.png "The Moby Project")
|
Docker is an open source project to pack, ship and run any application
|
||||||
|
as a lightweight container.
|
||||||
|
|
||||||
Moby is an open-source project created by Docker to enable and accelerate software containerization.
|
Docker containers are both *hardware-agnostic* and *platform-agnostic*.
|
||||||
|
This means they can run anywhere, from your laptop to the largest
|
||||||
|
cloud compute instance and everything in between - and they don't require
|
||||||
|
you to 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.
|
||||||
|
|
||||||
It provides a "Lego set" of toolkit components, the framework for assembling them into custom container-based systems, and a place for all container enthusiasts and professionals to experiment and exchange ideas.
|
Docker began as an open-source implementation of the deployment engine which
|
||||||
Components include container build tools, a container registry, orchestration tools, a runtime and more, and these can be used as building blocks in conjunction with other tools and projects.
|
powered [dotCloud](http://web.archive.org/web/20130530031104/https://www.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.
|
||||||
|
|
||||||
## Principles
|
![Docker logo](docs/static_files/docker-logo-compressed.png "Docker")
|
||||||
|
|
||||||
Moby is an open project guided by strong principles, aiming to be modular, flexible and without too strong an opinion on user experience.
|
## Security Disclosure
|
||||||
It is open to the community to help set its direction.
|
|
||||||
|
|
||||||
- Modular: the project includes lots of components that have well-defined functions and APIs that work together.
|
Security is very important to us. If you have any issue regarding security,
|
||||||
- Batteries included but swappable: Moby includes enough components to build fully featured container systems, but its modular architecture ensures that most of the components can be swapped by different implementations.
|
please disclose the information responsibly by sending an email to
|
||||||
- Usable security: Moby provides secure defaults without compromising usability.
|
security@docker.com and not by creating a GitHub issue.
|
||||||
- Developer focused: The APIs are intended to be functional and useful to build powerful tools.
|
|
||||||
They are not necessarily intended as end user tools but as components aimed at developers.
|
|
||||||
Documentation and UX is aimed at developers not end users.
|
|
||||||
|
|
||||||
## Audience
|
## Better than VMs
|
||||||
|
|
||||||
The Moby Project is intended for engineers, integrators and enthusiasts looking to modify, hack, fix, experiment, invent and build systems based on containers.
|
A common method for distributing applications and sandboxing their
|
||||||
It is not for people looking for a commercially supported system, but for people who want to work and learn with open source code.
|
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:
|
||||||
|
|
||||||
## Relationship with Docker
|
* *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.
|
||||||
|
|
||||||
The components and tools in the Moby Project are initially the open source components that Docker and the community have built for the Docker Project.
|
By contrast, Docker relies on a different sandboxing method known as
|
||||||
New projects can be added if they fit with the community goals. Docker is committed to using Moby as the upstream for the Docker Product.
|
*containerization*. Unlike traditional virtualization, containerization
|
||||||
However, other projects are also encouraged to use Moby as an upstream, and to reuse the components in diverse ways, and all these uses will be treated in the same way. External maintainers and contributors are welcomed.
|
takes place at the kernel level. Most modern operating system kernels
|
||||||
|
now support the primitives necessary for containerization, including
|
||||||
|
Linux with [openvz](https://openvz.org),
|
||||||
|
[vserver](http://linux-vserver.org) and more recently
|
||||||
|
[lxc](https://linuxcontainers.org/), Solaris with
|
||||||
|
[zones](https://docs.oracle.com/cd/E26502_01/html/E29024/preface-1.html#scrolltoc),
|
||||||
|
and FreeBSD with
|
||||||
|
[Jails](https://www.freebsd.org/doc/handbook/jails.html).
|
||||||
|
|
||||||
The Moby project is not intended as a location for support or feature requests for Docker products, but as a place for contributors to work on open source code, fix bugs, and make the code more useful.
|
Docker builds on top of these low-level primitives to offer developers a
|
||||||
The releases are supported by the maintainers, community and users, on a best efforts basis only, and are not intended for customers who want enterprise or commercial support; Docker EE is the appropriate product for these use cases.
|
portable format and runtime environment that solves all four 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.
|
||||||
|
|
||||||
-----
|
Perhaps best of all, because Docker operates at the OS level, it can still be
|
||||||
|
run inside a VM!
|
||||||
|
|
||||||
Legal
|
## Plays well with others
|
||||||
=====
|
|
||||||
|
Docker does not require you to 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
* *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 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 the problem of dependency hell by giving the developer a simple
|
||||||
|
way to express *all* their application's dependencies in one place, while
|
||||||
|
streamlining the process of assembling them. If this makes you think of
|
||||||
|
[XKCD 927](https://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*.
|
||||||
|
|
||||||
|
Here's a typical Docker build process:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
FROM ubuntu:12.04
|
||||||
|
RUN apt-get update && apt-get install -y python python-pip curl
|
||||||
|
RUN curl -sSL https://github.com/shykes/helloflask/archive/master.tar.gz | tar -xzv
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
Getting started
|
||||||
|
===============
|
||||||
|
|
||||||
|
Docker can be installed either on your computer for building applications or
|
||||||
|
on servers for running them. To get started, [check out the installation
|
||||||
|
instructions in the
|
||||||
|
documentation](https://docs.docker.com/engine/installation/).
|
||||||
|
|
||||||
|
Usage examples
|
||||||
|
==============
|
||||||
|
|
||||||
|
Docker can be used to run short-lived commands, long-running daemons
|
||||||
|
(app servers, databases, etc.), interactive shell sessions, etc.
|
||||||
|
|
||||||
|
You can find a [list of real-world
|
||||||
|
examples](https://docs.docker.com/engine/examples/) in the
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
Under the hood
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Under the hood, Docker is built on the following components:
|
||||||
|
|
||||||
|
* The
|
||||||
|
[cgroups](https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt)
|
||||||
|
and
|
||||||
|
[namespaces](http://man7.org/linux/man-pages/man7/namespaces.7.html)
|
||||||
|
capabilities of the Linux kernel
|
||||||
|
* The [Go](https://golang.org) programming language
|
||||||
|
* The [Docker Image Specification](https://github.com/docker/docker/blob/master/image/spec/v1.md)
|
||||||
|
* The [Libcontainer Specification](https://github.com/opencontainers/runc/blob/master/libcontainer/SPEC.md)
|
||||||
|
|
||||||
|
Contributing to Docker [![GoDoc](https://godoc.org/github.com/docker/docker?status.svg)](https://godoc.org/github.com/docker/docker)
|
||||||
|
======================
|
||||||
|
|
||||||
|
| **Master** (Linux) | **Experimental** (Linux) | **Windows** | **FreeBSD** |
|
||||||
|
|------------------|----------------------|---------|---------|
|
||||||
|
| [![Jenkins Build Status](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master/badge/icon)](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master/) | [![Jenkins Build Status](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master%20%28experimental%29/badge/icon)](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master%20%28experimental%29/) | [![Build Status](http://jenkins.dockerproject.org/job/Docker%20Master%20(windows)/badge/icon)](http://jenkins.dockerproject.org/job/Docker%20Master%20(windows)/) | [![Build Status](http://jenkins.dockerproject.org/job/Docker%20Master%20(freebsd)/badge/icon)](http://jenkins.dockerproject.org/job/Docker%20Master%20(freebsd)/) |
|
||||||
|
|
||||||
|
Want to hack on Docker? Awesome! We have [instructions to help you get
|
||||||
|
started contributing code or documentation](https://docs.docker.com/opensource/project/who-written-for/).
|
||||||
|
|
||||||
|
These instructions are probably not perfect, please let us know if anything
|
||||||
|
feels wrong or incomplete. Better yet, submit a PR and improve them yourself.
|
||||||
|
|
||||||
|
Getting the development builds
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Want to run Docker from a master build? You can download
|
||||||
|
master builds at [master.dockerproject.org](https://master.dockerproject.org).
|
||||||
|
They are updated with each commit merged into the master branch.
|
||||||
|
|
||||||
|
Don't know how to use that super cool new feature in the master build? Check
|
||||||
|
out the master docs at
|
||||||
|
[docs.master.dockerproject.org](http://docs.master.dockerproject.org).
|
||||||
|
|
||||||
|
How the project is run
|
||||||
|
======================
|
||||||
|
|
||||||
|
Docker is a very, very active project. If you want to learn more about how it is run,
|
||||||
|
or want to get more involved, the best place to start is [the project directory](https://github.com/docker/docker/tree/master/project).
|
||||||
|
|
||||||
|
We are always open to suggestions on process improvements, and are always looking for more maintainers.
|
||||||
|
|
||||||
|
### Talking to other Docker users and contributors
|
||||||
|
|
||||||
|
<table class="tg">
|
||||||
|
<col width="45%">
|
||||||
|
<col width="65%">
|
||||||
|
<tr>
|
||||||
|
<td>Internet Relay Chat (IRC)</td>
|
||||||
|
<td>
|
||||||
|
<p>
|
||||||
|
IRC is a direct line to our most knowledgeable Docker users; we have
|
||||||
|
both the <code>#docker</code> and <code>#docker-dev</code> group on
|
||||||
|
<strong>irc.freenode.net</strong>.
|
||||||
|
IRC is a rich chat protocol but it can overwhelm new users. You can search
|
||||||
|
<a href="https://botbot.me/freenode/docker/#" target="_blank">our chat archives</a>.
|
||||||
|
</p>
|
||||||
|
Read our <a href="https://docs.docker.com/opensource/get-help/#/irc-quickstart" target="_blank">IRC quickstart guide</a> for an easy way to get started.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Docker Community Forums</td>
|
||||||
|
<td>
|
||||||
|
The <a href="https://forums.docker.com/c/open-source-projects/de" target="_blank">Docker Engine</a>
|
||||||
|
group is for users of the Docker Engine project.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Google Groups</td>
|
||||||
|
<td>
|
||||||
|
The <a href="https://groups.google.com/forum/#!forum/docker-dev"
|
||||||
|
target="_blank">docker-dev</a> group is for contributors and other people
|
||||||
|
contributing to the Docker project. You can join this group without a
|
||||||
|
Google account by sending an email to <a
|
||||||
|
href="mailto:docker-dev+subscribe@googlegroups.com">docker-dev+subscribe@googlegroups.com</a>.
|
||||||
|
You'll receive a join-request message; simply reply to the message to
|
||||||
|
confirm your subscription.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Twitter</td>
|
||||||
|
<td>
|
||||||
|
You can follow <a href="https://twitter.com/docker/" target="_blank">Docker's Twitter feed</a>
|
||||||
|
to get updates on our products. You can also tweet us questions or just
|
||||||
|
share blogs or stories.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Stack Overflow</td>
|
||||||
|
<td>
|
||||||
|
Stack Overflow has over 7000 Docker questions listed. We regularly
|
||||||
|
monitor <a href="https://stackoverflow.com/search?tab=newest&q=docker" target="_blank">Docker questions</a>
|
||||||
|
and so do many other knowledgeable Docker users.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Legal
|
||||||
|
|
||||||
*Brought to you courtesy of our legal counsel. For more context,
|
*Brought to you courtesy of our legal counsel. For more context,
|
||||||
please see the [NOTICE](https://github.com/moby/moby/blob/master/NOTICE) document in this repo.*
|
please see the [NOTICE](https://github.com/docker/docker/blob/master/NOTICE) document in this repo.*
|
||||||
|
|
||||||
Use and transfer of Moby may be subject to certain restrictions by the
|
Use and transfer of Docker may be subject to certain restrictions by the
|
||||||
United States and other governments.
|
United States and other governments.
|
||||||
|
|
||||||
It is your responsibility to ensure that your use and/or transfer does not
|
It is your responsibility to ensure that your use and/or transfer does not
|
||||||
|
@ -50,8 +272,33 @@ violate applicable laws.
|
||||||
|
|
||||||
For more information, please see https://www.bis.doc.gov
|
For more information, please see https://www.bis.doc.gov
|
||||||
|
|
||||||
|
|
||||||
Licensing
|
Licensing
|
||||||
=========
|
=========
|
||||||
Moby is licensed under the Apache License, Version 2.0. See
|
Docker is licensed under the Apache License, Version 2.0. See
|
||||||
[LICENSE](https://github.com/moby/moby/blob/master/LICENSE) for the full
|
[LICENSE](https://github.com/docker/docker/blob/master/LICENSE) for the full
|
||||||
license text.
|
license text.
|
||||||
|
|
||||||
|
Other Docker Related Projects
|
||||||
|
=============================
|
||||||
|
There are a number of projects under development that are based on Docker's
|
||||||
|
core technology. These projects expand the tooling built around the
|
||||||
|
Docker platform to broaden its application and utility.
|
||||||
|
|
||||||
|
* [Docker Registry](https://github.com/docker/distribution): Registry
|
||||||
|
server for Docker (hosting/delivery of repositories and images)
|
||||||
|
* [Docker Machine](https://github.com/docker/machine): Machine management
|
||||||
|
for a container-centric world
|
||||||
|
* [Docker Swarm](https://github.com/docker/swarm): A Docker-native clustering
|
||||||
|
system
|
||||||
|
* [Docker Compose](https://github.com/docker/compose) (formerly Fig):
|
||||||
|
Define and run multi-container apps
|
||||||
|
* [Kitematic](https://github.com/docker/kitematic): The easiest way to use
|
||||||
|
Docker on Mac and Windows
|
||||||
|
|
||||||
|
If you know of another project underway that should be listed here, please help
|
||||||
|
us keep this list up-to-date by submitting a PR.
|
||||||
|
|
||||||
|
Awesome-Docker
|
||||||
|
==============
|
||||||
|
You can find more projects, tools and articles related to Docker on the [awesome-docker list](https://github.com/veggiemonk/awesome-docker). Add your project there.
|
||||||
|
|
149
ROADMAP.md
149
ROADMAP.md
|
@ -1,117 +1,118 @@
|
||||||
Moby Project Roadmap
|
Docker Engine Roadmap
|
||||||
====================
|
=====================
|
||||||
|
|
||||||
### How should I use this document?
|
### How should I use this document?
|
||||||
|
|
||||||
This document provides description of items that the project decided to prioritize. This should
|
This document provides description of items that the project decided to prioritize. This should
|
||||||
serve as a reference point for Moby contributors to understand where the project is going, and
|
serve as a reference point for Docker contributors to understand where the project is going, and
|
||||||
help determine if a contribution could be conflicting with some longer term plans.
|
help determine if a contribution could be conflicting with some longer terms plans.
|
||||||
|
|
||||||
The fact that a feature isn't listed here doesn't mean that a patch for it will automatically be
|
The fact that a feature isn't listed here doesn't mean that a patch for it will automatically be
|
||||||
refused! We are always happy to receive patches for new cool features we haven't thought about,
|
refused (except for those mentioned as "frozen features" below)! We are always happy to receive
|
||||||
or didn't judge to be a priority. Please however understand that such patches might take longer
|
patches for new cool features we haven't thought about, or didn't judge priority. Please however
|
||||||
for us to review.
|
understand that such patches might take longer for us to review.
|
||||||
|
|
||||||
### How can I help?
|
### How can I help?
|
||||||
|
|
||||||
Short term objectives are listed in
|
Short term objectives are listed in the [wiki](https://github.com/docker/docker/wiki) and described
|
||||||
[Issues](https://github.com/moby/moby/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap). Our
|
in [Issues](https://github.com/docker/docker/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap). Our
|
||||||
goal is to split down the workload in such way that anybody can jump in and help. Please comment on
|
goal is to split down the workload in such way that anybody can jump in and help. Please comment on
|
||||||
issues if you want to work on it to avoid duplicating effort! Similarly, if a maintainer is already
|
issues if you want to take it to avoid duplicating effort! Similarly, if a maintainer is already
|
||||||
assigned on an issue you'd like to participate in, pinging him on GitHub to offer your help is
|
assigned on an issue you'd like to participate in, pinging him on IRC or GitHub to offer your help is
|
||||||
the best way to go.
|
the best way to go.
|
||||||
|
|
||||||
### How can I add something to the roadmap?
|
### How can I add something to the roadmap?
|
||||||
|
|
||||||
The roadmap process is new to the Moby Project: we are only beginning to structure and document the
|
The roadmap process is new to the Docker Engine: we are only beginning to structure and document the
|
||||||
project objectives. Our immediate goal is to be more transparent, and work with our community to
|
project objectives. Our immediate goal is to be more transparent, and work with our community to
|
||||||
focus our efforts on fewer prioritized topics.
|
focus our efforts on fewer prioritized topics.
|
||||||
|
|
||||||
We hope to offer in the near future a process allowing anyone to propose a topic to the roadmap, but
|
We hope to offer in the near future a process allowing anyone to propose a topic to the roadmap, but
|
||||||
we are not quite there yet. For the time being, it is best to discuss with the maintainers on an
|
we are not quite there yet. For the time being, the BDFL remains the keeper of the roadmap, and we
|
||||||
issue, in the Slack channel, or in person at the Moby Summits that happen every few months.
|
won't be accepting pull requests adding or removing items from this file.
|
||||||
|
|
||||||
# 1. Features and refactoring
|
# 1. Features and refactoring
|
||||||
|
|
||||||
## 1.1 Runtime improvements
|
## 1.1 Runtime improvements
|
||||||
|
|
||||||
Over time we have accumulated a lot of functionality in the container runtime
|
We recently introduced [`runC`](https://runc.io) as a standalone low-level tool for container
|
||||||
aspect of Moby while also growing in other areas. Much of the container runtime
|
execution. The initial goal was to integrate runC as a replacement in the Engine for the traditional
|
||||||
pieces are now duplicated work available in other, lower level components such
|
default libcontainer `execdriver`, but the Engine internals were not ready for this.
|
||||||
as [containerd](https://containerd.io).
|
|
||||||
|
|
||||||
Moby currently only utilizes containerd for basic runtime state management, e.g. starting
|
As runC continued evolving, and the OCI specification along with it, we created
|
||||||
and stopping a container, which is what the pre-containerd 1.0 daemon provided.
|
[`containerd`](https://containerd.tools/), a daemon to control and monitor multiple `runC`. This is
|
||||||
Now that containerd is a full-fledged container runtime which supports full
|
the new target for Engine integration, as it can entirely replace the whole `execdriver`
|
||||||
container life-cycle management, we would like to start relying more on containerd
|
architecture, and container monitoring along with it.
|
||||||
and removing the bits in Moby which are now duplicated. This will necessitate
|
|
||||||
a significant effort to refactor and even remove large parts of Moby's codebase.
|
|
||||||
|
|
||||||
Tracking issues:
|
Docker Engine will rely on a long-running `containerd` companion daemon for all container execution
|
||||||
|
related operations. This could open the door in the future for Engine restarts without interrupting
|
||||||
|
running containers.
|
||||||
|
|
||||||
- [#38043](https://github.com/moby/moby/issues/38043) Proposal: containerd image integration
|
## 1.2 Plugins improvements
|
||||||
|
|
||||||
## 1.2 Image Builder
|
Docker Engine 1.7.0 introduced plugin support, initially for the use cases of volumes and networks
|
||||||
|
extensions. The plugin infrastructure was kept minimal as we were collecting use cases and real
|
||||||
|
world feedback before optimizing for any particular workflow.
|
||||||
|
|
||||||
Work is ongoing to integrate [BuildKit](https://github.com/moby/buildkit) into
|
In the future, we'd like plugins to become first class citizens, and encourage an ecosystem of
|
||||||
Moby and replace the "v0" build implementation. Buildkit offers better cache
|
plugins. This implies in particular making it trivially easy to distribute plugins as containers
|
||||||
management, parallelizable build steps, and better extensibility while also
|
through any Registry instance, as well as solving the commonly heard pain points of plugins needing
|
||||||
keeping builds portable, a chief tenent of Moby's builder.
|
to be treated as somewhat special (being active at all time, started before any other user
|
||||||
|
containers, and not as easily dismissed).
|
||||||
|
|
||||||
Upon completion of this effort, users will have a builder that performs better
|
## 1.3 Internal decoupling
|
||||||
while also being more extensible, enabling users to provide their own custom
|
|
||||||
syntax which can be either Dockerfile-like or something completely different.
|
|
||||||
|
|
||||||
See [buildpacks on buildkit](https://github.com/tonistiigi/buildkit-pack) as an
|
A lot of work has been done in trying to decouple the Docker Engine's internals. In particular, the
|
||||||
example of this extensibility.
|
API implementation has been refactored, and the Builder side of the daemon is now
|
||||||
|
[fully independent](https://github.com/docker/docker/tree/master/builder) while still residing in
|
||||||
|
the same repository.
|
||||||
|
|
||||||
New features for the builder and Dockerfile should be implemented first in the
|
We are exploring ways to go further with that decoupling, capitalizing on the work introduced by the
|
||||||
BuildKit backend using an external Dockerfile implementation from the container
|
runtime renovation and plugins improvement efforts. Indeed, the combination of `containerd` support
|
||||||
images. This allows everyone to test and evaluate the feature without upgrading
|
with the concept of "special" containers opens the door for bootstrapping more Engine internals
|
||||||
their daemon. New features should go to the experimental channel first, and can be
|
using the same facilities.
|
||||||
part of the `docker/dockerfile:experimental` image. From there they graduate to
|
|
||||||
`docker/dockerfile:latest` and binary releases. The Dockerfile frontend source
|
|
||||||
code is temporarily located at
|
|
||||||
[https://github.com/moby/buildkit/tree/master/frontend/dockerfile](https://github.com/moby/buildkit/tree/master/frontend/dockerfile)
|
|
||||||
with separate new features defined with go build tags.
|
|
||||||
|
|
||||||
Tracking issues:
|
## 1.4 Cluster capable Engine
|
||||||
|
|
||||||
- [#32925](https://github.com/moby/moby/issues/32925) discussion: builder future: buildkit
|
The community has been pushing for a more cluster capable Docker Engine, and a huge effort was spent
|
||||||
|
adding features such as multihost networking, and node discovery down at the Engine level. Yet, the
|
||||||
|
Engine is currently incapable of taking scheduling decisions alone, and continues relying on Swarm
|
||||||
|
for that.
|
||||||
|
|
||||||
## 1.3 Rootless Mode
|
We plan to complete this effort and make Engine fully cluster capable. Multiple instances of the
|
||||||
|
Docker Engine being already capable of discovering each other and establish overlay networking for
|
||||||
|
their container to communicate, the next step is for a given Engine to gain ability to dispatch work
|
||||||
|
to another node in the cluster. This will be introduced in a backward compatible way, such that a
|
||||||
|
`docker run` invocation on a particular node remains fully deterministic.
|
||||||
|
|
||||||
Running the daemon requires elevated privileges for many tasks. We would like to
|
# 2 Frozen features
|
||||||
support running the daemon as a normal, unprivileged user without requiring `suid`
|
|
||||||
binaries.
|
|
||||||
|
|
||||||
Tracking issues:
|
## 2.1 Docker exec
|
||||||
|
|
||||||
- [#37375](https://github.com/moby/moby/issues/37375) Proposal: allow running `dockerd` as an unprivileged user (aka rootless mode)
|
We won't accept patches expanding the surface of `docker exec`, which we intend to keep as a
|
||||||
|
*debugging* feature, as well as being strongly dependent on the Runtime ingredient effort.
|
||||||
|
|
||||||
## 1.4 Testing
|
## 2.2 Remote Registry Operations
|
||||||
|
|
||||||
Moby has many tests, both unit and integration. Moby needs more tests which can
|
A large amount of work is ongoing in the area of image distribution and provenance. This includes
|
||||||
cover the full spectrum functionality and edge cases out there.
|
moving to the V2 Registry API and heavily refactoring the code that powers these features. The
|
||||||
|
desired result is more secure, reliable and easier to use image distribution.
|
||||||
|
|
||||||
Tests in the `integration-cli` folder should also be migrated into (both in
|
Part of the problem with this part of the code base is the lack of a stable and flexible interface.
|
||||||
location and style) the `integration` folder. These newer tests are simpler to
|
If new features are added that access the registry without solidifying these interfaces, achieving
|
||||||
run in isolation, simpler to read, simpler to write, and more fully exercise the
|
feature parity will continue to be elusive. While we get a handle on this situation, we are imposing
|
||||||
API. Meanwhile tests of the docker CLI should generally live in docker/cli.
|
a moratorium on new code that accesses the Registry API in commands that don't already make remote
|
||||||
|
calls.
|
||||||
|
|
||||||
Tracking issues:
|
Currently, only the following commands cause interaction with a remote registry:
|
||||||
|
|
||||||
- [#32866](https://github.com/moby/moby/issues/32866) Replace integration-cli suite with API test suite
|
- push
|
||||||
|
- pull
|
||||||
|
- run
|
||||||
|
- build
|
||||||
|
- search
|
||||||
|
- login
|
||||||
|
|
||||||
## 1.5 Internal decoupling
|
In the interest of stabilizing the registry access model during this ongoing work, we are not
|
||||||
|
accepting additions to other commands that will cause remote interaction with the Registry API. This
|
||||||
A lot of work has been done in trying to decouple Moby internals. This process of creating
|
moratorium will lift when the goals of the distribution project have been met.
|
||||||
standalone projects with a well defined function that attract a dedicated community should continue.
|
|
||||||
As well as integrating `containerd` we would like to integrate [BuildKit](https://github.com/moby/buildkit)
|
|
||||||
as the next standalone component.
|
|
||||||
We see gRPC as the natural communication layer between decoupled components.
|
|
||||||
|
|
||||||
In addition to pushing out large components into other projects, much of the
|
|
||||||
internal code structure, and in particular the
|
|
||||||
["Daemon"](https://godoc.org/github.com/docker/docker/daemon#Daemon) object,
|
|
||||||
should be split into smaller, more manageable, and more testable components.
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
# Reporting security issues
|
|
||||||
|
|
||||||
The Moby maintainers take security seriously. If you discover a security issue, please bring it to their attention right away!
|
|
||||||
|
|
||||||
### Reporting a Vulnerability
|
|
||||||
|
|
||||||
Please **DO NOT** file a public issue, instead send your report privately to security@docker.com.
|
|
||||||
|
|
||||||
Security reports are greatly appreciated and we will publicly thank you for it, although we keep your name confidential if you request it. We also like to send gifts—if you're into schwag, make sure to let us know. We currently do not offer a paid security bounty program, but are not ruling it out in the future.
|
|
126
TESTING.md
126
TESTING.md
|
@ -1,126 +0,0 @@
|
||||||
# Testing
|
|
||||||
|
|
||||||
This document contains the Moby code testing guidelines. It should answer any
|
|
||||||
questions you may have as an aspiring Moby contributor.
|
|
||||||
|
|
||||||
## Test suites
|
|
||||||
|
|
||||||
Moby has two test suites (and one legacy test suite):
|
|
||||||
|
|
||||||
* Unit tests - use standard `go test` and
|
|
||||||
[gotest.tools/assert](https://godoc.org/gotest.tools/assert) assertions. They are located in
|
|
||||||
the package they test. Unit tests should be fast and test only their own
|
|
||||||
package.
|
|
||||||
* API integration tests - use standard `go test` and
|
|
||||||
[gotest.tools/assert](https://godoc.org/gotest.tools/assert) assertions. They are located in
|
|
||||||
`./integration/<component>` directories, where `component` is: container,
|
|
||||||
image, volume, etc. These tests perform HTTP requests to an API endpoint and
|
|
||||||
check the HTTP response and daemon state after the call.
|
|
||||||
|
|
||||||
The legacy test suite `integration-cli/` is deprecated. No new tests will be
|
|
||||||
added to this suite. Any tests in this suite which require updates should be
|
|
||||||
ported to either the unit test suite or the new API integration test suite.
|
|
||||||
|
|
||||||
## Writing new tests
|
|
||||||
|
|
||||||
Most code changes will fall into one of the following categories.
|
|
||||||
|
|
||||||
### Writing tests for new features
|
|
||||||
|
|
||||||
New code should be covered by unit tests. If the code is difficult to test with
|
|
||||||
unit tests, then that is a good sign that it should be refactored to make it
|
|
||||||
easier to reuse and maintain. Consider accepting unexported interfaces instead
|
|
||||||
of structs so that fakes can be provided for dependencies.
|
|
||||||
|
|
||||||
If the new feature includes a completely new API endpoint then a new API
|
|
||||||
integration test should be added to cover the success case of that endpoint.
|
|
||||||
|
|
||||||
If the new feature does not include a completely new API endpoint consider
|
|
||||||
adding the new API fields to the existing test for that endpoint. A new
|
|
||||||
integration test should **not** be added for every new API field or API error
|
|
||||||
case. Error cases should be handled by unit tests.
|
|
||||||
|
|
||||||
### Writing tests for bug fixes
|
|
||||||
|
|
||||||
Bugs fixes should include a unit test case which exercises the bug.
|
|
||||||
|
|
||||||
A bug fix may also include new assertions in existing integration tests for the
|
|
||||||
API endpoint.
|
|
||||||
|
|
||||||
### Writing new integration tests
|
|
||||||
|
|
||||||
Note the `integration-cli` tests are deprecated; new tests will be rejected by
|
|
||||||
the CI.
|
|
||||||
|
|
||||||
Instead, implement new tests under `integration/`.
|
|
||||||
|
|
||||||
### Integration tests environment considerations
|
|
||||||
|
|
||||||
When adding new tests or modifying existing tests under `integration/`, testing
|
|
||||||
environment should be properly considered. `skip.If` from
|
|
||||||
[gotest.tools/skip](https://godoc.org/gotest.tools/skip) can be used to make the
|
|
||||||
test run conditionally. Full testing environment conditions can be found at
|
|
||||||
[environment.go](https://github.com/moby/moby/blob/6b6eeed03b963a27085ea670f40cd5ff8a61f32e/testutil/environment/environment.go)
|
|
||||||
|
|
||||||
Here is a quick example. If the test needs to interact with a docker daemon on
|
|
||||||
the same host, the following condition should be checked within the test code
|
|
||||||
|
|
||||||
```go
|
|
||||||
skip.If(t, testEnv.IsRemoteDaemon())
|
|
||||||
// your integration test code
|
|
||||||
```
|
|
||||||
|
|
||||||
If a remote daemon is detected, the test will be skipped.
|
|
||||||
|
|
||||||
## Running tests
|
|
||||||
|
|
||||||
### Unit Tests
|
|
||||||
|
|
||||||
To run the unit test suite:
|
|
||||||
|
|
||||||
```
|
|
||||||
make test-unit
|
|
||||||
```
|
|
||||||
|
|
||||||
or `hack/test/unit` from inside a `BINDDIR=. make shell` container or properly
|
|
||||||
configured environment.
|
|
||||||
|
|
||||||
The following environment variables may be used to run a subset of tests:
|
|
||||||
|
|
||||||
* `TESTDIRS` - paths to directories to be tested, defaults to `./...`
|
|
||||||
* `TESTFLAGS` - flags passed to `go test`, to run tests which match a pattern
|
|
||||||
use `TESTFLAGS="-test.run TestNameOrPrefix"`
|
|
||||||
|
|
||||||
### Integration Tests
|
|
||||||
|
|
||||||
To run the integration test suite:
|
|
||||||
|
|
||||||
```
|
|
||||||
make test-integration
|
|
||||||
```
|
|
||||||
|
|
||||||
This make target runs both the "integration" suite and the "integration-cli"
|
|
||||||
suite.
|
|
||||||
|
|
||||||
You can specify which integration test dirs to build and run by specifying
|
|
||||||
the list of dirs in the TEST_INTEGRATION_DIR environment variable.
|
|
||||||
|
|
||||||
You can also explicitly skip either suite by setting (any value) in
|
|
||||||
TEST_SKIP_INTEGRATION and/or TEST_SKIP_INTEGRATION_CLI environment variables.
|
|
||||||
|
|
||||||
Flags specific to each suite can be set in the TESTFLAGS_INTEGRATION and
|
|
||||||
TESTFLAGS_INTEGRATION_CLI environment variables.
|
|
||||||
|
|
||||||
If all you want is to specify a test filter to run, you can set the
|
|
||||||
`TEST_FILTER` environment variable. This ends up getting passed directly to `go
|
|
||||||
test -run` (or `go test -check-f`, depending on the test suite). It will also
|
|
||||||
automatically set the other above mentioned environment variables accordingly.
|
|
||||||
|
|
||||||
### Go Version
|
|
||||||
|
|
||||||
You can change a version of golang used for building stuff that is being tested
|
|
||||||
by setting `GO_VERSION` variable, for example:
|
|
||||||
|
|
||||||
```
|
|
||||||
make GO_VERSION=1.12.8 test
|
|
||||||
```
|
|
11
VENDORING.md
11
VENDORING.md
|
@ -26,14 +26,13 @@ dependency on a package of a specific version or greater up to the next major
|
||||||
release, without encountering breaking changes.
|
release, without encountering breaking changes.
|
||||||
|
|
||||||
## Semantic Versioning
|
## Semantic Versioning
|
||||||
Annotated version tags should follow [Semantic Versioning](http://semver.org) policies:
|
Annotated version tags should follow Schema Versioning policies.
|
||||||
|
According to http://semver.org:
|
||||||
|
|
||||||
"Given a version number MAJOR.MINOR.PATCH, increment the:
|
"Given a version number MAJOR.MINOR.PATCH, increment the:
|
||||||
|
MAJOR version when you make incompatible API changes,
|
||||||
1. MAJOR version when you make incompatible API changes,
|
MINOR version when you add functionality in a backwards-compatible manner, and
|
||||||
2. MINOR version when you add functionality in a backwards-compatible manner, and
|
PATCH version when you make backwards-compatible bug fixes.
|
||||||
3. PATCH version when you make backwards-compatible bug fixes.
|
|
||||||
|
|
||||||
Additional labels for pre-release and build metadata are available as extensions
|
Additional labels for pre-release and build metadata are available as extensions
|
||||||
to the MAJOR.MINOR.PATCH format."
|
to the MAJOR.MINOR.PATCH format."
|
||||||
|
|
||||||
|
|
1
VERSION
Normal file
1
VERSION
Normal file
|
@ -0,0 +1 @@
|
||||||
|
17.03.0-ce-rc1
|
|
@ -10,17 +10,17 @@ It consists of various components in this repository:
|
||||||
- `client/` The Go client used by the command-line client. It can also be used by third-party Go programs.
|
- `client/` The Go client used by the command-line client. It can also be used by third-party Go programs.
|
||||||
- `daemon/` The daemon, which serves the API.
|
- `daemon/` The daemon, which serves the API.
|
||||||
|
|
||||||
## Swagger definition
|
## Swagger definition
|
||||||
|
|
||||||
The API is defined by the [Swagger](http://swagger.io/specification/) definition in `api/swagger.yaml`. This definition can be used to:
|
The API is defined by the [Swagger](http://swagger.io/specification/) definition in `api/swagger.yaml`. This definition can be used to:
|
||||||
|
|
||||||
1. Automatically generate documentation.
|
1. To automatically generate documentation.
|
||||||
2. Automatically generate the Go server and client. (A work-in-progress.)
|
2. To automatically generate the Go server and client. (A work-in-progress.)
|
||||||
3. Provide a machine readable version of the API for introspecting what it can do, automatically generating clients for other languages, etc.
|
3. Provide a machine readable version of the API for introspecting what it can do, automatically generating clients for other languages, etc.
|
||||||
|
|
||||||
## Updating the API documentation
|
## Updating the API documentation
|
||||||
|
|
||||||
The API documentation is generated entirely from `api/swagger.yaml`. If you make updates to the API, edit this file to represent the change in the documentation.
|
The API documentation is generated entirely from `api/swagger.yaml`. If you make updates to the API, you'll need to edit this file to represent the change in the documentation.
|
||||||
|
|
||||||
The file is split into two main sections:
|
The file is split into two main sections:
|
||||||
|
|
||||||
|
@ -29,14 +29,14 @@ The file is split into two main sections:
|
||||||
|
|
||||||
To make an edit, first look for the endpoint you want to edit under `paths`, then make the required edits. Endpoints may reference reusable objects with `$ref`, which can be found in the `definitions` section.
|
To make an edit, first look for the endpoint you want to edit under `paths`, then make the required edits. Endpoints may reference reusable objects with `$ref`, which can be found in the `definitions` section.
|
||||||
|
|
||||||
There is hopefully enough example material in the file for you to copy a similar pattern from elsewhere in the file (e.g. adding new fields or endpoints), but for the full reference, see the [Swagger specification](https://github.com/docker/docker/issues/27919).
|
There is hopefully enough example material in the file for you to copy a similar pattern from elsewhere in the file (e.g. adding new fields or endpoints), but for the full reference, see the [Swagger specification](https://github.com/docker/docker/issues/27919)
|
||||||
|
|
||||||
`swagger.yaml` is validated by `hack/validate/swagger` to ensure it is a valid Swagger definition. This is useful when making edits to ensure you are doing the right thing.
|
`swagger.yaml` is validated by `hack/validate/swagger` to ensure it is a valid Swagger definition. This is useful for when you are making edits to ensure you are doing the right thing.
|
||||||
|
|
||||||
## Viewing the API documentation
|
## Viewing the API documentation
|
||||||
|
|
||||||
When you make edits to `swagger.yaml`, you may want to check the generated API documentation to ensure it renders correctly.
|
When you make edits to `swagger.yaml`, you may want to check the generated API documentation to ensure it renders correctly.
|
||||||
|
|
||||||
Run `make swagger-docs` and a preview will be running at `http://localhost:9000`. Some of the styling may be incorrect, but you'll be able to ensure that it is generating the correct documentation.
|
All the documentation generation is done in the documentation repository, [docker/docker.github.io](https://github.com/docker/docker.github.io). The Swagger definition is vendored periodically into this repository, but you can manually copy over the Swagger definition to test changes.
|
||||||
|
|
||||||
The production documentation is generated by vendoring `swagger.yaml` into [docker/docker.github.io](https://github.com/docker/docker.github.io).
|
Copy `api/swagger.yaml` in this repository to `engine/api/[VERSION_NUMBER]/swagger.yaml` in the documentation repository, overwriting what is already there. Then, run `docker-compose up` in the documentation repository and browse to [http://localhost:4000/engine/api/](http://localhost:4000/engine/api/) when it finishes rendering.
|
||||||
|
|
172
api/common.go
172
api/common.go
|
@ -1,20 +1,166 @@
|
||||||
package api // import "github.com/docker/docker/api"
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"mime"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
|
"github.com/docker/libtrust"
|
||||||
|
)
|
||||||
|
|
||||||
// Common constants for daemon and client.
|
// Common constants for daemon and client.
|
||||||
const (
|
const (
|
||||||
// DefaultVersion of the current REST API.
|
// DefaultVersion of Current REST API
|
||||||
DefaultVersion = "1.45"
|
DefaultVersion string = "1.26"
|
||||||
|
|
||||||
// MinSupportedAPIVersion is the minimum API version that can be supported
|
|
||||||
// by the API server, specified as "major.minor". Note that the daemon
|
|
||||||
// may be configured with a different minimum API version, as returned
|
|
||||||
// in [github.com/docker/docker/api/types.Version.MinAPIVersion].
|
|
||||||
//
|
|
||||||
// API requests for API versions lower than the configured version produce
|
|
||||||
// an error.
|
|
||||||
MinSupportedAPIVersion = "1.24"
|
|
||||||
|
|
||||||
// NoBaseImageSpecifier is the symbol used by the FROM
|
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||||
// command to specify that no base image is to be used.
|
// command to specify that no base image is to be used.
|
||||||
NoBaseImageSpecifier = "scratch"
|
NoBaseImageSpecifier string = "scratch"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// byPortInfo is a temporary type used to sort types.Port by its fields
|
||||||
|
type byPortInfo []types.Port
|
||||||
|
|
||||||
|
func (r byPortInfo) Len() int { return len(r) }
|
||||||
|
func (r byPortInfo) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||||
|
func (r byPortInfo) Less(i, j int) bool {
|
||||||
|
if r[i].PrivatePort != r[j].PrivatePort {
|
||||||
|
return r[i].PrivatePort < r[j].PrivatePort
|
||||||
|
}
|
||||||
|
|
||||||
|
if r[i].IP != r[j].IP {
|
||||||
|
return r[i].IP < r[j].IP
|
||||||
|
}
|
||||||
|
|
||||||
|
if r[i].PublicPort != r[j].PublicPort {
|
||||||
|
return r[i].PublicPort < r[j].PublicPort
|
||||||
|
}
|
||||||
|
|
||||||
|
return r[i].Type < r[j].Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisplayablePorts returns formatted string representing open ports of container
|
||||||
|
// e.g. "0.0.0.0:80->9090/tcp, 9988/tcp"
|
||||||
|
// it's used by command 'docker ps'
|
||||||
|
func DisplayablePorts(ports []types.Port) string {
|
||||||
|
type portGroup struct {
|
||||||
|
first uint16
|
||||||
|
last uint16
|
||||||
|
}
|
||||||
|
groupMap := make(map[string]*portGroup)
|
||||||
|
var result []string
|
||||||
|
var hostMappings []string
|
||||||
|
var groupMapKeys []string
|
||||||
|
sort.Sort(byPortInfo(ports))
|
||||||
|
for _, port := range ports {
|
||||||
|
current := port.PrivatePort
|
||||||
|
portKey := port.Type
|
||||||
|
if port.IP != "" {
|
||||||
|
if port.PublicPort != current {
|
||||||
|
hostMappings = append(hostMappings, fmt.Sprintf("%s:%d->%d/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
portKey = fmt.Sprintf("%s/%s", port.IP, port.Type)
|
||||||
|
}
|
||||||
|
group := groupMap[portKey]
|
||||||
|
|
||||||
|
if group == nil {
|
||||||
|
groupMap[portKey] = &portGroup{first: current, last: current}
|
||||||
|
// record order that groupMap keys are created
|
||||||
|
groupMapKeys = append(groupMapKeys, portKey)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if current == (group.last + 1) {
|
||||||
|
group.last = current
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, formGroup(portKey, group.first, group.last))
|
||||||
|
groupMap[portKey] = &portGroup{first: current, last: current}
|
||||||
|
}
|
||||||
|
for _, portKey := range groupMapKeys {
|
||||||
|
g := groupMap[portKey]
|
||||||
|
result = append(result, formGroup(portKey, g.first, g.last))
|
||||||
|
}
|
||||||
|
result = append(result, hostMappings...)
|
||||||
|
return strings.Join(result, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func formGroup(key string, start, last uint16) string {
|
||||||
|
parts := strings.Split(key, "/")
|
||||||
|
groupType := parts[0]
|
||||||
|
var ip string
|
||||||
|
if len(parts) > 1 {
|
||||||
|
ip = parts[0]
|
||||||
|
groupType = parts[1]
|
||||||
|
}
|
||||||
|
group := strconv.Itoa(int(start))
|
||||||
|
if start != last {
|
||||||
|
group = fmt.Sprintf("%s-%d", group, last)
|
||||||
|
}
|
||||||
|
if ip != "" {
|
||||||
|
group = fmt.Sprintf("%s:%s->%s", ip, group, group)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/%s", group, groupType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesContentType validates the content type against the expected one
|
||||||
|
func MatchesContentType(contentType, expectedType string) bool {
|
||||||
|
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Error parsing media type: %s error: %v", contentType, err)
|
||||||
|
}
|
||||||
|
return err == nil && mimetype == expectedType
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadOrCreateTrustKey attempts to load the libtrust key at the given path,
|
||||||
|
// otherwise generates a new one
|
||||||
|
func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
|
||||||
|
err := system.MkdirAll(filepath.Dir(trustKeyPath), 0700)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
trustKey, err := libtrust.LoadKeyFile(trustKeyPath)
|
||||||
|
if err == libtrust.ErrKeyFileDoesNotExist {
|
||||||
|
trustKey, err = libtrust.GenerateECP256PrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error generating key: %s", err)
|
||||||
|
}
|
||||||
|
encodedKey, err := serializePrivateKey(trustKey, filepath.Ext(trustKeyPath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error serializing key: %s", err)
|
||||||
|
}
|
||||||
|
if err := ioutils.AtomicWriteFile(trustKeyPath, encodedKey, os.FileMode(0600)); err != nil {
|
||||||
|
return nil, fmt.Errorf("Error saving key file: %s", err)
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error loading key file %s: %s", trustKeyPath, err)
|
||||||
|
}
|
||||||
|
return trustKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func serializePrivateKey(key libtrust.PrivateKey, ext string) (encoded []byte, err error) {
|
||||||
|
if ext == ".json" || ext == ".jwk" {
|
||||||
|
encoded, err = json.Marshal(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to encode private key JWK: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pemBlock, err := key.PEMBlock()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to encode private key PEM: %s", err)
|
||||||
|
}
|
||||||
|
encoded = pem.EncodeToMemory(pemBlock)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
341
api/common_test.go
Normal file
341
api/common_test.go
Normal file
|
@ -0,0 +1,341 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ports struct {
|
||||||
|
ports []types.Port
|
||||||
|
expected string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisplayablePorts
|
||||||
|
func TestDisplayablePorts(t *testing.T) {
|
||||||
|
cases := []ports{
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
PrivatePort: 9988,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"9988/tcp"},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
PrivatePort: 9988,
|
||||||
|
Type: "udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"9988/udp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
IP: "0.0.0.0",
|
||||||
|
PrivatePort: 9988,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"0.0.0.0:0->9988/tcp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
PrivatePort: 9988,
|
||||||
|
PublicPort: 8899,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"9988/tcp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
IP: "4.3.2.1",
|
||||||
|
PrivatePort: 9988,
|
||||||
|
PublicPort: 8899,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"4.3.2.1:8899->9988/tcp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
IP: "4.3.2.1",
|
||||||
|
PrivatePort: 9988,
|
||||||
|
PublicPort: 9988,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"4.3.2.1:9988->9988/tcp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
PrivatePort: 9988,
|
||||||
|
Type: "udp",
|
||||||
|
}, {
|
||||||
|
PrivatePort: 9988,
|
||||||
|
Type: "udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"9988/udp, 9988/udp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
IP: "1.2.3.4",
|
||||||
|
PublicPort: 9998,
|
||||||
|
PrivatePort: 9998,
|
||||||
|
Type: "udp",
|
||||||
|
}, {
|
||||||
|
IP: "1.2.3.4",
|
||||||
|
PublicPort: 9999,
|
||||||
|
PrivatePort: 9999,
|
||||||
|
Type: "udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"1.2.3.4:9998-9999->9998-9999/udp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
IP: "1.2.3.4",
|
||||||
|
PublicPort: 8887,
|
||||||
|
PrivatePort: 9998,
|
||||||
|
Type: "udp",
|
||||||
|
}, {
|
||||||
|
IP: "1.2.3.4",
|
||||||
|
PublicPort: 8888,
|
||||||
|
PrivatePort: 9999,
|
||||||
|
Type: "udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"1.2.3.4:8887->9998/udp, 1.2.3.4:8888->9999/udp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
PrivatePort: 9998,
|
||||||
|
Type: "udp",
|
||||||
|
}, {
|
||||||
|
PrivatePort: 9999,
|
||||||
|
Type: "udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"9998-9999/udp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
IP: "1.2.3.4",
|
||||||
|
PrivatePort: 6677,
|
||||||
|
PublicPort: 7766,
|
||||||
|
Type: "tcp",
|
||||||
|
}, {
|
||||||
|
PrivatePort: 9988,
|
||||||
|
PublicPort: 8899,
|
||||||
|
Type: "udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"9988/udp, 1.2.3.4:7766->6677/tcp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
IP: "1.2.3.4",
|
||||||
|
PrivatePort: 9988,
|
||||||
|
PublicPort: 8899,
|
||||||
|
Type: "udp",
|
||||||
|
}, {
|
||||||
|
IP: "1.2.3.4",
|
||||||
|
PrivatePort: 9988,
|
||||||
|
PublicPort: 8899,
|
||||||
|
Type: "tcp",
|
||||||
|
}, {
|
||||||
|
IP: "4.3.2.1",
|
||||||
|
PrivatePort: 2233,
|
||||||
|
PublicPort: 3322,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"4.3.2.1:3322->2233/tcp, 1.2.3.4:8899->9988/tcp, 1.2.3.4:8899->9988/udp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
PrivatePort: 9988,
|
||||||
|
PublicPort: 8899,
|
||||||
|
Type: "udp",
|
||||||
|
}, {
|
||||||
|
IP: "1.2.3.4",
|
||||||
|
PrivatePort: 6677,
|
||||||
|
PublicPort: 7766,
|
||||||
|
Type: "tcp",
|
||||||
|
}, {
|
||||||
|
IP: "4.3.2.1",
|
||||||
|
PrivatePort: 2233,
|
||||||
|
PublicPort: 3322,
|
||||||
|
Type: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"9988/udp, 4.3.2.1:3322->2233/tcp, 1.2.3.4:7766->6677/tcp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]types.Port{
|
||||||
|
{
|
||||||
|
PrivatePort: 80,
|
||||||
|
Type: "tcp",
|
||||||
|
}, {
|
||||||
|
PrivatePort: 1024,
|
||||||
|
Type: "tcp",
|
||||||
|
}, {
|
||||||
|
PrivatePort: 80,
|
||||||
|
Type: "udp",
|
||||||
|
}, {
|
||||||
|
PrivatePort: 1024,
|
||||||
|
Type: "udp",
|
||||||
|
}, {
|
||||||
|
IP: "1.1.1.1",
|
||||||
|
PublicPort: 80,
|
||||||
|
PrivatePort: 1024,
|
||||||
|
Type: "tcp",
|
||||||
|
}, {
|
||||||
|
IP: "1.1.1.1",
|
||||||
|
PublicPort: 80,
|
||||||
|
PrivatePort: 1024,
|
||||||
|
Type: "udp",
|
||||||
|
}, {
|
||||||
|
IP: "1.1.1.1",
|
||||||
|
PublicPort: 1024,
|
||||||
|
PrivatePort: 80,
|
||||||
|
Type: "tcp",
|
||||||
|
}, {
|
||||||
|
IP: "1.1.1.1",
|
||||||
|
PublicPort: 1024,
|
||||||
|
PrivatePort: 80,
|
||||||
|
Type: "udp",
|
||||||
|
}, {
|
||||||
|
IP: "2.1.1.1",
|
||||||
|
PublicPort: 80,
|
||||||
|
PrivatePort: 1024,
|
||||||
|
Type: "tcp",
|
||||||
|
}, {
|
||||||
|
IP: "2.1.1.1",
|
||||||
|
PublicPort: 80,
|
||||||
|
PrivatePort: 1024,
|
||||||
|
Type: "udp",
|
||||||
|
}, {
|
||||||
|
IP: "2.1.1.1",
|
||||||
|
PublicPort: 1024,
|
||||||
|
PrivatePort: 80,
|
||||||
|
Type: "tcp",
|
||||||
|
}, {
|
||||||
|
IP: "2.1.1.1",
|
||||||
|
PublicPort: 1024,
|
||||||
|
PrivatePort: 80,
|
||||||
|
Type: "udp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"80/tcp, 80/udp, 1024/tcp, 1024/udp, 1.1.1.1:1024->80/tcp, 1.1.1.1:1024->80/udp, 2.1.1.1:1024->80/tcp, 2.1.1.1:1024->80/udp, 1.1.1.1:80->1024/tcp, 1.1.1.1:80->1024/udp, 2.1.1.1:80->1024/tcp, 2.1.1.1:80->1024/udp",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, port := range cases {
|
||||||
|
actual := DisplayablePorts(port.ports)
|
||||||
|
if port.expected != actual {
|
||||||
|
t.Fatalf("Expected %s, got %s.", port.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesContentType
|
||||||
|
func TestJsonContentType(t *testing.T) {
|
||||||
|
if !MatchesContentType("application/json", "application/json") {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !MatchesContentType("application/json; charset=utf-8", "application/json") {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if MatchesContentType("dockerapplication/json", "application/json") {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadOrCreateTrustKey
|
||||||
|
func TestLoadOrCreateTrustKeyInvalidKeyFile(t *testing.T) {
|
||||||
|
tmpKeyFolderPath, err := ioutil.TempDir("", "api-trustkey-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpKeyFolderPath)
|
||||||
|
|
||||||
|
tmpKeyFile, err := ioutil.TempFile(tmpKeyFolderPath, "keyfile")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := LoadOrCreateTrustKey(tmpKeyFile.Name()); err == nil {
|
||||||
|
t.Fatalf("expected an error, got nothing.")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadOrCreateTrustKeyCreateKey(t *testing.T) {
|
||||||
|
tmpKeyFolderPath, err := ioutil.TempDir("", "api-trustkey-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpKeyFolderPath)
|
||||||
|
|
||||||
|
// Without the need to create the folder hierarchy
|
||||||
|
tmpKeyFile := filepath.Join(tmpKeyFolderPath, "keyfile")
|
||||||
|
|
||||||
|
if key, err := LoadOrCreateTrustKey(tmpKeyFile); err != nil || key == nil {
|
||||||
|
t.Fatalf("expected a new key file, got : %v and %v", err, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(tmpKeyFile); err != nil {
|
||||||
|
t.Fatalf("Expected to find a file %s, got %v", tmpKeyFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// With the need to create the folder hierarchy as tmpKeyFie is in a path
|
||||||
|
// where some folders do not exist.
|
||||||
|
tmpKeyFile = filepath.Join(tmpKeyFolderPath, "folder/hierarchy/keyfile")
|
||||||
|
|
||||||
|
if key, err := LoadOrCreateTrustKey(tmpKeyFile); err != nil || key == nil {
|
||||||
|
t.Fatalf("expected a new key file, got : %v and %v", err, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(tmpKeyFile); err != nil {
|
||||||
|
t.Fatalf("Expected to find a file %s, got %v", tmpKeyFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// With no path at all
|
||||||
|
defer os.Remove("keyfile")
|
||||||
|
if key, err := LoadOrCreateTrustKey("keyfile"); err != nil || key == nil {
|
||||||
|
t.Fatalf("expected a new key file, got : %v and %v", err, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat("keyfile"); err != nil {
|
||||||
|
t.Fatalf("Expected to find a file keyfile, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadOrCreateTrustKeyLoadValidKey(t *testing.T) {
|
||||||
|
tmpKeyFile := filepath.Join("fixtures", "keyfile")
|
||||||
|
|
||||||
|
if key, err := LoadOrCreateTrustKey(tmpKeyFile); err != nil || key == nil {
|
||||||
|
t.Fatalf("expected a key file, got : %v and %v", err, key)
|
||||||
|
}
|
||||||
|
}
|
6
api/common_unix.go
Normal file
6
api/common_unix.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
// MinVersion represents Minimum REST API version supported
|
||||||
|
const MinVersion string = "1.12"
|
8
api/common_windows.go
Normal file
8
api/common_windows.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
// MinVersion represents Minimum REST API version supported
|
||||||
|
// Technically the first daemon API version released on Windows is v1.25 in
|
||||||
|
// engine version 1.13. However, some clients are explicitly using downlevel
|
||||||
|
// APIs (eg docker-compose v2.1 file format) and that is just too restrictive.
|
||||||
|
// Hence also allowing 1.24 on Windows.
|
||||||
|
const MinVersion string = "1.24"
|
47
api/errors/errors.go
Normal file
47
api/errors/errors.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// apiError is an error wrapper that also
|
||||||
|
// holds information about response status codes.
|
||||||
|
type apiError struct {
|
||||||
|
error
|
||||||
|
statusCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPErrorStatusCode returns a status code.
|
||||||
|
func (e apiError) HTTPErrorStatusCode() int {
|
||||||
|
return e.statusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrorWithStatusCode allows you to associate
|
||||||
|
// a specific HTTP Status Code to an error.
|
||||||
|
// The Server will take that code and set
|
||||||
|
// it as the response status.
|
||||||
|
func NewErrorWithStatusCode(err error, code int) error {
|
||||||
|
return apiError{err, code}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBadRequestError creates a new API error
|
||||||
|
// that has the 400 HTTP status code associated to it.
|
||||||
|
func NewBadRequestError(err error) error {
|
||||||
|
return NewErrorWithStatusCode(err, http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequestForbiddenError creates a new API error
|
||||||
|
// that has the 403 HTTP status code associated to it.
|
||||||
|
func NewRequestForbiddenError(err error) error {
|
||||||
|
return NewErrorWithStatusCode(err, http.StatusForbidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequestNotFoundError creates a new API error
|
||||||
|
// that has the 404 HTTP status code associated to it.
|
||||||
|
func NewRequestNotFoundError(err error) error {
|
||||||
|
return NewErrorWithStatusCode(err, http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequestConflictError creates a new API error
|
||||||
|
// that has the 409 HTTP status code associated to it.
|
||||||
|
func NewRequestConflictError(err error) error {
|
||||||
|
return NewErrorWithStatusCode(err, http.StatusConflict)
|
||||||
|
}
|
|
@ -1,130 +0,0 @@
|
||||||
package build // import "github.com/docker/docker/api/server/backend/build"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/distribution/reference"
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/backend"
|
|
||||||
"github.com/docker/docker/api/types/events"
|
|
||||||
"github.com/docker/docker/builder"
|
|
||||||
buildkit "github.com/docker/docker/builder/builder-next"
|
|
||||||
daemonevents "github.com/docker/docker/daemon/events"
|
|
||||||
"github.com/docker/docker/image"
|
|
||||||
"github.com/docker/docker/pkg/stringid"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageComponent provides an interface for working with images
|
|
||||||
type ImageComponent interface {
|
|
||||||
SquashImage(from string, to string) (string, error)
|
|
||||||
TagImage(context.Context, image.ID, reference.Named) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Builder defines interface for running a build
|
|
||||||
type Builder interface {
|
|
||||||
Build(context.Context, backend.BuildConfig) (*builder.Result, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backend provides build functionality to the API router
|
|
||||||
type Backend struct {
|
|
||||||
builder Builder
|
|
||||||
imageComponent ImageComponent
|
|
||||||
buildkit *buildkit.Builder
|
|
||||||
eventsService *daemonevents.Events
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBackend creates a new build backend from components
|
|
||||||
func NewBackend(components ImageComponent, builder Builder, buildkit *buildkit.Builder, es *daemonevents.Events) (*Backend, error) {
|
|
||||||
return &Backend{imageComponent: components, builder: builder, buildkit: buildkit, eventsService: es}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterGRPC registers buildkit controller to the grpc server.
|
|
||||||
func (b *Backend) RegisterGRPC(s *grpc.Server) {
|
|
||||||
if b.buildkit != nil {
|
|
||||||
b.buildkit.RegisterGRPC(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build builds an image from a Source
|
|
||||||
func (b *Backend) Build(ctx context.Context, config backend.BuildConfig) (string, error) {
|
|
||||||
options := config.Options
|
|
||||||
useBuildKit := options.Version == types.BuilderBuildKit
|
|
||||||
|
|
||||||
tags, err := sanitizeRepoAndTags(options.Tags)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var build *builder.Result
|
|
||||||
if useBuildKit {
|
|
||||||
build, err = b.buildkit.Build(ctx, config)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
build, err = b.builder.Build(ctx, config)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if build == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
imageID := build.ImageID
|
|
||||||
if options.Squash {
|
|
||||||
if imageID, err = squashBuild(build, b.imageComponent); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if config.ProgressWriter.AuxFormatter != nil {
|
|
||||||
if err = config.ProgressWriter.AuxFormatter.Emit("moby.image.id", types.BuildResult{ID: imageID}); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !useBuildKit {
|
|
||||||
stdout := config.ProgressWriter.StdoutFormatter
|
|
||||||
fmt.Fprintf(stdout, "Successfully built %s\n", stringid.TruncateID(imageID))
|
|
||||||
}
|
|
||||||
if imageID != "" && !useBuildKit {
|
|
||||||
err = tagImages(ctx, b.imageComponent, config.ProgressWriter.StdoutFormatter, image.ID(imageID), tags)
|
|
||||||
}
|
|
||||||
return imageID, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PruneCache removes all cached build sources
|
|
||||||
func (b *Backend) PruneCache(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
|
|
||||||
buildCacheSize, cacheIDs, err := b.buildkit.Prune(ctx, opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to prune build cache")
|
|
||||||
}
|
|
||||||
b.eventsService.Log(events.ActionPrune, events.BuilderEventType, events.Actor{
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"reclaimed": strconv.FormatInt(buildCacheSize, 10),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return &types.BuildCachePruneReport{SpaceReclaimed: uint64(buildCacheSize), CachesDeleted: cacheIDs}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cancel cancels the build by ID
|
|
||||||
func (b *Backend) Cancel(ctx context.Context, id string) error {
|
|
||||||
return b.buildkit.Cancel(ctx, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func squashBuild(build *builder.Result, imageComponent ImageComponent) (string, error) {
|
|
||||||
var fromID string
|
|
||||||
if build.FromImage != nil {
|
|
||||||
fromID = build.FromImage.ImageID()
|
|
||||||
}
|
|
||||||
imageID, err := imageComponent.SquashImage(build.ImageID, fromID)
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.Wrap(err, "error squashing image")
|
|
||||||
}
|
|
||||||
return imageID, nil
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
package build // import "github.com/docker/docker/api/server/backend/build"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/distribution/reference"
|
|
||||||
"github.com/docker/docker/image"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// tagImages creates image tags for the imageID.
|
|
||||||
func tagImages(ctx context.Context, ic ImageComponent, stdout io.Writer, imageID image.ID, repoAndTags []reference.Named) error {
|
|
||||||
for _, rt := range repoAndTags {
|
|
||||||
if err := ic.TagImage(ctx, imageID, rt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, _ = fmt.Fprintln(stdout, "Successfully tagged", reference.FamiliarString(rt))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sanitizeRepoAndTags parses the raw "t" parameter received from the client
|
|
||||||
// to a slice of repoAndTag. It removes duplicates, and validates each name
|
|
||||||
// to not contain a digest.
|
|
||||||
func sanitizeRepoAndTags(names []string) (repoAndTags []reference.Named, err error) {
|
|
||||||
uniqNames := map[string]struct{}{}
|
|
||||||
for _, repo := range names {
|
|
||||||
if repo == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ref, err := reference.ParseNormalizedNamed(repo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := ref.(reference.Digested); ok {
|
|
||||||
return nil, errors.New("build tag cannot contain a digest")
|
|
||||||
}
|
|
||||||
|
|
||||||
ref = reference.TagNameOnly(ref)
|
|
||||||
nameWithTag := ref.String()
|
|
||||||
if _, exists := uniqNames[nameWithTag]; !exists {
|
|
||||||
uniqNames[nameWithTag] = struct{}{}
|
|
||||||
repoAndTags = append(repoAndTags, ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return repoAndTags, nil
|
|
||||||
}
|
|
|
@ -1,152 +0,0 @@
|
||||||
package httpstatus // import "github.com/docker/docker/api/server/httpstatus"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
cerrdefs "github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/containerd/log"
|
|
||||||
"github.com/docker/distribution/registry/api/errcode"
|
|
||||||
"github.com/docker/docker/errdefs"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
type causer interface {
|
|
||||||
Cause() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromError retrieves status code from error message.
|
|
||||||
func FromError(err error) int {
|
|
||||||
if err == nil {
|
|
||||||
log.G(context.TODO()).WithError(err).Error("unexpected HTTP error handling")
|
|
||||||
return http.StatusInternalServerError
|
|
||||||
}
|
|
||||||
|
|
||||||
var statusCode int
|
|
||||||
|
|
||||||
// Stop right there
|
|
||||||
// Are you sure you should be adding a new error class here? Do one of the existing ones work?
|
|
||||||
|
|
||||||
// Note that the below functions are already checking the error causal chain for matches.
|
|
||||||
switch {
|
|
||||||
case errdefs.IsNotFound(err):
|
|
||||||
statusCode = http.StatusNotFound
|
|
||||||
case errdefs.IsInvalidParameter(err):
|
|
||||||
statusCode = http.StatusBadRequest
|
|
||||||
case errdefs.IsConflict(err):
|
|
||||||
statusCode = http.StatusConflict
|
|
||||||
case errdefs.IsUnauthorized(err):
|
|
||||||
statusCode = http.StatusUnauthorized
|
|
||||||
case errdefs.IsUnavailable(err):
|
|
||||||
statusCode = http.StatusServiceUnavailable
|
|
||||||
case errdefs.IsForbidden(err):
|
|
||||||
statusCode = http.StatusForbidden
|
|
||||||
case errdefs.IsNotModified(err):
|
|
||||||
statusCode = http.StatusNotModified
|
|
||||||
case errdefs.IsNotImplemented(err):
|
|
||||||
statusCode = http.StatusNotImplemented
|
|
||||||
case errdefs.IsSystem(err) || errdefs.IsUnknown(err) || errdefs.IsDataLoss(err) || errdefs.IsDeadline(err) || errdefs.IsCancelled(err):
|
|
||||||
statusCode = http.StatusInternalServerError
|
|
||||||
default:
|
|
||||||
statusCode = statusCodeFromGRPCError(err)
|
|
||||||
if statusCode != http.StatusInternalServerError {
|
|
||||||
return statusCode
|
|
||||||
}
|
|
||||||
statusCode = statusCodeFromContainerdError(err)
|
|
||||||
if statusCode != http.StatusInternalServerError {
|
|
||||||
return statusCode
|
|
||||||
}
|
|
||||||
statusCode = statusCodeFromDistributionError(err)
|
|
||||||
if statusCode != http.StatusInternalServerError {
|
|
||||||
return statusCode
|
|
||||||
}
|
|
||||||
if e, ok := err.(causer); ok {
|
|
||||||
return FromError(e.Cause())
|
|
||||||
}
|
|
||||||
|
|
||||||
log.G(context.TODO()).WithFields(log.Fields{
|
|
||||||
"module": "api",
|
|
||||||
"error": err,
|
|
||||||
"error_type": fmt.Sprintf("%T", err),
|
|
||||||
}).Debug("FIXME: Got an API for which error does not match any expected type!!!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if statusCode == 0 {
|
|
||||||
statusCode = http.StatusInternalServerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return statusCode
|
|
||||||
}
|
|
||||||
|
|
||||||
// statusCodeFromGRPCError returns status code according to gRPC error
|
|
||||||
func statusCodeFromGRPCError(err error) int {
|
|
||||||
switch status.Code(err) {
|
|
||||||
case codes.InvalidArgument: // code 3
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case codes.NotFound: // code 5
|
|
||||||
return http.StatusNotFound
|
|
||||||
case codes.AlreadyExists: // code 6
|
|
||||||
return http.StatusConflict
|
|
||||||
case codes.PermissionDenied: // code 7
|
|
||||||
return http.StatusForbidden
|
|
||||||
case codes.FailedPrecondition: // code 9
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case codes.Unauthenticated: // code 16
|
|
||||||
return http.StatusUnauthorized
|
|
||||||
case codes.OutOfRange: // code 11
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case codes.Unimplemented: // code 12
|
|
||||||
return http.StatusNotImplemented
|
|
||||||
case codes.Unavailable: // code 14
|
|
||||||
return http.StatusServiceUnavailable
|
|
||||||
default:
|
|
||||||
// codes.Canceled(1)
|
|
||||||
// codes.Unknown(2)
|
|
||||||
// codes.DeadlineExceeded(4)
|
|
||||||
// codes.ResourceExhausted(8)
|
|
||||||
// codes.Aborted(10)
|
|
||||||
// codes.Internal(13)
|
|
||||||
// codes.DataLoss(15)
|
|
||||||
return http.StatusInternalServerError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// statusCodeFromDistributionError returns status code according to registry errcode
|
|
||||||
// code is loosely based on errcode.ServeJSON() in docker/distribution
|
|
||||||
func statusCodeFromDistributionError(err error) int {
|
|
||||||
switch errs := err.(type) {
|
|
||||||
case errcode.Errors:
|
|
||||||
if len(errs) < 1 {
|
|
||||||
return http.StatusInternalServerError
|
|
||||||
}
|
|
||||||
if _, ok := errs[0].(errcode.ErrorCoder); ok {
|
|
||||||
return statusCodeFromDistributionError(errs[0])
|
|
||||||
}
|
|
||||||
case errcode.ErrorCoder:
|
|
||||||
return errs.ErrorCode().Descriptor().HTTPStatusCode
|
|
||||||
}
|
|
||||||
return http.StatusInternalServerError
|
|
||||||
}
|
|
||||||
|
|
||||||
// statusCodeFromContainerdError returns status code for containerd errors when
|
|
||||||
// consumed directly (not through gRPC)
|
|
||||||
func statusCodeFromContainerdError(err error) int {
|
|
||||||
switch {
|
|
||||||
case cerrdefs.IsInvalidArgument(err):
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case cerrdefs.IsNotFound(err):
|
|
||||||
return http.StatusNotFound
|
|
||||||
case cerrdefs.IsAlreadyExists(err):
|
|
||||||
return http.StatusConflict
|
|
||||||
case cerrdefs.IsFailedPrecondition(err):
|
|
||||||
return http.StatusPreconditionFailed
|
|
||||||
case cerrdefs.IsUnavailable(err):
|
|
||||||
return http.StatusServiceUnavailable
|
|
||||||
case cerrdefs.IsNotImplemented(err):
|
|
||||||
return http.StatusNotImplemented
|
|
||||||
default:
|
|
||||||
return http.StatusInternalServerError
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
package httputils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
@ -12,4 +12,5 @@ import (
|
||||||
// container configuration.
|
// container configuration.
|
||||||
type ContainerDecoder interface {
|
type ContainerDecoder interface {
|
||||||
DecodeConfig(src io.Reader) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error)
|
DecodeConfig(src io.Reader) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error)
|
||||||
|
DecodeHostConfig(src io.Reader) (*container.HostConfig, error)
|
||||||
}
|
}
|
||||||
|
|
103
api/server/httputils/errors.go
Normal file
103
api/server/httputils/errors.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package httputils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/versions"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// httpStatusError is an interface
|
||||||
|
// that errors with custom status codes
|
||||||
|
// implement to tell the api layer
|
||||||
|
// which response status to set.
|
||||||
|
type httpStatusError interface {
|
||||||
|
HTTPErrorStatusCode() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// inputValidationError is an interface
|
||||||
|
// that errors generated by invalid
|
||||||
|
// inputs can implement to tell the
|
||||||
|
// api layer to set a 400 status code
|
||||||
|
// in the response.
|
||||||
|
type inputValidationError interface {
|
||||||
|
IsValidationError() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHTTPErrorStatusCode retrieves status code from error message
|
||||||
|
func GetHTTPErrorStatusCode(err error) int {
|
||||||
|
if err == nil {
|
||||||
|
logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
var statusCode int
|
||||||
|
errMsg := err.Error()
|
||||||
|
|
||||||
|
switch e := err.(type) {
|
||||||
|
case httpStatusError:
|
||||||
|
statusCode = e.HTTPErrorStatusCode()
|
||||||
|
case inputValidationError:
|
||||||
|
statusCode = http.StatusBadRequest
|
||||||
|
default:
|
||||||
|
// FIXME: this is brittle and should not be necessary, but we still need to identify if
|
||||||
|
// there are errors falling back into this logic.
|
||||||
|
// If we need to differentiate between different possible error types,
|
||||||
|
// we should create appropriate error types that implement the httpStatusError interface.
|
||||||
|
errStr := strings.ToLower(errMsg)
|
||||||
|
for _, status := range []struct {
|
||||||
|
keyword string
|
||||||
|
code int
|
||||||
|
}{
|
||||||
|
{"not found", http.StatusNotFound},
|
||||||
|
{"no such", http.StatusNotFound},
|
||||||
|
{"bad parameter", http.StatusBadRequest},
|
||||||
|
{"no command", http.StatusBadRequest},
|
||||||
|
{"conflict", http.StatusConflict},
|
||||||
|
{"impossible", http.StatusNotAcceptable},
|
||||||
|
{"wrong login/password", http.StatusUnauthorized},
|
||||||
|
{"unauthorized", http.StatusUnauthorized},
|
||||||
|
{"hasn't been activated", http.StatusForbidden},
|
||||||
|
{"this node", http.StatusServiceUnavailable},
|
||||||
|
{"needs to be unlocked", http.StatusServiceUnavailable},
|
||||||
|
{"certificates have expired", http.StatusServiceUnavailable},
|
||||||
|
} {
|
||||||
|
if strings.Contains(errStr, status.keyword) {
|
||||||
|
statusCode = status.code
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if statusCode == 0 {
|
||||||
|
statusCode = http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiVersionSupportsJSONErrors(version string) bool {
|
||||||
|
const firstAPIVersionWithJSONErrors = "1.23"
|
||||||
|
return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeErrorHandler makes an HTTP handler that decodes a Docker error and
|
||||||
|
// returns it in the response.
|
||||||
|
func MakeErrorHandler(err error) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
statusCode := GetHTTPErrorStatusCode(err)
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
if apiVersionSupportsJSONErrors(vars["version"]) {
|
||||||
|
response := &types.ErrorResponse{
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
WriteJSON(w, statusCode, response)
|
||||||
|
} else {
|
||||||
|
http.Error(w, grpc.ErrorDesc(err), statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
package httputils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/distribution/reference"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BoolValue transforms a form value in different formats into a boolean type.
|
// BoolValue transforms a form value in different formats into a boolean type.
|
||||||
|
@ -16,7 +15,7 @@ func BoolValue(r *http.Request, k string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// BoolValueOrDefault returns the default bool passed if the query param is
|
// BoolValueOrDefault returns the default bool passed if the query param is
|
||||||
// missing, otherwise it's just a proxy to boolValue above.
|
// missing, otherwise it's just a proxy to boolValue above
|
||||||
func BoolValueOrDefault(r *http.Request, k string, d bool) bool {
|
func BoolValueOrDefault(r *http.Request, k string, d bool) bool {
|
||||||
if _, ok := r.Form[k]; !ok {
|
if _, ok := r.Form[k]; !ok {
|
||||||
return d
|
return d
|
||||||
|
@ -39,59 +38,20 @@ func Int64ValueOrZero(r *http.Request, k string) int64 {
|
||||||
func Int64ValueOrDefault(r *http.Request, field string, def int64) (int64, error) {
|
func Int64ValueOrDefault(r *http.Request, field string, def int64) (int64, error) {
|
||||||
if r.Form.Get(field) != "" {
|
if r.Form.Get(field) != "" {
|
||||||
value, err := strconv.ParseInt(r.Form.Get(field), 10, 64)
|
value, err := strconv.ParseInt(r.Form.Get(field), 10, 64)
|
||||||
|
if err != nil {
|
||||||
return value, err
|
return value, err
|
||||||
}
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
return def, nil
|
return def, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RepoTagReference parses form values "repo" and "tag" and returns a valid
|
|
||||||
// reference with repository and tag.
|
|
||||||
// If repo is empty, then a nil reference is returned.
|
|
||||||
// If no tag is given, then the default "latest" tag is set.
|
|
||||||
func RepoTagReference(repo, tag string) (reference.NamedTagged, error) {
|
|
||||||
if repo == "" {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ref, err := reference.ParseNormalizedNamed(repo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, isDigested := ref.(reference.Digested); isDigested {
|
|
||||||
return nil, fmt.Errorf("cannot import digest reference")
|
|
||||||
}
|
|
||||||
|
|
||||||
if tag != "" {
|
|
||||||
return reference.WithTag(ref, tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
withDefaultTag := reference.TagNameOnly(ref)
|
|
||||||
|
|
||||||
namedTagged, ok := withDefaultTag.(reference.NamedTagged)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unexpected reference: %q", ref.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return namedTagged, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArchiveOptions stores archive information for different operations.
|
// ArchiveOptions stores archive information for different operations.
|
||||||
type ArchiveOptions struct {
|
type ArchiveOptions struct {
|
||||||
Name string
|
Name string
|
||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
type badParameterError struct {
|
|
||||||
param string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e badParameterError) Error() string {
|
|
||||||
return "bad parameter: " + e.param + "cannot be empty"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e badParameterError) InvalidParameter() {}
|
|
||||||
|
|
||||||
// ArchiveFormValues parses form values and turns them into ArchiveOptions.
|
// ArchiveFormValues parses form values and turns them into ArchiveOptions.
|
||||||
// It fails if the archive name and path are not in the request.
|
// It fails if the archive name and path are not in the request.
|
||||||
func ArchiveFormValues(r *http.Request, vars map[string]string) (ArchiveOptions, error) {
|
func ArchiveFormValues(r *http.Request, vars map[string]string) (ArchiveOptions, error) {
|
||||||
|
@ -100,12 +60,14 @@ func ArchiveFormValues(r *http.Request, vars map[string]string) (ArchiveOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
name := vars["name"]
|
name := vars["name"]
|
||||||
if name == "" {
|
path := filepath.FromSlash(r.Form.Get("path"))
|
||||||
return ArchiveOptions{}, badParameterError{"name"}
|
|
||||||
}
|
switch {
|
||||||
path := r.Form.Get("path")
|
case name == "":
|
||||||
if path == "" {
|
return ArchiveOptions{}, fmt.Errorf("bad parameter: 'name' cannot be empty")
|
||||||
return ArchiveOptions{}, badParameterError{"path"}
|
case path == "":
|
||||||
|
return ArchiveOptions{}, fmt.Errorf("bad parameter: 'path' cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
return ArchiveOptions{name, path}, nil
|
return ArchiveOptions{name, path}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
package httputils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -23,7 +23,7 @@ func TestBoolValue(t *testing.T) {
|
||||||
for c, e := range cases {
|
for c, e := range cases {
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("test", c)
|
v.Set("test", c)
|
||||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
r, _ := http.NewRequest("POST", "", nil)
|
||||||
r.Form = v
|
r.Form = v
|
||||||
|
|
||||||
a := BoolValue(r, "test")
|
a := BoolValue(r, "test")
|
||||||
|
@ -34,14 +34,14 @@ func TestBoolValue(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBoolValueOrDefault(t *testing.T) {
|
func TestBoolValueOrDefault(t *testing.T) {
|
||||||
r, _ := http.NewRequest(http.MethodGet, "", nil)
|
r, _ := http.NewRequest("GET", "", nil)
|
||||||
if !BoolValueOrDefault(r, "queryparam", true) {
|
if !BoolValueOrDefault(r, "queryparam", true) {
|
||||||
t.Fatal("Expected to get true default value, got false")
|
t.Fatal("Expected to get true default value, got false")
|
||||||
}
|
}
|
||||||
|
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("param", "")
|
v.Set("param", "")
|
||||||
r, _ = http.NewRequest(http.MethodGet, "", nil)
|
r, _ = http.NewRequest("GET", "", nil)
|
||||||
r.Form = v
|
r.Form = v
|
||||||
if BoolValueOrDefault(r, "param", true) {
|
if BoolValueOrDefault(r, "param", true) {
|
||||||
t.Fatal("Expected not to get true")
|
t.Fatal("Expected not to get true")
|
||||||
|
@ -59,7 +59,7 @@ func TestInt64ValueOrZero(t *testing.T) {
|
||||||
for c, e := range cases {
|
for c, e := range cases {
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("test", c)
|
v.Set("test", c)
|
||||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
r, _ := http.NewRequest("POST", "", nil)
|
||||||
r.Form = v
|
r.Form = v
|
||||||
|
|
||||||
a := Int64ValueOrZero(r, "test")
|
a := Int64ValueOrZero(r, "test")
|
||||||
|
@ -79,7 +79,7 @@ func TestInt64ValueOrDefault(t *testing.T) {
|
||||||
for c, e := range cases {
|
for c, e := range cases {
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("test", c)
|
v.Set("test", c)
|
||||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
r, _ := http.NewRequest("POST", "", nil)
|
||||||
r.Form = v
|
r.Form = v
|
||||||
|
|
||||||
a, err := Int64ValueOrDefault(r, "test", -1)
|
a, err := Int64ValueOrDefault(r, "test", -1)
|
||||||
|
@ -95,11 +95,11 @@ func TestInt64ValueOrDefault(t *testing.T) {
|
||||||
func TestInt64ValueOrDefaultWithError(t *testing.T) {
|
func TestInt64ValueOrDefaultWithError(t *testing.T) {
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("test", "invalid")
|
v.Set("test", "invalid")
|
||||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
r, _ := http.NewRequest("POST", "", nil)
|
||||||
r.Form = v
|
r.Form = v
|
||||||
|
|
||||||
_, err := Int64ValueOrDefault(r, "test", -1)
|
_, err := Int64ValueOrDefault(r, "test", -1)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected an error.")
|
t.Fatalf("Expected an error.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
package httputils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"fmt"
|
||||||
"encoding/json"
|
|
||||||
"io"
|
"io"
|
||||||
"mime"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/errdefs"
|
"golang.org/x/net/context"
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
"github.com/docker/docker/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIVersionKey is the client's requested API version.
|
// APIVersionKey is the client's requested API version.
|
||||||
type APIVersionKey struct{}
|
const APIVersionKey = "api-version"
|
||||||
|
|
||||||
|
// UAStringKey is used as key type for user-agent string in net/context struct
|
||||||
|
const UAStringKey = "upstream-user-agent"
|
||||||
|
|
||||||
// APIFunc is an adapter to allow the use of ordinary functions as Docker API endpoints.
|
// APIFunc is an adapter to allow the use of ordinary functions as Docker API endpoints.
|
||||||
// Any function that has the appropriate signature can be registered as an API endpoint (e.g. getVersion).
|
// Any function that has the appropriate signature can be registered as an API endpoint (e.g. getVersion).
|
||||||
|
@ -27,7 +29,7 @@ func HijackConnection(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// Flush the options to make sure the client sets the raw mode
|
// Flush the options to make sure the client sets the raw mode
|
||||||
_, _ = conn.Write([]byte{})
|
conn.Write([]byte{})
|
||||||
return conn, conn, nil
|
return conn, conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,9 +39,9 @@ func CloseStreams(streams ...interface{}) {
|
||||||
if tcpc, ok := stream.(interface {
|
if tcpc, ok := stream.(interface {
|
||||||
CloseWrite() error
|
CloseWrite() error
|
||||||
}); ok {
|
}); ok {
|
||||||
_ = tcpc.CloseWrite()
|
tcpc.CloseWrite()
|
||||||
} else if closer, ok := stream.(io.Closer); ok {
|
} else if closer, ok := stream.(io.Closer); ok {
|
||||||
_ = closer.Close()
|
closer.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,50 +51,17 @@ func CheckForJSON(r *http.Request) error {
|
||||||
ct := r.Header.Get("Content-Type")
|
ct := r.Header.Get("Content-Type")
|
||||||
|
|
||||||
// No Content-Type header is ok as long as there's no Body
|
// No Content-Type header is ok as long as there's no Body
|
||||||
if ct == "" && (r.Body == nil || r.ContentLength == 0) {
|
if ct == "" {
|
||||||
|
if r.Body == nil || r.ContentLength == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Otherwise it better be json
|
// Otherwise it better be json
|
||||||
return matchesContentType(ct, "application/json")
|
if api.MatchesContentType(ct, "application/json") {
|
||||||
}
|
|
||||||
|
|
||||||
// ReadJSON validates the request to have the correct content-type, and decodes
|
|
||||||
// the request's Body into out.
|
|
||||||
func ReadJSON(r *http.Request, out interface{}) error {
|
|
||||||
err := CheckForJSON(r)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if r.Body == nil || r.ContentLength == 0 {
|
|
||||||
// an empty body is not invalid, so don't return an error; see
|
|
||||||
// https://lists.w3.org/Archives/Public/ietf-http-wg/2010JulSep/0272.html
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return fmt.Errorf("Content-Type specified (%s) must be 'application/json'", ct)
|
||||||
dec := json.NewDecoder(r.Body)
|
|
||||||
err = dec.Decode(out)
|
|
||||||
defer r.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
return errdefs.InvalidParameter(errors.New("invalid JSON: got EOF while reading request body"))
|
|
||||||
}
|
|
||||||
return errdefs.InvalidParameter(errors.Wrap(err, "invalid JSON"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if dec.More() {
|
|
||||||
return errdefs.InvalidParameter(errors.New("unexpected content after JSON"))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteJSON writes the value v to the http response stream as json with standard json encoding.
|
|
||||||
func WriteJSON(w http.ResponseWriter, code int, v interface{}) error {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(code)
|
|
||||||
enc := json.NewEncoder(w)
|
|
||||||
enc.SetEscapeHTML(false)
|
|
||||||
return enc.Encode(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseForm ensures the request form is parsed even with invalid content types.
|
// ParseForm ensures the request form is parsed even with invalid content types.
|
||||||
|
@ -102,33 +71,20 @@ func ParseForm(r *http.Request) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
|
if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
|
||||||
return errdefs.InvalidParameter(err)
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// VersionFromContext returns an API version from the context using APIVersionKey.
|
// VersionFromContext returns an API version from the context using APIVersionKey.
|
||||||
// It panics if the context value does not have version.Version type.
|
// It panics if the context value does not have version.Version type.
|
||||||
func VersionFromContext(ctx context.Context) string {
|
func VersionFromContext(ctx context.Context) (ver string) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return ""
|
return
|
||||||
|
}
|
||||||
|
val := ctx.Value(APIVersionKey)
|
||||||
|
if val == nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if val := ctx.Value(APIVersionKey{}); val != nil {
|
|
||||||
return val.(string)
|
return val.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// matchesContentType validates the content type against the expected one
|
|
||||||
func matchesContentType(contentType, expectedType string) error {
|
|
||||||
mimetype, _, err := mime.ParseMediaType(contentType)
|
|
||||||
if err != nil {
|
|
||||||
return errdefs.InvalidParameter(errors.Wrapf(err, "malformed Content-Type header (%s)", contentType))
|
|
||||||
}
|
|
||||||
if mimetype != expectedType {
|
|
||||||
return errdefs.InvalidParameter(errors.Errorf("unsupported Content-Type header (%s): must be '%s'", contentType, expectedType))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,130 +0,0 @@
|
||||||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// matchesContentType
|
|
||||||
func TestJsonContentType(t *testing.T) {
|
|
||||||
err := matchesContentType("application/json", "application/json")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = matchesContentType("application/json; charset=utf-8", "application/json")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := "unsupported Content-Type header (dockerapplication/json): must be 'application/json'"
|
|
||||||
err = matchesContentType("dockerapplication/json", "application/json")
|
|
||||||
if err == nil || err.Error() != expected {
|
|
||||||
t.Errorf(`expected "%s", got "%v"`, expected, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = "malformed Content-Type header (foo;;;bar): mime: invalid media parameter"
|
|
||||||
err = matchesContentType("foo;;;bar", "application/json")
|
|
||||||
if err == nil || err.Error() != expected {
|
|
||||||
t.Errorf(`expected "%s", got "%v"`, expected, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadJSON(t *testing.T) {
|
|
||||||
t.Run("nil body", func(t *testing.T) {
|
|
||||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
foo := struct{}{}
|
|
||||||
err = ReadJSON(req, &foo)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("empty body", func(t *testing.T) {
|
|
||||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", strings.NewReader(""))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
foo := struct{ SomeField string }{}
|
|
||||||
err = ReadJSON(req, &foo)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if foo.SomeField != "" {
|
|
||||||
t.Errorf("expected: '', got: %s", foo.SomeField)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with valid request", func(t *testing.T) {
|
|
||||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", strings.NewReader(`{"SomeField":"some value"}`))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
foo := struct{ SomeField string }{}
|
|
||||||
err = ReadJSON(req, &foo)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if foo.SomeField != "some value" {
|
|
||||||
t.Errorf("expected: 'some value', got: %s", foo.SomeField)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("with whitespace", func(t *testing.T) {
|
|
||||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", strings.NewReader(`
|
|
||||||
|
|
||||||
{"SomeField":"some value"}
|
|
||||||
|
|
||||||
`))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
foo := struct{ SomeField string }{}
|
|
||||||
err = ReadJSON(req, &foo)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if foo.SomeField != "some value" {
|
|
||||||
t.Errorf("expected: 'some value', got: %s", foo.SomeField)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with extra content", func(t *testing.T) {
|
|
||||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", strings.NewReader(`{"SomeField":"some value"} and more content`))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
foo := struct{ SomeField string }{}
|
|
||||||
err = ReadJSON(req, &foo)
|
|
||||||
if err == nil {
|
|
||||||
t.Error("expected an error, got none")
|
|
||||||
}
|
|
||||||
expected := "unexpected content after JSON"
|
|
||||||
if err.Error() != expected {
|
|
||||||
t.Errorf("expected: '%s', got: %s", expected, err.Error())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("invalid JSON", func(t *testing.T) {
|
|
||||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/some/path", strings.NewReader(`{invalid json`))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
foo := struct{ SomeField string }{}
|
|
||||||
err = ReadJSON(req, &foo)
|
|
||||||
if err == nil {
|
|
||||||
t.Error("expected an error, got none")
|
|
||||||
}
|
|
||||||
expected := "invalid JSON: invalid character 'i' looking for beginning of object key string"
|
|
||||||
if err.Error() != expected {
|
|
||||||
t.Errorf("expected: '%s', got: %s", expected, err.Error())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
17
api/server/httputils/httputils_write_json.go
Normal file
17
api/server/httputils/httputils_write_json.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
package httputils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WriteJSON writes the value v to the http response stream as json with standard json encoding.
|
||||||
|
func WriteJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(code)
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.SetEscapeHTML(false)
|
||||||
|
return enc.Encode(v)
|
||||||
|
}
|
16
api/server/httputils/httputils_write_json_go16.go
Normal file
16
api/server/httputils/httputils_write_json_go16.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// +build go1.6,!go1.7
|
||||||
|
|
||||||
|
package httputils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WriteJSON writes the value v to the http response stream as json with standard json encoding.
|
||||||
|
func WriteJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(code)
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
return enc.Encode(v)
|
||||||
|
}
|
|
@ -1,84 +0,0 @@
|
||||||
package httputils // import "github.com/docker/docker/api/server/httputils"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/backend"
|
|
||||||
"github.com/docker/docker/api/types/container"
|
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WriteLogStream writes an encoded byte stream of log messages from the
|
|
||||||
// messages channel, multiplexing them with a stdcopy.Writer if mux is true
|
|
||||||
func WriteLogStream(_ context.Context, w io.Writer, msgs <-chan *backend.LogMessage, config *container.LogsOptions, mux bool) {
|
|
||||||
wf := ioutils.NewWriteFlusher(w)
|
|
||||||
defer wf.Close()
|
|
||||||
|
|
||||||
wf.Flush()
|
|
||||||
|
|
||||||
outStream := io.Writer(wf)
|
|
||||||
errStream := outStream
|
|
||||||
sysErrStream := errStream
|
|
||||||
if mux {
|
|
||||||
sysErrStream = stdcopy.NewStdWriter(outStream, stdcopy.Systemerr)
|
|
||||||
errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
|
|
||||||
outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
msg, ok := <-msgs
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// check if the message contains an error. if so, write that error
|
|
||||||
// and exit
|
|
||||||
if msg.Err != nil {
|
|
||||||
fmt.Fprintf(sysErrStream, "Error grabbing logs: %v\n", msg.Err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
logLine := msg.Line
|
|
||||||
if config.Details {
|
|
||||||
logLine = append(attrsByteSlice(msg.Attrs), ' ')
|
|
||||||
logLine = append(logLine, msg.Line...)
|
|
||||||
}
|
|
||||||
if config.Timestamps {
|
|
||||||
logLine = append([]byte(msg.Timestamp.Format(jsonmessage.RFC3339NanoFixed)+" "), logLine...)
|
|
||||||
}
|
|
||||||
if msg.Source == "stdout" && config.ShowStdout {
|
|
||||||
_, _ = outStream.Write(logLine)
|
|
||||||
}
|
|
||||||
if msg.Source == "stderr" && config.ShowStderr {
|
|
||||||
_, _ = errStream.Write(logLine)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type byKey []backend.LogAttr
|
|
||||||
|
|
||||||
func (b byKey) Len() int { return len(b) }
|
|
||||||
func (b byKey) Less(i, j int) bool { return b[i].Key < b[j].Key }
|
|
||||||
func (b byKey) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
|
||||||
|
|
||||||
func attrsByteSlice(a []backend.LogAttr) []byte {
|
|
||||||
// Note this sorts "a" in-place. That is fine here - nothing else is
|
|
||||||
// going to use Attrs or care about the order.
|
|
||||||
sort.Sort(byKey(a))
|
|
||||||
|
|
||||||
var ret []byte
|
|
||||||
for i, pair := range a {
|
|
||||||
k, v := url.QueryEscape(pair.Key), url.QueryEscape(pair.Value)
|
|
||||||
ret = append(ret, []byte(k)...)
|
|
||||||
ret = append(ret, '=')
|
|
||||||
ret = append(ret, []byte(v)...)
|
|
||||||
if i != len(a)-1 {
|
|
||||||
ret = append(ret, ',')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
package server // import "github.com/docker/docker/api/server"
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containerd/log"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/api/server/middleware"
|
"github.com/docker/docker/api/server/middleware"
|
||||||
)
|
)
|
||||||
|
@ -16,7 +16,7 @@ func (s *Server) handlerWithGlobalMiddlewares(handler httputils.APIFunc) httputi
|
||||||
next = m.WrapHandler(next)
|
next = m.WrapHandler(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
if log.GetLevel() == log.DebugLevel {
|
if s.cfg.Logging && logrus.GetLevel() == logrus.DebugLevel {
|
||||||
next = middleware.DebugRequestMiddleware(next)
|
next = middleware.DebugRequestMiddleware(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
package middleware // import "github.com/docker/docker/api/server/middleware"
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/containerd/log"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/api/types/registry"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CORSMiddleware injects CORS headers to each request
|
// CORSMiddleware injects CORS headers to each request
|
||||||
|
@ -29,9 +28,9 @@ func (c CORSMiddleware) WrapHandler(handler func(ctx context.Context, w http.Res
|
||||||
corsHeaders = "*"
|
corsHeaders = "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
log.G(ctx).Debugf("CORS header is enabled and set to: %s", corsHeaders)
|
logrus.Debugf("CORS header is enabled and set to: %s", corsHeaders)
|
||||||
w.Header().Add("Access-Control-Allow-Origin", corsHeaders)
|
w.Header().Add("Access-Control-Allow-Origin", corsHeaders)
|
||||||
w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, "+registry.AuthHeader)
|
w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
|
||||||
w.Header().Add("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS")
|
w.Header().Add("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS")
|
||||||
return handler(ctx, w, r, vars)
|
return handler(ctx, w, r, vars)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
package middleware // import "github.com/docker/docker/api/server/middleware"
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/log"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DebugRequestMiddleware dumps the request to logger
|
// DebugRequestMiddleware dumps the request to logger
|
||||||
func DebugRequestMiddleware(handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error) func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func DebugRequestMiddleware(handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error) func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
log.G(ctx).Debugf("Calling %s %s", r.Method, r.RequestURI)
|
logrus.Debugf("Calling %s %s", r.Method, r.RequestURI)
|
||||||
|
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != "POST" {
|
||||||
return handler(ctx, w, r, vars)
|
return handler(ctx, w, r, vars)
|
||||||
}
|
}
|
||||||
if err := httputils.CheckForJSON(r); err != nil {
|
if err := httputils.CheckForJSON(r); err != nil {
|
||||||
|
@ -44,9 +44,9 @@ func DebugRequestMiddleware(handler func(ctx context.Context, w http.ResponseWri
|
||||||
maskSecretKeys(postForm)
|
maskSecretKeys(postForm)
|
||||||
formStr, errMarshal := json.Marshal(postForm)
|
formStr, errMarshal := json.Marshal(postForm)
|
||||||
if errMarshal == nil {
|
if errMarshal == nil {
|
||||||
log.G(ctx).Debugf("form data: %s", string(formStr))
|
logrus.Debugf("form data: %s", string(formStr))
|
||||||
} else {
|
} else {
|
||||||
log.G(ctx).Debugf("form data: %q", postForm)
|
logrus.Debugf("form data: %q", postForm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,24 +61,10 @@ func maskSecretKeys(inp interface{}) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if form, ok := inp.(map[string]interface{}); ok {
|
if form, ok := inp.(map[string]interface{}); ok {
|
||||||
scrub := []string{
|
|
||||||
// Note: The Data field contains the base64-encoded secret in 'secret'
|
|
||||||
// and 'config' create and update requests. Currently, no other POST
|
|
||||||
// API endpoints use a data field, so we scrub this field unconditionally.
|
|
||||||
// Change this handling to be conditional if a new endpoint is added
|
|
||||||
// in future where this field should not be scrubbed.
|
|
||||||
"data",
|
|
||||||
"jointoken",
|
|
||||||
"password",
|
|
||||||
"secret",
|
|
||||||
"signingcakey",
|
|
||||||
"unlockkey",
|
|
||||||
}
|
|
||||||
loop0:
|
loop0:
|
||||||
for k, v := range form {
|
for k, v := range form {
|
||||||
for _, m := range scrub {
|
for _, m := range []string{"password", "secret", "jointoken", "unlockkey"} {
|
||||||
if strings.EqualFold(m, k) {
|
if strings.EqualFold(m, k) {
|
||||||
form[k] = "*****"
|
form[k] = "*****"
|
||||||
continue loop0
|
continue loop0
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
package middleware // import "github.com/docker/docker/api/server/middleware"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"gotest.tools/v3/assert"
|
|
||||||
is "gotest.tools/v3/assert/cmp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMaskSecretKeys(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
doc string
|
|
||||||
input map[string]interface{}
|
|
||||||
expected map[string]interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
doc: "secret/config create and update requests",
|
|
||||||
input: map[string]interface{}{"Data": "foo", "Name": "name", "Labels": map[string]interface{}{}},
|
|
||||||
expected: map[string]interface{}{"Data": "*****", "Name": "name", "Labels": map[string]interface{}{}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "masking other fields (recursively)",
|
|
||||||
input: map[string]interface{}{
|
|
||||||
"password": "pass",
|
|
||||||
"secret": "secret",
|
|
||||||
"jointoken": "jointoken",
|
|
||||||
"unlockkey": "unlockkey",
|
|
||||||
"signingcakey": "signingcakey",
|
|
||||||
"other": map[string]interface{}{
|
|
||||||
"password": "pass",
|
|
||||||
"secret": "secret",
|
|
||||||
"jointoken": "jointoken",
|
|
||||||
"unlockkey": "unlockkey",
|
|
||||||
"signingcakey": "signingcakey",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: map[string]interface{}{
|
|
||||||
"password": "*****",
|
|
||||||
"secret": "*****",
|
|
||||||
"jointoken": "*****",
|
|
||||||
"unlockkey": "*****",
|
|
||||||
"signingcakey": "*****",
|
|
||||||
"other": map[string]interface{}{
|
|
||||||
"password": "*****",
|
|
||||||
"secret": "*****",
|
|
||||||
"jointoken": "*****",
|
|
||||||
"unlockkey": "*****",
|
|
||||||
"signingcakey": "*****",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "case insensitive field matching",
|
|
||||||
input: map[string]interface{}{
|
|
||||||
"PASSWORD": "pass",
|
|
||||||
"other": map[string]interface{}{
|
|
||||||
"PASSWORD": "pass",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: map[string]interface{}{
|
|
||||||
"PASSWORD": "*****",
|
|
||||||
"other": map[string]interface{}{
|
|
||||||
"PASSWORD": "*****",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, testcase := range tests {
|
|
||||||
t.Run(testcase.doc, func(t *testing.T) {
|
|
||||||
maskSecretKeys(testcase.input)
|
|
||||||
assert.Check(t, is.DeepEqual(testcase.expected, testcase.input))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,9 @@
|
||||||
package middleware // import "github.com/docker/docker/api/server/middleware"
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExperimentalMiddleware is a the middleware in charge of adding the
|
// ExperimentalMiddleware is a the middleware in charge of adding the
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package middleware // import "github.com/docker/docker/api/server/middleware"
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Middleware is an interface to allow the use of ordinary functions as Docker API filters.
|
// Middleware is an interface to allow the use of ordinary functions as Docker API filters.
|
||||||
|
|
|
@ -1,86 +1,50 @@
|
||||||
package middleware // import "github.com/docker/docker/api/server/middleware"
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/docker/docker/api"
|
"github.com/docker/docker/api/errors"
|
||||||
"github.com/docker/docker/api/server/httputils"
|
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// VersionMiddleware is a middleware that
|
// VersionMiddleware is a middleware that
|
||||||
// validates the client and server versions.
|
// validates the client and server versions.
|
||||||
type VersionMiddleware struct {
|
type VersionMiddleware struct {
|
||||||
serverVersion string
|
serverVersion string
|
||||||
|
defaultVersion string
|
||||||
// defaultAPIVersion is the default API version provided by the API server,
|
minVersion string
|
||||||
// specified as "major.minor". It is usually configured to the latest API
|
|
||||||
// version [github.com/docker/docker/api.DefaultVersion].
|
|
||||||
//
|
|
||||||
// API requests for API versions greater than this version are rejected by
|
|
||||||
// the server and produce a [versionUnsupportedError].
|
|
||||||
defaultAPIVersion string
|
|
||||||
|
|
||||||
// minAPIVersion is the minimum API version provided by the API server,
|
|
||||||
// specified as "major.minor".
|
|
||||||
//
|
|
||||||
// API requests for API versions lower than this version are rejected by
|
|
||||||
// the server and produce a [versionUnsupportedError].
|
|
||||||
minAPIVersion string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVersionMiddleware creates a VersionMiddleware with the given versions.
|
// NewVersionMiddleware creates a new VersionMiddleware
|
||||||
func NewVersionMiddleware(serverVersion, defaultAPIVersion, minAPIVersion string) (*VersionMiddleware, error) {
|
// with the default versions.
|
||||||
if versions.LessThan(defaultAPIVersion, api.MinSupportedAPIVersion) || versions.GreaterThan(defaultAPIVersion, api.DefaultVersion) {
|
func NewVersionMiddleware(s, d, m string) VersionMiddleware {
|
||||||
return nil, fmt.Errorf("invalid default API version (%s): must be between %s and %s", defaultAPIVersion, api.MinSupportedAPIVersion, api.DefaultVersion)
|
return VersionMiddleware{
|
||||||
|
serverVersion: s,
|
||||||
|
defaultVersion: d,
|
||||||
|
minVersion: m,
|
||||||
}
|
}
|
||||||
if versions.LessThan(minAPIVersion, api.MinSupportedAPIVersion) || versions.GreaterThan(minAPIVersion, api.DefaultVersion) {
|
|
||||||
return nil, fmt.Errorf("invalid minimum API version (%s): must be between %s and %s", minAPIVersion, api.MinSupportedAPIVersion, api.DefaultVersion)
|
|
||||||
}
|
}
|
||||||
if versions.GreaterThan(minAPIVersion, defaultAPIVersion) {
|
|
||||||
return nil, fmt.Errorf("invalid API version: the minimum API version (%s) is higher than the default version (%s)", minAPIVersion, defaultAPIVersion)
|
|
||||||
}
|
|
||||||
return &VersionMiddleware{
|
|
||||||
serverVersion: serverVersion,
|
|
||||||
defaultAPIVersion: defaultAPIVersion,
|
|
||||||
minAPIVersion: minAPIVersion,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type versionUnsupportedError struct {
|
|
||||||
version, minVersion, maxVersion string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e versionUnsupportedError) Error() string {
|
|
||||||
if e.minVersion != "" {
|
|
||||||
return fmt.Sprintf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", e.version, e.minVersion)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("client version %s is too new. Maximum supported API version is %s", e.version, e.maxVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e versionUnsupportedError) InvalidParameter() {}
|
|
||||||
|
|
||||||
// WrapHandler returns a new handler function wrapping the previous one in the request chain.
|
// WrapHandler returns a new handler function wrapping the previous one in the request chain.
|
||||||
func (v VersionMiddleware) WrapHandler(handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error) func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (v VersionMiddleware) WrapHandler(handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error) func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
w.Header().Set("Server", fmt.Sprintf("Docker/%s (%s)", v.serverVersion, runtime.GOOS))
|
|
||||||
w.Header().Set("API-Version", v.defaultAPIVersion)
|
|
||||||
w.Header().Set("OSType", runtime.GOOS)
|
|
||||||
|
|
||||||
apiVersion := vars["version"]
|
apiVersion := vars["version"]
|
||||||
if apiVersion == "" {
|
if apiVersion == "" {
|
||||||
apiVersion = v.defaultAPIVersion
|
apiVersion = v.defaultVersion
|
||||||
}
|
}
|
||||||
if versions.LessThan(apiVersion, v.minAPIVersion) {
|
|
||||||
return versionUnsupportedError{version: apiVersion, minVersion: v.minAPIVersion}
|
if versions.LessThan(apiVersion, v.minVersion) {
|
||||||
|
return errors.NewBadRequestError(fmt.Errorf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", apiVersion, v.minVersion))
|
||||||
}
|
}
|
||||||
if versions.GreaterThan(apiVersion, v.defaultAPIVersion) {
|
|
||||||
return versionUnsupportedError{version: apiVersion, maxVersion: v.defaultAPIVersion}
|
header := fmt.Sprintf("Docker/%s (%s)", v.serverVersion, runtime.GOOS)
|
||||||
}
|
w.Header().Set("Server", header)
|
||||||
ctx = context.WithValue(ctx, httputils.APIVersionKey{}, apiVersion)
|
w.Header().Set("API-Version", v.defaultVersion)
|
||||||
|
ctx = context.WithValue(ctx, "api-version", apiVersion)
|
||||||
return handler(ctx, w, r, vars)
|
return handler(ctx, w, r, vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,146 +1,57 @@
|
||||||
package middleware // import "github.com/docker/docker/api/server/middleware"
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"runtime"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/api"
|
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"gotest.tools/v3/assert"
|
"golang.org/x/net/context"
|
||||||
is "gotest.tools/v3/assert/cmp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewVersionMiddlewareValidation(t *testing.T) {
|
func TestVersionMiddleware(t *testing.T) {
|
||||||
tests := []struct {
|
|
||||||
doc, defaultVersion, minVersion, expectedErr string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
doc: "defaults",
|
|
||||||
defaultVersion: api.DefaultVersion,
|
|
||||||
minVersion: api.MinSupportedAPIVersion,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "invalid default lower than min",
|
|
||||||
defaultVersion: api.MinSupportedAPIVersion,
|
|
||||||
minVersion: api.DefaultVersion,
|
|
||||||
expectedErr: fmt.Sprintf("invalid API version: the minimum API version (%s) is higher than the default version (%s)", api.DefaultVersion, api.MinSupportedAPIVersion),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "invalid default too low",
|
|
||||||
defaultVersion: "0.1",
|
|
||||||
minVersion: api.MinSupportedAPIVersion,
|
|
||||||
expectedErr: fmt.Sprintf("invalid default API version (0.1): must be between %s and %s", api.MinSupportedAPIVersion, api.DefaultVersion),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "invalid default too high",
|
|
||||||
defaultVersion: "9999.9999",
|
|
||||||
minVersion: api.DefaultVersion,
|
|
||||||
expectedErr: fmt.Sprintf("invalid default API version (9999.9999): must be between %s and %s", api.MinSupportedAPIVersion, api.DefaultVersion),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "invalid minimum too low",
|
|
||||||
defaultVersion: api.MinSupportedAPIVersion,
|
|
||||||
minVersion: "0.1",
|
|
||||||
expectedErr: fmt.Sprintf("invalid minimum API version (0.1): must be between %s and %s", api.MinSupportedAPIVersion, api.DefaultVersion),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "invalid minimum too high",
|
|
||||||
defaultVersion: api.DefaultVersion,
|
|
||||||
minVersion: "9999.9999",
|
|
||||||
expectedErr: fmt.Sprintf("invalid minimum API version (9999.9999): must be between %s and %s", api.MinSupportedAPIVersion, api.DefaultVersion),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
tc := tc
|
|
||||||
t.Run(tc.doc, func(t *testing.T) {
|
|
||||||
_, err := NewVersionMiddleware("1.2.3", tc.defaultVersion, tc.minVersion)
|
|
||||||
if tc.expectedErr == "" {
|
|
||||||
assert.Check(t, err)
|
|
||||||
} else {
|
|
||||||
assert.Check(t, is.Error(err, tc.expectedErr))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVersionMiddlewareVersion(t *testing.T) {
|
|
||||||
expectedVersion := "<not set>"
|
|
||||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
v := httputils.VersionFromContext(ctx)
|
if httputils.VersionFromContext(ctx) == "" {
|
||||||
assert.Check(t, is.Equal(expectedVersion, v))
|
t.Fatalf("Expected version, got empty string")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := NewVersionMiddleware("1.2.3", api.DefaultVersion, api.MinSupportedAPIVersion)
|
defaultVersion := "1.10.0"
|
||||||
assert.NilError(t, err)
|
minVersion := "1.2.0"
|
||||||
|
m := NewVersionMiddleware(defaultVersion, defaultVersion, minVersion)
|
||||||
h := m.WrapHandler(handler)
|
h := m.WrapHandler(handler)
|
||||||
|
|
||||||
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
|
req, _ := http.NewRequest("GET", "/containers/json", nil)
|
||||||
resp := httptest.NewRecorder()
|
resp := httptest.NewRecorder()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
if err := h(ctx, resp, req, map[string]string{}); err != nil {
|
||||||
tests := []struct {
|
t.Fatal(err)
|
||||||
reqVersion string
|
|
||||||
expectedVersion string
|
|
||||||
errString string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
expectedVersion: api.DefaultVersion,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
reqVersion: api.MinSupportedAPIVersion,
|
|
||||||
expectedVersion: api.MinSupportedAPIVersion,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
reqVersion: "0.1",
|
|
||||||
errString: fmt.Sprintf("client version 0.1 is too old. Minimum supported API version is %s, please upgrade your client to a newer version", api.MinSupportedAPIVersion),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
reqVersion: "9999.9999",
|
|
||||||
errString: fmt.Sprintf("client version 9999.9999 is too new. Maximum supported API version is %s", api.DefaultVersion),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
expectedVersion = test.expectedVersion
|
|
||||||
|
|
||||||
err := h(ctx, resp, req, map[string]string{"version": test.reqVersion})
|
|
||||||
|
|
||||||
if test.errString != "" {
|
|
||||||
assert.Check(t, is.Error(err, test.errString))
|
|
||||||
} else {
|
|
||||||
assert.Check(t, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVersionMiddlewareWithErrorsReturnsHeaders(t *testing.T) {
|
func TestVersionMiddlewareWithErrors(t *testing.T) {
|
||||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
v := httputils.VersionFromContext(ctx)
|
if httputils.VersionFromContext(ctx) == "" {
|
||||||
assert.Check(t, len(v) != 0)
|
t.Fatalf("Expected version, got empty string")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := NewVersionMiddleware("1.2.3", api.DefaultVersion, api.MinSupportedAPIVersion)
|
defaultVersion := "1.10.0"
|
||||||
assert.NilError(t, err)
|
minVersion := "1.2.0"
|
||||||
|
m := NewVersionMiddleware(defaultVersion, defaultVersion, minVersion)
|
||||||
h := m.WrapHandler(handler)
|
h := m.WrapHandler(handler)
|
||||||
|
|
||||||
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
|
req, _ := http.NewRequest("GET", "/containers/json", nil)
|
||||||
resp := httptest.NewRecorder()
|
resp := httptest.NewRecorder()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
vars := map[string]string{"version": "0.1"}
|
vars := map[string]string{"version": "0.1"}
|
||||||
err = h(ctx, resp, req, vars)
|
err := h(ctx, resp, req, vars)
|
||||||
assert.Check(t, is.ErrorContains(err, ""))
|
|
||||||
|
|
||||||
hdr := resp.Result().Header
|
if !strings.Contains(err.Error(), "client version 0.1 is too old. Minimum supported API version is 1.2.0") {
|
||||||
assert.Check(t, is.Contains(hdr.Get("Server"), "Docker/1.2.3"))
|
t.Fatalf("Expected too old client error, got %v", err)
|
||||||
assert.Check(t, is.Contains(hdr.Get("Server"), runtime.GOOS))
|
}
|
||||||
assert.Check(t, is.Equal(hdr.Get("API-Version"), api.DefaultVersion))
|
|
||||||
assert.Check(t, is.Equal(hdr.Get("OSType"), runtime.GOOS))
|
|
||||||
}
|
}
|
||||||
|
|
41
api/server/profiler.go
Normal file
41
api/server/profiler.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"expvar"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/pprof"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
const debugPathPrefix = "/debug/"
|
||||||
|
|
||||||
|
func profilerSetup(mainRouter *mux.Router) {
|
||||||
|
var r = mainRouter.PathPrefix(debugPathPrefix).Subrouter()
|
||||||
|
r.HandleFunc("/vars", expVars)
|
||||||
|
r.HandleFunc("/pprof/", pprof.Index)
|
||||||
|
r.HandleFunc("/pprof/cmdline", pprof.Cmdline)
|
||||||
|
r.HandleFunc("/pprof/profile", pprof.Profile)
|
||||||
|
r.HandleFunc("/pprof/symbol", pprof.Symbol)
|
||||||
|
r.HandleFunc("/pprof/trace", pprof.Trace)
|
||||||
|
r.HandleFunc("/pprof/block", pprof.Handler("block").ServeHTTP)
|
||||||
|
r.HandleFunc("/pprof/heap", pprof.Handler("heap").ServeHTTP)
|
||||||
|
r.HandleFunc("/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
|
||||||
|
r.HandleFunc("/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replicated from expvar.go as not public.
|
||||||
|
func expVars(w http.ResponseWriter, r *http.Request) {
|
||||||
|
first := true
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
fmt.Fprintf(w, "{\n")
|
||||||
|
expvar.Do(func(kv expvar.KeyValue) {
|
||||||
|
if !first {
|
||||||
|
fmt.Fprintf(w, ",\n")
|
||||||
|
}
|
||||||
|
first = false
|
||||||
|
fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
|
||||||
|
})
|
||||||
|
fmt.Fprintf(w, "\n}\n")
|
||||||
|
}
|
|
@ -1,23 +1,20 @@
|
||||||
package build // import "github.com/docker/docker/api/server/router/build"
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"io"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Backend abstracts an image builder whose only purpose is to build an image referenced by an imageID.
|
// Backend abstracts an image builder whose only purpose is to build an image referenced by an imageID.
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
// Build a Docker image returning the id of the image
|
// Build builds a Docker image referenced by an imageID string.
|
||||||
|
//
|
||||||
|
// Note: Tagging an image should not be done by a Builder, it should instead be done
|
||||||
|
// by the caller.
|
||||||
|
//
|
||||||
// TODO: make this return a reference instead of string
|
// TODO: make this return a reference instead of string
|
||||||
Build(context.Context, backend.BuildConfig) (string, error)
|
BuildFromContext(ctx context.Context, src io.ReadCloser, remote string, buildOptions *types.ImageBuildOptions, pg backend.ProgressWriter) (string, error)
|
||||||
|
|
||||||
// Prune build cache
|
|
||||||
PruneCache(context.Context, types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error)
|
|
||||||
Cancel(context.Context, string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type experimentalProvider interface {
|
|
||||||
HasExperimental() bool
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,17 @@
|
||||||
package build // import "github.com/docker/docker/api/server/router/build"
|
package build
|
||||||
|
|
||||||
import (
|
import "github.com/docker/docker/api/server/router"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/server/router"
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// buildRouter is a router to talk with the build controller
|
// buildRouter is a router to talk with the build controller
|
||||||
type buildRouter struct {
|
type buildRouter struct {
|
||||||
backend Backend
|
backend Backend
|
||||||
daemon experimentalProvider
|
|
||||||
routes []router.Route
|
routes []router.Route
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRouter initializes a new build router
|
// NewRouter initializes a new build router
|
||||||
func NewRouter(b Backend, d experimentalProvider) router.Router {
|
func NewRouter(b Backend) router.Router {
|
||||||
r := &buildRouter{
|
r := &buildRouter{
|
||||||
backend: b,
|
backend: b,
|
||||||
daemon: d,
|
|
||||||
}
|
}
|
||||||
r.initRoutes()
|
r.initRoutes()
|
||||||
return r
|
return r
|
||||||
|
@ -31,30 +24,6 @@ func (r *buildRouter) Routes() []router.Route {
|
||||||
|
|
||||||
func (r *buildRouter) initRoutes() {
|
func (r *buildRouter) initRoutes() {
|
||||||
r.routes = []router.Route{
|
r.routes = []router.Route{
|
||||||
router.NewPostRoute("/build", r.postBuild),
|
router.Cancellable(router.NewPostRoute("/build", r.postBuild)),
|
||||||
router.NewPostRoute("/build/prune", r.postPrune),
|
|
||||||
router.NewPostRoute("/build/cancel", r.postCancel),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuilderVersion derives the default docker builder version from the config.
|
|
||||||
//
|
|
||||||
// The default on Linux is version "2" (BuildKit), but the daemon can be
|
|
||||||
// configured to recommend version "1" (classic Builder). Windows does not
|
|
||||||
// yet support BuildKit for native Windows images, and uses "1" (classic builder)
|
|
||||||
// as a default.
|
|
||||||
//
|
|
||||||
// This value is only a recommendation as advertised by the daemon, and it is
|
|
||||||
// up to the client to choose which builder to use.
|
|
||||||
func BuilderVersion(features map[string]bool) types.BuilderVersion {
|
|
||||||
// TODO(thaJeztah) move the default to daemon/config
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
return types.BuilderV1
|
|
||||||
}
|
|
||||||
|
|
||||||
bv := types.BuilderBuildKit
|
|
||||||
if v, ok := features["buildkit"]; ok && !v {
|
|
||||||
bv = types.BuilderV1
|
|
||||||
}
|
|
||||||
return bv
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package build // import "github.com/docker/docker/api/server/router/build"
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -14,104 +12,81 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/containerd/log"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/filters"
|
|
||||||
"github.com/docker/docker/api/types/registry"
|
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/docker/pkg/progress"
|
"github.com/docker/docker/pkg/progress"
|
||||||
"github.com/docker/docker/pkg/streamformatter"
|
"github.com/docker/docker/pkg/streamformatter"
|
||||||
units "github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/pkg/errors"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
type invalidParam struct {
|
|
||||||
error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e invalidParam) InvalidParameter() {}
|
|
||||||
|
|
||||||
func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) {
|
func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) {
|
||||||
options := &types.ImageBuildOptions{
|
version := httputils.VersionFromContext(ctx)
|
||||||
Version: types.BuilderV1, // Builder V1 is the default, but can be overridden
|
options := &types.ImageBuildOptions{}
|
||||||
Dockerfile: r.FormValue("dockerfile"),
|
if httputils.BoolValue(r, "forcerm") && versions.GreaterThanOrEqualTo(version, "1.12") {
|
||||||
SuppressOutput: httputils.BoolValue(r, "q"),
|
|
||||||
NoCache: httputils.BoolValue(r, "nocache"),
|
|
||||||
ForceRemove: httputils.BoolValue(r, "forcerm"),
|
|
||||||
PullParent: httputils.BoolValue(r, "pull"),
|
|
||||||
MemorySwap: httputils.Int64ValueOrZero(r, "memswap"),
|
|
||||||
Memory: httputils.Int64ValueOrZero(r, "memory"),
|
|
||||||
CPUShares: httputils.Int64ValueOrZero(r, "cpushares"),
|
|
||||||
CPUPeriod: httputils.Int64ValueOrZero(r, "cpuperiod"),
|
|
||||||
CPUQuota: httputils.Int64ValueOrZero(r, "cpuquota"),
|
|
||||||
CPUSetCPUs: r.FormValue("cpusetcpus"),
|
|
||||||
CPUSetMems: r.FormValue("cpusetmems"),
|
|
||||||
CgroupParent: r.FormValue("cgroupparent"),
|
|
||||||
NetworkMode: r.FormValue("networkmode"),
|
|
||||||
Tags: r.Form["t"],
|
|
||||||
ExtraHosts: r.Form["extrahosts"],
|
|
||||||
SecurityOpt: r.Form["securityopt"],
|
|
||||||
Squash: httputils.BoolValue(r, "squash"),
|
|
||||||
Target: r.FormValue("target"),
|
|
||||||
RemoteContext: r.FormValue("remote"),
|
|
||||||
SessionID: r.FormValue("session"),
|
|
||||||
BuildID: r.FormValue("buildid"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if runtime.GOOS != "windows" && options.SecurityOpt != nil {
|
|
||||||
// SecurityOpt only supports "credentials-spec" on Windows, and not used on other platforms.
|
|
||||||
return nil, invalidParam{errors.New("security options are not supported on " + runtime.GOOS)}
|
|
||||||
}
|
|
||||||
|
|
||||||
if httputils.BoolValue(r, "forcerm") {
|
|
||||||
options.Remove = true
|
options.Remove = true
|
||||||
} else if r.FormValue("rm") == "" {
|
} else if r.FormValue("rm") == "" && versions.GreaterThanOrEqualTo(version, "1.12") {
|
||||||
options.Remove = true
|
options.Remove = true
|
||||||
} else {
|
} else {
|
||||||
options.Remove = httputils.BoolValue(r, "rm")
|
options.Remove = httputils.BoolValue(r, "rm")
|
||||||
}
|
}
|
||||||
version := httputils.VersionFromContext(ctx)
|
if httputils.BoolValue(r, "pull") && versions.GreaterThanOrEqualTo(version, "1.16") {
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
options.PullParent = true
|
||||||
options.Platform = r.FormValue("platform")
|
|
||||||
}
|
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.40") {
|
|
||||||
outputsJSON := r.FormValue("outputs")
|
|
||||||
if outputsJSON != "" {
|
|
||||||
var outputs []types.ImageBuildOutput
|
|
||||||
if err := json.Unmarshal([]byte(outputsJSON), &outputs); err != nil {
|
|
||||||
return nil, invalidParam{errors.Wrap(err, "invalid outputs specified")}
|
|
||||||
}
|
|
||||||
options.Outputs = outputs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s := r.Form.Get("shmsize"); s != "" {
|
options.Dockerfile = r.FormValue("dockerfile")
|
||||||
shmSize, err := strconv.ParseInt(s, 10, 64)
|
options.SuppressOutput = httputils.BoolValue(r, "q")
|
||||||
|
options.NoCache = httputils.BoolValue(r, "nocache")
|
||||||
|
options.ForceRemove = httputils.BoolValue(r, "forcerm")
|
||||||
|
options.MemorySwap = httputils.Int64ValueOrZero(r, "memswap")
|
||||||
|
options.Memory = httputils.Int64ValueOrZero(r, "memory")
|
||||||
|
options.CPUShares = httputils.Int64ValueOrZero(r, "cpushares")
|
||||||
|
options.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod")
|
||||||
|
options.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota")
|
||||||
|
options.CPUSetCPUs = r.FormValue("cpusetcpus")
|
||||||
|
options.CPUSetMems = r.FormValue("cpusetmems")
|
||||||
|
options.CgroupParent = r.FormValue("cgroupparent")
|
||||||
|
options.NetworkMode = r.FormValue("networkmode")
|
||||||
|
options.Tags = r.Form["t"]
|
||||||
|
options.SecurityOpt = r.Form["securityopt"]
|
||||||
|
options.Squash = httputils.BoolValue(r, "squash")
|
||||||
|
|
||||||
|
if r.Form.Get("shmsize") != "" {
|
||||||
|
shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
options.ShmSize = shmSize
|
options.ShmSize = shmSize
|
||||||
}
|
}
|
||||||
|
|
||||||
if i := r.FormValue("isolation"); i != "" {
|
if i := container.Isolation(r.FormValue("isolation")); i != "" {
|
||||||
options.Isolation = container.Isolation(i)
|
if !container.Isolation.IsValid(i) {
|
||||||
if !options.Isolation.IsValid() {
|
return nil, fmt.Errorf("Unsupported isolation: %q", i)
|
||||||
return nil, invalidParam{errors.Errorf("unsupported isolation: %q", i)}
|
|
||||||
}
|
}
|
||||||
|
options.Isolation = i
|
||||||
}
|
}
|
||||||
|
|
||||||
if ulimitsJSON := r.FormValue("ulimits"); ulimitsJSON != "" {
|
if runtime.GOOS != "windows" && options.SecurityOpt != nil {
|
||||||
buildUlimits := []*units.Ulimit{}
|
return nil, fmt.Errorf("the daemon on this platform does not support --security-opt to build")
|
||||||
|
}
|
||||||
|
|
||||||
|
var buildUlimits = []*units.Ulimit{}
|
||||||
|
ulimitsJSON := r.FormValue("ulimits")
|
||||||
|
if ulimitsJSON != "" {
|
||||||
if err := json.Unmarshal([]byte(ulimitsJSON), &buildUlimits); err != nil {
|
if err := json.Unmarshal([]byte(ulimitsJSON), &buildUlimits); err != nil {
|
||||||
return nil, invalidParam{errors.Wrap(err, "error reading ulimit settings")}
|
return nil, err
|
||||||
}
|
}
|
||||||
options.Ulimits = buildUlimits
|
options.Ulimits = buildUlimits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var buildArgs = map[string]*string{}
|
||||||
|
buildArgsJSON := r.FormValue("buildargs")
|
||||||
|
|
||||||
// Note that there are two ways a --build-arg might appear in the
|
// Note that there are two ways a --build-arg might appear in the
|
||||||
// json of the query param:
|
// json of the query param:
|
||||||
// "foo":"bar"
|
// "foo":"bar"
|
||||||
|
@ -124,189 +99,34 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
||||||
// the fact they mentioned it, we need to pass that along to the builder
|
// the fact they mentioned it, we need to pass that along to the builder
|
||||||
// so that it can print a warning about "foo" being unused if there is
|
// so that it can print a warning about "foo" being unused if there is
|
||||||
// no "ARG foo" in the Dockerfile.
|
// no "ARG foo" in the Dockerfile.
|
||||||
if buildArgsJSON := r.FormValue("buildargs"); buildArgsJSON != "" {
|
if buildArgsJSON != "" {
|
||||||
buildArgs := map[string]*string{}
|
|
||||||
if err := json.Unmarshal([]byte(buildArgsJSON), &buildArgs); err != nil {
|
if err := json.Unmarshal([]byte(buildArgsJSON), &buildArgs); err != nil {
|
||||||
return nil, invalidParam{errors.Wrap(err, "error reading build args")}
|
return nil, err
|
||||||
}
|
}
|
||||||
options.BuildArgs = buildArgs
|
options.BuildArgs = buildArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
if labelsJSON := r.FormValue("labels"); labelsJSON != "" {
|
var labels = map[string]string{}
|
||||||
labels := map[string]string{}
|
labelsJSON := r.FormValue("labels")
|
||||||
|
if labelsJSON != "" {
|
||||||
if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil {
|
if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil {
|
||||||
return nil, invalidParam{errors.Wrap(err, "error reading labels")}
|
return nil, err
|
||||||
}
|
}
|
||||||
options.Labels = labels
|
options.Labels = labels
|
||||||
}
|
}
|
||||||
|
|
||||||
if cacheFromJSON := r.FormValue("cachefrom"); cacheFromJSON != "" {
|
var cacheFrom = []string{}
|
||||||
cacheFrom := []string{}
|
cacheFromJSON := r.FormValue("cachefrom")
|
||||||
|
if cacheFromJSON != "" {
|
||||||
if err := json.Unmarshal([]byte(cacheFromJSON), &cacheFrom); err != nil {
|
if err := json.Unmarshal([]byte(cacheFromJSON), &cacheFrom); err != nil {
|
||||||
return nil, invalidParam{errors.Wrap(err, "error reading cache-from")}
|
return nil, err
|
||||||
}
|
}
|
||||||
options.CacheFrom = cacheFrom
|
options.CacheFrom = cacheFrom
|
||||||
}
|
}
|
||||||
|
|
||||||
if bv := r.FormValue("version"); bv != "" {
|
|
||||||
v, err := parseVersion(bv)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
options.Version = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return options, nil
|
return options, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseVersion(s string) (types.BuilderVersion, error) {
|
|
||||||
switch types.BuilderVersion(s) {
|
|
||||||
case types.BuilderV1:
|
|
||||||
return types.BuilderV1, nil
|
|
||||||
case types.BuilderBuildKit:
|
|
||||||
return types.BuilderBuildKit, nil
|
|
||||||
default:
|
|
||||||
return "", invalidParam{errors.Errorf("invalid version %q", s)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (br *buildRouter) postPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fltrs, err := filters.FromJSON(r.Form.Get("filters"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ksfv := r.FormValue("keep-storage")
|
|
||||||
if ksfv == "" {
|
|
||||||
ksfv = "0"
|
|
||||||
}
|
|
||||||
ks, err := strconv.Atoi(ksfv)
|
|
||||||
if err != nil {
|
|
||||||
return invalidParam{errors.Wrapf(err, "keep-storage is in bytes and expects an integer, got %v", ksfv)}
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := types.BuildCachePruneOptions{
|
|
||||||
All: httputils.BoolValue(r, "all"),
|
|
||||||
Filters: fltrs,
|
|
||||||
KeepStorage: int64(ks),
|
|
||||||
}
|
|
||||||
|
|
||||||
report, err := br.backend.PruneCache(ctx, opts)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return httputils.WriteJSON(w, http.StatusOK, report)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (br *buildRouter) postCancel(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
id := r.FormValue("id")
|
|
||||||
if id == "" {
|
|
||||||
return invalidParam{errors.New("build ID not provided")}
|
|
||||||
}
|
|
||||||
|
|
||||||
return br.backend.Cancel(ctx, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
var (
|
|
||||||
notVerboseBuffer = bytes.NewBuffer(nil)
|
|
||||||
version = httputils.VersionFromContext(ctx)
|
|
||||||
)
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
body := r.Body
|
|
||||||
var ww io.Writer = w
|
|
||||||
if body != nil {
|
|
||||||
// there is a possibility that output is written before request body
|
|
||||||
// has been fully read so we need to protect against it.
|
|
||||||
// this can be removed when
|
|
||||||
// https://github.com/golang/go/issues/15527
|
|
||||||
// https://github.com/golang/go/issues/22209
|
|
||||||
// has been fixed
|
|
||||||
body, ww = wrapOutputBufferedUntilRequestRead(body, ww)
|
|
||||||
}
|
|
||||||
|
|
||||||
output := ioutils.NewWriteFlusher(ww)
|
|
||||||
defer func() { _ = output.Close() }()
|
|
||||||
|
|
||||||
errf := func(err error) error {
|
|
||||||
if httputils.BoolValue(r, "q") && notVerboseBuffer.Len() > 0 {
|
|
||||||
_, _ = output.Write(notVerboseBuffer.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not write the error in the http output if it's still empty.
|
|
||||||
// This prevents from writing a 200(OK) when there is an internal error.
|
|
||||||
if !output.Flushed() {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = output.Write(streamformatter.FormatError(err))
|
|
||||||
if err != nil {
|
|
||||||
log.G(ctx).Warnf("could not write error response: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buildOptions, err := newImageBuildOptions(ctx, r)
|
|
||||||
if err != nil {
|
|
||||||
return errf(err)
|
|
||||||
}
|
|
||||||
buildOptions.AuthConfigs = getAuthConfigs(r.Header)
|
|
||||||
|
|
||||||
if buildOptions.Squash && !br.daemon.HasExperimental() {
|
|
||||||
return invalidParam{errors.New("squash is only supported with experimental mode")}
|
|
||||||
}
|
|
||||||
|
|
||||||
out := io.Writer(output)
|
|
||||||
if buildOptions.SuppressOutput {
|
|
||||||
out = notVerboseBuffer
|
|
||||||
}
|
|
||||||
|
|
||||||
// Currently, only used if context is from a remote url.
|
|
||||||
// Look at code in DetectContextFromRemoteURL for more information.
|
|
||||||
createProgressReader := func(in io.ReadCloser) io.ReadCloser {
|
|
||||||
progressOutput := streamformatter.NewJSONProgressOutput(out, true)
|
|
||||||
return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", buildOptions.RemoteContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
wantAux := versions.GreaterThanOrEqualTo(version, "1.30")
|
|
||||||
|
|
||||||
imgID, err := br.backend.Build(ctx, backend.BuildConfig{
|
|
||||||
Source: body,
|
|
||||||
Options: buildOptions,
|
|
||||||
ProgressWriter: buildProgressWriter(out, wantAux, createProgressReader),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return errf(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything worked so if -q was provided the output from the daemon
|
|
||||||
// should be just the image ID and we'll print that to stdout.
|
|
||||||
if buildOptions.SuppressOutput {
|
|
||||||
_, _ = fmt.Fprintln(streamformatter.NewStdoutWriter(output), imgID)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAuthConfigs(header http.Header) map[string]registry.AuthConfig {
|
|
||||||
authConfigs := map[string]registry.AuthConfig{}
|
|
||||||
authConfigsEncoded := header.Get("X-Registry-Config")
|
|
||||||
|
|
||||||
if authConfigsEncoded == "" {
|
|
||||||
return authConfigs
|
|
||||||
}
|
|
||||||
|
|
||||||
authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
|
|
||||||
// Pulling an image does not error when no auth is provided so to remain
|
|
||||||
// consistent with the existing api decode errors are ignored
|
|
||||||
_ = json.NewDecoder(authConfigsJSON).Decode(&authConfigs)
|
|
||||||
return authConfigs
|
|
||||||
}
|
|
||||||
|
|
||||||
type syncWriter struct {
|
type syncWriter struct {
|
||||||
w io.Writer
|
w io.Writer
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
@ -319,118 +139,87 @@ func (s *syncWriter) Write(b []byte) (count int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildProgressWriter(out io.Writer, wantAux bool, createProgressReader func(io.ReadCloser) io.ReadCloser) backend.ProgressWriter {
|
func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
out = &syncWriter{w: out}
|
var (
|
||||||
|
authConfigs = map[string]types.AuthConfig{}
|
||||||
|
authConfigsEncoded = r.Header.Get("X-Registry-Config")
|
||||||
|
notVerboseBuffer = bytes.NewBuffer(nil)
|
||||||
|
)
|
||||||
|
|
||||||
var aux *streamformatter.AuxFormatter
|
if authConfigsEncoded != "" {
|
||||||
if wantAux {
|
authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
|
||||||
aux = &streamformatter.AuxFormatter{Writer: out}
|
if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); 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.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return backend.ProgressWriter{
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
output := ioutils.NewWriteFlusher(w)
|
||||||
|
defer output.Close()
|
||||||
|
sf := streamformatter.NewJSONStreamFormatter()
|
||||||
|
errf := func(err error) error {
|
||||||
|
if httputils.BoolValue(r, "q") && notVerboseBuffer.Len() > 0 {
|
||||||
|
output.Write(notVerboseBuffer.Bytes())
|
||||||
|
}
|
||||||
|
// Do not write the error in the http output if it's still empty.
|
||||||
|
// This prevents from writing a 200(OK) when there is an internal error.
|
||||||
|
if !output.Flushed() {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = w.Write(sf.FormatError(err))
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warnf("could not write error response: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buildOptions, err := newImageBuildOptions(ctx, r)
|
||||||
|
if err != nil {
|
||||||
|
return errf(err)
|
||||||
|
}
|
||||||
|
buildOptions.AuthConfigs = authConfigs
|
||||||
|
|
||||||
|
remoteURL := r.FormValue("remote")
|
||||||
|
|
||||||
|
// Currently, only used if context is from a remote url.
|
||||||
|
// Look at code in DetectContextFromRemoteURL for more information.
|
||||||
|
createProgressReader := func(in io.ReadCloser) io.ReadCloser {
|
||||||
|
progressOutput := sf.NewProgressOutput(output, true)
|
||||||
|
if buildOptions.SuppressOutput {
|
||||||
|
progressOutput = sf.NewProgressOutput(notVerboseBuffer, true)
|
||||||
|
}
|
||||||
|
return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", remoteURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := io.Writer(output)
|
||||||
|
if buildOptions.SuppressOutput {
|
||||||
|
out = notVerboseBuffer
|
||||||
|
}
|
||||||
|
out = &syncWriter{w: out}
|
||||||
|
stdout := &streamformatter.StdoutFormatter{Writer: out, StreamFormatter: sf}
|
||||||
|
stderr := &streamformatter.StderrFormatter{Writer: out, StreamFormatter: sf}
|
||||||
|
|
||||||
|
pg := backend.ProgressWriter{
|
||||||
Output: out,
|
Output: out,
|
||||||
StdoutFormatter: streamformatter.NewStdoutWriter(out),
|
StdoutFormatter: stdout,
|
||||||
StderrFormatter: streamformatter.NewStderrWriter(out),
|
StderrFormatter: stderr,
|
||||||
AuxFormatter: aux,
|
|
||||||
ProgressReaderFunc: createProgressReader,
|
ProgressReaderFunc: createProgressReader,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
type flusher interface {
|
imgID, err := br.backend.BuildFromContext(ctx, r.Body, remoteURL, buildOptions, pg)
|
||||||
Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrapOutputBufferedUntilRequestRead(rc io.ReadCloser, out io.Writer) (io.ReadCloser, io.Writer) {
|
|
||||||
var fl flusher = &ioutils.NopFlusher{}
|
|
||||||
if f, ok := out.(flusher); ok {
|
|
||||||
fl = f
|
|
||||||
}
|
|
||||||
|
|
||||||
w := &wcf{
|
|
||||||
buf: bytes.NewBuffer(nil),
|
|
||||||
Writer: out,
|
|
||||||
flusher: fl,
|
|
||||||
}
|
|
||||||
r := bufio.NewReader(rc)
|
|
||||||
_, err := r.Peek(1)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rc, out
|
return errf(err)
|
||||||
}
|
|
||||||
rc = &rcNotifier{
|
|
||||||
Reader: r,
|
|
||||||
Closer: rc,
|
|
||||||
notify: w.notify,
|
|
||||||
}
|
|
||||||
return rc, w
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type rcNotifier struct {
|
// Everything worked so if -q was provided the output from the daemon
|
||||||
io.Reader
|
// should be just the image ID and we'll print that to stdout.
|
||||||
io.Closer
|
if buildOptions.SuppressOutput {
|
||||||
notify func()
|
stdout := &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf}
|
||||||
|
fmt.Fprintf(stdout, "%s\n", string(imgID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rcNotifier) Read(b []byte) (int, error) {
|
return nil
|
||||||
n, err := r.Reader.Read(b)
|
|
||||||
if err != nil {
|
|
||||||
r.notify()
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rcNotifier) Close() error {
|
|
||||||
r.notify()
|
|
||||||
return r.Closer.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
type wcf struct {
|
|
||||||
io.Writer
|
|
||||||
flusher
|
|
||||||
mu sync.Mutex
|
|
||||||
ready bool
|
|
||||||
buf *bytes.Buffer
|
|
||||||
flushed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *wcf) Flush() {
|
|
||||||
w.mu.Lock()
|
|
||||||
w.flushed = true
|
|
||||||
if !w.ready {
|
|
||||||
w.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.mu.Unlock()
|
|
||||||
w.flusher.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *wcf) Flushed() bool {
|
|
||||||
w.mu.Lock()
|
|
||||||
b := w.flushed
|
|
||||||
w.mu.Unlock()
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *wcf) Write(b []byte) (int, error) {
|
|
||||||
w.mu.Lock()
|
|
||||||
if !w.ready {
|
|
||||||
n, err := w.buf.Write(b)
|
|
||||||
w.mu.Unlock()
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
w.mu.Unlock()
|
|
||||||
return w.Writer.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *wcf) notify() {
|
|
||||||
w.mu.Lock()
|
|
||||||
if !w.ready {
|
|
||||||
if w.buf.Len() > 0 {
|
|
||||||
_, _ = io.Copy(w.Writer, w.buf)
|
|
||||||
}
|
|
||||||
if w.flushed {
|
|
||||||
w.flusher.Flush()
|
|
||||||
}
|
|
||||||
w.ready = true
|
|
||||||
}
|
|
||||||
w.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package checkpoint // import "github.com/docker/docker/api/server/router/checkpoint"
|
package checkpoint
|
||||||
|
|
||||||
import "github.com/docker/docker/api/types/checkpoint"
|
import "github.com/docker/docker/api/types"
|
||||||
|
|
||||||
// Backend for Checkpoint
|
// Backend for Checkpoint
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
CheckpointCreate(container string, config checkpoint.CreateOptions) error
|
CheckpointCreate(container string, config types.CheckpointCreateOptions) error
|
||||||
CheckpointDelete(container string, config checkpoint.DeleteOptions) error
|
CheckpointDelete(container string, config types.CheckpointDeleteOptions) error
|
||||||
CheckpointList(container string, config checkpoint.ListOptions) ([]checkpoint.Summary, error)
|
CheckpointList(container string, config types.CheckpointListOptions) ([]types.Checkpoint, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package checkpoint // import "github.com/docker/docker/api/server/router/checkpoint"
|
package checkpoint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
|
@ -29,8 +29,8 @@ func (r *checkpointRouter) Routes() []router.Route {
|
||||||
|
|
||||||
func (r *checkpointRouter) initRoutes() {
|
func (r *checkpointRouter) initRoutes() {
|
||||||
r.routes = []router.Route{
|
r.routes = []router.Route{
|
||||||
router.NewGetRoute("/containers/{name:.*}/checkpoints", r.getContainerCheckpoints, router.Experimental),
|
router.Experimental(router.NewGetRoute("/containers/{name:.*}/checkpoints", r.getContainerCheckpoints)),
|
||||||
router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint, router.Experimental),
|
router.Experimental(router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint)),
|
||||||
router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint, router.Experimental),
|
router.Experimental(router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package checkpoint // import "github.com/docker/docker/api/server/router/checkpoint"
|
package checkpoint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/api/types/checkpoint"
|
"github.com/docker/docker/api/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *checkpointRouter) postContainerCheckpoint(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *checkpointRouter) postContainerCheckpoint(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
@ -13,8 +14,10 @@ func (s *checkpointRouter) postContainerCheckpoint(ctx context.Context, w http.R
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var options checkpoint.CreateOptions
|
var options types.CheckpointCreateOptions
|
||||||
if err := httputils.ReadJSON(r, &options); err != nil {
|
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
if err := decoder.Decode(&options); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,9 +35,10 @@ func (s *checkpointRouter) getContainerCheckpoints(ctx context.Context, w http.R
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
checkpoints, err := s.backend.CheckpointList(vars["name"], checkpoint.ListOptions{
|
checkpoints, err := s.backend.CheckpointList(vars["name"], types.CheckpointListOptions{
|
||||||
CheckpointDir: r.Form.Get("dir"),
|
CheckpointDir: r.Form.Get("dir"),
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -47,10 +51,11 @@ func (s *checkpointRouter) deleteContainerCheckpoint(ctx context.Context, w http
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.backend.CheckpointDelete(vars["name"], checkpoint.DeleteOptions{
|
err := s.backend.CheckpointDelete(vars["name"], types.CheckpointDeleteOptions{
|
||||||
CheckpointDir: r.Form.Get("dir"),
|
CheckpointDir: r.Form.Get("dir"),
|
||||||
CheckpointID: vars["checkpoint"],
|
CheckpointID: vars["checkpoint"],
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
package container // import "github.com/docker/docker/api/server/router/container"
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
containerpkg "github.com/docker/docker/container"
|
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,42 +18,44 @@ type execBackend interface {
|
||||||
ContainerExecCreate(name string, config *types.ExecConfig) (string, error)
|
ContainerExecCreate(name string, config *types.ExecConfig) (string, error)
|
||||||
ContainerExecInspect(id string) (*backend.ExecInspect, error)
|
ContainerExecInspect(id string) (*backend.ExecInspect, error)
|
||||||
ContainerExecResize(name string, height, width int) error
|
ContainerExecResize(name string, height, width int) error
|
||||||
ContainerExecStart(ctx context.Context, name string, options container.ExecStartOptions) error
|
ContainerExecStart(ctx context.Context, name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error
|
||||||
ExecExists(name string) (bool, error)
|
ExecExists(name string) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyBackend includes functions to implement to provide container copy functionality.
|
// copyBackend includes functions to implement to provide container copy functionality.
|
||||||
type copyBackend interface {
|
type copyBackend interface {
|
||||||
ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error)
|
ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error)
|
||||||
ContainerExport(ctx context.Context, name string, out io.Writer) error
|
ContainerCopy(name string, res string) (io.ReadCloser, error)
|
||||||
ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error
|
ContainerExport(name string, out io.Writer) error
|
||||||
|
ContainerExtractToDir(name, path string, noOverwriteDirNonDir bool, content io.Reader) error
|
||||||
ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error)
|
ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// stateBackend includes functions to implement to provide container state lifecycle functionality.
|
// stateBackend includes functions to implement to provide container state lifecycle functionality.
|
||||||
type stateBackend interface {
|
type stateBackend interface {
|
||||||
ContainerCreate(ctx context.Context, config backend.ContainerCreateConfig) (container.CreateResponse, error)
|
ContainerCreate(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error)
|
||||||
ContainerKill(name string, signal string) error
|
ContainerKill(name string, sig uint64) error
|
||||||
ContainerPause(name string) error
|
ContainerPause(name string) error
|
||||||
ContainerRename(oldName, newName string) error
|
ContainerRename(oldName, newName string) error
|
||||||
ContainerResize(name string, height, width int) error
|
ContainerResize(name string, height, width int) error
|
||||||
ContainerRestart(ctx context.Context, name string, options container.StopOptions) error
|
ContainerRestart(name string, seconds *int) error
|
||||||
ContainerRm(name string, config *backend.ContainerRmConfig) error
|
ContainerRm(name string, config *types.ContainerRmConfig) error
|
||||||
ContainerStart(ctx context.Context, name string, checkpoint string, checkpointDir string) error
|
ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
|
||||||
ContainerStop(ctx context.Context, name string, options container.StopOptions) error
|
ContainerStop(name string, seconds *int) error
|
||||||
ContainerUnpause(name string) error
|
ContainerUnpause(name string) error
|
||||||
ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error)
|
ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error)
|
||||||
ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
|
ContainerWait(name string, timeout time.Duration) (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// monitorBackend includes functions to implement to provide containers monitoring functionality.
|
// monitorBackend includes functions to implement to provide containers monitoring functionality.
|
||||||
type monitorBackend interface {
|
type monitorBackend interface {
|
||||||
ContainerChanges(ctx context.Context, name string) ([]archive.Change, error)
|
ContainerChanges(name string) ([]archive.Change, error)
|
||||||
ContainerInspect(ctx context.Context, name string, size bool, version string) (interface{}, error)
|
ContainerInspect(name string, size bool, version string) (interface{}, error)
|
||||||
ContainerLogs(ctx context.Context, name string, config *container.LogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error)
|
ContainerLogs(ctx context.Context, name string, config *backend.ContainerLogsConfig, started chan struct{}) error
|
||||||
ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) error
|
ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) error
|
||||||
ContainerTop(name string, psArgs string) (*container.ContainerTopOKBody, error)
|
ContainerTop(name string, psArgs string) (*types.ContainerProcessList, error)
|
||||||
Containers(ctx context.Context, config *container.ListOptions) ([]*types.Container, error)
|
|
||||||
|
Containers(config *types.ContainerListOptions) ([]*types.Container, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// attachBackend includes function to implement to provide container attaching functionality.
|
// attachBackend includes function to implement to provide container attaching functionality.
|
||||||
|
@ -62,16 +65,11 @@ type attachBackend interface {
|
||||||
|
|
||||||
// systemBackend includes functions to implement to provide system wide containers functionality
|
// systemBackend includes functions to implement to provide system wide containers functionality
|
||||||
type systemBackend interface {
|
type systemBackend interface {
|
||||||
ContainersPrune(ctx context.Context, pruneFilters filters.Args) (*types.ContainersPruneReport, error)
|
ContainersPrune(pruneFilters filters.Args) (*types.ContainersPruneReport, error)
|
||||||
}
|
|
||||||
|
|
||||||
type commitBackend interface {
|
|
||||||
CreateImageFromContainer(ctx context.Context, name string, config *backend.CreateImageConfig) (imageID string, err error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backend is all the methods that need to be implemented to provide container specific functionality.
|
// Backend is all the methods that need to be implemented to provide container specific functionality.
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
commitBackend
|
|
||||||
execBackend
|
execBackend
|
||||||
copyBackend
|
copyBackend
|
||||||
stateBackend
|
stateBackend
|
||||||
|
|
|
@ -1,24 +1,30 @@
|
||||||
package container // import "github.com/docker/docker/api/server/router/container"
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/api/server/router"
|
"github.com/docker/docker/api/server/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type validationError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (validationError) IsValidationError() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// containerRouter is a router to talk with the container controller
|
// containerRouter is a router to talk with the container controller
|
||||||
type containerRouter struct {
|
type containerRouter struct {
|
||||||
backend Backend
|
backend Backend
|
||||||
decoder httputils.ContainerDecoder
|
decoder httputils.ContainerDecoder
|
||||||
routes []router.Route
|
routes []router.Route
|
||||||
cgroup2 bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRouter initializes a new container router
|
// NewRouter initializes a new container router
|
||||||
func NewRouter(b Backend, decoder httputils.ContainerDecoder, cgroup2 bool) router.Router {
|
func NewRouter(b Backend, decoder httputils.ContainerDecoder) router.Router {
|
||||||
r := &containerRouter{
|
r := &containerRouter{
|
||||||
backend: b,
|
backend: b,
|
||||||
decoder: decoder,
|
decoder: decoder,
|
||||||
cgroup2: cgroup2,
|
|
||||||
}
|
}
|
||||||
r.initRoutes()
|
r.initRoutes()
|
||||||
return r
|
return r
|
||||||
|
@ -40,8 +46,8 @@ func (r *containerRouter) initRoutes() {
|
||||||
router.NewGetRoute("/containers/{name:.*}/changes", r.getContainersChanges),
|
router.NewGetRoute("/containers/{name:.*}/changes", r.getContainersChanges),
|
||||||
router.NewGetRoute("/containers/{name:.*}/json", r.getContainersByName),
|
router.NewGetRoute("/containers/{name:.*}/json", r.getContainersByName),
|
||||||
router.NewGetRoute("/containers/{name:.*}/top", r.getContainersTop),
|
router.NewGetRoute("/containers/{name:.*}/top", r.getContainersTop),
|
||||||
router.NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs),
|
router.Cancellable(router.NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs)),
|
||||||
router.NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats),
|
router.Cancellable(router.NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats)),
|
||||||
router.NewGetRoute("/containers/{name:.*}/attach/ws", r.wsContainersAttach),
|
router.NewGetRoute("/containers/{name:.*}/attach/ws", r.wsContainersAttach),
|
||||||
router.NewGetRoute("/exec/{id:.*}/json", r.getExecByID),
|
router.NewGetRoute("/exec/{id:.*}/json", r.getExecByID),
|
||||||
router.NewGetRoute("/containers/{name:.*}/archive", r.getContainersArchive),
|
router.NewGetRoute("/containers/{name:.*}/archive", r.getContainersArchive),
|
||||||
|
@ -56,13 +62,13 @@ func (r *containerRouter) initRoutes() {
|
||||||
router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
|
router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
|
||||||
router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
|
router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
|
||||||
router.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
|
router.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
|
||||||
|
router.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy), // Deprecated since 1.8, Errors out since 1.12
|
||||||
router.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
|
router.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
|
||||||
router.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
|
router.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
|
||||||
router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
||||||
router.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
|
router.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
|
||||||
router.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
|
router.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
|
||||||
router.NewPostRoute("/containers/prune", r.postContainersPrune),
|
router.NewPostRoute("/containers/prune", r.postContainersPrune),
|
||||||
router.NewPostRoute("/commit", r.postCommit),
|
|
||||||
// PUT
|
// PUT
|
||||||
router.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
|
router.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
|
||||||
// DELETE
|
// DELETE
|
||||||
|
|
|
@ -1,79 +1,37 @@
|
||||||
package container // import "github.com/docker/docker/api/server/router/container"
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd/platforms"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/containerd/log"
|
|
||||||
"github.com/docker/docker/api/server/httpstatus"
|
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/api/types/mount"
|
|
||||||
"github.com/docker/docker/api/types/network"
|
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
containerpkg "github.com/docker/docker/container"
|
|
||||||
"github.com/docker/docker/errdefs"
|
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/pkg/signal"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
"golang.org/x/net/context"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *containerRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := httputils.CheckForJSON(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
config, _, _, err := s.decoder.DecodeConfig(r.Body)
|
|
||||||
if err != nil && !errors.Is(err, io.EOF) { // Do not fail if body is empty.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ref, err := httputils.RepoTagReference(r.Form.Get("repo"), r.Form.Get("tag"))
|
|
||||||
if err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
imgID, err := s.backend.CreateImageFromContainer(ctx, r.Form.Get("container"), &backend.CreateImageConfig{
|
|
||||||
Pause: httputils.BoolValueOrDefault(r, "pause", true), // TODO(dnephin): remove pause arg, and always pause in backend
|
|
||||||
Tag: ref,
|
|
||||||
Author: r.Form.Get("author"),
|
|
||||||
Comment: r.Form.Get("comment"),
|
|
||||||
Config: config,
|
|
||||||
Changes: r.Form["changes"],
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{ID: imgID})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *containerRouter) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
filter, err := filters.FromJSON(r.Form.Get("filters"))
|
filter, err := filters.FromParam(r.Form.Get("filters"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &container.ListOptions{
|
config := &types.ContainerListOptions{
|
||||||
All: httputils.BoolValue(r, "all"),
|
All: httputils.BoolValue(r, "all"),
|
||||||
Size: httputils.BoolValue(r, "size"),
|
Size: httputils.BoolValue(r, "size"),
|
||||||
Since: r.Form.Get("since"),
|
Since: r.Form.Get("since"),
|
||||||
|
@ -89,7 +47,7 @@ func (s *containerRouter) getContainersJSON(ctx context.Context, w http.Response
|
||||||
config.Limit = limit
|
config.Limit = limit
|
||||||
}
|
}
|
||||||
|
|
||||||
containers, err := s.backend.Containers(ctx, config)
|
containers, err := s.backend.Containers(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -106,16 +64,14 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons
|
||||||
if !stream {
|
if !stream {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
}
|
}
|
||||||
var oneShot bool
|
|
||||||
if versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.41") {
|
config := &backend.ContainerStatsConfig{
|
||||||
oneShot = httputils.BoolValueOrDefault(r, "one-shot", false)
|
Stream: stream,
|
||||||
|
OutStream: w,
|
||||||
|
Version: string(httputils.VersionFromContext(ctx)),
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.backend.ContainerStats(ctx, vars["name"], &backend.ContainerStatsConfig{
|
return s.backend.ContainerStats(ctx, vars["name"], config)
|
||||||
Stream: stream,
|
|
||||||
OneShot: oneShot,
|
|
||||||
OutStream: w,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
@ -130,42 +86,41 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
|
||||||
// with the appropriate status code.
|
// with the appropriate status code.
|
||||||
stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr")
|
stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr")
|
||||||
if !(stdout || stderr) {
|
if !(stdout || stderr) {
|
||||||
return errdefs.InvalidParameter(errors.New("Bad parameters: you must choose at least one stream"))
|
return fmt.Errorf("Bad parameters: you must choose at least one stream")
|
||||||
}
|
}
|
||||||
|
|
||||||
containerName := vars["name"]
|
containerName := vars["name"]
|
||||||
logsConfig := &container.LogsOptions{
|
logsConfig := &backend.ContainerLogsConfig{
|
||||||
|
ContainerLogsOptions: types.ContainerLogsOptions{
|
||||||
Follow: httputils.BoolValue(r, "follow"),
|
Follow: httputils.BoolValue(r, "follow"),
|
||||||
Timestamps: httputils.BoolValue(r, "timestamps"),
|
Timestamps: httputils.BoolValue(r, "timestamps"),
|
||||||
Since: r.Form.Get("since"),
|
Since: r.Form.Get("since"),
|
||||||
Until: r.Form.Get("until"),
|
|
||||||
Tail: r.Form.Get("tail"),
|
Tail: r.Form.Get("tail"),
|
||||||
ShowStdout: stdout,
|
ShowStdout: stdout,
|
||||||
ShowStderr: stderr,
|
ShowStderr: stderr,
|
||||||
Details: httputils.BoolValue(r, "details"),
|
Details: httputils.BoolValue(r, "details"),
|
||||||
|
},
|
||||||
|
OutStream: w,
|
||||||
}
|
}
|
||||||
|
|
||||||
msgs, tty, err := s.backend.ContainerLogs(ctx, containerName, logsConfig)
|
chStarted := make(chan struct{})
|
||||||
if err != nil {
|
if err := s.backend.ContainerLogs(ctx, containerName, logsConfig, chStarted); err != nil {
|
||||||
|
select {
|
||||||
|
case <-chStarted:
|
||||||
|
// The client may be expecting all of the data we're sending to
|
||||||
|
// be multiplexed, so send it through OutStream, which will
|
||||||
|
// have been set up to handle that if needed.
|
||||||
|
fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %v\n", err)
|
||||||
|
default:
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
contentType := types.MediaTypeRawStream
|
|
||||||
if !tty && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
|
|
||||||
contentType = types.MediaTypeMultiplexedStream
|
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", contentType)
|
|
||||||
|
|
||||||
// if has a tty, we're not muxing streams. if it doesn't, we are. simple.
|
|
||||||
// this is the point of no return for writing a response. once we call
|
|
||||||
// WriteLogStream, the response has been started and errors will be
|
|
||||||
// returned in band by WriteLogStream
|
|
||||||
httputils.WriteLogStream(ctx, w, msgs, logsConfig, !tty)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *containerRouter) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
return s.backend.ContainerExport(ctx, vars["name"], w)
|
return s.backend.ContainerExport(vars["name"], w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
@ -175,17 +130,33 @@ func (s *containerRouter) postContainersStart(ctx context.Context, w http.Respon
|
||||||
// net/http otherwise seems to swallow any headers related to chunked encoding
|
// net/http otherwise seems to swallow any headers related to chunked encoding
|
||||||
// including r.TransferEncoding
|
// including r.TransferEncoding
|
||||||
// allow a nil body for backwards compatibility
|
// allow a nil body for backwards compatibility
|
||||||
//
|
|
||||||
|
version := httputils.VersionFromContext(ctx)
|
||||||
|
var hostConfig *container.HostConfig
|
||||||
// A non-nil json object is at least 7 characters.
|
// A non-nil json object is at least 7 characters.
|
||||||
if r.ContentLength > 7 || r.ContentLength == -1 {
|
if r.ContentLength > 7 || r.ContentLength == -1 {
|
||||||
return errdefs.InvalidParameter(errors.New("starting container with non-empty request body was deprecated since API v1.22 and removed in v1.24"))
|
if versions.GreaterThanOrEqualTo(version, "1.24") {
|
||||||
|
return validationError{fmt.Errorf("starting container with non-empty request body was deprecated since v1.10 and removed in v1.12")}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := httputils.CheckForJSON(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := s.decoder.DecodeHostConfig(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hostConfig = c
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.backend.ContainerStart(ctx, vars["name"], r.Form.Get("checkpoint"), r.Form.Get("checkpoint-dir")); err != nil {
|
checkpoint := r.Form.Get("checkpoint")
|
||||||
|
checkpointDir := r.Form.Get("checkpoint-dir")
|
||||||
|
if err := s.backend.ContainerStart(vars["name"], hostConfig, checkpoint, checkpointDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,37 +169,56 @@ func (s *containerRouter) postContainersStop(ctx context.Context, w http.Respons
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var seconds *int
|
||||||
options container.StopOptions
|
|
||||||
version = httputils.VersionFromContext(ctx)
|
|
||||||
)
|
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
|
||||||
options.Signal = r.Form.Get("signal")
|
|
||||||
}
|
|
||||||
if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
|
if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
|
||||||
valSeconds, err := strconv.Atoi(tmpSeconds)
|
valSeconds, err := strconv.Atoi(tmpSeconds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
options.Timeout = &valSeconds
|
seconds = &valSeconds
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.backend.ContainerStop(ctx, vars["name"], options); err != nil {
|
if err := s.backend.ContainerStop(vars["name"], seconds); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *containerRouter) postContainersKill(_ context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
type errContainerIsRunning interface {
|
||||||
|
ContainerIsRunning() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sig syscall.Signal
|
||||||
name := vars["name"]
|
name := vars["name"]
|
||||||
if err := s.backend.ContainerKill(name, r.Form.Get("signal")); err != nil {
|
|
||||||
return errors.Wrapf(err, "cannot kill container: %s", name)
|
// If we have a signal, look at it. Otherwise, do nothing
|
||||||
|
if sigStr := r.Form.Get("signal"); sigStr != "" {
|
||||||
|
var err error
|
||||||
|
if sig, err = signal.ParseSignal(sigStr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
|
||||||
|
var isStopped bool
|
||||||
|
if e, ok := err.(errContainerIsRunning); ok {
|
||||||
|
isStopped = !e.ContainerIsRunning()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return error that's not caused because the container is stopped.
|
||||||
|
// Return error if the container is not running and the api is >= 1.20
|
||||||
|
// to keep backwards compatibility.
|
||||||
|
version := httputils.VersionFromContext(ctx)
|
||||||
|
if versions.GreaterThanOrEqualTo(version, "1.20") || !isStopped {
|
||||||
|
return fmt.Errorf("Cannot kill container %s: %v", name, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
@ -240,26 +230,21 @@ func (s *containerRouter) postContainersRestart(ctx context.Context, w http.Resp
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var seconds *int
|
||||||
options container.StopOptions
|
|
||||||
version = httputils.VersionFromContext(ctx)
|
|
||||||
)
|
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
|
||||||
options.Signal = r.Form.Get("signal")
|
|
||||||
}
|
|
||||||
if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
|
if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
|
||||||
valSeconds, err := strconv.Atoi(tmpSeconds)
|
valSeconds, err := strconv.Atoi(tmpSeconds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
options.Timeout = &valSeconds
|
seconds = &valSeconds
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.backend.ContainerRestart(ctx, vars["name"], options); err != nil {
|
if err := s.backend.ContainerRestart(vars["name"], seconds); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,72 +277,18 @@ func (s *containerRouter) postContainersUnpause(ctx context.Context, w http.Resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *containerRouter) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
// Behavior changed in version 1.30 to handle wait condition and to
|
status, err := s.backend.ContainerWait(vars["name"], -1*time.Second)
|
||||||
// return headers immediately.
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
|
||||||
legacyBehaviorPre130 := versions.LessThan(version, "1.30")
|
|
||||||
legacyRemovalWaitPre134 := false
|
|
||||||
|
|
||||||
// The wait condition defaults to "not-running".
|
|
||||||
waitCondition := containerpkg.WaitConditionNotRunning
|
|
||||||
if !legacyBehaviorPre130 {
|
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if v := r.Form.Get("condition"); v != "" {
|
|
||||||
switch container.WaitCondition(v) {
|
|
||||||
case container.WaitConditionNotRunning:
|
|
||||||
waitCondition = containerpkg.WaitConditionNotRunning
|
|
||||||
case container.WaitConditionNextExit:
|
|
||||||
waitCondition = containerpkg.WaitConditionNextExit
|
|
||||||
case container.WaitConditionRemoved:
|
|
||||||
waitCondition = containerpkg.WaitConditionRemoved
|
|
||||||
legacyRemovalWaitPre134 = versions.LessThan(version, "1.34")
|
|
||||||
default:
|
|
||||||
return errdefs.InvalidParameter(errors.Errorf("invalid condition: %q", v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
waitC, err := s.backend.ContainerWait(ctx, vars["name"], waitCondition)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
return httputils.WriteJSON(w, http.StatusOK, &container.ContainerWaitOKBody{
|
||||||
|
StatusCode: int64(status),
|
||||||
if !legacyBehaviorPre130 {
|
|
||||||
// Write response header immediately.
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
if flusher, ok := w.(http.Flusher); ok {
|
|
||||||
flusher.Flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block on the result of the wait operation.
|
|
||||||
status := <-waitC
|
|
||||||
|
|
||||||
// With API < 1.34, wait on WaitConditionRemoved did not return
|
|
||||||
// in case container removal failed. The only way to report an
|
|
||||||
// error back to the client is to not write anything (i.e. send
|
|
||||||
// an empty response which will be treated as an error).
|
|
||||||
if legacyRemovalWaitPre134 && status.Err() != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var waitError *container.WaitExitError
|
|
||||||
if status.Err() != nil {
|
|
||||||
waitError = &container.WaitExitError{Message: status.Err().Error()}
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.NewEncoder(w).Encode(&container.WaitResponse{
|
|
||||||
StatusCode: int64(status.ExitCode()),
|
|
||||||
Error: waitError,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *containerRouter) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
changes, err := s.backend.ContainerChanges(ctx, vars["name"])
|
changes, err := s.backend.ContainerChanges(vars["name"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -396,26 +327,15 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := httputils.CheckForJSON(r); err != nil {
|
||||||
var updateConfig container.UpdateConfig
|
|
||||||
if err := httputils.ReadJSON(r, &updateConfig); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if versions.LessThan(httputils.VersionFromContext(ctx), "1.40") {
|
|
||||||
updateConfig.PidsLimit = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
|
var updateConfig container.UpdateConfig
|
||||||
// Ignore KernelMemory removed in API 1.42.
|
|
||||||
updateConfig.KernelMemory = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if updateConfig.PidsLimit != nil && *updateConfig.PidsLimit <= 0 {
|
decoder := json.NewDecoder(r.Body)
|
||||||
// Both `0` and `-1` are accepted to set "unlimited" when updating.
|
if err := decoder.Decode(&updateConfig); err != nil {
|
||||||
// Historically, any negative value was accepted, so treat them as
|
return err
|
||||||
// "unlimited" as well.
|
|
||||||
var unlimited int64
|
|
||||||
updateConfig.PidsLimit = &unlimited
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hostConfig := &container.HostConfig{
|
hostConfig := &container.HostConfig{
|
||||||
|
@ -444,290 +364,37 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
|
||||||
|
|
||||||
config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body)
|
config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, io.EOF) {
|
|
||||||
return errdefs.InvalidParameter(errors.New("invalid JSON: got EOF while reading request body"))
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if config == nil {
|
|
||||||
return errdefs.InvalidParameter(runconfig.ErrEmptyConfig)
|
|
||||||
}
|
|
||||||
if hostConfig == nil {
|
|
||||||
hostConfig = &container.HostConfig{}
|
|
||||||
}
|
|
||||||
if networkingConfig == nil {
|
|
||||||
networkingConfig = &network.NetworkingConfig{}
|
|
||||||
}
|
|
||||||
if networkingConfig.EndpointsConfig == nil {
|
|
||||||
networkingConfig.EndpointsConfig = make(map[string]*network.EndpointSettings)
|
|
||||||
}
|
|
||||||
// The NetworkMode "default" is used as a way to express a container should
|
|
||||||
// be attached to the OS-dependant default network, in an OS-independent
|
|
||||||
// way. Doing this conversion as soon as possible ensures we have less
|
|
||||||
// NetworkMode to handle down the path (including in the
|
|
||||||
// backward-compatibility layer we have just below).
|
|
||||||
//
|
|
||||||
// Note that this is not the only place where this conversion has to be
|
|
||||||
// done (as there are various other places where containers get created).
|
|
||||||
if hostConfig.NetworkMode == "" || hostConfig.NetworkMode.IsDefault() {
|
|
||||||
hostConfig.NetworkMode = runconfig.DefaultDaemonNetworkMode()
|
|
||||||
if nw, ok := networkingConfig.EndpointsConfig[network.NetworkDefault]; ok {
|
|
||||||
networkingConfig.EndpointsConfig[hostConfig.NetworkMode.NetworkName()] = nw
|
|
||||||
delete(networkingConfig.EndpointsConfig, network.NetworkDefault)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
version := httputils.VersionFromContext(ctx)
|
||||||
|
adjustCPUShares := versions.LessThan(version, "1.19")
|
||||||
|
|
||||||
// When using API 1.24 and under, the client is responsible for removing the container
|
// When using API 1.24 and under, the client is responsible for removing the container
|
||||||
if versions.LessThan(version, "1.25") {
|
if hostConfig != nil && versions.LessThan(version, "1.25") {
|
||||||
hostConfig.AutoRemove = false
|
hostConfig.AutoRemove = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if versions.LessThan(version, "1.40") {
|
ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
|
||||||
// Ignore BindOptions.NonRecursive because it was added in API 1.40.
|
|
||||||
for _, m := range hostConfig.Mounts {
|
|
||||||
if bo := m.BindOptions; bo != nil {
|
|
||||||
bo.NonRecursive = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore KernelMemoryTCP because it was added in API 1.40.
|
|
||||||
hostConfig.KernelMemoryTCP = 0
|
|
||||||
|
|
||||||
// Older clients (API < 1.40) expects the default to be shareable, make them happy
|
|
||||||
if hostConfig.IpcMode.IsEmpty() {
|
|
||||||
hostConfig.IpcMode = container.IPCModeShareable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if versions.LessThan(version, "1.41") {
|
|
||||||
// Older clients expect the default to be "host" on cgroup v1 hosts
|
|
||||||
if !s.cgroup2 && hostConfig.CgroupnsMode.IsEmpty() {
|
|
||||||
hostConfig.CgroupnsMode = container.CgroupnsModeHost
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var platform *ocispec.Platform
|
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.41") {
|
|
||||||
if v := r.Form.Get("platform"); v != "" {
|
|
||||||
p, err := platforms.Parse(v)
|
|
||||||
if err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
platform = &p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if versions.LessThan(version, "1.42") {
|
|
||||||
for _, m := range hostConfig.Mounts {
|
|
||||||
// Ignore BindOptions.CreateMountpoint because it was added in API 1.42.
|
|
||||||
if bo := m.BindOptions; bo != nil {
|
|
||||||
bo.CreateMountpoint = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// These combinations are invalid, but weren't validated in API < 1.42.
|
|
||||||
// We reset them here, so that validation doesn't produce an error.
|
|
||||||
if o := m.VolumeOptions; o != nil && m.Type != mount.TypeVolume {
|
|
||||||
m.VolumeOptions = nil
|
|
||||||
}
|
|
||||||
if o := m.TmpfsOptions; o != nil && m.Type != mount.TypeTmpfs {
|
|
||||||
m.TmpfsOptions = nil
|
|
||||||
}
|
|
||||||
if bo := m.BindOptions; bo != nil {
|
|
||||||
// Ignore BindOptions.CreateMountpoint because it was added in API 1.42.
|
|
||||||
bo.CreateMountpoint = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
|
||||||
// ConsoleSize is not respected by Linux daemon before API 1.42
|
|
||||||
hostConfig.ConsoleSize = [2]uint{0, 0}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
|
||||||
// Ignore KernelMemory removed in API 1.42.
|
|
||||||
hostConfig.KernelMemory = 0
|
|
||||||
for _, m := range hostConfig.Mounts {
|
|
||||||
if o := m.VolumeOptions; o != nil && m.Type != mount.TypeVolume {
|
|
||||||
return errdefs.InvalidParameter(fmt.Errorf("VolumeOptions must not be specified on mount type %q", m.Type))
|
|
||||||
}
|
|
||||||
if o := m.BindOptions; o != nil && m.Type != mount.TypeBind {
|
|
||||||
return errdefs.InvalidParameter(fmt.Errorf("BindOptions must not be specified on mount type %q", m.Type))
|
|
||||||
}
|
|
||||||
if o := m.TmpfsOptions; o != nil && m.Type != mount.TypeTmpfs {
|
|
||||||
return errdefs.InvalidParameter(fmt.Errorf("TmpfsOptions must not be specified on mount type %q", m.Type))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if versions.LessThan(version, "1.43") {
|
|
||||||
// Ignore Annotations because it was added in API v1.43.
|
|
||||||
hostConfig.Annotations = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultReadOnlyNonRecursive := false
|
|
||||||
if versions.LessThan(version, "1.44") {
|
|
||||||
if config.Healthcheck != nil {
|
|
||||||
// StartInterval was added in API 1.44
|
|
||||||
config.Healthcheck.StartInterval = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set ReadOnlyNonRecursive to true because it was added in API 1.44
|
|
||||||
// Before that all read-only mounts were non-recursive.
|
|
||||||
// Keep that behavior for clients on older APIs.
|
|
||||||
defaultReadOnlyNonRecursive = true
|
|
||||||
|
|
||||||
for _, m := range hostConfig.Mounts {
|
|
||||||
if m.Type == mount.TypeBind {
|
|
||||||
if m.BindOptions != nil && m.BindOptions.ReadOnlyForceRecursive {
|
|
||||||
// NOTE: that technically this is a breaking change for older
|
|
||||||
// API versions, and we should ignore the new field.
|
|
||||||
// However, this option may be incorrectly set by a client with
|
|
||||||
// the expectation that the failing to apply recursive read-only
|
|
||||||
// is enforced, so we decided to produce an error instead,
|
|
||||||
// instead of silently ignoring.
|
|
||||||
return errdefs.InvalidParameter(errors.New("BindOptions.ReadOnlyForceRecursive needs API v1.44 or newer"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creating a container connected to several networks is not supported until v1.44.
|
|
||||||
if len(networkingConfig.EndpointsConfig) > 1 {
|
|
||||||
l := make([]string, 0, len(networkingConfig.EndpointsConfig))
|
|
||||||
for k := range networkingConfig.EndpointsConfig {
|
|
||||||
l = append(l, k)
|
|
||||||
}
|
|
||||||
return errdefs.InvalidParameter(errors.Errorf("Container cannot be created with multiple network endpoints: %s", strings.Join(l, ", ")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if versions.LessThan(version, "1.45") {
|
|
||||||
for _, m := range hostConfig.Mounts {
|
|
||||||
if m.VolumeOptions != nil && m.VolumeOptions.Subpath != "" {
|
|
||||||
return errdefs.InvalidParameter(errors.New("VolumeOptions.Subpath needs API v1.45 or newer"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var warnings []string
|
|
||||||
if warn, err := handleMACAddressBC(config, hostConfig, networkingConfig, version); err != nil {
|
|
||||||
return err
|
|
||||||
} else if warn != "" {
|
|
||||||
warnings = append(warnings, warn)
|
|
||||||
}
|
|
||||||
|
|
||||||
if hostConfig.PidsLimit != nil && *hostConfig.PidsLimit <= 0 {
|
|
||||||
// Don't set a limit if either no limit was specified, or "unlimited" was
|
|
||||||
// explicitly set.
|
|
||||||
// Both `0` and `-1` are accepted as "unlimited", and historically any
|
|
||||||
// negative value was accepted, so treat those as "unlimited" as well.
|
|
||||||
hostConfig.PidsLimit = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ccr, err := s.backend.ContainerCreate(ctx, backend.ContainerCreateConfig{
|
|
||||||
Name: name,
|
Name: name,
|
||||||
Config: config,
|
Config: config,
|
||||||
HostConfig: hostConfig,
|
HostConfig: hostConfig,
|
||||||
NetworkingConfig: networkingConfig,
|
NetworkingConfig: networkingConfig,
|
||||||
Platform: platform,
|
AdjustCPUShares: adjustCPUShares,
|
||||||
DefaultReadOnlyNonRecursive: defaultReadOnlyNonRecursive,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ccr.Warnings = append(ccr.Warnings, warnings...)
|
|
||||||
return httputils.WriteJSON(w, http.StatusCreated, ccr)
|
return httputils.WriteJSON(w, http.StatusCreated, ccr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleMACAddressBC takes care of backward-compatibility for the container-wide MAC address by mutating the
|
|
||||||
// networkingConfig to set the endpoint-specific MACAddress field introduced in API v1.44. It returns a warning message
|
|
||||||
// or an error if the container-wide field was specified for API >= v1.44.
|
|
||||||
func handleMACAddressBC(config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, version string) (string, error) {
|
|
||||||
deprecatedMacAddress := config.MacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
|
|
||||||
|
|
||||||
// For older versions of the API, migrate the container-wide MAC address to EndpointsConfig.
|
|
||||||
if versions.LessThan(version, "1.44") {
|
|
||||||
if deprecatedMacAddress == "" {
|
|
||||||
// If a MAC address is supplied in EndpointsConfig, discard it because the old API
|
|
||||||
// would have ignored it.
|
|
||||||
for _, ep := range networkingConfig.EndpointsConfig {
|
|
||||||
ep.MacAddress = ""
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
if !hostConfig.NetworkMode.IsBridge() && !hostConfig.NetworkMode.IsUserDefined() {
|
|
||||||
return "", runconfig.ErrConflictContainerNetworkAndMac
|
|
||||||
}
|
|
||||||
|
|
||||||
// There cannot be more than one entry in EndpointsConfig with API < 1.44.
|
|
||||||
|
|
||||||
// If there's no EndpointsConfig, create a place to store the configured address. It is
|
|
||||||
// safe to use NetworkMode as the network name, whether it's a name or id/short-id, as
|
|
||||||
// it will be normalised later and there is no other EndpointSettings object that might
|
|
||||||
// refer to this network/endpoint.
|
|
||||||
if len(networkingConfig.EndpointsConfig) == 0 {
|
|
||||||
nwName := hostConfig.NetworkMode.NetworkName()
|
|
||||||
networkingConfig.EndpointsConfig[nwName] = &network.EndpointSettings{}
|
|
||||||
}
|
|
||||||
// There's exactly one network in EndpointsConfig, either from the API or just-created.
|
|
||||||
// Migrate the container-wide setting to it.
|
|
||||||
// No need to check for a match between NetworkMode and the names/ids in EndpointsConfig,
|
|
||||||
// the old version of the API would have applied the address to this network anyway.
|
|
||||||
for _, ep := range networkingConfig.EndpointsConfig {
|
|
||||||
ep.MacAddress = deprecatedMacAddress
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The container-wide MacAddress parameter is deprecated and should now be specified in EndpointsConfig.
|
|
||||||
if deprecatedMacAddress == "" {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
var warning string
|
|
||||||
if hostConfig.NetworkMode.IsBridge() || hostConfig.NetworkMode.IsUserDefined() {
|
|
||||||
nwName := hostConfig.NetworkMode.NetworkName()
|
|
||||||
// If there's no endpoint config, create a place to store the configured address.
|
|
||||||
if len(networkingConfig.EndpointsConfig) == 0 {
|
|
||||||
networkingConfig.EndpointsConfig[nwName] = &network.EndpointSettings{
|
|
||||||
MacAddress: deprecatedMacAddress,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// There is existing endpoint config - if it's not indexed by NetworkMode.Name(), we
|
|
||||||
// can't tell which network the container-wide settings was intended for. NetworkMode,
|
|
||||||
// the keys in EndpointsConfig and the NetworkID in EndpointsConfig may mix network
|
|
||||||
// name/id/short-id. It's not safe to create EndpointsConfig under the NetworkMode
|
|
||||||
// name to store the container-wide MAC address, because that may result in two sets
|
|
||||||
// of EndpointsConfig for the same network and one set will be discarded later. So,
|
|
||||||
// reject the request ...
|
|
||||||
ep, ok := networkingConfig.EndpointsConfig[nwName]
|
|
||||||
if !ok {
|
|
||||||
return "", errdefs.InvalidParameter(errors.New("if a container-wide MAC address is supplied, HostConfig.NetworkMode must match the identity of a network in NetworkSettings.Networks"))
|
|
||||||
}
|
|
||||||
// ep is the endpoint that needs the container-wide MAC address; migrate the address
|
|
||||||
// to it, or bail out if there's a mismatch.
|
|
||||||
if ep.MacAddress == "" {
|
|
||||||
ep.MacAddress = deprecatedMacAddress
|
|
||||||
} else if ep.MacAddress != deprecatedMacAddress {
|
|
||||||
return "", errdefs.InvalidParameter(errors.New("the container-wide MAC address must match the endpoint-specific MAC address for the main network, or be left empty"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
warning = "The container-wide MacAddress field is now deprecated. It should be specified in EndpointsConfig instead."
|
|
||||||
config.MacAddress = "" //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
|
|
||||||
|
|
||||||
return warning, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
name := vars["name"]
|
name := vars["name"]
|
||||||
config := &backend.ContainerRmConfig{
|
config := &types.ContainerRmConfig{
|
||||||
ForceRemove: httputils.BoolValue(r, "force"),
|
ForceRemove: httputils.BoolValue(r, "force"),
|
||||||
RemoveVolume: httputils.BoolValue(r, "v"),
|
RemoveVolume: httputils.BoolValue(r, "v"),
|
||||||
RemoveLink: httputils.BoolValue(r, "link"),
|
RemoveLink: httputils.BoolValue(r, "link"),
|
||||||
|
@ -749,11 +416,11 @@ func (s *containerRouter) postContainersResize(ctx context.Context, w http.Respo
|
||||||
|
|
||||||
height, err := strconv.Atoi(r.Form.Get("h"))
|
height, err := strconv.Atoi(r.Form.Get("h"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errdefs.InvalidParameter(err)
|
return err
|
||||||
}
|
}
|
||||||
width, err := strconv.Atoi(r.Form.Get("w"))
|
width, err := strconv.Atoi(r.Form.Get("w"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errdefs.InvalidParameter(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.backend.ContainerResize(vars["name"], height, width)
|
return s.backend.ContainerResize(vars["name"], height, width)
|
||||||
|
@ -771,11 +438,10 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
||||||
|
|
||||||
hijacker, ok := w.(http.Hijacker)
|
hijacker, ok := w.(http.Hijacker)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errdefs.InvalidParameter(errors.Errorf("error attaching to container %s, hijack connection missing", containerName))
|
return fmt.Errorf("error attaching to container %s, hijack connection missing", containerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentType := types.MediaTypeRawStream
|
setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
|
||||||
setupStreams := func(multiplexed bool) (io.ReadCloser, io.Writer, io.Writer, error) {
|
|
||||||
conn, _, err := hijacker.Hijack()
|
conn, _, err := hijacker.Hijack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
|
@ -785,10 +451,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
||||||
conn.Write([]byte{})
|
conn.Write([]byte{})
|
||||||
|
|
||||||
if upgrade {
|
if upgrade {
|
||||||
if multiplexed && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
|
fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
|
||||||
contentType = types.MediaTypeMultiplexedStream
|
|
||||||
}
|
|
||||||
fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: "+contentType+"\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||||
}
|
}
|
||||||
|
@ -812,16 +475,16 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = s.backend.ContainerAttach(containerName, attachConfig); err != nil {
|
if err = s.backend.ContainerAttach(containerName, attachConfig); err != nil {
|
||||||
log.G(ctx).WithError(err).Errorf("Handler for %s %s returned error", r.Method, r.URL.Path)
|
logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
|
||||||
// Remember to close stream if error happens
|
// Remember to close stream if error happens
|
||||||
conn, _, errHijack := hijacker.Hijack()
|
conn, _, errHijack := hijacker.Hijack()
|
||||||
if errHijack != nil {
|
if errHijack == nil {
|
||||||
log.G(ctx).WithError(err).Errorf("Handler for %s %s: unable to close stream; error when hijacking connection", r.Method, r.URL.Path)
|
statusCode := httputils.GetHTTPErrorStatusCode(err)
|
||||||
} else {
|
|
||||||
statusCode := httpstatus.FromError(err)
|
|
||||||
statusText := http.StatusText(statusCode)
|
statusText := http.StatusText(statusCode)
|
||||||
fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: %s\r\n\r\n%s\r\n", statusCode, statusText, contentType, err.Error())
|
fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n%s\r\n", statusCode, statusText, err.Error())
|
||||||
httputils.CloseStreams(conn)
|
httputils.CloseStreams(conn)
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("Error Hijacking: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -839,9 +502,7 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
started := make(chan struct{})
|
started := make(chan struct{})
|
||||||
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
|
||||||
|
|
||||||
setupStreams := func(multiplexed bool) (io.ReadCloser, io.Writer, io.Writer, error) {
|
|
||||||
wsChan := make(chan *websocket.Conn)
|
wsChan := make(chan *websocket.Conn)
|
||||||
h := func(conn *websocket.Conn) {
|
h := func(conn *websocket.Conn) {
|
||||||
wsChan <- conn
|
wsChan <- conn
|
||||||
|
@ -855,41 +516,25 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
|
||||||
}()
|
}()
|
||||||
|
|
||||||
conn := <-wsChan
|
conn := <-wsChan
|
||||||
// In case version 1.28 and above, a binary frame will be sent.
|
|
||||||
// See 28176 for details.
|
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.28") {
|
|
||||||
conn.PayloadType = websocket.BinaryFrame
|
|
||||||
}
|
|
||||||
return conn, conn, conn, nil
|
return conn, conn, conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
useStdin, useStdout, useStderr := true, true, true
|
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
|
||||||
useStdin = httputils.BoolValue(r, "stdin")
|
|
||||||
useStdout = httputils.BoolValue(r, "stdout")
|
|
||||||
useStderr = httputils.BoolValue(r, "stderr")
|
|
||||||
}
|
|
||||||
|
|
||||||
attachConfig := &backend.ContainerAttachConfig{
|
attachConfig := &backend.ContainerAttachConfig{
|
||||||
GetStreams: setupStreams,
|
GetStreams: setupStreams,
|
||||||
UseStdin: useStdin,
|
|
||||||
UseStdout: useStdout,
|
|
||||||
UseStderr: useStderr,
|
|
||||||
Logs: httputils.BoolValue(r, "logs"),
|
Logs: httputils.BoolValue(r, "logs"),
|
||||||
Stream: httputils.BoolValue(r, "stream"),
|
Stream: httputils.BoolValue(r, "stream"),
|
||||||
DetachKeys: detachKeys,
|
DetachKeys: detachKeys,
|
||||||
MuxStreams: false, // never multiplex, as we rely on websocket to manage distinct streams
|
UseStdin: true,
|
||||||
|
UseStdout: true,
|
||||||
|
UseStderr: true,
|
||||||
|
MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.backend.ContainerAttach(containerName, attachConfig)
|
err = s.backend.ContainerAttach(containerName, attachConfig)
|
||||||
close(done)
|
close(done)
|
||||||
select {
|
select {
|
||||||
case <-started:
|
case <-started:
|
||||||
if err != nil {
|
logrus.Errorf("Error attaching websocket: %s", err)
|
||||||
log.G(ctx).Errorf("Error attaching websocket: %s", err)
|
|
||||||
} else {
|
|
||||||
log.G(ctx).Debug("websocket connection was closed by client")
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
@ -901,12 +546,12 @@ func (s *containerRouter) postContainersPrune(ctx context.Context, w http.Respon
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pruneReport, err := s.backend.ContainersPrune(ctx, pruneFilters)
|
pruneReport, err := s.backend.ContainersPrune(pruneFilters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,160 +0,0 @@
|
||||||
package container
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
|
||||||
"github.com/docker/docker/api/types/network"
|
|
||||||
"gotest.tools/v3/assert"
|
|
||||||
is "gotest.tools/v3/assert/cmp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHandleMACAddressBC(t *testing.T) {
|
|
||||||
testcases := []struct {
|
|
||||||
name string
|
|
||||||
apiVersion string
|
|
||||||
ctrWideMAC string
|
|
||||||
networkMode container.NetworkMode
|
|
||||||
epConfig map[string]*network.EndpointSettings
|
|
||||||
expEpWithCtrWideMAC string
|
|
||||||
expEpWithNoMAC string
|
|
||||||
expCtrWideMAC string
|
|
||||||
expWarning string
|
|
||||||
expError string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "old api ctr-wide mac mix id and name",
|
|
||||||
apiVersion: "1.43",
|
|
||||||
ctrWideMAC: "11:22:33:44:55:66",
|
|
||||||
networkMode: "aNetId",
|
|
||||||
epConfig: map[string]*network.EndpointSettings{"aNetName": {}},
|
|
||||||
expEpWithCtrWideMAC: "aNetName",
|
|
||||||
expCtrWideMAC: "11:22:33:44:55:66",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "old api clear ep mac",
|
|
||||||
apiVersion: "1.43",
|
|
||||||
networkMode: "aNetId",
|
|
||||||
epConfig: map[string]*network.EndpointSettings{"aNetName": {MacAddress: "11:22:33:44:55:66"}},
|
|
||||||
expEpWithNoMAC: "aNetName",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "old api no-network ctr-wide mac",
|
|
||||||
apiVersion: "1.43",
|
|
||||||
networkMode: "none",
|
|
||||||
ctrWideMAC: "11:22:33:44:55:66",
|
|
||||||
expError: "conflicting options: mac-address and the network mode",
|
|
||||||
expCtrWideMAC: "11:22:33:44:55:66",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "old api create ep",
|
|
||||||
apiVersion: "1.43",
|
|
||||||
networkMode: "aNetId",
|
|
||||||
ctrWideMAC: "11:22:33:44:55:66",
|
|
||||||
epConfig: map[string]*network.EndpointSettings{},
|
|
||||||
expEpWithCtrWideMAC: "aNetId",
|
|
||||||
expCtrWideMAC: "11:22:33:44:55:66",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "old api migrate ctr-wide mac",
|
|
||||||
apiVersion: "1.43",
|
|
||||||
ctrWideMAC: "11:22:33:44:55:66",
|
|
||||||
networkMode: "aNetName",
|
|
||||||
epConfig: map[string]*network.EndpointSettings{"aNetName": {}},
|
|
||||||
expEpWithCtrWideMAC: "aNetName",
|
|
||||||
expCtrWideMAC: "11:22:33:44:55:66",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "new api no macs",
|
|
||||||
apiVersion: "1.44",
|
|
||||||
networkMode: "aNetId",
|
|
||||||
epConfig: map[string]*network.EndpointSettings{"aNetName": {}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "new api ep specific mac",
|
|
||||||
apiVersion: "1.44",
|
|
||||||
networkMode: "aNetName",
|
|
||||||
epConfig: map[string]*network.EndpointSettings{"aNetName": {MacAddress: "11:22:33:44:55:66"}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "new api migrate ctr-wide mac to new ep",
|
|
||||||
apiVersion: "1.44",
|
|
||||||
ctrWideMAC: "11:22:33:44:55:66",
|
|
||||||
networkMode: "aNetName",
|
|
||||||
epConfig: map[string]*network.EndpointSettings{},
|
|
||||||
expEpWithCtrWideMAC: "aNetName",
|
|
||||||
expWarning: "The container-wide MacAddress field is now deprecated",
|
|
||||||
expCtrWideMAC: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "new api migrate ctr-wide mac to existing ep",
|
|
||||||
apiVersion: "1.44",
|
|
||||||
ctrWideMAC: "11:22:33:44:55:66",
|
|
||||||
networkMode: "aNetName",
|
|
||||||
epConfig: map[string]*network.EndpointSettings{"aNetName": {}},
|
|
||||||
expEpWithCtrWideMAC: "aNetName",
|
|
||||||
expWarning: "The container-wide MacAddress field is now deprecated",
|
|
||||||
expCtrWideMAC: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "new api mode vs name mismatch",
|
|
||||||
apiVersion: "1.44",
|
|
||||||
ctrWideMAC: "11:22:33:44:55:66",
|
|
||||||
networkMode: "aNetId",
|
|
||||||
epConfig: map[string]*network.EndpointSettings{"aNetName": {}},
|
|
||||||
expError: "if a container-wide MAC address is supplied, HostConfig.NetworkMode must match the identity of a network in NetworkSettings.Networks",
|
|
||||||
expCtrWideMAC: "11:22:33:44:55:66",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "new api mac mismatch",
|
|
||||||
apiVersion: "1.44",
|
|
||||||
ctrWideMAC: "11:22:33:44:55:66",
|
|
||||||
networkMode: "aNetName",
|
|
||||||
epConfig: map[string]*network.EndpointSettings{"aNetName": {MacAddress: "00:11:22:33:44:55"}},
|
|
||||||
expError: "the container-wide MAC address must match the endpoint-specific MAC address",
|
|
||||||
expCtrWideMAC: "11:22:33:44:55:66",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testcases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
cfg := &container.Config{
|
|
||||||
MacAddress: tc.ctrWideMAC, //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
|
|
||||||
}
|
|
||||||
hostCfg := &container.HostConfig{
|
|
||||||
NetworkMode: tc.networkMode,
|
|
||||||
}
|
|
||||||
epConfig := make(map[string]*network.EndpointSettings, len(tc.epConfig))
|
|
||||||
for k, v := range tc.epConfig {
|
|
||||||
v := v
|
|
||||||
epConfig[k] = v
|
|
||||||
}
|
|
||||||
netCfg := &network.NetworkingConfig{
|
|
||||||
EndpointsConfig: epConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
warning, err := handleMACAddressBC(cfg, hostCfg, netCfg, tc.apiVersion)
|
|
||||||
|
|
||||||
if tc.expError == "" {
|
|
||||||
assert.Check(t, err)
|
|
||||||
} else {
|
|
||||||
assert.Check(t, is.ErrorContains(err, tc.expError))
|
|
||||||
}
|
|
||||||
if tc.expWarning == "" {
|
|
||||||
assert.Check(t, is.Equal(warning, ""))
|
|
||||||
} else {
|
|
||||||
assert.Check(t, is.Contains(warning, tc.expWarning))
|
|
||||||
}
|
|
||||||
if tc.expEpWithCtrWideMAC != "" {
|
|
||||||
got := netCfg.EndpointsConfig[tc.expEpWithCtrWideMAC].MacAddress
|
|
||||||
assert.Check(t, is.Equal(got, tc.ctrWideMAC))
|
|
||||||
}
|
|
||||||
if tc.expEpWithNoMAC != "" {
|
|
||||||
got := netCfg.EndpointsConfig[tc.expEpWithNoMAC].MacAddress
|
|
||||||
assert.Check(t, is.Equal(got, ""))
|
|
||||||
}
|
|
||||||
gotCtrWideMAC := cfg.MacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
|
|
||||||
assert.Check(t, is.Equal(gotCtrWideMAC, tc.expCtrWideMAC))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +1,63 @@
|
||||||
package container // import "github.com/docker/docker/api/server/router/container"
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"compress/flate"
|
|
||||||
"compress/gzip"
|
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
gddohttputil "github.com/golang/gddo/httputil"
|
"github.com/docker/docker/api/types/versions"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// setContainerPathStatHeader encodes the stat to JSON, base64 encode, and place in a header.
|
// postContainersCopy is deprecated in favor of getContainersArchive.
|
||||||
|
func (s *containerRouter) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
// Deprecated since 1.8, Errors out since 1.12
|
||||||
|
version := httputils.VersionFromContext(ctx)
|
||||||
|
if versions.GreaterThanOrEqualTo(version, "1.24") {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := httputils.CheckForJSON(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := types.CopyConfig{}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Resource == "" {
|
||||||
|
return fmt.Errorf("Path cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := s.backend.ContainerCopy(vars["name"], cfg.Resource)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(strings.ToLower(err.Error()), "no such container") {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("Could not find the file %s in container %s", cfg.Resource, vars["name"])
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer data.Close()
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/x-tar")
|
||||||
|
if _, err := io.Copy(w, data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Encode the stat to JSON, base64 encode, and place in a header.
|
||||||
func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
|
func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
|
||||||
statJSON, err := json.Marshal(stat)
|
statJSON, err := json.Marshal(stat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -43,29 +86,6 @@ func (s *containerRouter) headContainersArchive(ctx context.Context, w http.Resp
|
||||||
return setContainerPathStatHeader(stat, w.Header())
|
return setContainerPathStatHeader(stat, w.Header())
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeCompressedResponse(w http.ResponseWriter, r *http.Request, body io.Reader) error {
|
|
||||||
var cw io.Writer
|
|
||||||
switch gddohttputil.NegotiateContentEncoding(r, []string{"gzip", "deflate"}) {
|
|
||||||
case "gzip":
|
|
||||||
gw := gzip.NewWriter(w)
|
|
||||||
defer gw.Close()
|
|
||||||
cw = gw
|
|
||||||
w.Header().Set("Content-Encoding", "gzip")
|
|
||||||
case "deflate":
|
|
||||||
fw, err := flate.NewWriter(w, flate.DefaultCompression)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer fw.Close()
|
|
||||||
cw = fw
|
|
||||||
w.Header().Set("Content-Encoding", "deflate")
|
|
||||||
default:
|
|
||||||
cw = w
|
|
||||||
}
|
|
||||||
_, err := io.Copy(cw, body)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *containerRouter) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
v, err := httputils.ArchiveFormValues(r, vars)
|
v, err := httputils.ArchiveFormValues(r, vars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -83,7 +103,9 @@ func (s *containerRouter) getContainersArchive(ctx context.Context, w http.Respo
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/x-tar")
|
w.Header().Set("Content-Type", "application/x-tar")
|
||||||
return writeCompressedResponse(w, r, tarArchive)
|
_, err = io.Copy(w, tarArchive)
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *containerRouter) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
@ -93,7 +115,5 @@ func (s *containerRouter) putContainersArchive(ctx context.Context, w http.Respo
|
||||||
}
|
}
|
||||||
|
|
||||||
noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir")
|
noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir")
|
||||||
copyUIDGID := httputils.BoolValue(r, "copyUIDGID")
|
return s.backend.ContainerExtractToDir(v.Name, v.Path, noOverwriteDirNonDir, r.Body)
|
||||||
|
|
||||||
return s.backend.ContainerExtractToDir(v.Name, v.Path, copyUIDGID, noOverwriteDirNonDir, r.Body)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
package container // import "github.com/docker/docker/api/server/router/container"
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/containerd/log"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/docker/errdefs"
|
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *containerRouter) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
@ -25,38 +24,28 @@ func (s *containerRouter) getExecByID(ctx context.Context, w http.ResponseWriter
|
||||||
return httputils.WriteJSON(w, http.StatusOK, eConfig)
|
return httputils.WriteJSON(w, http.StatusOK, eConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
type execCommandError struct{}
|
|
||||||
|
|
||||||
func (execCommandError) Error() string {
|
|
||||||
return "No exec command specified"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (execCommandError) InvalidParameter() {}
|
|
||||||
|
|
||||||
func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := httputils.CheckForJSON(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name := vars["name"]
|
||||||
|
|
||||||
execConfig := &types.ExecConfig{}
|
execConfig := &types.ExecConfig{}
|
||||||
if err := httputils.ReadJSON(r, execConfig); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(execConfig.Cmd) == 0 {
|
if len(execConfig.Cmd) == 0 {
|
||||||
return execCommandError{}
|
return fmt.Errorf("No exec command specified")
|
||||||
}
|
|
||||||
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
|
||||||
if versions.LessThan(version, "1.42") {
|
|
||||||
// Not supported by API versions before 1.42
|
|
||||||
execConfig.ConsoleSize = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register an instance of Exec in container.
|
// Register an instance of Exec in container.
|
||||||
id, err := s.backend.ContainerExecCreate(vars["name"], execConfig)
|
id, err := s.backend.ContainerExecCreate(name, execConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.G(ctx).Errorf("Error setting up exec command in container %s: %v", vars["name"], err)
|
logrus.Errorf("Error setting up exec command in container %s: %v", name, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +60,13 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version := httputils.VersionFromContext(ctx)
|
||||||
|
if versions.GreaterThan(version, "1.21") {
|
||||||
|
if err := httputils.CheckForJSON(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
execName = vars["name"]
|
execName = vars["name"]
|
||||||
stdin, inStream io.ReadCloser
|
stdin, inStream io.ReadCloser
|
||||||
|
@ -78,7 +74,7 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||||
)
|
)
|
||||||
|
|
||||||
execStartCheck := &types.ExecStartCheck{}
|
execStartCheck := &types.ExecStartCheck{}
|
||||||
if err := httputils.ReadJSON(r, execStartCheck); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,20 +82,6 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if execStartCheck.ConsoleSize != nil {
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
|
||||||
|
|
||||||
// Not supported before 1.42
|
|
||||||
if versions.LessThan(version, "1.42") {
|
|
||||||
execStartCheck.ConsoleSize = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// No console without tty
|
|
||||||
if !execStartCheck.Tty {
|
|
||||||
execStartCheck.ConsoleSize = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !execStartCheck.Detach {
|
if !execStartCheck.Detach {
|
||||||
var err error
|
var err error
|
||||||
// Setting up the streaming http interface.
|
// Setting up the streaming http interface.
|
||||||
|
@ -110,11 +92,7 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||||
defer httputils.CloseStreams(inStream, outStream)
|
defer httputils.CloseStreams(inStream, outStream)
|
||||||
|
|
||||||
if _, ok := r.Header["Upgrade"]; ok {
|
if _, ok := r.Header["Upgrade"]; ok {
|
||||||
contentType := types.MediaTypeRawStream
|
fmt.Fprint(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n")
|
||||||
if !execStartCheck.Tty && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
|
|
||||||
contentType = types.MediaTypeMultiplexedStream
|
|
||||||
}
|
|
||||||
fmt.Fprint(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: "+contentType+"\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n")
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprint(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n")
|
fmt.Fprint(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n")
|
||||||
}
|
}
|
||||||
|
@ -133,21 +111,14 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options := container.ExecStartOptions{
|
|
||||||
Stdin: stdin,
|
|
||||||
Stdout: stdout,
|
|
||||||
Stderr: stderr,
|
|
||||||
ConsoleSize: execStartCheck.ConsoleSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now run the user process in container.
|
// Now run the user process in container.
|
||||||
// Maybe we should we pass ctx here if we're not detaching?
|
// Maybe we should we pass ctx here if we're not detaching?
|
||||||
if err := s.backend.ContainerExecStart(context.Background(), execName, options); err != nil {
|
if err := s.backend.ContainerExecStart(context.Background(), execName, stdin, stdout, stderr); err != nil {
|
||||||
if execStartCheck.Detach {
|
if execStartCheck.Detach {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
stdout.Write([]byte(err.Error() + "\r\n"))
|
stdout.Write([]byte(err.Error() + "\r\n"))
|
||||||
log.G(ctx).Errorf("Error running exec %s in container: %v", execName, err)
|
logrus.Errorf("Error running exec in container: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -158,11 +129,11 @@ func (s *containerRouter) postContainerExecResize(ctx context.Context, w http.Re
|
||||||
}
|
}
|
||||||
height, err := strconv.Atoi(r.Form.Get("h"))
|
height, err := strconv.Atoi(r.Form.Get("h"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errdefs.InvalidParameter(err)
|
return err
|
||||||
}
|
}
|
||||||
width, err := strconv.Atoi(r.Form.Get("w"))
|
width, err := strconv.Atoi(r.Form.Get("w"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errdefs.InvalidParameter(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.backend.ContainerExecResize(vars["name"], height, width)
|
return s.backend.ContainerExecResize(vars["name"], height, width)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package container // import "github.com/docker/docker/api/server/router/container"
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// getContainersByName inspects container's configuration and serializes it as json.
|
// getContainersByName inspects container's configuration and serializes it as json.
|
||||||
|
@ -12,7 +12,7 @@ func (s *containerRouter) getContainersByName(ctx context.Context, w http.Respon
|
||||||
displaySize := httputils.BoolValue(r, "size")
|
displaySize := httputils.BoolValue(r, "size")
|
||||||
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
version := httputils.VersionFromContext(ctx)
|
||||||
json, err := s.backend.ContainerInspect(ctx, vars["name"], displaySize, version)
|
json, err := s.backend.ContainerInspect(vars["name"], displaySize, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
package debug // import "github.com/docker/docker/api/server/router/debug"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"expvar"
|
|
||||||
"net/http"
|
|
||||||
"net/http/pprof"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/server/httputils"
|
|
||||||
"github.com/docker/docker/api/server/router"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewRouter creates a new debug router
|
|
||||||
// The debug router holds endpoints for debug the daemon, such as those for pprof.
|
|
||||||
func NewRouter() router.Router {
|
|
||||||
r := &debugRouter{}
|
|
||||||
r.initRoutes()
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
type debugRouter struct {
|
|
||||||
routes []router.Route
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *debugRouter) initRoutes() {
|
|
||||||
r.routes = []router.Route{
|
|
||||||
router.NewGetRoute("/vars", frameworkAdaptHandler(expvar.Handler())),
|
|
||||||
router.NewGetRoute("/pprof/", frameworkAdaptHandlerFunc(pprof.Index)),
|
|
||||||
router.NewGetRoute("/pprof/cmdline", frameworkAdaptHandlerFunc(pprof.Cmdline)),
|
|
||||||
router.NewGetRoute("/pprof/profile", frameworkAdaptHandlerFunc(pprof.Profile)),
|
|
||||||
router.NewGetRoute("/pprof/symbol", frameworkAdaptHandlerFunc(pprof.Symbol)),
|
|
||||||
router.NewGetRoute("/pprof/trace", frameworkAdaptHandlerFunc(pprof.Trace)),
|
|
||||||
router.NewGetRoute("/pprof/{name}", handlePprof),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *debugRouter) Routes() []router.Route {
|
|
||||||
return r.routes
|
|
||||||
}
|
|
||||||
|
|
||||||
func frameworkAdaptHandler(handler http.Handler) httputils.APIFunc {
|
|
||||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
handler.ServeHTTP(w, r)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func frameworkAdaptHandlerFunc(handler http.HandlerFunc) httputils.APIFunc {
|
|
||||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
handler(w, r)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package debug // import "github.com/docker/docker/api/server/router/debug"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
"net/http/pprof"
|
|
||||||
)
|
|
||||||
|
|
||||||
func handlePprof(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
pprof.Handler(vars["name"]).ServeHTTP(w, r)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package distribution // import "github.com/docker/docker/api/server/router/distribution"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/distribution/reference"
|
|
||||||
"github.com/docker/distribution"
|
|
||||||
"github.com/docker/docker/api/types/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Backend is all the methods that need to be implemented
|
|
||||||
// to provide image specific functionality.
|
|
||||||
type Backend interface {
|
|
||||||
GetRepositories(context.Context, reference.Named, *registry.AuthConfig) ([]distribution.Repository, error)
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package distribution // import "github.com/docker/docker/api/server/router/distribution"
|
|
||||||
|
|
||||||
import "github.com/docker/docker/api/server/router"
|
|
||||||
|
|
||||||
// distributionRouter is a router to talk with the registry
|
|
||||||
type distributionRouter struct {
|
|
||||||
backend Backend
|
|
||||||
routes []router.Route
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRouter initializes a new distribution router
|
|
||||||
func NewRouter(backend Backend) router.Router {
|
|
||||||
r := &distributionRouter{
|
|
||||||
backend: backend,
|
|
||||||
}
|
|
||||||
r.initRoutes()
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routes returns the available routes
|
|
||||||
func (r *distributionRouter) Routes() []router.Route {
|
|
||||||
return r.routes
|
|
||||||
}
|
|
||||||
|
|
||||||
// initRoutes initializes the routes in the distribution router
|
|
||||||
func (r *distributionRouter) initRoutes() {
|
|
||||||
r.routes = []router.Route{
|
|
||||||
// GET
|
|
||||||
router.NewGetRoute("/distribution/{name:.*}/json", r.getDistributionInfo),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,168 +0,0 @@
|
||||||
package distribution // import "github.com/docker/docker/api/server/router/distribution"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/distribution/reference"
|
|
||||||
"github.com/docker/distribution"
|
|
||||||
"github.com/docker/distribution/manifest/manifestlist"
|
|
||||||
"github.com/docker/distribution/manifest/schema1"
|
|
||||||
"github.com/docker/distribution/manifest/schema2"
|
|
||||||
"github.com/docker/docker/api/server/httputils"
|
|
||||||
"github.com/docker/docker/api/types/registry"
|
|
||||||
distributionpkg "github.com/docker/docker/distribution"
|
|
||||||
"github.com/docker/docker/errdefs"
|
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
imgName := vars["name"]
|
|
||||||
|
|
||||||
// TODO why is reference.ParseAnyReference() / reference.ParseNormalizedNamed() not using the reference.ErrTagInvalidFormat (and so on) errors?
|
|
||||||
ref, err := reference.ParseAnyReference(imgName)
|
|
||||||
if err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
namedRef, ok := ref.(reference.Named)
|
|
||||||
if !ok {
|
|
||||||
if _, ok := ref.(reference.Digested); ok {
|
|
||||||
// full image ID
|
|
||||||
return errors.Errorf("no manifest found for full image ID")
|
|
||||||
}
|
|
||||||
return errdefs.InvalidParameter(errors.Errorf("unknown image reference format: %s", imgName))
|
|
||||||
}
|
|
||||||
|
|
||||||
// For a search it is not an error if no auth was given. Ignore invalid
|
|
||||||
// AuthConfig to increase compatibility with the existing API.
|
|
||||||
authConfig, _ := registry.DecodeAuthConfig(r.Header.Get(registry.AuthHeader))
|
|
||||||
repos, err := s.backend.GetRepositories(ctx, namedRef, authConfig)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch the manifest; if a mirror is configured, try the mirror first,
|
|
||||||
// but continue with upstream on failure.
|
|
||||||
//
|
|
||||||
// FIXME(thaJeztah): construct "repositories" on-demand;
|
|
||||||
// GetRepositories() will attempt to connect to all endpoints (registries),
|
|
||||||
// but we may only need the first one if it contains the manifest we're
|
|
||||||
// looking for, or if the configured mirror is a pull-through mirror.
|
|
||||||
//
|
|
||||||
// Logic for this could be implemented similar to "distribution.Pull()",
|
|
||||||
// which uses the "pullEndpoints" utility to iterate over the list
|
|
||||||
// of endpoints;
|
|
||||||
//
|
|
||||||
// - https://github.com/moby/moby/blob/12c7411b6b7314bef130cd59f1c7384a7db06d0b/distribution/pull.go#L17-L31
|
|
||||||
// - https://github.com/moby/moby/blob/12c7411b6b7314bef130cd59f1c7384a7db06d0b/distribution/pull.go#L76-L152
|
|
||||||
var lastErr error
|
|
||||||
for _, repo := range repos {
|
|
||||||
distributionInspect, err := s.fetchManifest(ctx, repo, namedRef)
|
|
||||||
if err != nil {
|
|
||||||
lastErr = err
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return httputils.WriteJSON(w, http.StatusOK, distributionInspect)
|
|
||||||
}
|
|
||||||
return lastErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *distributionRouter) fetchManifest(ctx context.Context, distrepo distribution.Repository, namedRef reference.Named) (registry.DistributionInspect, error) {
|
|
||||||
var distributionInspect registry.DistributionInspect
|
|
||||||
if canonicalRef, ok := namedRef.(reference.Canonical); !ok {
|
|
||||||
namedRef = reference.TagNameOnly(namedRef)
|
|
||||||
|
|
||||||
taggedRef, ok := namedRef.(reference.NamedTagged)
|
|
||||||
if !ok {
|
|
||||||
return registry.DistributionInspect{}, errdefs.InvalidParameter(errors.Errorf("image reference not tagged: %s", namedRef))
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptor, err := distrepo.Tags(ctx).Get(ctx, taggedRef.Tag())
|
|
||||||
if err != nil {
|
|
||||||
return registry.DistributionInspect{}, err
|
|
||||||
}
|
|
||||||
distributionInspect.Descriptor = ocispec.Descriptor{
|
|
||||||
MediaType: descriptor.MediaType,
|
|
||||||
Digest: descriptor.Digest,
|
|
||||||
Size: descriptor.Size,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO(nishanttotla): Once manifests can be looked up as a blob, the
|
|
||||||
// descriptor should be set using blobsrvc.Stat(ctx, canonicalRef.Digest())
|
|
||||||
// instead of having to manually fill in the fields
|
|
||||||
distributionInspect.Descriptor.Digest = canonicalRef.Digest()
|
|
||||||
}
|
|
||||||
|
|
||||||
// we have a digest, so we can retrieve the manifest
|
|
||||||
mnfstsrvc, err := distrepo.Manifests(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return registry.DistributionInspect{}, err
|
|
||||||
}
|
|
||||||
mnfst, err := mnfstsrvc.Get(ctx, distributionInspect.Descriptor.Digest)
|
|
||||||
if err != nil {
|
|
||||||
switch err {
|
|
||||||
case reference.ErrReferenceInvalidFormat,
|
|
||||||
reference.ErrTagInvalidFormat,
|
|
||||||
reference.ErrDigestInvalidFormat,
|
|
||||||
reference.ErrNameContainsUppercase,
|
|
||||||
reference.ErrNameEmpty,
|
|
||||||
reference.ErrNameTooLong,
|
|
||||||
reference.ErrNameNotCanonical:
|
|
||||||
return registry.DistributionInspect{}, errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
return registry.DistributionInspect{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
mediaType, payload, err := mnfst.Payload()
|
|
||||||
if err != nil {
|
|
||||||
return registry.DistributionInspect{}, err
|
|
||||||
}
|
|
||||||
// update MediaType because registry might return something incorrect
|
|
||||||
distributionInspect.Descriptor.MediaType = mediaType
|
|
||||||
if distributionInspect.Descriptor.Size == 0 {
|
|
||||||
distributionInspect.Descriptor.Size = int64(len(payload))
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve platform information depending on the type of manifest
|
|
||||||
switch mnfstObj := mnfst.(type) {
|
|
||||||
case *manifestlist.DeserializedManifestList:
|
|
||||||
for _, m := range mnfstObj.Manifests {
|
|
||||||
distributionInspect.Platforms = append(distributionInspect.Platforms, ocispec.Platform{
|
|
||||||
Architecture: m.Platform.Architecture,
|
|
||||||
OS: m.Platform.OS,
|
|
||||||
OSVersion: m.Platform.OSVersion,
|
|
||||||
OSFeatures: m.Platform.OSFeatures,
|
|
||||||
Variant: m.Platform.Variant,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
case *schema2.DeserializedManifest:
|
|
||||||
blobStore := distrepo.Blobs(ctx)
|
|
||||||
configJSON, err := blobStore.Get(ctx, mnfstObj.Config.Digest)
|
|
||||||
var platform ocispec.Platform
|
|
||||||
if err == nil {
|
|
||||||
err := json.Unmarshal(configJSON, &platform)
|
|
||||||
if err == nil && (platform.OS != "" || platform.Architecture != "") {
|
|
||||||
distributionInspect.Platforms = append(distributionInspect.Platforms, platform)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *schema1.SignedManifest:
|
|
||||||
if os.Getenv("DOCKER_ENABLE_DEPRECATED_PULL_SCHEMA_1_IMAGE") == "" {
|
|
||||||
return registry.DistributionInspect{}, distributionpkg.DeprecatedSchema1ImageError(namedRef)
|
|
||||||
}
|
|
||||||
platform := ocispec.Platform{
|
|
||||||
Architecture: mnfstObj.Architecture,
|
|
||||||
OS: "linux",
|
|
||||||
}
|
|
||||||
distributionInspect.Platforms = append(distributionInspect.Platforms, platform)
|
|
||||||
}
|
|
||||||
return distributionInspect, nil
|
|
||||||
}
|
|
|
@ -1,12 +1,19 @@
|
||||||
package router // import "github.com/docker/docker/api/server/router"
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
apierrors "github.com/docker/docker/api/errors"
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errExperimentalFeature = errors.New("This experimental feature is disabled by default. Start the Docker daemon with --experimental in order to enable it.")
|
||||||
|
)
|
||||||
|
|
||||||
// ExperimentalRoute defines an experimental API route that can be enabled or disabled.
|
// ExperimentalRoute defines an experimental API route that can be enabled or disabled.
|
||||||
type ExperimentalRoute interface {
|
type ExperimentalRoute interface {
|
||||||
Route
|
Route
|
||||||
|
@ -32,19 +39,11 @@ func (r *experimentalRoute) Disable() {
|
||||||
r.handler = experimentalHandler
|
r.handler = experimentalHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
type notImplementedError struct{}
|
|
||||||
|
|
||||||
func (notImplementedError) Error() string {
|
|
||||||
return "This experimental feature is disabled by default. Start the Docker daemon in experimental mode in order to enable it."
|
|
||||||
}
|
|
||||||
|
|
||||||
func (notImplementedError) NotImplemented() {}
|
|
||||||
|
|
||||||
func experimentalHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func experimentalHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
return notImplementedError{}
|
return apierrors.NewErrorWithStatusCode(errExperimentalFeature, http.StatusNotImplemented)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler returns the APIFunc to let the server wrap it in middlewares.
|
// Handler returns returns the APIFunc to let the server wrap it in middlewares.
|
||||||
func (r *experimentalRoute) Handler() httputils.APIFunc {
|
func (r *experimentalRoute) Handler() httputils.APIFunc {
|
||||||
return r.handler
|
return r.handler
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
package grpc // import "github.com/docker/docker/api/server/router/grpc"
|
|
||||||
|
|
||||||
import "google.golang.org/grpc"
|
|
||||||
|
|
||||||
// Backend abstracts a registerable GRPC service.
|
|
||||||
type Backend interface {
|
|
||||||
RegisterGRPC(*grpc.Server)
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
package grpc // import "github.com/docker/docker/api/server/router/grpc"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/server/router"
|
|
||||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
|
||||||
"github.com/moby/buildkit/util/grpcerrors"
|
|
||||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
|
||||||
"golang.org/x/net/http2"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
type grpcRouter struct {
|
|
||||||
routes []router.Route
|
|
||||||
grpcServer *grpc.Server
|
|
||||||
h2Server *http2.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRouter initializes a new grpc http router
|
|
||||||
func NewRouter(backends ...Backend) router.Router {
|
|
||||||
unary := grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(unaryInterceptor(), grpcerrors.UnaryServerInterceptor))
|
|
||||||
stream := grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(otelgrpc.StreamServerInterceptor(), grpcerrors.StreamServerInterceptor)) //nolint:staticcheck // TODO(thaJeztah): ignore SA1019 for deprecated options: see https://github.com/moby/moby/issues/47437
|
|
||||||
|
|
||||||
r := &grpcRouter{
|
|
||||||
h2Server: &http2.Server{},
|
|
||||||
grpcServer: grpc.NewServer(unary, stream),
|
|
||||||
}
|
|
||||||
for _, b := range backends {
|
|
||||||
b.RegisterGRPC(r.grpcServer)
|
|
||||||
}
|
|
||||||
r.initRoutes()
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routes returns the available routers to the session controller
|
|
||||||
func (gr *grpcRouter) Routes() []router.Route {
|
|
||||||
return gr.routes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gr *grpcRouter) initRoutes() {
|
|
||||||
gr.routes = []router.Route{
|
|
||||||
router.NewPostRoute("/grpc", gr.serveGRPC),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func unaryInterceptor() grpc.UnaryServerInterceptor {
|
|
||||||
withTrace := otelgrpc.UnaryServerInterceptor() //nolint:staticcheck // TODO(thaJeztah): ignore SA1019 for deprecated options: see https://github.com/moby/moby/issues/47437
|
|
||||||
|
|
||||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
|
||||||
// This method is used by the clients to send their traces to buildkit so they can be included
|
|
||||||
// in the daemon trace and stored in the build history record. This method can not be traced because
|
|
||||||
// it would cause an infinite loop.
|
|
||||||
if strings.HasSuffix(info.FullMethod, "opentelemetry.proto.collector.trace.v1.TraceService/Export") {
|
|
||||||
return handler(ctx, req)
|
|
||||||
}
|
|
||||||
return withTrace(ctx, req, info, handler)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package grpc // import "github.com/docker/docker/api/server/router/grpc"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"golang.org/x/net/http2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (gr *grpcRouter) serveGRPC(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
h, ok := w.(http.Hijacker)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("handler does not support hijack")
|
|
||||||
}
|
|
||||||
proto := r.Header.Get("Upgrade")
|
|
||||||
if proto == "" {
|
|
||||||
return errors.New("no upgrade proto in request")
|
|
||||||
}
|
|
||||||
if proto != "h2c" {
|
|
||||||
return errors.Errorf("protocol %s not supported", proto)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, _, err := h.Hijack()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
resp := &http.Response{
|
|
||||||
StatusCode: http.StatusSwitchingProtocols,
|
|
||||||
ProtoMajor: 1,
|
|
||||||
ProtoMinor: 1,
|
|
||||||
Header: http.Header{},
|
|
||||||
}
|
|
||||||
resp.Header.Set("Connection", "Upgrade")
|
|
||||||
resp.Header.Set("Upgrade", proto)
|
|
||||||
|
|
||||||
// set raw mode
|
|
||||||
conn.Write([]byte{})
|
|
||||||
resp.Write(conn)
|
|
||||||
|
|
||||||
// https://godoc.org/golang.org/x/net/http2#Server.ServeConn
|
|
||||||
// TODO: is it a problem that conn has already been written to?
|
|
||||||
gr.h2Server.ServeConn(conn, &http2.ServeConnOpts{Handler: gr.grpcServer})
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,47 +1,45 @@
|
||||||
package image // import "github.com/docker/docker/api/server/router/image"
|
package image
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/distribution/reference"
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/api/types/image"
|
|
||||||
"github.com/docker/docker/api/types/registry"
|
"github.com/docker/docker/api/types/registry"
|
||||||
dockerimage "github.com/docker/docker/image"
|
"golang.org/x/net/context"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Backend is all the methods that need to be implemented
|
// Backend is all the methods that need to be implemented
|
||||||
// to provide image specific functionality.
|
// to provide image specific functionality.
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
|
containerBackend
|
||||||
imageBackend
|
imageBackend
|
||||||
importExportBackend
|
importExportBackend
|
||||||
registryBackend
|
registryBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type containerBackend interface {
|
||||||
|
Commit(name string, config *backend.ContainerCommitConfig) (imageID string, err error)
|
||||||
|
}
|
||||||
|
|
||||||
type imageBackend interface {
|
type imageBackend interface {
|
||||||
ImageDelete(ctx context.Context, imageRef string, force, prune bool) ([]image.DeleteResponse, error)
|
ImageDelete(imageRef string, force, prune bool) ([]types.ImageDelete, error)
|
||||||
ImageHistory(ctx context.Context, imageName string) ([]*image.HistoryResponseItem, error)
|
ImageHistory(imageName string) ([]*types.ImageHistory, error)
|
||||||
Images(ctx context.Context, opts image.ListOptions) ([]*image.Summary, error)
|
Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error)
|
||||||
GetImage(ctx context.Context, refOrID string, options backend.GetImageOpts) (*dockerimage.Image, error)
|
LookupImage(name string) (*types.ImageInspect, error)
|
||||||
TagImage(ctx context.Context, id dockerimage.ID, newRef reference.Named) error
|
TagImage(imageName, repository, tag string) error
|
||||||
ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error)
|
ImagesPrune(pruneFilters filters.Args) (*types.ImagesPruneReport, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type importExportBackend interface {
|
type importExportBackend interface {
|
||||||
LoadImage(ctx context.Context, inTar io.ReadCloser, outStream io.Writer, quiet bool) error
|
LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error
|
||||||
ImportImage(ctx context.Context, ref reference.Named, platform *ocispec.Platform, msg string, layerReader io.Reader, changes []string) (dockerimage.ID, error)
|
ImportImage(src string, repository, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
|
||||||
ExportImage(ctx context.Context, names []string, outStream io.Writer) error
|
ExportImage(names []string, outStream io.Writer) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type registryBackend interface {
|
type registryBackend interface {
|
||||||
PullImage(ctx context.Context, ref reference.Named, platform *ocispec.Platform, metaHeaders map[string][]string, authConfig *registry.AuthConfig, outStream io.Writer) error
|
PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
|
||||||
PushImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *registry.AuthConfig, outStream io.Writer) error
|
PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
|
||||||
}
|
SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, limit int, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
|
||||||
|
|
||||||
type Searcher interface {
|
|
||||||
Search(ctx context.Context, searchFilters filters.Args, term string, limit int, authConfig *registry.AuthConfig, headers map[string][]string) ([]registry.SearchResult, error)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +1,50 @@
|
||||||
package image // import "github.com/docker/docker/api/server/router/image"
|
package image
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/api/server/router"
|
"github.com/docker/docker/api/server/router"
|
||||||
"github.com/docker/docker/image"
|
|
||||||
"github.com/docker/docker/layer"
|
|
||||||
"github.com/docker/docker/reference"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// imageRouter is a router to talk with the image controller
|
// imageRouter is a router to talk with the image controller
|
||||||
type imageRouter struct {
|
type imageRouter struct {
|
||||||
backend Backend
|
backend Backend
|
||||||
searcher Searcher
|
decoder httputils.ContainerDecoder
|
||||||
referenceBackend reference.Store
|
|
||||||
imageStore image.Store
|
|
||||||
layerStore layer.Store
|
|
||||||
routes []router.Route
|
routes []router.Route
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRouter initializes a new image router
|
// NewRouter initializes a new image router
|
||||||
func NewRouter(backend Backend, searcher Searcher, referenceBackend reference.Store, imageStore image.Store, layerStore layer.Store) router.Router {
|
func NewRouter(backend Backend, decoder httputils.ContainerDecoder) router.Router {
|
||||||
ir := &imageRouter{
|
r := &imageRouter{
|
||||||
backend: backend,
|
backend: backend,
|
||||||
searcher: searcher,
|
decoder: decoder,
|
||||||
referenceBackend: referenceBackend,
|
|
||||||
imageStore: imageStore,
|
|
||||||
layerStore: layerStore,
|
|
||||||
}
|
}
|
||||||
ir.initRoutes()
|
r.initRoutes()
|
||||||
return ir
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routes returns the available routes to the image controller
|
// Routes returns the available routes to the image controller
|
||||||
func (ir *imageRouter) Routes() []router.Route {
|
func (r *imageRouter) Routes() []router.Route {
|
||||||
return ir.routes
|
return r.routes
|
||||||
}
|
}
|
||||||
|
|
||||||
// initRoutes initializes the routes in the image router
|
// initRoutes initializes the routes in the image router
|
||||||
func (ir *imageRouter) initRoutes() {
|
func (r *imageRouter) initRoutes() {
|
||||||
ir.routes = []router.Route{
|
r.routes = []router.Route{
|
||||||
// GET
|
// GET
|
||||||
router.NewGetRoute("/images/json", ir.getImagesJSON),
|
router.NewGetRoute("/images/json", r.getImagesJSON),
|
||||||
router.NewGetRoute("/images/search", ir.getImagesSearch),
|
router.NewGetRoute("/images/search", r.getImagesSearch),
|
||||||
router.NewGetRoute("/images/get", ir.getImagesGet),
|
router.NewGetRoute("/images/get", r.getImagesGet),
|
||||||
router.NewGetRoute("/images/{name:.*}/get", ir.getImagesGet),
|
router.NewGetRoute("/images/{name:.*}/get", r.getImagesGet),
|
||||||
router.NewGetRoute("/images/{name:.*}/history", ir.getImagesHistory),
|
router.NewGetRoute("/images/{name:.*}/history", r.getImagesHistory),
|
||||||
router.NewGetRoute("/images/{name:.*}/json", ir.getImagesByName),
|
router.NewGetRoute("/images/{name:.*}/json", r.getImagesByName),
|
||||||
// POST
|
// POST
|
||||||
router.NewPostRoute("/images/load", ir.postImagesLoad),
|
router.NewPostRoute("/commit", r.postCommit),
|
||||||
router.NewPostRoute("/images/create", ir.postImagesCreate),
|
router.NewPostRoute("/images/load", r.postImagesLoad),
|
||||||
router.NewPostRoute("/images/{name:.*}/push", ir.postImagesPush),
|
router.Cancellable(router.NewPostRoute("/images/create", r.postImagesCreate)),
|
||||||
router.NewPostRoute("/images/{name:.*}/tag", ir.postImagesTag),
|
router.Cancellable(router.NewPostRoute("/images/{name:.*}/push", r.postImagesPush)),
|
||||||
router.NewPostRoute("/images/prune", ir.postImagesPrune),
|
router.NewPostRoute("/images/{name:.*}/tag", r.postImagesTag),
|
||||||
|
router.NewPostRoute("/images/prune", r.postImagesPrune),
|
||||||
// DELETE
|
// DELETE
|
||||||
router.NewDeleteRoute("/images/{name:.*}", ir.deleteImages),
|
router.NewDeleteRoute("/images/{name:.*}", r.deleteImages),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,68 +1,93 @@
|
||||||
package image // import "github.com/docker/docker/api/server/router/image"
|
package image
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/platforms"
|
|
||||||
"github.com/distribution/reference"
|
|
||||||
"github.com/docker/docker/api"
|
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
imagetypes "github.com/docker/docker/api/types/image"
|
|
||||||
"github.com/docker/docker/api/types/registry"
|
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/docker/builder/remotecontext"
|
|
||||||
"github.com/docker/docker/dockerversion"
|
|
||||||
"github.com/docker/docker/errdefs"
|
|
||||||
"github.com/docker/docker/image"
|
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/docker/pkg/progress"
|
|
||||||
"github.com/docker/docker/pkg/streamformatter"
|
"github.com/docker/docker/pkg/streamformatter"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/docker/docker/registry"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
"golang.org/x/net/context"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := httputils.CheckForJSON(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cname := r.Form.Get("container")
|
||||||
|
|
||||||
|
pause := httputils.BoolValue(r, "pause")
|
||||||
|
version := httputils.VersionFromContext(ctx)
|
||||||
|
if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") {
|
||||||
|
pause = true
|
||||||
|
}
|
||||||
|
|
||||||
|
c, _, _, err := s.decoder.DecodeConfig(r.Body)
|
||||||
|
if err != nil && err != io.EOF { //Do not fail if body is empty.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c == nil {
|
||||||
|
c = &container.Config{}
|
||||||
|
}
|
||||||
|
|
||||||
|
commitCfg := &backend.ContainerCommitConfig{
|
||||||
|
ContainerCommitConfig: types.ContainerCommitConfig{
|
||||||
|
Pause: pause,
|
||||||
|
Repo: r.Form.Get("repo"),
|
||||||
|
Tag: r.Form.Get("tag"),
|
||||||
|
Author: r.Form.Get("author"),
|
||||||
|
Comment: r.Form.Get("comment"),
|
||||||
|
Config: c,
|
||||||
|
MergeConfigs: true,
|
||||||
|
},
|
||||||
|
Changes: r.Form["changes"],
|
||||||
|
}
|
||||||
|
|
||||||
|
imgID, err := s.backend.Commit(cname, commitCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{
|
||||||
|
ID: string(imgID),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Creates an image from Pull or from Import
|
// Creates an image from Pull or from Import
|
||||||
func (ir *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
img = r.Form.Get("fromImage")
|
image = r.Form.Get("fromImage")
|
||||||
repo = r.Form.Get("repo")
|
repo = r.Form.Get("repo")
|
||||||
tag = r.Form.Get("tag")
|
tag = r.Form.Get("tag")
|
||||||
comment = r.Form.Get("message")
|
message = r.Form.Get("message")
|
||||||
progressErr error
|
err error
|
||||||
output = ioutils.NewWriteFlusher(w)
|
output = ioutils.NewWriteFlusher(w)
|
||||||
platform *ocispec.Platform
|
|
||||||
)
|
)
|
||||||
defer output.Close()
|
defer output.Close()
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
if image != "" { //pull
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
|
||||||
if p := r.FormValue("platform"); p != "" {
|
|
||||||
sp, err := platforms.Parse(p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
platform = &sp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if img != "" { // pull
|
|
||||||
metaHeaders := map[string][]string{}
|
metaHeaders := map[string][]string{}
|
||||||
for k, v := range r.Header {
|
for k, v := range r.Header {
|
||||||
if strings.HasPrefix(k, "X-Meta-") {
|
if strings.HasPrefix(k, "X-Meta-") {
|
||||||
|
@ -70,92 +95,37 @@ func (ir *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case: "pull -a" may send an image name with a
|
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||||
// trailing :. This is ugly, but let's not break API
|
authConfig := &types.AuthConfig{}
|
||||||
// compatibility.
|
if authEncoded != "" {
|
||||||
imgName := strings.TrimSuffix(img, ":")
|
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||||
|
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
|
||||||
ref, err := reference.ParseNormalizedNamed(imgName)
|
// for a pull it is not an error if no auth was given
|
||||||
if err != nil {
|
// to increase compatibility with the existing api it is defaulting to be empty
|
||||||
return errdefs.InvalidParameter(err)
|
authConfig = &types.AuthConfig{}
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(thaJeztah) this could use a WithTagOrDigest() utility
|
|
||||||
if tag != "" {
|
|
||||||
// The "tag" could actually be a digest.
|
|
||||||
var dgst digest.Digest
|
|
||||||
dgst, err = digest.Parse(tag)
|
|
||||||
if err == nil {
|
|
||||||
ref, err = reference.WithDigest(reference.TrimNamed(ref), dgst)
|
|
||||||
} else {
|
|
||||||
ref, err = reference.WithTag(ref, tag)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateRepoName(ref); err != nil {
|
err = s.backend.PullImage(ctx, image, tag, metaHeaders, authConfig, output)
|
||||||
return errdefs.Forbidden(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// For a pull it is not an error if no auth was given. Ignore invalid
|
|
||||||
// AuthConfig to increase compatibility with the existing API.
|
|
||||||
authConfig, _ := registry.DecodeAuthConfig(r.Header.Get(registry.AuthHeader))
|
|
||||||
progressErr = ir.backend.PullImage(ctx, ref, platform, metaHeaders, authConfig, output)
|
|
||||||
} else { //import
|
} else { //import
|
||||||
src := r.Form.Get("fromSrc")
|
src := r.Form.Get("fromSrc")
|
||||||
|
// 'err' MUST NOT be defined within this block, we need any error
|
||||||
tagRef, err := httputils.RepoTagReference(repo, tag)
|
// generated from the download to be available to the output
|
||||||
if err != nil {
|
// stream processing below
|
||||||
return errdefs.InvalidParameter(err)
|
err = s.backend.ImportImage(src, repo, tag, message, r.Body, output, r.Form["changes"])
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(comment) == 0 {
|
|
||||||
comment = "Imported from " + src
|
|
||||||
}
|
|
||||||
|
|
||||||
var layerReader io.ReadCloser
|
|
||||||
defer r.Body.Close()
|
|
||||||
if src == "-" {
|
|
||||||
layerReader = r.Body
|
|
||||||
} else {
|
|
||||||
if len(strings.Split(src, "://")) == 1 {
|
|
||||||
src = "http://" + src
|
|
||||||
}
|
|
||||||
u, err := url.Parse(src)
|
|
||||||
if err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := remotecontext.GetWithStatusError(u.String())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if !output.Flushed() {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
output.Write(streamformatter.FormatStatus("", "Downloading from %s", u))
|
sf := streamformatter.NewJSONStreamFormatter()
|
||||||
progressOutput := streamformatter.NewJSONProgressOutput(output, true)
|
output.Write(sf.FormatError(err))
|
||||||
layerReader = progress.NewProgressReader(resp.Body, progressOutput, resp.ContentLength, "", "Importing")
|
|
||||||
defer layerReader.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
var id image.ID
|
|
||||||
id, progressErr = ir.backend.ImportImage(ctx, tagRef, platform, comment, layerReader, r.Form["changes"])
|
|
||||||
|
|
||||||
if progressErr == nil {
|
|
||||||
output.Write(streamformatter.FormatStatus("", id.String()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if progressErr != nil {
|
|
||||||
if !output.Flushed() {
|
|
||||||
return progressErr
|
|
||||||
}
|
|
||||||
_, _ = output.Write(streamformatter.FormatError(progressErr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ir *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
metaHeaders := map[string][]string{}
|
metaHeaders := map[string][]string{}
|
||||||
for k, v := range r.Header {
|
for k, v := range r.Header {
|
||||||
if strings.HasPrefix(k, "X-Meta-") {
|
if strings.HasPrefix(k, "X-Meta-") {
|
||||||
|
@ -165,56 +135,42 @@ func (ir *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
authConfig := &types.AuthConfig{}
|
||||||
|
|
||||||
var authConfig *registry.AuthConfig
|
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||||
if authEncoded := r.Header.Get(registry.AuthHeader); authEncoded != "" {
|
if authEncoded != "" {
|
||||||
// the new format is to handle the authConfig as a header. Ignore invalid
|
// the new format is to handle the authConfig as a header
|
||||||
// AuthConfig to increase compatibility with the existing API.
|
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||||
authConfig, _ = registry.DecodeAuthConfig(authEncoded)
|
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
|
||||||
|
// to increase compatibility to existing api it is defaulting to be empty
|
||||||
|
authConfig = &types.AuthConfig{}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// the old format is supported for compatibility if there was no authConfig header
|
// the old format is supported for compatibility if there was no authConfig header
|
||||||
var err error
|
if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
|
||||||
authConfig, err = registry.DecodeAuthConfigBody(r.Body)
|
return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "bad parameters and missing X-Registry-Auth")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
image := vars["name"]
|
||||||
|
tag := r.Form.Get("tag")
|
||||||
|
|
||||||
output := ioutils.NewWriteFlusher(w)
|
output := ioutils.NewWriteFlusher(w)
|
||||||
defer output.Close()
|
defer output.Close()
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
img := vars["name"]
|
if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, output); err != nil {
|
||||||
tag := r.Form.Get("tag")
|
|
||||||
|
|
||||||
var ref reference.Named
|
|
||||||
|
|
||||||
// Tag is empty only in case PushOptions.All is true.
|
|
||||||
if tag != "" {
|
|
||||||
r, err := httputils.RepoTagReference(img, tag)
|
|
||||||
if err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
ref = r
|
|
||||||
} else {
|
|
||||||
r, err := reference.ParseNormalizedNamed(img)
|
|
||||||
if err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
ref = r
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ir.backend.PushImage(ctx, ref, metaHeaders, authConfig, output); err != nil {
|
|
||||||
if !output.Flushed() {
|
if !output.Flushed() {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, _ = output.Write(streamformatter.FormatError(err))
|
sf := streamformatter.NewJSONStreamFormatter()
|
||||||
|
output.Write(sf.FormatError(err))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ir *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -230,16 +186,17 @@ func (ir *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter,
|
||||||
names = r.Form["names"]
|
names = r.Form["names"]
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ir.backend.ExportImage(ctx, names, output); err != nil {
|
if err := s.backend.ExportImage(names, output); err != nil {
|
||||||
if !output.Flushed() {
|
if !output.Flushed() {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, _ = output.Write(streamformatter.FormatError(err))
|
sf := streamformatter.NewJSONStreamFormatter()
|
||||||
|
output.Write(sf.FormatError(err))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ir *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -249,21 +206,13 @@ func (ir *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter
|
||||||
|
|
||||||
output := ioutils.NewWriteFlusher(w)
|
output := ioutils.NewWriteFlusher(w)
|
||||||
defer output.Close()
|
defer output.Close()
|
||||||
if err := ir.backend.LoadImage(ctx, r.Body, output, quiet); err != nil {
|
if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
|
||||||
_, _ = output.Write(streamformatter.FormatError(err))
|
output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type missingImageError struct{}
|
func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
|
||||||
func (missingImageError) Error() string {
|
|
||||||
return "image name cannot be blank"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (missingImageError) InvalidParameter() {}
|
|
||||||
|
|
||||||
func (ir *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -271,13 +220,13 @@ func (ir *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter,
|
||||||
name := vars["name"]
|
name := vars["name"]
|
||||||
|
|
||||||
if strings.TrimSpace(name) == "" {
|
if strings.TrimSpace(name) == "" {
|
||||||
return missingImageError{}
|
return fmt.Errorf("image name cannot be blank")
|
||||||
}
|
}
|
||||||
|
|
||||||
force := httputils.BoolValue(r, "force")
|
force := httputils.BoolValue(r, "force")
|
||||||
prune := !httputils.BoolValue(r, "noprune")
|
prune := !httputils.BoolValue(r, "noprune")
|
||||||
|
|
||||||
list, err := ir.backend.ImageDelete(ctx, name, force, prune)
|
list, err := s.backend.ImageDelete(name, force, prune)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -285,162 +234,42 @@ func (ir *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter,
|
||||||
return httputils.WriteJSON(w, http.StatusOK, list)
|
return httputils.WriteJSON(w, http.StatusOK, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ir *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
img, err := ir.backend.GetImage(ctx, vars["name"], backend.GetImageOpts{Details: true})
|
imageInspect, err := s.backend.LookupImage(vars["name"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
imageInspect, err := ir.toImageInspect(img)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
|
||||||
if versions.LessThan(version, "1.44") {
|
|
||||||
imageInspect.VirtualSize = imageInspect.Size //nolint:staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.44.
|
|
||||||
|
|
||||||
if imageInspect.Created == "" {
|
|
||||||
// backwards compatibility for Created not existing returning "0001-01-01T00:00:00Z"
|
|
||||||
// https://github.com/moby/moby/issues/47368
|
|
||||||
imageInspect.Created = time.Time{}.Format(time.RFC3339Nano)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.45") {
|
|
||||||
imageInspect.Container = "" //nolint:staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.45.
|
|
||||||
imageInspect.ContainerConfig = nil //nolint:staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.45.
|
|
||||||
}
|
|
||||||
return httputils.WriteJSON(w, http.StatusOK, imageInspect)
|
return httputils.WriteJSON(w, http.StatusOK, imageInspect)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ir *imageRouter) toImageInspect(img *image.Image) (*types.ImageInspect, error) {
|
func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
var repoTags, repoDigests []string
|
|
||||||
for _, ref := range img.Details.References {
|
|
||||||
switch ref.(type) {
|
|
||||||
case reference.NamedTagged:
|
|
||||||
repoTags = append(repoTags, reference.FamiliarString(ref))
|
|
||||||
case reference.Canonical:
|
|
||||||
repoDigests = append(repoDigests, reference.FamiliarString(ref))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
comment := img.Comment
|
|
||||||
if len(comment) == 0 && len(img.History) > 0 {
|
|
||||||
comment = img.History[len(img.History)-1].Comment
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we output empty arrays instead of nil.
|
|
||||||
if repoTags == nil {
|
|
||||||
repoTags = []string{}
|
|
||||||
}
|
|
||||||
if repoDigests == nil {
|
|
||||||
repoDigests = []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var created string
|
|
||||||
if img.Created != nil {
|
|
||||||
created = img.Created.Format(time.RFC3339Nano)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.ImageInspect{
|
|
||||||
ID: img.ID().String(),
|
|
||||||
RepoTags: repoTags,
|
|
||||||
RepoDigests: repoDigests,
|
|
||||||
Parent: img.Parent.String(),
|
|
||||||
Comment: comment,
|
|
||||||
Created: created,
|
|
||||||
Container: img.Container, //nolint:staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.45.
|
|
||||||
ContainerConfig: &img.ContainerConfig, //nolint:staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.45.
|
|
||||||
DockerVersion: img.DockerVersion,
|
|
||||||
Author: img.Author,
|
|
||||||
Config: img.Config,
|
|
||||||
Architecture: img.Architecture,
|
|
||||||
Variant: img.Variant,
|
|
||||||
Os: img.OperatingSystem(),
|
|
||||||
OsVersion: img.OSVersion,
|
|
||||||
Size: img.Details.Size,
|
|
||||||
GraphDriver: types.GraphDriverData{
|
|
||||||
Name: img.Details.Driver,
|
|
||||||
Data: img.Details.Metadata,
|
|
||||||
},
|
|
||||||
RootFS: rootFSToAPIType(img.RootFS),
|
|
||||||
Metadata: imagetypes.Metadata{
|
|
||||||
LastTagTime: img.Details.LastUpdated,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
|
|
||||||
var layers []string
|
|
||||||
for _, l := range rootfs.DiffIDs {
|
|
||||||
layers = append(layers, l.String())
|
|
||||||
}
|
|
||||||
return types.RootFS{
|
|
||||||
Type: rootfs.Type,
|
|
||||||
Layers: layers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ir *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
imageFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
imageFilters, err := filters.FromParam(r.Form.Get("filters"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
version := httputils.VersionFromContext(ctx)
|
||||||
if versions.LessThan(version, "1.41") {
|
|
||||||
// NOTE: filter is a shell glob string applied to repository names.
|
|
||||||
filterParam := r.Form.Get("filter")
|
filterParam := r.Form.Get("filter")
|
||||||
if filterParam != "" {
|
if versions.LessThan(version, "1.28") && filterParam != "" {
|
||||||
imageFilters.Add("reference", filterParam)
|
imageFilters.Add("reference", filterParam)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var sharedSize bool
|
images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false)
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
|
||||||
// NOTE: Support for the "shared-size" parameter was added in API 1.42.
|
|
||||||
sharedSize = httputils.BoolValue(r, "shared-size")
|
|
||||||
}
|
|
||||||
|
|
||||||
images, err := ir.backend.Images(ctx, imagetypes.ListOptions{
|
|
||||||
All: httputils.BoolValue(r, "all"),
|
|
||||||
Filters: imageFilters,
|
|
||||||
SharedSize: sharedSize,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
useNone := versions.LessThan(version, "1.43")
|
|
||||||
withVirtualSize := versions.LessThan(version, "1.44")
|
|
||||||
for _, img := range images {
|
|
||||||
if useNone {
|
|
||||||
if len(img.RepoTags) == 0 && len(img.RepoDigests) == 0 {
|
|
||||||
img.RepoTags = append(img.RepoTags, "<none>:<none>")
|
|
||||||
img.RepoDigests = append(img.RepoDigests, "<none>@<none>")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if img.RepoTags == nil {
|
|
||||||
img.RepoTags = []string{}
|
|
||||||
}
|
|
||||||
if img.RepoDigests == nil {
|
|
||||||
img.RepoDigests = []string{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if withVirtualSize {
|
|
||||||
img.VirtualSize = img.Size //nolint:staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.44.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return httputils.WriteJSON(w, http.StatusOK, images)
|
return httputils.WriteJSON(w, http.StatusOK, images)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ir *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
history, err := ir.backend.ImageHistory(ctx, vars["name"])
|
name := vars["name"]
|
||||||
|
history, err := s.backend.ImageHistory(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -448,92 +277,68 @@ func (ir *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWrit
|
||||||
return httputils.WriteJSON(w, http.StatusOK, history)
|
return httputils.WriteJSON(w, http.StatusOK, history)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ir *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil {
|
||||||
ref, err := httputils.RepoTagReference(r.Form.Get("repo"), r.Form.Get("tag"))
|
|
||||||
if ref == nil || err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
refName := reference.FamiliarName(ref)
|
|
||||||
if refName == string(digest.Canonical) {
|
|
||||||
return errdefs.InvalidParameter(errors.New("refusing to create an ambiguous tag using digest algorithm as name"))
|
|
||||||
}
|
|
||||||
|
|
||||||
img, err := ir.backend.GetImage(ctx, vars["name"], backend.GetImageOpts{})
|
|
||||||
if err != nil {
|
|
||||||
return errdefs.NotFound(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ir.backend.TagImage(ctx, img.ID(), ref); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ir *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
var (
|
||||||
|
config *types.AuthConfig
|
||||||
|
authEncoded = r.Header.Get("X-Registry-Auth")
|
||||||
|
headers = map[string][]string{}
|
||||||
|
)
|
||||||
|
|
||||||
var limit int
|
if authEncoded != "" {
|
||||||
if r.Form.Get("limit") != "" {
|
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||||
var err error
|
if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
|
||||||
limit, err = strconv.Atoi(r.Form.Get("limit"))
|
// for a search it is not an error if no auth was given
|
||||||
if err != nil || limit < 0 {
|
// to increase compatibility with the existing api it is defaulting to be empty
|
||||||
return errdefs.InvalidParameter(errors.Wrap(err, "invalid limit specified"))
|
config = &types.AuthConfig{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
searchFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// For a search it is not an error if no auth was given. Ignore invalid
|
|
||||||
// AuthConfig to increase compatibility with the existing API.
|
|
||||||
authConfig, _ := registry.DecodeAuthConfig(r.Header.Get(registry.AuthHeader))
|
|
||||||
|
|
||||||
headers := http.Header{}
|
|
||||||
for k, v := range r.Header {
|
for k, v := range r.Header {
|
||||||
k = http.CanonicalHeaderKey(k)
|
|
||||||
if strings.HasPrefix(k, "X-Meta-") {
|
if strings.HasPrefix(k, "X-Meta-") {
|
||||||
headers[k] = v
|
headers[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
headers.Set("User-Agent", dockerversion.DockerUserAgent(ctx))
|
limit := registry.DefaultSearchLimit
|
||||||
res, err := ir.searcher.Search(ctx, searchFilters, r.Form.Get("term"), limit, authConfig, headers)
|
if r.Form.Get("limit") != "" {
|
||||||
|
limitValue, err := strconv.Atoi(r.Form.Get("limit"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return httputils.WriteJSON(w, http.StatusOK, res)
|
limit = limitValue
|
||||||
|
}
|
||||||
|
query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return httputils.WriteJSON(w, http.StatusOK, query.Results)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ir *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pruneReport, err := ir.backend.ImagesPrune(ctx, pruneFilters)
|
pruneReport, err := s.backend.ImagesPrune(pruneFilters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return httputils.WriteJSON(w, http.StatusOK, pruneReport)
|
return httputils.WriteJSON(w, http.StatusOK, pruneReport)
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateRepoName validates the name of a repository.
|
|
||||||
func validateRepoName(name reference.Named) error {
|
|
||||||
familiarName := reference.FamiliarName(name)
|
|
||||||
if familiarName == api.NoBaseImageSpecifier {
|
|
||||||
return fmt.Errorf("'%s' is a reserved name", familiarName)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue