mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-19 20:26:54 +00:00
Compare commits
1028 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6743a6e665 | |||
| 89f99343ff | |||
| 7ce97dbe30 | |||
| c957d9c768 | |||
| 8562c423b8 | |||
| e4f721e73e | |||
| 835acd96ec | |||
| 73fe42e3db | |||
| f9a3558a9d | |||
| bc915d0856 | |||
| 0b18b1b309 | |||
| 6b14d9a645 | |||
| 5b46d8f0fa | |||
| 7c667f9d6e | |||
| 5446aac0e5 | |||
| 4a0606423b | |||
| 409212be24 | |||
| 0009852cb0 | |||
| 38ecc7fc32 | |||
| 69bf17ffbd | |||
| 90477e8e94 | |||
| 1fa560e6eb | |||
| b019229996 | |||
| c8c1c472b2 | |||
| 057f53f503 | |||
| 4a864a7552 | |||
| 6620102926 | |||
| e7cf75c103 | |||
| 07d2eae6d5 | |||
| 2d605ce53b | |||
| 9b072fb722 | |||
| f324f4851f | |||
| c87cbd3862 | |||
| e38a9ecef6 | |||
| 6ffbea3c94 | |||
| f0a5d03400 | |||
| 2d9ea188d4 | |||
| 3409de971c | |||
| 9d0eef55ea | |||
| 6f5eb78519 | |||
| 8258996cf2 | |||
| 288275ab60 | |||
| c7b3e7e770 | |||
| 48badd2a2e | |||
| d9065fcdb5 | |||
| 137c12f19a | |||
| ee4de4d163 | |||
| 9e4addde76 | |||
| 2bd30cd6cb | |||
| 56fdb05258 | |||
| 39be36658d | |||
| fb4cce5e53 | |||
| 414dfbf681 | |||
| 7beb026f0e | |||
| c6dd451073 | |||
| 04329e0b3e | |||
| 6cc38775bb | |||
| cfddca2bf9 | |||
| 3715e60c71 | |||
| 2434bd8e63 | |||
| 12a71c8954 | |||
| 8c5d511aa6 | |||
| 00ab6d9d47 | |||
| db4f20404d | |||
| dae50f9b5e | |||
| 9ea7267285 | |||
| 1ed3cb2b6c | |||
| 4c54751cab | |||
| 01059cba78 | |||
| ed14fe2afa | |||
| 6c56f917d3 | |||
| 7f8954cb79 | |||
| 3111aa7293 | |||
| a5eec723f8 | |||
| 7c44209037 | |||
| 17999c70c3 | |||
| 45fdce8a0d | |||
| cac8f4f0d0 | |||
| 70f0d6d84c | |||
| 61de962979 | |||
| 82415adc81 | |||
| d7eb85bb1f | |||
| 2093616d7f | |||
| 7f58fb5f01 | |||
| 4fccf8adfa | |||
| be8e126a0f | |||
| 49c1b51ae2 | |||
| 26fb160610 | |||
| 4bb2449188 | |||
| 51e721ab07 | |||
| 3b2c9fa049 | |||
| 567687fd98 | |||
| 5eb4a6b933 | |||
| e98ec2b567 | |||
| dd49002055 | |||
| b5cc077864 | |||
| ab868ad79c | |||
| 6ae2cb7ad1 | |||
| d080f6524b | |||
| f814ae437f | |||
| 84bc41a3fb | |||
| 0f878cfa00 | |||
| 3037e4f7c4 | |||
| 3c2b128582 | |||
| 68c7d24a12 | |||
| 166412e529 | |||
| c844effeda | |||
| 7271dca8cc | |||
| 95495d20be | |||
| 3bd8016259 | |||
| 84614fdae0 | |||
| 38ac1f908e | |||
| b0fd4f882f | |||
| 96974170f8 | |||
| cd18e7bb73 | |||
| 4ae81e145e | |||
| 3272b3d120 | |||
| 754c10430b | |||
| e9eaef6f1e | |||
| 80c32162b5 | |||
| 81d6f350f7 | |||
| 4870fb36d4 | |||
| 06699f73fb | |||
| 754d81bd69 | |||
| 998699316c | |||
| d6a94fbf22 | |||
| 9703c3a90e | |||
| 50be74ba14 | |||
| 10005433eb | |||
| 16220e0681 | |||
| e137f2d081 | |||
| ad91d2e97a | |||
| bace51571f | |||
| 55468e2ac4 | |||
| fd1b21b646 | |||
| 2920550211 | |||
| 626c05036c | |||
| 3d510f15da | |||
| 24c4594cc3 | |||
| 8fe675d799 | |||
| 42d8fe6105 | |||
| df6d928370 | |||
| b1c1f42bcc | |||
| 0b2cff35ba | |||
| 0ef740a5bf | |||
| f9df813a99 | |||
| 5e540bdc13 | |||
| 672bae2dc0 | |||
| 111d2f3487 | |||
| b1cb1b1df4 | |||
| 279bbbedb2 | |||
| 317a5462e4 | |||
| ea4a06740b | |||
| 50bcf17c39 | |||
| e6e210164e | |||
| 2a66beef7f | |||
| 95e3a4ca6d | |||
| 433956cc47 | |||
| 6caaa8a635 | |||
| 243f98ff6d | |||
| f753f6d597 | |||
| b3f999e863 | |||
| e48a222828 | |||
| abd72d4008 | |||
| 5a5f9e93e9 | |||
| 6f22cc9b5c | |||
| fdc73cc3fc | |||
| 9b9d70ad27 | |||
| 26c9b58504 | |||
| ba019dc0d0 | |||
| 0ca2c3abf5 | |||
| 9dd6c8822d | |||
| 80513d85cf | |||
| 572f008e89 | |||
| 04b0fb6314 | |||
| 0634c653d5 | |||
| 0bd016b1c3 | |||
| 38da43184d | |||
| 5572148477 | |||
| 6eb03c53db | |||
| b582977bc8 | |||
| 4579d5ac78 | |||
| 61de442c49 | |||
| 9f8891a855 | |||
| 44ea7e91fb | |||
| 5caccd27cd | |||
| 5e8038d0b0 | |||
| 459c2c66c3 | |||
| c1af0ac082 | |||
| 7a74422724 | |||
| 70a814c3f3 | |||
| ea65c38ac0 | |||
| 31a52a31c6 | |||
| 47cdae243a | |||
| fa13f7cde8 | |||
| f11ba3135b | |||
| b3b7eb2723 | |||
| 5dadfa8b69 | |||
| 17d6c6c7e5 | |||
| 7ef08f39ed | |||
| 2cec06fbcd | |||
| d30990d7b1 | |||
| b857dadb33 | |||
| 688dd8477e | |||
| a9378a0ab5 | |||
| b6621a1b6c | |||
| 8720f71b52 | |||
| 3bc375c6e4 | |||
| a0f6ff7d58 | |||
| 90be489f40 | |||
| 9a71dd2b90 | |||
| bdba406492 | |||
| 459451de8b | |||
| f950de5754 | |||
| bd06432ba3 | |||
| 5f156d8c25 | |||
| 7dbb5fafcd | |||
| e3772538c0 | |||
| 1c0c68b18f | |||
| e134ccada7 | |||
| 759ed647fb | |||
| 78698855ce | |||
| eecf6cd48c | |||
| 7296c845b7 | |||
| 6f8c4480e4 | |||
| ee2c476f22 | |||
| cefb72080d | |||
| 936b2c6afe | |||
| 9a9724ad56 | |||
| 349aef06d5 | |||
| 903cd2b9e3 | |||
| 562cc6254f | |||
| dea78fc2ce | |||
| 82e2dec06d | |||
| 25f4f5588e | |||
| ebe27cb34c | |||
| 0ce37336b3 | |||
| 831747d59e | |||
| 1269dd35dc | |||
| b779bc3102 | |||
| 137df3048c | |||
| 6b21e98432 | |||
| fd8b25c802 | |||
| 1544c5edb6 | |||
| 654b555d09 | |||
| 386aefb9fc | |||
| c0a48a676d | |||
| e14eaba784 | |||
| 37f58d266f | |||
| 88cacbf529 | |||
| 32053b965b | |||
| 02683dfa09 | |||
| e4147bcad4 | |||
| 2b174d3d9d | |||
| 1ee53d4fb7 | |||
| 62ce5b6929 | |||
| 354a2e105d | |||
| 429423624c | |||
| 888d1d0e20 | |||
| 354147df01 | |||
| c99ed5ae5d | |||
| 9bac520c12 | |||
| cb00396f61 | |||
| c340ca4f5d | |||
| 2167f40a76 | |||
| 96e37f671a | |||
| 7ead74d903 | |||
| 819166c16c | |||
| eeeae2c235 | |||
| f1988f046f | |||
| 9572edc34a | |||
| 3faba5fbe2 | |||
| efabc8ee1a | |||
| 76a5fdc3d4 | |||
| 5fd15da7da | |||
| ce5bbed858 | |||
| 5929502b4e | |||
| 90801ab939 | |||
| fefc9a3a6a | |||
| b8bed8832b | |||
| c5733e6a20 | |||
| e5e6eaacee | |||
| 9415db7e95 | |||
| 9bd8a9b66b | |||
| ecff4badcd | |||
| 008a8ffdd2 | |||
| 19f7bfcda9 | |||
| 9b8cfb6c79 | |||
| 9d22c7a2d5 | |||
| 616950b030 | |||
| a456f20b44 | |||
| e6be51a0f9 | |||
| 1425b185db | |||
| c48ac77840 | |||
| ee1304a35e | |||
| 0c08913d52 | |||
| fcf9daad91 | |||
| eb7b2ed6bb | |||
| 85bef48bf5 | |||
| 10c97a310a | |||
| f11b7acc73 | |||
| 31cbf76d0c | |||
| 5ceff3f191 | |||
| 2d1158790d | |||
| c8a46cb0b2 | |||
| 011b4ca256 | |||
| faf9e34067 | |||
| 98f60aef13 | |||
| 76a559ccb2 | |||
| d760947b2e | |||
| 9ed5cf08a3 | |||
| 15c24db9a5 | |||
| ec43113662 | |||
| ba9496ae48 | |||
| 3c61ede074 | |||
| 272a3eef94 | |||
| 44465098cb | |||
| 1d0ee18d6b | |||
| d959739125 | |||
| 90ebc3b455 | |||
| 23d3e4bb03 | |||
| 6b8129d1fe | |||
| 211449a1ca | |||
| a6ed990593 | |||
| 8acd006fd9 | |||
| 997ba4b0da | |||
| 6bf2440650 | |||
| 41ca7c0f13 | |||
| 35cbcc2fc1 | |||
| 6dd84ba2ab | |||
| 286afb9a77 | |||
| 2a84157352 | |||
| f848f34da2 | |||
| 7f46dc03d5 | |||
| 457aeaa2e1 | |||
| d88fe447df | |||
| ca79576585 | |||
| b6f1b4ad35 | |||
| 87959dbfac | |||
| 7ec7173b76 | |||
| 09cdd0524e | |||
| 70c4b6e619 | |||
| 59e49e1db0 | |||
| 43763805fb | |||
| b8c319996f | |||
| ab9e71b616 | |||
| 898114a6d0 | |||
| 78676f19c1 | |||
| 1988064ef2 | |||
| fbc0ea32e8 | |||
| 5759b95e7e | |||
| cbb69531a8 | |||
| a41f431d11 | |||
| 164208fde5 | |||
| 3dc30d47e1 | |||
| 5b870d7fa6 | |||
| d45fcc6c80 | |||
| 2a468ed7e4 | |||
| aacb47d628 | |||
| 5fccbd1243 | |||
| 9f1e324b53 | |||
| d997351e97 | |||
| b143c05d64 | |||
| 5dd28c1f9d | |||
| 7e3088c43b | |||
| 85c2c77188 | |||
| cc2aab7816 | |||
| 215a1136f7 | |||
| 10305dc5e8 | |||
| 799d9605d6 | |||
| 8aa520b8c0 | |||
| bba2551847 | |||
| 7ae6a505f2 | |||
| 8eeafa058c | |||
| 135f5324b1 | |||
| 9f1e8eea8f | |||
| d7ac3fa863 | |||
| 3ce11c1587 | |||
| 6947fbc2b0 | |||
| 9f8881b810 | |||
| df11b10680 | |||
| e9d8e38c47 | |||
| 8b8413266c | |||
| 2d6952e8a5 | |||
| 30cde97e5c | |||
| 18381faee6 | |||
| ba29d31c9c | |||
| 770daa15f3 | |||
| 1ed15550f5 | |||
| 66112a7ae5 | |||
| f1da5edc8b | |||
| 5ab6883b92 | |||
| fd2e945d15 | |||
| 1c916dbd83 | |||
| 29ced5d0c4 | |||
| cde607108b | |||
| 3f61002b05 | |||
| 960791ba60 | |||
| 5c88a6fdca | |||
| b7a6d14bdc | |||
| aef15ffc5d | |||
| 436fca704f | |||
| 9e1a41aae5 | |||
| df7ab6f3db | |||
| 5aeb48af77 | |||
| df46bfdd4d | |||
| 2e7b088164 | |||
| 2f2779b6a5 | |||
| 3977f30869 | |||
| bba762b192 | |||
| 6cd2d89469 | |||
| e1f3a5ad0a | |||
| f26045a534 | |||
| e904cbec03 | |||
| 2fcae333e8 | |||
| 0a925d07a8 | |||
| dfbb5520e3 | |||
| a6e0a6852d | |||
| 167783eb13 | |||
| bd9cc788d0 | |||
| 47272f9cc5 | |||
| 6df6e2e405 | |||
| 7948b755c7 | |||
| 837a66e79b | |||
| 5ffcecf130 | |||
| aedd45346a | |||
| 313b37129a | |||
| 3f8d60ac2f | |||
| 424fa59859 | |||
| 3e523ae015 | |||
| f6ed590596 | |||
| 53b1f993ab | |||
| d1fada4bf7 | |||
| f39323c1e3 | |||
| 575a104b3f | |||
| d1585f1d66 | |||
| 424702b91a | |||
| ff155ce6ee | |||
| 28a17c8a03 | |||
| 703e2264ba | |||
| 60d97a4a63 | |||
| 6c4d4c70c6 | |||
| 693ff58cd2 | |||
| 4b933dd2ea | |||
| 4e53e80934 | |||
| fbb268c12a | |||
| 54b50479af | |||
| b93d99b25d | |||
| 46d9fd6a11 | |||
| ec5a73d18e | |||
| 4ce24eb73a | |||
| 7342d59114 | |||
| c6c195a465 | |||
| 9b2426c606 | |||
| 599b76000d | |||
| 355f640d4d | |||
| 53812e51f4 | |||
| 4ac8fd98b3 | |||
| 6b1b1ad31c | |||
| 68a4beeba4 | |||
| f6753cd963 | |||
| ef453f7118 | |||
| 883fcfe425 | |||
| 7e420ad850 | |||
| 79aac6584d | |||
| 4fb88d2e11 | |||
| 90326939c8 | |||
| 03f65b3d0d | |||
| 8a61e2b151 | |||
| 4d89910820 | |||
| 650feb29f1 | |||
| 30cf473f26 | |||
| 72e55cb0ec | |||
| 697cdb8e2f | |||
| 4f3b0d0f4a | |||
| c50e78e799 | |||
| e26ee9a317 | |||
| d50881e446 | |||
| 2c18e1cd05 | |||
| 4393be7100 | |||
| d50d56b352 | |||
| d8ff9ef2b5 | |||
| d3198fa8c4 | |||
| 394ccfac07 | |||
| 8a0efadee1 | |||
| e82358586d | |||
| e4d8a8e1ca | |||
| c7ab9b4a5c | |||
| 5c1ad6f90c | |||
| 644c158837 | |||
| 967c50cfe9 | |||
| b4a89489ae | |||
| dc945f5559 | |||
| 8b09f8a302 | |||
| a7cfb098d4 | |||
| 15a232fd06 | |||
| f5f7fee2ec | |||
| f5e2c6f14b | |||
| d626f77e36 | |||
| 13873e6841 | |||
| 0eade329dc | |||
| 83416f68de | |||
| 230cfc6ed2 | |||
| 9bde19e72a | |||
| 0e3674d7d0 | |||
| a09ba62d18 | |||
| e873686017 | |||
| bca8159174 | |||
| 3e596da9ea | |||
| ceb11d9660 | |||
| beab255cc0 | |||
| 8bd6322760 | |||
| 6e3740450d | |||
| ca8f24679e | |||
| c4cb55b84f | |||
| d550d729e1 | |||
| 1eb1083066 | |||
| 753bf40f15 | |||
| e62745922f | |||
| 4bfbeb80a6 | |||
| e5c160e1d6 | |||
| 0bfad28b86 | |||
| 4293861c1e | |||
| 6f89a8ee1b | |||
| 5cee7e8116 | |||
| b0a65f93a5 | |||
| f83d05c3be | |||
| 51249a3aa0 | |||
| 2231c5161e | |||
| a7c00bc1a2 | |||
| f078f75bf2 | |||
| c465ba7bde | |||
| ba0eba6960 | |||
| 650d5d5d7a | |||
| 5d54f50751 | |||
| 81cc8ebc93 | |||
| fa9e54fbf1 | |||
| f1412f2942 | |||
| ff97bc79cb | |||
| b173314cca | |||
| 8e05a3e30f | |||
| 158c536267 | |||
| 98ec623a8b | |||
| ad3df49958 | |||
| 316eb2979e | |||
| 34a17a5de1 | |||
| f1f6738d97 | |||
| 00843d18d8 | |||
| 030df72279 | |||
| 22d539866c | |||
| cc73fc03e8 | |||
| 764aa1a583 | |||
| 3b28bada0a | |||
| bf892dcfcc | |||
| 46f40c34d2 | |||
| dc2a7ab750 | |||
| 03f969d0b3 | |||
| c536cec5ea | |||
| 5220f3b535 | |||
| 196aa6d62d | |||
| 1a2ccf59ee | |||
| 5146232723 | |||
| 1e2765dabb | |||
| 4f349cdd4b | |||
| 89ec5399af | |||
| 7ac357c795 | |||
| c7e1f91a9e | |||
| 34679e568a | |||
| b8559991dc | |||
| 5110095310 | |||
| 0262d40ba7 | |||
| 1d5f479e01 | |||
| de3944219f | |||
| 0a0e9701f7 | |||
| b1009d1fdd | |||
| 70cae97ee2 | |||
| 510ecbf371 | |||
| c76fef1791 | |||
| 10d30c6457 | |||
| 38e5764132 | |||
| 27fd64c67f | |||
| a8026239fc | |||
| 81c679acae | |||
| 592908c0f5 | |||
| 56b33e9f27 | |||
| 4d212f7853 | |||
| ecf528fcb6 | |||
| b9094633f3 | |||
| 3d6617ffe7 | |||
| c016d2defd | |||
| 38295d4b48 | |||
| 818190b1ff | |||
| 04cd81401a | |||
| e542238f2a | |||
| 6168a992c7 | |||
| 9f530aba7f | |||
| 203776a40c | |||
| 70092b3ee0 | |||
| c2bc637a03 | |||
| 4177b0bae0 | |||
| b2d06b6fba | |||
| 6b341f2b9d | |||
| fe6c0e44be | |||
| aadb3407d3 | |||
| b899977ee2 | |||
| 9ce0a20c01 | |||
| e35a5ae463 | |||
| 287e8167f2 | |||
| 9994a35b5d | |||
| ed248207d7 | |||
| 612a1fb4ab | |||
| 2a237615c0 | |||
| c471b7aba5 | |||
| 33ed76b0f7 | |||
| 4640c4abe8 | |||
| f6106fc3ce | |||
| 73ad1b2674 | |||
| 57c8f4244b | |||
| 931645c460 | |||
| 61c9f4db41 | |||
| 08b3dc8d9f | |||
| f8387f6904 | |||
| b8a8ac9b58 | |||
| ad5355151c | |||
| 5b289cd1aa | |||
| cc375a1e48 | |||
| 3273209a9c | |||
| 263220fa30 | |||
| af9dc3050b | |||
| 6805241fe2 | |||
| d80c4244d3 | |||
| 6a0050d0f0 | |||
| efe4f0dfa9 | |||
| 3529e3dac7 | |||
| e0af23dc18 | |||
| 044c4e00a0 | |||
| 8534090476 | |||
| d93eca2250 | |||
| a8e67849b9 | |||
| da44d0fccb | |||
| 565535073f | |||
| 831b00303f | |||
| b5eea8f33b | |||
| 3d3db0d4af | |||
| aec46ac0b2 | |||
| ac120567e8 | |||
| d9b261221a | |||
| 08a867b82f | |||
| cb2def9f90 | |||
| 6206cbe193 | |||
| b7599d58cb | |||
| a15b676ee1 | |||
| bb161b7789 | |||
| 67e670b79f | |||
| c9a28b4951 | |||
| 7374852be9 | |||
| b985dca578 | |||
| 2c3cd949c8 | |||
| 7b077c16d6 | |||
| f618de1543 | |||
| 35aebdc803 | |||
| f224a9f3d6 | |||
| 58121c59ff | |||
| 63940145a4 | |||
| efd47654dd | |||
| d4d3504122 | |||
| b304920021 | |||
| fe8cfc5b39 | |||
| 23dab01ed2 | |||
| e27c904b99 | |||
| 35e7a7c3e2 | |||
| f7e43d45b3 | |||
| a4ddf0e362 | |||
| 0f49141150 | |||
| c4b9884ab5 | |||
| 2c6fa8375b | |||
| fe6a7c8e57 | |||
| c5e0ea02f7 | |||
| 5237670b5b | |||
| be07c53d2d | |||
| ac1794d3e2 | |||
| bc12094251 | |||
| c26c1eb8ba | |||
| ba332b7d12 | |||
| 16b5b6e49f | |||
| b245bcd458 | |||
| dff25c9e46 | |||
| 408cffac94 | |||
| d7c2fe0f20 | |||
| 85a0f56fe7 | |||
| dab0447ae0 | |||
| ff3adb135d | |||
| 8d3bc4b98c | |||
| 40bd10dc95 | |||
| dfb63b0425 | |||
| 07c45e499d | |||
| 06f6c0c7e5 | |||
| e84a938f2e | |||
| f3bea61c80 | |||
| 00c1874b8e | |||
| de52a3bcaa | |||
| 102724700b | |||
| 3e7f9c636a | |||
| ef7bb7242f | |||
| b898111d3a | |||
| b9aaf8c758 | |||
| 70ac38025c | |||
| 637cf3a4b7 | |||
| 137995cb94 | |||
| 67bca8ab8a | |||
| 1367bb41fc | |||
| cc5cd65e6d | |||
| d136f5e027 | |||
| 70aa63b92a | |||
| 69f3def58d | |||
| 3f23ae663b | |||
| 1a77580030 | |||
| 5f1593c7b8 | |||
| 5d57f56362 | |||
| dce1488ae5 | |||
| f9b5eb0cac | |||
| 8b15b7958a | |||
| 27a8f9937e | |||
| f5557f4f43 | |||
| 05529ab584 | |||
| 084d464081 | |||
| ef24b8e485 | |||
| af59c80b4a | |||
| bf384a3e4f | |||
| 7feb176233 | |||
| d080d262b3 | |||
| 69cde5a302 | |||
| 9af6b57a5d | |||
| f9a2b3650b | |||
| c57faa91e2 | |||
| 7390cc5300 | |||
| 6b44c05205 | |||
| 84c9a6684f | |||
| 6ce76cd9ed | |||
| d594c6fcd8 | |||
| 1f61084d83 | |||
| 8f2dca5386 | |||
| bb2039c925 | |||
| 3e0b0c59e6 | |||
| 6fbacc6edb | |||
| 5ed84009b3 | |||
| 72c04ab87c | |||
| ce99a462f8 | |||
| aab82c5c22 | |||
| e0dad9a153 | |||
| 0659ceb0d0 | |||
| 9e9f3aa36b | |||
| 5276655b69 | |||
| 6a4ac63aaa | |||
| 1032202b88 | |||
| 468bc7d819 | |||
| 0800650665 | |||
| a5e2fa2b2e | |||
| f12e18d7ef | |||
| 5ced3ab323 | |||
| 500a314713 | |||
| b48d744b07 | |||
| ad96fc310b | |||
| 9a9a12b351 | |||
| 8d2739df98 | |||
| 0b089bc7d5 | |||
| e49ab10556 | |||
| e496d40107 | |||
| 3face3c521 | |||
| 084af30f39 | |||
| d2cd142ce1 | |||
| 0cce1fb37f | |||
| 4964ab0821 | |||
| 91274625ba | |||
| 22873eae31 | |||
| ffeec2bdd1 | |||
| e2b2c2ae81 | |||
| 044b56e94f | |||
| b0af811272 | |||
| f8f4deb0b2 | |||
| 5b65284649 | |||
| a21ba12f4e | |||
| 3fea79bfd8 | |||
| c1e2e4d9c6 | |||
| 86a6e4914f | |||
| f50b916ca6 | |||
| e809919e26 | |||
| 5c9e5bfb0f | |||
| d4836cd7ec | |||
| 0a9ec21818 | |||
| 41d9270a40 | |||
| 8505183252 | |||
| 030f61df3d | |||
| 9431acbc0a | |||
| f95333010e | |||
| 5ab9654bc4 | |||
| 2f1a7c903f | |||
| 75c082c4b2 | |||
| 2d730c93b4 | |||
| 9ed4400baf | |||
| 75f6929b44 | |||
| a687448c4d | |||
| 5fa26e3df8 | |||
| b149d9d0d9 | |||
| 4a92b8a0ef | |||
| 06668de15e | |||
| a83a769347 | |||
| 3754ad5737 | |||
| f5c388b35a | |||
| 868f85b271 | |||
| c7a04fda2a | |||
| 8cc8ee9254 | |||
| bfc51cf660 | |||
| bfccd328be | |||
| 18b1381335 | |||
| 9dbc36b441 | |||
| b619220ce1 | |||
| f39987afe8 | |||
| 26ff3bd2e9 | |||
| cc45e027f1 | |||
| b43dc0d93c | |||
| cfeab585c0 | |||
| e34f562a77 | |||
| 4c7cf30260 | |||
| b71cf69380 | |||
| 6adb64e113 | |||
| d94aeb2876 | |||
| 887882c2b6 | |||
| 8724e8953d | |||
| 4a71323ec3 | |||
| 3f5ea1bb80 | |||
| f41f62b6cc | |||
| de50082000 | |||
| 2ae174e491 | |||
| 28ac78c02f | |||
| 75f8bdd970 | |||
| 6a274e48dc | |||
| 28b79e4a84 | |||
| 7841d6ab9a | |||
| 0f4e5f7149 | |||
| 0e0c7e521c | |||
| 5c6446f335 | |||
| 4e25d2982b | |||
| 5832715052 | |||
| 1d6e443119 | |||
| ccbe539e86 | |||
| 0f85fadb4e | |||
| bf3c1e6a3a | |||
| 0bab97fcba | |||
| c09c497c71 | |||
| 0d09439ace | |||
| 2a593bea12 | |||
| 26c03d561a | |||
| 0a2ec227f8 | |||
| e77ea5aa7a | |||
| 1cfae07a6e | |||
| dfcdde4d98 | |||
| 929f2c2f40 | |||
| 2cd058ce4f | |||
| 8a8de53d3d | |||
| 41d85c014d | |||
| c2c9e99d84 | |||
| 2bf73c4b1a | |||
| 4742a3964f | |||
| 90b8cebda6 | |||
| d7661cb48b | |||
| 51cdcf3c9d | |||
| 9d3ad6d892 | |||
| 0c330a7823 | |||
| d3e5179c29 | |||
| 60ed0a7f77 | |||
| 6cf0e27ee0 | |||
| 421786e925 | |||
| 6bca8ec3c9 | |||
| 4553b6af4b | |||
| 6963b9c716 | |||
| 93d134c61f | |||
| 972a94b449 | |||
| c19a00d4cb | |||
| ed1d9d022d | |||
| 6ca22add18 | |||
| bb2e6c72d2 | |||
| cc6138d946 | |||
| 4ca0aad855 | |||
| 351f6b8ec0 | |||
| 6c887be769 | |||
| 8b2fcddcd2 | |||
| 3edc88f76d | |||
| 40ea67a596 | |||
| f809037128 | |||
| 2c162292b7 | |||
| 9cfc223cc0 | |||
| 273f50c741 | |||
| 56bf275e32 | |||
| bfbac726c7 | |||
| b9e6be25d1 | |||
| 621e3d8587 | |||
| e06df594f5 | |||
| be60047b43 | |||
| f989d68014 | |||
| f52514a038 | |||
| 9af963aba0 | |||
| e863a07b89 | |||
| c473ae14fd | |||
| 663d50464e | |||
| 8e6ad2171a | |||
| 3a7b4230b2 | |||
| fb396a71d5 | |||
| ada852aa70 | |||
| 55885daa56 | |||
| 12460f41a4 | |||
| 1825e06944 | |||
| 4ed3e3a5b2 | |||
| fed85c3296 | |||
| 47c56e4353 | |||
| 4862d723a4 | |||
| 3d17c3bb66 | |||
| 8ad1d3fec6 | |||
| 35e498beca | |||
| 33358f80e5 | |||
| f32cab874c | |||
| f3c23fc463 | |||
| 00f7ced864 | |||
| 2eee192366 | |||
| 83cb288575 | |||
| af06d289e4 | |||
| 7886056d74 | |||
| 5bac5302e5 | |||
| 25c42cc0d0 | |||
| bdc55be9b4 | |||
| 72afd792fd | |||
| 0fe564cbb6 | |||
| 542de786c2 | |||
| 776600fabb | |||
| e5af7a0e86 | |||
| df75411b0f | |||
| 967bc13ae6 | |||
| eb45602d2f | |||
| 289e0d244b | |||
| 37169dadda | |||
| 8c9cd0418d | |||
| 8492e62306 | |||
| 18c7c67308 | |||
| 5170a2c096 | |||
| 0852170e8c | |||
| 01d570ad30 | |||
| d0215376f8 | |||
| 693ba98cb9 | |||
| d06ffd0a5f | |||
| 98f15cae89 | |||
| afac39d308 | |||
| 1e43ce350d | |||
| db647c3b60 | |||
| 4dbdd98b41 | |||
| 6e1d887722 | |||
| 33bd41df94 | |||
| a62dabe0b4 | |||
| fdb932fa17 | |||
| f3af9c8bdb | |||
| 2f5dc43ca2 | |||
| 303345dc6a | |||
| 70842ea942 | |||
| b48b6de623 | |||
| 5329d28536 | |||
| 912bafcf36 | |||
| 35250b685c | |||
| c451d597f2 | |||
| e89aec0dfb | |||
| f0f753d70a | |||
| 0eb88a0751 | |||
| b2930933c9 | |||
| ec8173b517 | |||
| 091dbc1034 | |||
| ae5c8e3b69 | |||
| 6c0c6b120a | |||
| a65f7acbbf | |||
| aa5fc634f1 | |||
| 7b4e6fc47b | |||
| 0e697ebe6e | |||
| d883540440 | |||
| b061572916 | |||
| cc997a7f44 | |||
| 39999edcc9 | |||
| c2cf349bcd | |||
| 23b0fdc896 | |||
| c358a4cd35 | |||
| 26fa9b0d0d | |||
| 3101941120 | |||
| 77adf6abf1 | |||
| 7b552c7a37 | |||
| c9207bc0aa | |||
| e1b57b2a87 | |||
| 6bd15a68cf | |||
| 5297a25b4e | |||
| a059701fa5 | |||
| 09a9958e22 | |||
| c6816dfc67 | |||
| b04dd6b7bc | |||
| 9356049680 | |||
| ceeb22dc6e | |||
| 6b2b6cb3fc | |||
| a2ea8f2ad8 | |||
| 04f76b67c9 | |||
| 2d61de4625 | |||
| 0e69318b27 | |||
| 547d6c6656 | |||
| c051ed7953 | |||
| 6b12d37ced | |||
| e4c77b047d | |||
| 76106b494b | |||
| ae45ffc1fe | |||
| f29dcfd215 | |||
| 42263dafcf | |||
| 9f7698a637 | |||
| ba5e098052 | |||
| 4cb9479ce4 | |||
| e1c5e9b561 | |||
| bcc0968a2c | |||
| 17c19f395f | |||
| 59e48b3468 | |||
| a43199f143 | |||
| 323259ec4e | |||
| b442db9935 | |||
| 6f8fdb8aa8 | |||
| c923774c41 | |||
| d821426fa0 | |||
| 7e0dfbf4cd | |||
| d841b779fd |
+8
-2
@@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.8.1 (2015-08-12)
|
||||||
|
|
||||||
|
### Distribution
|
||||||
|
|
||||||
|
* Fix a bug where pushing multiple tags would result in invalid images
|
||||||
|
|
||||||
## 1.8.0 (2015-08-11)
|
## 1.8.0 (2015-08-11)
|
||||||
|
|
||||||
### Distribution
|
### Distribution
|
||||||
@@ -36,7 +42,7 @@
|
|||||||
+ Add logging driver for GELF via UDP
|
+ Add logging driver for GELF via UDP
|
||||||
+ Allow to copy files from host to containers with `docker cp`
|
+ Allow to copy files from host to containers with `docker cp`
|
||||||
+ Promote volume drivers from experimental to master
|
+ Promote volume drivers from experimental to master
|
||||||
+ Add rollover log driver, and --log-driver-opts flag
|
+ Add rollover options to json-file log driver, and --log-driver-opts flag
|
||||||
+ Add memory swappiness tuning options
|
+ Add memory swappiness tuning options
|
||||||
* Remove cgroup read-only flag when privileged
|
* Remove cgroup read-only flag when privileged
|
||||||
* Make /proc, /sys, & /dev readonly for readonly containers
|
* Make /proc, /sys, & /dev readonly for readonly containers
|
||||||
@@ -57,7 +63,7 @@
|
|||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
- Add missing 'Names' field to /containers/json API output
|
- Add missing 'Names' field to /containers/json API output
|
||||||
- Make `docker rmi --dangling` safe when pulling
|
- Make `docker rmi` of dangling images safe while pulling
|
||||||
- Devicemapper: Change default basesize to 100G
|
- Devicemapper: Change default basesize to 100G
|
||||||
- Go Scheduler issue with sync.Mutex and gcc
|
- Go Scheduler issue with sync.Mutex and gcc
|
||||||
- Fix issue where Search API endpoint would panic due to empty AuthConfig
|
- Fix issue where Search API endpoint would panic due to empty AuthConfig
|
||||||
|
|||||||
+3
-2
@@ -40,6 +40,7 @@ RUN apt-get update && apt-get install -y \
|
|||||||
createrepo \
|
createrepo \
|
||||||
curl \
|
curl \
|
||||||
dpkg-sig \
|
dpkg-sig \
|
||||||
|
gcc-mingw-w64 \
|
||||||
git \
|
git \
|
||||||
iptables \
|
iptables \
|
||||||
libapparmor-dev \
|
libapparmor-dev \
|
||||||
@@ -127,7 +128,7 @@ RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint
|
|||||||
RUN gem install --no-rdoc --no-ri fpm --version 1.3.2
|
RUN gem install --no-rdoc --no-ri fpm --version 1.3.2
|
||||||
|
|
||||||
# Install registry
|
# Install registry
|
||||||
ENV REGISTRY_COMMIT 2317f721a3d8428215a2b65da4ae85212ed473b4
|
ENV REGISTRY_COMMIT ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& export GOPATH="$(mktemp -d)" \
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||||
@@ -147,7 +148,7 @@ RUN set -x \
|
|||||||
&& rm -rf "$GOPATH"
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
# Get the "docker-py" source so we can run their integration tests
|
# Get the "docker-py" source so we can run their integration tests
|
||||||
ENV DOCKER_PY_COMMIT 8a87001d09852058f08a807ab6e8491d57ca1e88
|
ENV DOCKER_PY_COMMIT 139850f3f3b17357bab5ba3edfb745fb14043764
|
||||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||||
&& cd /docker-py \
|
&& cd /docker-py \
|
||||||
&& git checkout -q $DOCKER_PY_COMMIT
|
&& git checkout -q $DOCKER_PY_COMMIT
|
||||||
|
|||||||
+1
-1
@@ -309,7 +309,7 @@ made through a pull request.
|
|||||||
# - If the pull request has some small problems that need to be changed, make
|
# - If the pull request has some small problems that need to be changed, make
|
||||||
# a comment adressing the issues.
|
# a comment adressing the issues.
|
||||||
# - If the changes needed to a PR are small, you can add a "LGTM once the
|
# - If the changes needed to a PR are small, you can add a "LGTM once the
|
||||||
# following comments are adressed..." this will reduce needless back and
|
# following comments are addressed..." this will reduce needless back and
|
||||||
# forth.
|
# forth.
|
||||||
# - If the PR only needs a few changes before being merged, any MAINTAINER can
|
# - If the PR only needs a few changes before being merged, any MAINTAINER can
|
||||||
# make a replacement PR that incorporates the existing commits and fixes the
|
# make a replacement PR that incorporates the existing commits and fixes the
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
Docker: the Linux container engine
|
Docker: the container engine [](https://github.com/docker/docker/releases/latest)
|
||||||
==================================
|
============================
|
||||||
|
|
||||||
Docker is an open source project to pack, ship and run any application
|
Docker is an open source project to pack, ship and run any application
|
||||||
as a lightweight container.
|
as a lightweight container.
|
||||||
|
|
||||||
Docker containers are both *hardware-agnostic* and *platform-agnostic*.
|
Docker containers are both *hardware-agnostic* and *platform-agnostic*.
|
||||||
This means they can run anywhere, from your laptop to the largest
|
This means they can run anywhere, from your laptop to the largest
|
||||||
EC2 compute instance and everything in between - and they don't require
|
cloud compute instance and everything in between - and they don't require
|
||||||
you to use a particular language, framework or packaging system. That
|
you to use a particular language, framework or packaging system. That
|
||||||
makes them great building blocks for deploying and scaling web apps,
|
makes them great building blocks for deploying and scaling web apps,
|
||||||
databases, and backend services without depending on a particular stack
|
databases, and backend services without depending on a particular stack
|
||||||
@@ -174,13 +174,14 @@ Under the hood, Docker is built on the following components:
|
|||||||
capabilities of the Linux kernel
|
capabilities of the Linux kernel
|
||||||
* The [Go](https://golang.org) programming language
|
* The [Go](https://golang.org) programming language
|
||||||
* The [Docker Image Specification](https://github.com/docker/docker/blob/master/image/spec/v1.md)
|
* The [Docker Image Specification](https://github.com/docker/docker/blob/master/image/spec/v1.md)
|
||||||
* The [Libcontainer Specification](https://github.com/docker/libcontainer/blob/master/SPEC.md)
|
* The [Libcontainer Specification](https://github.com/opencontainers/runc/blob/master/libcontainer/SPEC.md)
|
||||||
|
|
||||||
Contributing to Docker
|
Contributing to Docker [](https://godoc.org/github.com/docker/docker)
|
||||||
======================
|
======================
|
||||||
|
|
||||||
[](https://godoc.org/github.com/docker/docker)
|
| **Master** (Linux) | **Experimental** (linux) | **Windows** | **FreeBSD** |
|
||||||
[](https://jenkins.dockerproject.org/job/Docker%20Master/)
|
|------------------|----------------------|---------|---------|
|
||||||
|
| [](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master/) | [](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master%20%28experimental%29/) | [/badge/icon)](http://jenkins.dockerproject.org/job/Docker%20Master%20(windows)/) | [/badge/icon)](http://jenkins.dockerproject.org/job/Docker%20Master%20(freebsd)/) |
|
||||||
|
|
||||||
Want to hack on Docker? Awesome! We have [instructions to help you get
|
Want to hack on Docker? Awesome! We have [instructions to help you get
|
||||||
started contributing code or documentation](https://docs.docker.com/project/who-written-for/).
|
started contributing code or documentation](https://docs.docker.com/project/who-written-for/).
|
||||||
@@ -247,7 +248,7 @@ We are always open to suggestions on process improvements, and are always lookin
|
|||||||
<tr>
|
<tr>
|
||||||
<td>Stack Overflow</td>
|
<td>Stack Overflow</td>
|
||||||
<td>
|
<td>
|
||||||
Stack Overflow has over 7000K Docker questions listed. We regularly
|
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>
|
monitor <a href="https://stackoverflow.com/search?tab=newest&q=docker" target="_blank">Docker questions</a>
|
||||||
and so do many other knowledgeable Docker users.
|
and so do many other knowledgeable Docker users.
|
||||||
</td>
|
</td>
|
||||||
@@ -293,3 +294,7 @@ Docker on Mac and Windows
|
|||||||
|
|
||||||
If you know of another project underway that should be listed here, please help
|
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.
|
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.
|
||||||
|
|||||||
+1
-1
@@ -134,7 +134,7 @@ We won't accept patches expanding the surface of `docker exec`, which we intend
|
|||||||
|
|
||||||
## 2.2 Dockerfile syntax
|
## 2.2 Dockerfile syntax
|
||||||
|
|
||||||
The Dockerfile syntax as we know it is simple, and has proven succesful in supporting all our
|
The Dockerfile syntax as we know it is simple, and has proven successful in supporting all our
|
||||||
[official images](https://github.com/docker-library/official-images). Although this is *not* a
|
[official images](https://github.com/docker-library/official-images). Although this is *not* a
|
||||||
definitive move, we temporarily won't accept more patches to the Dockerfile syntax for several
|
definitive move, we temporarily won't accept more patches to the Dockerfile syntax for several
|
||||||
reasons:
|
reasons:
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+41
-18
@@ -31,7 +31,6 @@ import (
|
|||||||
"github.com/docker/docker/pkg/parsers"
|
"github.com/docker/docker/pkg/parsers"
|
||||||
"github.com/docker/docker/pkg/progressreader"
|
"github.com/docker/docker/pkg/progressreader"
|
||||||
"github.com/docker/docker/pkg/streamformatter"
|
"github.com/docker/docker/pkg/streamformatter"
|
||||||
"github.com/docker/docker/pkg/symlink"
|
|
||||||
"github.com/docker/docker/pkg/ulimit"
|
"github.com/docker/docker/pkg/ulimit"
|
||||||
"github.com/docker/docker/pkg/units"
|
"github.com/docker/docker/pkg/units"
|
||||||
"github.com/docker/docker/pkg/urlutil"
|
"github.com/docker/docker/pkg/urlutil"
|
||||||
@@ -60,8 +59,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||||||
flMemoryString := cmd.String([]string{"m", "-memory"}, "", "Memory limit")
|
flMemoryString := cmd.String([]string{"m", "-memory"}, "", "Memory limit")
|
||||||
flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Total memory (memory + swap), '-1' to disable swap")
|
flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Total memory (memory + swap), '-1' to disable swap")
|
||||||
flCPUShares := cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
|
flCPUShares := cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
|
||||||
flCpuPeriod := cmd.Int64([]string{"-cpu-period"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
|
flCPUPeriod := cmd.Int64([]string{"-cpu-period"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
|
||||||
flCpuQuota := cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
flCPUQuota := cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
||||||
flCPUSetCpus := cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
|
flCPUSetCpus := cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
|
||||||
flCPUSetMems := cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)")
|
flCPUSetMems := cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)")
|
||||||
flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
|
flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
|
||||||
@@ -243,8 +242,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||||||
v.Set("cpusetcpus", *flCPUSetCpus)
|
v.Set("cpusetcpus", *flCPUSetCpus)
|
||||||
v.Set("cpusetmems", *flCPUSetMems)
|
v.Set("cpusetmems", *flCPUSetMems)
|
||||||
v.Set("cpushares", strconv.FormatInt(*flCPUShares, 10))
|
v.Set("cpushares", strconv.FormatInt(*flCPUShares, 10))
|
||||||
v.Set("cpuquota", strconv.FormatInt(*flCpuQuota, 10))
|
v.Set("cpuquota", strconv.FormatInt(*flCPUQuota, 10))
|
||||||
v.Set("cpuperiod", strconv.FormatInt(*flCpuPeriod, 10))
|
v.Set("cpuperiod", strconv.FormatInt(*flCPUPeriod, 10))
|
||||||
v.Set("memory", strconv.FormatInt(memory, 10))
|
v.Set("memory", strconv.FormatInt(memory, 10))
|
||||||
v.Set("memswap", strconv.FormatInt(memorySwap, 10))
|
v.Set("memswap", strconv.FormatInt(memorySwap, 10))
|
||||||
v.Set("cgroupparent", *flCgroupParent)
|
v.Set("cgroupparent", *flCgroupParent)
|
||||||
@@ -252,11 +251,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||||||
v.Set("dockerfile", relDockerfile)
|
v.Set("dockerfile", relDockerfile)
|
||||||
|
|
||||||
ulimitsVar := flUlimits.GetList()
|
ulimitsVar := flUlimits.GetList()
|
||||||
ulimitsJson, err := json.Marshal(ulimitsVar)
|
ulimitsJSON, err := json.Marshal(ulimitsVar)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Set("ulimits", string(ulimitsJson))
|
v.Set("ulimits", string(ulimitsJSON))
|
||||||
|
|
||||||
headers := http.Header(make(map[string][]string))
|
headers := http.Header(make(map[string][]string))
|
||||||
buf, err := json.Marshal(cli.configFile.AuthConfigs)
|
buf, err := json.Marshal(cli.configFile.AuthConfigs)
|
||||||
@@ -308,6 +307,12 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isUNC returns true if the path is UNC (one starting \\). It always returns
|
||||||
|
// false on Linux.
|
||||||
|
func isUNC(path string) bool {
|
||||||
|
return runtime.GOOS == "windows" && strings.HasPrefix(path, `\\`)
|
||||||
|
}
|
||||||
|
|
||||||
// getDockerfileRelPath uses the given context directory for a `docker build`
|
// getDockerfileRelPath uses the given context directory for a `docker build`
|
||||||
// and returns the absolute path to the context directory, the relative path of
|
// and returns the absolute path to the context directory, the relative path of
|
||||||
// the dockerfile in that context directory, and a non-nil error on success.
|
// the dockerfile in that context directory, and a non-nil error on success.
|
||||||
@@ -318,9 +323,16 @@ func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDi
|
|||||||
|
|
||||||
// The context dir might be a symbolic link, so follow it to the actual
|
// The context dir might be a symbolic link, so follow it to the actual
|
||||||
// target directory.
|
// target directory.
|
||||||
absContextDir, err = filepath.EvalSymlinks(absContextDir)
|
//
|
||||||
if err != nil {
|
// FIXME. We use isUNC (always false on non-Windows platforms) to workaround
|
||||||
return "", "", fmt.Errorf("unable to evaluate symlinks in context path: %v", err)
|
// an issue in golang. On Windows, EvalSymLinks does not work on UNC file
|
||||||
|
// paths (those starting with \\). This hack means that when using links
|
||||||
|
// on UNC paths, they will not be followed.
|
||||||
|
if !isUNC(absContextDir) {
|
||||||
|
absContextDir, err = filepath.EvalSymlinks(absContextDir)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to evaluate symlinks in context path: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stat, err := os.Lstat(absContextDir)
|
stat, err := os.Lstat(absContextDir)
|
||||||
@@ -354,15 +366,22 @@ func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDi
|
|||||||
absDockerfile = filepath.Join(absContextDir, absDockerfile)
|
absDockerfile = filepath.Join(absContextDir, absDockerfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that 'filename' is within the build context
|
// Evaluate symlinks in the path to the Dockerfile too.
|
||||||
absDockerfile, err = symlink.FollowSymlinkInScope(absDockerfile, absContextDir)
|
//
|
||||||
if err != nil {
|
// FIXME. We use isUNC (always false on non-Windows platforms) to workaround
|
||||||
return "", "", fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", givenDockerfile, givenContextDir)
|
// an issue in golang. On Windows, EvalSymLinks does not work on UNC file
|
||||||
|
// paths (those starting with \\). This hack means that when using links
|
||||||
|
// on UNC paths, they will not be followed.
|
||||||
|
if !isUNC(absDockerfile) {
|
||||||
|
absDockerfile, err = filepath.EvalSymlinks(absDockerfile)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to evaluate symlinks in Dockerfile path: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Lstat(absDockerfile); err != nil {
|
if _, err := os.Lstat(absDockerfile); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return "", "", fmt.Errorf("Cannot locate Dockerfile: absDockerfile: %q", absDockerfile)
|
return "", "", fmt.Errorf("Cannot locate Dockerfile: %q", absDockerfile)
|
||||||
}
|
}
|
||||||
return "", "", fmt.Errorf("unable to stat Dockerfile: %v", err)
|
return "", "", fmt.Errorf("unable to stat Dockerfile: %v", err)
|
||||||
}
|
}
|
||||||
@@ -371,6 +390,10 @@ func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDi
|
|||||||
return "", "", fmt.Errorf("unable to get relative Dockerfile path: %v", err)
|
return "", "", fmt.Errorf("unable to get relative Dockerfile path: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
|
||||||
|
return "", "", fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", givenDockerfile, givenContextDir)
|
||||||
|
}
|
||||||
|
|
||||||
return absContextDir, relDockerfile, nil
|
return absContextDir, relDockerfile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,7 +445,7 @@ func getContextFromReader(r io.Reader, dockerfileName string) (absContextDir, re
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := archive.Untar(buf, absContextDir, nil); err != nil {
|
if err := archive.Untar(buf, absContextDir, nil); err != nil {
|
||||||
return "", "", fmt.Errorf("unable to extract stdin to temporary context direcotry: %v", err)
|
return "", "", fmt.Errorf("unable to extract stdin to temporary context directory: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return getDockerfileRelPath(absContextDir, dockerfileName)
|
return getDockerfileRelPath(absContextDir, dockerfileName)
|
||||||
@@ -459,7 +482,7 @@ func getContextFromURL(out io.Writer, remoteURL, dockerfileName string) (absCont
|
|||||||
In: response.Body,
|
In: response.Body,
|
||||||
Out: out,
|
Out: out,
|
||||||
Formatter: streamformatter.NewStreamFormatter(),
|
Formatter: streamformatter.NewStreamFormatter(),
|
||||||
Size: int(response.ContentLength),
|
Size: response.ContentLength,
|
||||||
NewLines: true,
|
NewLines: true,
|
||||||
ID: "",
|
ID: "",
|
||||||
Action: fmt.Sprintf("Downloading build context from remote url: %s", remoteURL),
|
Action: fmt.Sprintf("Downloading build context from remote url: %s", remoteURL),
|
||||||
@@ -544,7 +567,7 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist
|
|||||||
// Replace the line with a resolved "FROM repo@digest"
|
// Replace the line with a resolved "FROM repo@digest"
|
||||||
repo, tag := parsers.ParseRepositoryTag(matches[1])
|
repo, tag := parsers.ParseRepositoryTag(matches[1])
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
tag = tags.DEFAULTTAG
|
tag = tags.DefaultTag
|
||||||
}
|
}
|
||||||
|
|
||||||
repoInfo, err := registry.ParseRepositoryInfo(repo)
|
repoInfo, err := registry.ParseRepositoryInfo(repo)
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ type DockerCli struct {
|
|||||||
transport *http.Transport
|
transport *http.Transport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize calls the init function that will setup the configuration for the client
|
||||||
|
// such as the TLS, tcp and other parameters used to run the client.
|
||||||
func (cli *DockerCli) Initialize() error {
|
func (cli *DockerCli) Initialize() error {
|
||||||
if cli.init == nil {
|
if cli.init == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -77,6 +79,8 @@ func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PsFormat returns the format string specified in the configuration.
|
||||||
|
// String contains columns and format specification, for example {{ID}\t{{Name}}.
|
||||||
func (cli *DockerCli) PsFormat() string {
|
func (cli *DockerCli) PsFormat() string {
|
||||||
return cli.configFile.PsFormat
|
return cli.configFile.PsFormat
|
||||||
}
|
}
|
||||||
@@ -94,6 +98,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
|
|||||||
}
|
}
|
||||||
|
|
||||||
cli.init = func() error {
|
cli.init = func() error {
|
||||||
|
|
||||||
clientFlags.PostParse()
|
clientFlags.PostParse()
|
||||||
|
|
||||||
hosts := clientFlags.Common.Hosts
|
hosts := clientFlags.Common.Hosts
|
||||||
|
|||||||
+3
-2
@@ -15,6 +15,7 @@ import (
|
|||||||
Cli "github.com/docker/docker/cli"
|
Cli "github.com/docker/docker/cli"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
type copyDirection int
|
type copyDirection int
|
||||||
@@ -101,7 +102,7 @@ func (cli *DockerCli) CmdCp(args ...string) error {
|
|||||||
// client, a `:` could be part of an absolute Windows path, in which case it
|
// client, a `:` could be part of an absolute Windows path, in which case it
|
||||||
// is immediately proceeded by a backslash.
|
// is immediately proceeded by a backslash.
|
||||||
func splitCpArg(arg string) (container, path string) {
|
func splitCpArg(arg string) (container, path string) {
|
||||||
if filepath.IsAbs(arg) {
|
if system.IsAbs(arg) {
|
||||||
// Explicit local absolute path, e.g., `C:\foo` or `/foo`.
|
// Explicit local absolute path, e.g., `C:\foo` or `/foo`.
|
||||||
return "", arg
|
return "", arg
|
||||||
}
|
}
|
||||||
@@ -236,7 +237,7 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string) (er
|
|||||||
// If the destination is a symbolic link, we should evaluate it.
|
// If the destination is a symbolic link, we should evaluate it.
|
||||||
if err == nil && dstStat.Mode&os.ModeSymlink != 0 {
|
if err == nil && dstStat.Mode&os.ModeSymlink != 0 {
|
||||||
linkTarget := dstStat.LinkTarget
|
linkTarget := dstStat.LinkTarget
|
||||||
if !filepath.IsAbs(linkTarget) {
|
if !system.IsAbs(linkTarget) {
|
||||||
// Join with the parent directory.
|
// Join with the parent directory.
|
||||||
dstParent, _ := archive.SplitPathDirEntry(dstPath)
|
dstParent, _ := archive.SplitPathDirEntry(dstPath)
|
||||||
linkTarget = filepath.Join(dstParent, linkTarget)
|
linkTarget = filepath.Join(dstParent, linkTarget)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
|
|||||||
repos, tag := parsers.ParseRepositoryTag(image)
|
repos, tag := parsers.ParseRepositoryTag(image)
|
||||||
// pull only the image tagged 'latest' if no tag was specified
|
// pull only the image tagged 'latest' if no tag was specified
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
tag = tags.DEFAULTTAG
|
tag = tags.DefaultTag
|
||||||
}
|
}
|
||||||
v.Set("fromImage", repos)
|
v.Set("fromImage", repos)
|
||||||
v.Set("tag", tag)
|
v.Set("tag", tag)
|
||||||
@@ -96,7 +96,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
|
|||||||
|
|
||||||
repo, tag := parsers.ParseRepositoryTag(config.Image)
|
repo, tag := parsers.ParseRepositoryTag(config.Image)
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
tag = tags.DEFAULTTAG
|
tag = tags.DefaultTag
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := registry.ParseReference(tag)
|
ref := registry.ParseReference(tag)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
Cli "github.com/docker/docker/cli"
|
Cli "github.com/docker/docker/cli"
|
||||||
@@ -22,7 +21,7 @@ func (cli *DockerCli) CmdExport(args ...string) error {
|
|||||||
cmd.ParseFlags(args, true)
|
cmd.ParseFlags(args, true)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
output io.Writer = cli.out
|
output = cli.out
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if *outfile != "" {
|
if *outfile != "" {
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
if strings.Contains(err.Error(), "connection refused") {
|
||||||
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
//
|
//
|
||||||
// Usage: docker images [OPTIONS] [REPOSITORY]
|
// Usage: docker images [OPTIONS] [REPOSITORY]
|
||||||
func (cli *DockerCli) CmdImages(args ...string) error {
|
func (cli *DockerCli) CmdImages(args ...string) error {
|
||||||
cmd := Cli.Subcmd("images", []string{"[REPOSITORY]"}, "List images", true)
|
cmd := Cli.Subcmd("images", []string{"[REPOSITORY[:TAG]]"}, "List images", true)
|
||||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
|
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
|
||||||
all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)")
|
all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)")
|
||||||
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
|
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
|
|||||||
cmd := Cli.Subcmd("import", []string{"file|URL|- [REPOSITORY[:TAG]]"}, "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true)
|
cmd := Cli.Subcmd("import", []string{"file|URL|- [REPOSITORY[:TAG]]"}, "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true)
|
||||||
flChanges := opts.NewListOpts(nil)
|
flChanges := opts.NewListOpts(nil)
|
||||||
cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
|
cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
|
||||||
|
message := cmd.String([]string{"m", "-message"}, "", "Set commit message for imported image")
|
||||||
cmd.Require(flag.Min, 1)
|
cmd.Require(flag.Min, 1)
|
||||||
|
|
||||||
cmd.ParseFlags(args, true)
|
cmd.ParseFlags(args, true)
|
||||||
@@ -35,6 +36,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
|
|||||||
|
|
||||||
v.Set("fromSrc", src)
|
v.Set("fromSrc", src)
|
||||||
v.Set("repo", repository)
|
v.Set("repo", repository)
|
||||||
|
v.Set("message", *message)
|
||||||
for _, change := range flChanges.GetAll() {
|
for _, change := range flChanges.GetAll() {
|
||||||
v.Add("changes", change)
|
v.Add("changes", change)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -61,8 +61,8 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
|||||||
fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", info.DockerRootDir)
|
fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", info.DockerRootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
ioutils.FprintfIfNotEmpty(cli.out, "Http Proxy: %s\n", info.HttpProxy)
|
ioutils.FprintfIfNotEmpty(cli.out, "Http Proxy: %s\n", info.HTTPProxy)
|
||||||
ioutils.FprintfIfNotEmpty(cli.out, "Https Proxy: %s\n", info.HttpsProxy)
|
ioutils.FprintfIfNotEmpty(cli.out, "Https Proxy: %s\n", info.HTTPSProxy)
|
||||||
ioutils.FprintfIfNotEmpty(cli.out, "No Proxy: %s\n", info.NoProxy)
|
ioutils.FprintfIfNotEmpty(cli.out, "No Proxy: %s\n", info.NoProxy)
|
||||||
|
|
||||||
if info.IndexServerAddress != "" {
|
if info.IndexServerAddress != "" {
|
||||||
@@ -82,12 +82,12 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
|||||||
fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
|
fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
|
||||||
}
|
}
|
||||||
if !info.IPv4Forwarding {
|
if !info.IPv4Forwarding {
|
||||||
fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled.\n")
|
fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled\n")
|
||||||
}
|
}
|
||||||
if !info.BridgeNfIptables {
|
if !info.BridgeNfIptables {
|
||||||
fmt.Fprintf(cli.err, "WARNING: bridge-nf-call-iptables is disabled\n")
|
fmt.Fprintf(cli.err, "WARNING: bridge-nf-call-iptables is disabled\n")
|
||||||
}
|
}
|
||||||
if !info.BridgeNfIp6tables {
|
if !info.BridgeNfIP6tables {
|
||||||
fmt.Fprintf(cli.err, "WARNING: bridge-nf-call-ip6tables is disabled\n")
|
fmt.Fprintf(cli.err, "WARNING: bridge-nf-call-ip6tables is disabled\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,14 @@ func (cli *DockerCli) CmdLogout(args ...string) error {
|
|||||||
|
|
||||||
if _, ok := cli.configFile.AuthConfigs[serverAddress]; !ok {
|
if _, ok := cli.configFile.AuthConfigs[serverAddress]; !ok {
|
||||||
fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress)
|
fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress)
|
||||||
} else {
|
return nil
|
||||||
fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress)
|
|
||||||
delete(cli.configFile.AuthConfigs, serverAddress)
|
|
||||||
|
|
||||||
if err := cli.configFile.Save(); err != nil {
|
|
||||||
return fmt.Errorf("Failed to save docker config: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress)
|
||||||
|
delete(cli.configFile.AuthConfigs, serverAddress)
|
||||||
|
if err := cli.configFile.Save(); err != nil {
|
||||||
|
return fmt.Errorf("Failed to save docker config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
nwclient "github.com/docker/libnetwork/client"
|
nwclient "github.com/docker/libnetwork/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CmdNetwork is used to create, display and configure network endpoints.
|
||||||
func (cli *DockerCli) CmdNetwork(args ...string) error {
|
func (cli *DockerCli) CmdNetwork(args ...string) error {
|
||||||
nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.callWrapper))
|
nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.callWrapper))
|
||||||
args = append([]string{"network"}, args...)
|
args = append([]string{"network"}, args...)
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
|||||||
|
|
||||||
f := *format
|
f := *format
|
||||||
if len(f) == 0 {
|
if len(f) == 0 {
|
||||||
if len(cli.PsFormat()) > 0 {
|
if len(cli.PsFormat()) > 0 && !*quiet {
|
||||||
f = cli.PsFormat()
|
f = cli.PsFormat()
|
||||||
} else {
|
} else {
|
||||||
f = "table"
|
f = "table"
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ func (c *containerContext) Image() string {
|
|||||||
if c.c.Image == "" {
|
if c.c.Image == "" {
|
||||||
return "<no image>"
|
return "<no image>"
|
||||||
}
|
}
|
||||||
|
if c.trunc {
|
||||||
|
return stringutils.Truncate(c.c.Image, 12)
|
||||||
|
}
|
||||||
return c.c.Image
|
return c.c.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestContainerPsContext(t *testing.T) {
|
func TestContainerPsContext(t *testing.T) {
|
||||||
containerId := stringid.GenerateRandomID()
|
containerID := stringid.GenerateRandomID()
|
||||||
unix := time.Now().Unix()
|
unix := time.Now().Unix()
|
||||||
|
|
||||||
var ctx containerContext
|
var ctx containerContext
|
||||||
@@ -23,12 +23,12 @@ func TestContainerPsContext(t *testing.T) {
|
|||||||
expHeader string
|
expHeader string
|
||||||
call func() string
|
call func() string
|
||||||
}{
|
}{
|
||||||
{types.Container{ID: containerId}, true, stringid.TruncateID(containerId), idHeader, ctx.ID},
|
{types.Container{ID: containerID}, true, stringid.TruncateID(containerID), idHeader, ctx.ID},
|
||||||
{types.Container{Names: []string{"/foobar_baz"}}, true, "foobar_baz", namesHeader, ctx.Names},
|
{types.Container{Names: []string{"/foobar_baz"}}, true, "foobar_baz", namesHeader, ctx.Names},
|
||||||
{types.Container{Image: "ubuntu"}, true, "ubuntu", imageHeader, ctx.Image},
|
{types.Container{Image: "ubuntu"}, true, "ubuntu", imageHeader, ctx.Image},
|
||||||
{types.Container{Image: ""}, true, "<no image>", imageHeader, ctx.Image},
|
{types.Container{Image: ""}, true, "<no image>", imageHeader, ctx.Image},
|
||||||
{types.Container{Command: "sh -c 'ls -la'"}, true, `"sh -c 'ls -la'"`, commandHeader, ctx.Command},
|
{types.Container{Command: "sh -c 'ls -la'"}, true, `"sh -c 'ls -la'"`, commandHeader, ctx.Command},
|
||||||
{types.Container{Created: int(unix)}, true, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt},
|
{types.Container{Created: unix}, true, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt},
|
||||||
{types.Container{Ports: []types.Port{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}}, true, "8080/tcp", portsHeader, ctx.Ports},
|
{types.Container{Ports: []types.Port{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}}, true, "8080/tcp", portsHeader, ctx.Ports},
|
||||||
{types.Container{Status: "RUNNING"}, true, "RUNNING", statusHeader, ctx.Status},
|
{types.Container{Status: "RUNNING"}, true, "RUNNING", statusHeader, ctx.Status},
|
||||||
{types.Container{SizeRw: 10}, true, "10 B", sizeHeader, ctx.Size},
|
{types.Container{SizeRw: 10}, true, "10 B", sizeHeader, ctx.Size},
|
||||||
|
|||||||
@@ -14,14 +14,22 @@ const (
|
|||||||
defaultQuietFormat = "{{.ID}}"
|
defaultQuietFormat = "{{.ID}}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Context contains information required by the formatter to print the output as desired.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
|
// Output is the output stream to which the formatted string is written.
|
||||||
Output io.Writer
|
Output io.Writer
|
||||||
|
// Format is used to choose raw, table or custom format for the output.
|
||||||
Format string
|
Format string
|
||||||
Size bool
|
// Size when set to true will display the size of the output.
|
||||||
Quiet bool
|
Size bool
|
||||||
Trunc bool
|
// Quiet when set to true will simply print minimal information.
|
||||||
|
Quiet bool
|
||||||
|
// Trunc when set to true will truncate the output of certain fields such as Container ID.
|
||||||
|
Trunc bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format helps to format the output using the parameters set in the Context.
|
||||||
|
// Currently Format allow to display in raw, table or custom format the output.
|
||||||
func Format(ctx Context, containers []types.Container) {
|
func Format(ctx Context, containers []types.Container) {
|
||||||
switch ctx.Format {
|
switch ctx.Format {
|
||||||
case tableFormatKey:
|
case tableFormatKey:
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
|||||||
|
|
||||||
taglessRemote, tag := parsers.ParseRepositoryTag(remote)
|
taglessRemote, tag := parsers.ParseRepositoryTag(remote)
|
||||||
if tag == "" && !*allTags {
|
if tag == "" && !*allTags {
|
||||||
tag = tags.DEFAULTTAG
|
tag = tags.DefaultTag
|
||||||
fmt.Fprintf(cli.out, "Using default tag: %s\n", tag)
|
fmt.Fprintf(cli.out, "Using default tag: %s\n", tag)
|
||||||
} else if tag != "" && *allTags {
|
} else if tag != "" && *allTags {
|
||||||
return fmt.Errorf("tag can't be used with --all-tags/-a")
|
return fmt.Errorf("tag can't be used with --all-tags/-a")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
// CmdRestart restarts one or more running containers.
|
// CmdRestart restarts one or more running containers.
|
||||||
//
|
//
|
||||||
// Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
|
// Usage: docker restart [OPTIONS] CONTAINER [CONTAINER...]
|
||||||
func (cli *DockerCli) CmdRestart(args ...string) error {
|
func (cli *DockerCli) CmdRestart(args ...string) error {
|
||||||
cmd := Cli.Subcmd("restart", []string{"CONTAINER [CONTAINER...]"}, "Restart a running container", true)
|
cmd := Cli.Subcmd("restart", []string{"CONTAINER [CONTAINER...]"}, "Restart a running container", true)
|
||||||
nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container")
|
nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container")
|
||||||
|
|||||||
+2
-2
@@ -63,11 +63,11 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(hostConfig.Dns) > 0 {
|
if len(hostConfig.DNS) > 0 {
|
||||||
// check the DNS settings passed via --dns against
|
// check the DNS settings passed via --dns against
|
||||||
// localhost regexp to warn if they are trying to
|
// localhost regexp to warn if they are trying to
|
||||||
// set a DNS to a localhost address
|
// set a DNS to a localhost address
|
||||||
for _, dnsIP := range hostConfig.Dns {
|
for _, dnsIP := range hostConfig.DNS {
|
||||||
if dns.IsLocalhost(dnsIP) {
|
if dns.IsLocalhost(dnsIP) {
|
||||||
fmt.Fprintf(cli.err, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
|
fmt.Fprintf(cli.err, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
|
||||||
break
|
break
|
||||||
|
|||||||
+2
-3
@@ -2,7 +2,6 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -17,13 +16,13 @@ import (
|
|||||||
// Usage: docker save [OPTIONS] IMAGE [IMAGE...]
|
// Usage: docker save [OPTIONS] IMAGE [IMAGE...]
|
||||||
func (cli *DockerCli) CmdSave(args ...string) error {
|
func (cli *DockerCli) CmdSave(args ...string) error {
|
||||||
cmd := Cli.Subcmd("save", []string{"IMAGE [IMAGE...]"}, "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
|
cmd := Cli.Subcmd("save", []string{"IMAGE [IMAGE...]"}, "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
|
||||||
outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
|
outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT")
|
||||||
cmd.Require(flag.Min, 1)
|
cmd.Require(flag.Min, 1)
|
||||||
|
|
||||||
cmd.ParseFlags(args, true)
|
cmd.ParseFlags(args, true)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
output io.Writer = cli.out
|
output = cli.out
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if *outfile != "" {
|
if *outfile != "" {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import (
|
|||||||
nwclient "github.com/docker/libnetwork/client"
|
nwclient "github.com/docker/libnetwork/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CmdService is used to manage network services.
|
||||||
|
// service command is user to publish, attach and list a service from a container.
|
||||||
func (cli *DockerCli) CmdService(args ...string) error {
|
func (cli *DockerCli) CmdService(args ...string) error {
|
||||||
nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.callWrapper))
|
nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.callWrapper))
|
||||||
args = append([]string{"service"}, args...)
|
args = append([]string{"service"}, args...)
|
||||||
|
|||||||
+26
-8
@@ -25,6 +25,8 @@ type containerStats struct {
|
|||||||
MemoryPercentage float64
|
MemoryPercentage float64
|
||||||
NetworkRx float64
|
NetworkRx float64
|
||||||
NetworkTx float64
|
NetworkTx float64
|
||||||
|
BlockRead float64
|
||||||
|
BlockWrite float64
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
@@ -63,9 +65,10 @@ func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
|
|||||||
memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0
|
memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0
|
||||||
cpuPercent = 0.0
|
cpuPercent = 0.0
|
||||||
)
|
)
|
||||||
previousCPU = v.PreCpuStats.CpuUsage.TotalUsage
|
previousCPU = v.PreCPUStats.CPUUsage.TotalUsage
|
||||||
previousSystem = v.PreCpuStats.SystemUsage
|
previousSystem = v.PreCPUStats.SystemUsage
|
||||||
cpuPercent = calculateCPUPercent(previousCPU, previousSystem, v)
|
cpuPercent = calculateCPUPercent(previousCPU, previousSystem, v)
|
||||||
|
blkRead, blkWrite := calculateBlockIO(v.BlkioStats)
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.CPUPercentage = cpuPercent
|
s.CPUPercentage = cpuPercent
|
||||||
s.Memory = float64(v.MemoryStats.Usage)
|
s.Memory = float64(v.MemoryStats.Usage)
|
||||||
@@ -73,6 +76,8 @@ func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
|
|||||||
s.MemoryPercentage = memPercent
|
s.MemoryPercentage = memPercent
|
||||||
s.NetworkRx = float64(v.Network.RxBytes)
|
s.NetworkRx = float64(v.Network.RxBytes)
|
||||||
s.NetworkTx = float64(v.Network.TxBytes)
|
s.NetworkTx = float64(v.Network.TxBytes)
|
||||||
|
s.BlockRead = float64(blkRead)
|
||||||
|
s.BlockWrite = float64(blkWrite)
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
u <- nil
|
u <- nil
|
||||||
if !streamStats {
|
if !streamStats {
|
||||||
@@ -110,12 +115,13 @@ func (s *containerStats) Display(w io.Writer) error {
|
|||||||
if s.err != nil {
|
if s.err != nil {
|
||||||
return s.err
|
return s.err
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "%s\t%.2f%%\t%s/%s\t%.2f%%\t%s/%s\n",
|
fmt.Fprintf(w, "%s\t%.2f%%\t%s / %s\t%.2f%%\t%s / %s\t%s / %s\n",
|
||||||
s.Name,
|
s.Name,
|
||||||
s.CPUPercentage,
|
s.CPUPercentage,
|
||||||
units.HumanSize(s.Memory), units.HumanSize(s.MemoryLimit),
|
units.HumanSize(s.Memory), units.HumanSize(s.MemoryLimit),
|
||||||
s.MemoryPercentage,
|
s.MemoryPercentage,
|
||||||
units.HumanSize(s.NetworkRx), units.HumanSize(s.NetworkTx))
|
units.HumanSize(s.NetworkRx), units.HumanSize(s.NetworkTx),
|
||||||
|
units.HumanSize(s.BlockRead), units.HumanSize(s.BlockWrite))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +148,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
|
|||||||
fmt.Fprint(cli.out, "\033[2J")
|
fmt.Fprint(cli.out, "\033[2J")
|
||||||
fmt.Fprint(cli.out, "\033[H")
|
fmt.Fprint(cli.out, "\033[H")
|
||||||
}
|
}
|
||||||
io.WriteString(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O\n")
|
io.WriteString(w, "CONTAINER\tCPU %\tMEM USAGE / LIMIT\tMEM %\tNET I/O\tBLOCK I/O\n")
|
||||||
}
|
}
|
||||||
for _, n := range names {
|
for _, n := range names {
|
||||||
s := &containerStats{Name: n}
|
s := &containerStats{Name: n}
|
||||||
@@ -190,13 +196,25 @@ func calculateCPUPercent(previousCPU, previousSystem uint64, v *types.Stats) flo
|
|||||||
var (
|
var (
|
||||||
cpuPercent = 0.0
|
cpuPercent = 0.0
|
||||||
// calculate the change for the cpu usage of the container in between readings
|
// calculate the change for the cpu usage of the container in between readings
|
||||||
cpuDelta = float64(v.CpuStats.CpuUsage.TotalUsage - previousCPU)
|
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage - previousCPU)
|
||||||
// calculate the change for the entire system between readings
|
// calculate the change for the entire system between readings
|
||||||
systemDelta = float64(v.CpuStats.SystemUsage - previousSystem)
|
systemDelta = float64(v.CPUStats.SystemUsage - previousSystem)
|
||||||
)
|
)
|
||||||
|
|
||||||
if systemDelta > 0.0 && cpuDelta > 0.0 {
|
if systemDelta > 0.0 && cpuDelta > 0.0 {
|
||||||
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0
|
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CPUStats.CPUUsage.PercpuUsage)) * 100.0
|
||||||
}
|
}
|
||||||
return cpuPercent
|
return cpuPercent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calculateBlockIO(blkio types.BlkioStats) (blkRead uint64, blkWrite uint64) {
|
||||||
|
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||||
|
switch strings.ToLower(bioEntry.Op) {
|
||||||
|
case "read":
|
||||||
|
blkRead = blkRead + bioEntry.Value
|
||||||
|
case "write":
|
||||||
|
blkWrite = blkWrite + bioEntry.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDisplay(t *testing.T) {
|
func TestDisplay(t *testing.T) {
|
||||||
@@ -15,6 +17,8 @@ func TestDisplay(t *testing.T) {
|
|||||||
MemoryPercentage: 100.0 / 2048.0 * 100.0,
|
MemoryPercentage: 100.0 / 2048.0 * 100.0,
|
||||||
NetworkRx: 100 * 1024 * 1024,
|
NetworkRx: 100 * 1024 * 1024,
|
||||||
NetworkTx: 800 * 1024 * 1024,
|
NetworkTx: 800 * 1024 * 1024,
|
||||||
|
BlockRead: 100 * 1024 * 1024,
|
||||||
|
BlockWrite: 800 * 1024 * 1024,
|
||||||
mu: sync.RWMutex{},
|
mu: sync.RWMutex{},
|
||||||
}
|
}
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
@@ -22,8 +26,21 @@ func TestDisplay(t *testing.T) {
|
|||||||
t.Fatalf("c.Display() gave error: %s", err)
|
t.Fatalf("c.Display() gave error: %s", err)
|
||||||
}
|
}
|
||||||
got := b.String()
|
got := b.String()
|
||||||
want := "app\t30.00%\t104.9 MB/2.147 GB\t4.88%\t104.9 MB/838.9 MB\n"
|
want := "app\t30.00%\t104.9 MB / 2.147 GB\t4.88%\t104.9 MB / 838.9 MB\t104.9 MB / 838.9 MB\n"
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Fatalf("c.Display() = %q, want %q", got, want)
|
t.Fatalf("c.Display() = %q, want %q", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCalculBlockIO(t *testing.T) {
|
||||||
|
blkio := types.BlkioStats{
|
||||||
|
IoServiceBytesRecursive: []types.BlkioStatEntry{{8, 0, "read", 1234}, {8, 1, "read", 4567}, {8, 0, "write", 123}, {8, 1, "write", 456}},
|
||||||
|
}
|
||||||
|
blkRead, blkWrite := calculateBlockIO(blkio)
|
||||||
|
if blkRead != 5801 {
|
||||||
|
t.Fatalf("blkRead = %d, want 5801", blkRead)
|
||||||
|
}
|
||||||
|
if blkWrite != 579 {
|
||||||
|
t.Fatalf("blkWrite = %d, want 579", blkWrite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+43
-39
@@ -30,7 +30,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
errConnectionFailed = errors.New("Cannot connect to the Docker daemon. Is the docker daemon running on this host?")
|
||||||
)
|
)
|
||||||
|
|
||||||
type serverResponse struct {
|
type serverResponse struct {
|
||||||
@@ -94,13 +94,14 @@ func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers m
|
|||||||
if resp != nil {
|
if resp != nil {
|
||||||
serverResp.statusCode = resp.StatusCode
|
serverResp.statusCode = resp.StatusCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
if types.IsTimeout(err) || strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
|
||||||
return serverResp, errConnectionRefused
|
return serverResp, errConnectionFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
if cli.tlsConfig == nil {
|
if cli.tlsConfig == nil && strings.Contains(err.Error(), "malformed HTTP response") {
|
||||||
return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?\n* Is your docker daemon up and running?", err)
|
return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)
|
||||||
}
|
}
|
||||||
if cli.tlsConfig != nil && strings.Contains(err.Error(), "remote error: bad certificate") {
|
if cli.tlsConfig != nil && strings.Contains(err.Error(), "remote error: bad certificate") {
|
||||||
return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err)
|
return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err)
|
||||||
@@ -125,48 +126,51 @@ func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers m
|
|||||||
return serverResp, nil
|
return serverResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reader, out io.Writer, index *registry.IndexInfo, cmdName string) (io.ReadCloser, int, error) {
|
// cmdAttempt builds the corresponding registry Auth Header from the given
|
||||||
cmdAttempt := func(authConfig cliconfig.AuthConfig) (io.ReadCloser, int, error) {
|
// authConfig. It returns the servers body, status, error response
|
||||||
buf, err := json.Marshal(authConfig)
|
func (cli *DockerCli) cmdAttempt(authConfig cliconfig.AuthConfig, method, path string, in io.Reader, out io.Writer) (io.ReadCloser, int, error) {
|
||||||
if err != nil {
|
buf, err := json.Marshal(authConfig)
|
||||||
return nil, -1, err
|
if err != nil {
|
||||||
}
|
return nil, -1, err
|
||||||
registryAuthHeader := []string{
|
|
||||||
base64.URLEncoding.EncodeToString(buf),
|
|
||||||
}
|
|
||||||
|
|
||||||
// begin the request
|
|
||||||
serverResp, err := cli.clientRequest(method, path, in, map[string][]string{
|
|
||||||
"X-Registry-Auth": registryAuthHeader,
|
|
||||||
})
|
|
||||||
if err == nil && out != nil {
|
|
||||||
// If we are streaming output, complete the stream since
|
|
||||||
// errors may not appear until later.
|
|
||||||
err = cli.streamBody(serverResp.body, serverResp.header.Get("Content-Type"), true, out, nil)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// Since errors in a stream appear after status 200 has been written,
|
|
||||||
// we may need to change the status code.
|
|
||||||
if strings.Contains(err.Error(), "Authentication is required") ||
|
|
||||||
strings.Contains(err.Error(), "Status 401") ||
|
|
||||||
strings.Contains(err.Error(), "401 Unauthorized") ||
|
|
||||||
strings.Contains(err.Error(), "status code 401") {
|
|
||||||
serverResp.statusCode = http.StatusUnauthorized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return serverResp.body, serverResp.statusCode, err
|
|
||||||
}
|
}
|
||||||
|
registryAuthHeader := []string{
|
||||||
|
base64.URLEncoding.EncodeToString(buf),
|
||||||
|
}
|
||||||
|
|
||||||
|
// begin the request
|
||||||
|
serverResp, err := cli.clientRequest(method, path, in, map[string][]string{
|
||||||
|
"X-Registry-Auth": registryAuthHeader,
|
||||||
|
})
|
||||||
|
if err == nil && out != nil {
|
||||||
|
// If we are streaming output, complete the stream since
|
||||||
|
// errors may not appear until later.
|
||||||
|
err = cli.streamBody(serverResp.body, serverResp.header.Get("Content-Type"), true, out, nil)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
// Since errors in a stream appear after status 200 has been written,
|
||||||
|
// we may need to change the status code.
|
||||||
|
if strings.Contains(err.Error(), "Authentication is required") ||
|
||||||
|
strings.Contains(err.Error(), "Status 401") ||
|
||||||
|
strings.Contains(err.Error(), "401 Unauthorized") ||
|
||||||
|
strings.Contains(err.Error(), "status code 401") {
|
||||||
|
serverResp.statusCode = http.StatusUnauthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return serverResp.body, serverResp.statusCode, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reader, out io.Writer, index *registry.IndexInfo, cmdName string) (io.ReadCloser, int, error) {
|
||||||
|
|
||||||
// Resolve the Auth config relevant for this server
|
// Resolve the Auth config relevant for this server
|
||||||
authConfig := registry.ResolveAuthConfig(cli.configFile, index)
|
authConfig := registry.ResolveAuthConfig(cli.configFile, index)
|
||||||
body, statusCode, err := cmdAttempt(authConfig)
|
body, statusCode, err := cli.cmdAttempt(authConfig, method, path, in, out)
|
||||||
if statusCode == http.StatusUnauthorized {
|
if statusCode == http.StatusUnauthorized {
|
||||||
fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
|
fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
|
||||||
if err = cli.CmdLogin(index.GetAuthConfigKey()); err != nil {
|
if err = cli.CmdLogin(index.GetAuthConfigKey()); err != nil {
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
authConfig = registry.ResolveAuthConfig(cli.configFile, index)
|
authConfig = registry.ResolveAuthConfig(cli.configFile, index)
|
||||||
return cmdAttempt(authConfig)
|
return cli.cmdAttempt(authConfig, method, path, in, out)
|
||||||
}
|
}
|
||||||
return body, statusCode, err
|
return body, statusCode, err
|
||||||
}
|
}
|
||||||
@@ -277,7 +281,7 @@ func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
|
|||||||
serverResp, err := cli.call("GET", "/containers/"+containerID+"/json", nil, nil)
|
serverResp, err := cli.call("GET", "/containers/"+containerID+"/json", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If we can't connect, then the daemon probably died.
|
// If we can't connect, then the daemon probably died.
|
||||||
if err != errConnectionRefused {
|
if err != errConnectionFailed {
|
||||||
return false, -1, err
|
return false, -1, err
|
||||||
}
|
}
|
||||||
return false, -1, nil
|
return false, -1, nil
|
||||||
@@ -299,7 +303,7 @@ func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
|
|||||||
serverResp, err := cli.call("GET", "/exec/"+execID+"/json", nil, nil)
|
serverResp, err := cli.call("GET", "/exec/"+execID+"/json", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If we can't connect, then the daemon probably died.
|
// If we can't connect, then the daemon probably died.
|
||||||
if err != errConnectionRefused {
|
if err != errConnectionFailed {
|
||||||
return false, -1, err
|
return false, -1, err
|
||||||
}
|
}
|
||||||
return false, -1, nil
|
return false, -1, nil
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import (
|
|||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var VersionTemplate = `Client:
|
var versionTemplate = `Client:
|
||||||
Version: {{.Client.Version}}
|
Version: {{.Client.Version}}
|
||||||
API version: {{.Client.ApiVersion}}
|
API version: {{.Client.APIVersion}}
|
||||||
Go version: {{.Client.GoVersion}}
|
Go version: {{.Client.GoVersion}}
|
||||||
Git commit: {{.Client.GitCommit}}
|
Git commit: {{.Client.GitCommit}}
|
||||||
Built: {{.Client.BuildTime}}
|
Built: {{.Client.BuildTime}}
|
||||||
@@ -24,14 +24,14 @@ var VersionTemplate = `Client:
|
|||||||
|
|
||||||
Server:
|
Server:
|
||||||
Version: {{.Server.Version}}
|
Version: {{.Server.Version}}
|
||||||
API version: {{.Server.ApiVersion}}
|
API version: {{.Server.APIVersion}}
|
||||||
Go version: {{.Server.GoVersion}}
|
Go version: {{.Server.GoVersion}}
|
||||||
Git commit: {{.Server.GitCommit}}
|
Git commit: {{.Server.GitCommit}}
|
||||||
Built: {{.Server.BuildTime}}
|
Built: {{.Server.BuildTime}}
|
||||||
OS/Arch: {{.Server.Os}}/{{.Server.Arch}}{{if .Server.Experimental}}
|
OS/Arch: {{.Server.Os}}/{{.Server.Arch}}{{if .Server.Experimental}}
|
||||||
Experimental: {{.Server.Experimental}}{{end}}{{end}}`
|
Experimental: {{.Server.Experimental}}{{end}}{{end}}`
|
||||||
|
|
||||||
type VersionData struct {
|
type versionData struct {
|
||||||
Client types.Version
|
Client types.Version
|
||||||
ServerOK bool
|
ServerOK bool
|
||||||
Server types.Version
|
Server types.Version
|
||||||
@@ -49,7 +49,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
|
|||||||
|
|
||||||
cmd.ParseFlags(args, true)
|
cmd.ParseFlags(args, true)
|
||||||
if *tmplStr == "" {
|
if *tmplStr == "" {
|
||||||
*tmplStr = VersionTemplate
|
*tmplStr = versionTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
var tmpl *template.Template
|
var tmpl *template.Template
|
||||||
@@ -58,10 +58,10 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
|
|||||||
Status: "Template parsing error: " + err.Error()}
|
Status: "Template parsing error: " + err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
vd := VersionData{
|
vd := versionData{
|
||||||
Client: types.Version{
|
Client: types.Version{
|
||||||
Version: dockerversion.VERSION,
|
Version: dockerversion.VERSION,
|
||||||
ApiVersion: api.Version,
|
APIVersion: api.Version,
|
||||||
GoVersion: runtime.Version(),
|
GoVersion: runtime.Version(),
|
||||||
GitCommit: dockerversion.GITCOMMIT,
|
GitCommit: dockerversion.GITCOMMIT,
|
||||||
BuildTime: dockerversion.BUILDTIME,
|
BuildTime: dockerversion.BUILDTIME,
|
||||||
|
|||||||
@@ -0,0 +1,232 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"text/tabwriter"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
Cli "github.com/docker/docker/cli"
|
||||||
|
"github.com/docker/docker/opts"
|
||||||
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
|
"github.com/docker/docker/pkg/parsers/filters"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdVolume is the parent subcommand for all volume commands
|
||||||
|
//
|
||||||
|
// Usage: docker volume <COMMAND> <OPTS>
|
||||||
|
func (cli *DockerCli) CmdVolume(args ...string) error {
|
||||||
|
description := "Manage Docker volumes\n\nCommands:\n"
|
||||||
|
commands := [][]string{
|
||||||
|
{"create", "Create a volume"},
|
||||||
|
{"inspect", "Return low-level information on a volume"},
|
||||||
|
{"ls", "List volumes"},
|
||||||
|
{"rm", "Remove a volume"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cmd := range commands {
|
||||||
|
description += fmt.Sprintf(" %-25.25s%s\n", cmd[0], cmd[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
description += "\nRun 'docker volume COMMAND --help' for more information on a command."
|
||||||
|
cmd := Cli.Subcmd("volume", []string{"[COMMAND]"}, description, true)
|
||||||
|
cmd.Require(flag.Exact, 0)
|
||||||
|
cmd.ParseFlags(args, true)
|
||||||
|
|
||||||
|
return cli.CmdVolumeLs(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdVolumeLs outputs a list of Docker volumes.
|
||||||
|
//
|
||||||
|
// Usage: docker volume ls [OPTIONS]
|
||||||
|
func (cli *DockerCli) CmdVolumeLs(args ...string) error {
|
||||||
|
cmd := Cli.Subcmd("volume ls", nil, "List volumes", true)
|
||||||
|
|
||||||
|
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display volume names")
|
||||||
|
flFilter := opts.NewListOpts(nil)
|
||||||
|
cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'dangling=true')")
|
||||||
|
|
||||||
|
cmd.Require(flag.Exact, 0)
|
||||||
|
cmd.ParseFlags(args, true)
|
||||||
|
|
||||||
|
volFilterArgs := filters.Args{}
|
||||||
|
for _, f := range flFilter.GetAll() {
|
||||||
|
var err error
|
||||||
|
volFilterArgs, err = filters.ParseFlag(f, volFilterArgs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v := url.Values{}
|
||||||
|
if len(volFilterArgs) > 0 {
|
||||||
|
filterJSON, err := filters.ToParam(volFilterArgs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Set("filters", filterJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := cli.call("GET", "/volumes?"+v.Encode(), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var volumes types.VolumesListResponse
|
||||||
|
if err := json.NewDecoder(resp.body).Decode(&volumes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||||
|
if !*quiet {
|
||||||
|
fmt.Fprintf(w, "DRIVER \tVOLUME NAME")
|
||||||
|
fmt.Fprintf(w, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, vol := range volumes.Volumes {
|
||||||
|
if *quiet {
|
||||||
|
fmt.Fprintln(w, vol.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s\t%s\n", vol.Driver, vol.Name)
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdVolumeInspect displays low-level information on one or more volumes.
|
||||||
|
//
|
||||||
|
// Usage: docker volume inspect [OPTIONS] VOLUME [VOLUME...]
|
||||||
|
func (cli *DockerCli) CmdVolumeInspect(args ...string) error {
|
||||||
|
cmd := Cli.Subcmd("volume inspect", []string{"[VOLUME NAME]"}, "Return low-level information on a volume", true)
|
||||||
|
tmplStr := cmd.String([]string{"f", "-format"}, "", "Format the output using the given go template.")
|
||||||
|
if err := cmd.Parse(args); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Require(flag.Min, 1)
|
||||||
|
cmd.ParseFlags(args, true)
|
||||||
|
|
||||||
|
var tmpl *template.Template
|
||||||
|
if *tmplStr != "" {
|
||||||
|
var err error
|
||||||
|
tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var status = 0
|
||||||
|
var volumes []*types.Volume
|
||||||
|
for _, name := range cmd.Args() {
|
||||||
|
resp, err := cli.call("GET", "/volumes/"+name, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var volume types.Volume
|
||||||
|
if err := json.NewDecoder(resp.body).Decode(&volume); err != nil {
|
||||||
|
fmt.Fprintf(cli.err, "%s\n", err)
|
||||||
|
status = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpl == nil {
|
||||||
|
volumes = append(volumes, &volume)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tmpl.Execute(cli.out, &volume); err != nil {
|
||||||
|
if err := tmpl.Execute(cli.out, &volume); err != nil {
|
||||||
|
fmt.Fprintf(cli.err, "%s\n", err)
|
||||||
|
status = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
io.WriteString(cli.out, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpl != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.MarshalIndent(volumes, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(cli.out, bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
io.WriteString(cli.out, "\n")
|
||||||
|
|
||||||
|
if status != 0 {
|
||||||
|
return Cli.StatusError{StatusCode: status}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdVolumeCreate creates a new container from a given image.
|
||||||
|
//
|
||||||
|
// Usage: docker volume create [OPTIONS]
|
||||||
|
func (cli *DockerCli) CmdVolumeCreate(args ...string) error {
|
||||||
|
cmd := Cli.Subcmd("volume create", nil, "Create a volume", true)
|
||||||
|
flDriver := cmd.String([]string{"d", "-driver"}, "local", "Specify volume driver name")
|
||||||
|
flName := cmd.String([]string{"-name"}, "", "Specify volume name")
|
||||||
|
|
||||||
|
flDriverOpts := opts.NewMapOpts(nil, nil)
|
||||||
|
cmd.Var(flDriverOpts, []string{"o", "-opt"}, "Set driver specific options")
|
||||||
|
|
||||||
|
cmd.Require(flag.Exact, 0)
|
||||||
|
cmd.ParseFlags(args, true)
|
||||||
|
|
||||||
|
volReq := &types.VolumeCreateRequest{
|
||||||
|
Driver: *flDriver,
|
||||||
|
DriverOpts: flDriverOpts.GetAll(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flName != "" {
|
||||||
|
volReq.Name = *flName
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := cli.call("POST", "/volumes", volReq, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var vol types.Volume
|
||||||
|
if err := json.NewDecoder(resp.body).Decode(&vol); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintf(cli.out, "%s\n", vol.Name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdVolumeRm removes one or more containers.
|
||||||
|
//
|
||||||
|
// Usage: docker volume rm VOLUME [VOLUME...]
|
||||||
|
func (cli *DockerCli) CmdVolumeRm(args ...string) error {
|
||||||
|
cmd := Cli.Subcmd("volume rm", []string{"[NAME]"}, "Remove a volume", true)
|
||||||
|
cmd.Require(flag.Min, 1)
|
||||||
|
cmd.ParseFlags(args, true)
|
||||||
|
|
||||||
|
var status = 0
|
||||||
|
for _, name := range cmd.Args() {
|
||||||
|
_, err := cli.call("DELETE", "/volumes/"+name, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(cli.err, "%s\n", err)
|
||||||
|
status = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(cli.out, "%s\n", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if status != 0 {
|
||||||
|
return Cli.StatusError{StatusCode: status}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+17
-12
@@ -16,22 +16,26 @@ import (
|
|||||||
|
|
||||||
// Common constants for daemon and client.
|
// Common constants for daemon and client.
|
||||||
const (
|
const (
|
||||||
// Current REST API version
|
// Version of Current REST API
|
||||||
Version version.Version = "1.20"
|
Version version.Version = "1.21"
|
||||||
|
|
||||||
// Minimun REST API version supported
|
// MinVersion represents Minimun REST API version supported
|
||||||
MinVersion version.Version = "1.12"
|
MinVersion version.Version = "1.12"
|
||||||
|
|
||||||
// Default filename with Docker commands, read by docker build
|
// DefaultDockerfileName is the Default filename with Docker commands, read by docker build
|
||||||
DefaultDockerfileName string = "Dockerfile"
|
DefaultDockerfileName string = "Dockerfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ByPrivatePort []types.Port
|
// byPrivatePort is temporary type used to sort types.Port by PrivatePort
|
||||||
|
type byPrivatePort []types.Port
|
||||||
|
|
||||||
func (r ByPrivatePort) Len() int { return len(r) }
|
func (r byPrivatePort) Len() int { return len(r) }
|
||||||
func (r ByPrivatePort) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
func (r byPrivatePort) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||||
func (r ByPrivatePort) Less(i, j int) bool { return r[i].PrivatePort < r[j].PrivatePort }
|
func (r byPrivatePort) Less(i, j int) bool { return r[i].PrivatePort < r[j].PrivatePort }
|
||||||
|
|
||||||
|
// 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 {
|
func DisplayablePorts(ports []types.Port) string {
|
||||||
var (
|
var (
|
||||||
result = []string{}
|
result = []string{}
|
||||||
@@ -41,7 +45,7 @@ func DisplayablePorts(ports []types.Port) string {
|
|||||||
)
|
)
|
||||||
firstInGroupMap = make(map[string]int)
|
firstInGroupMap = make(map[string]int)
|
||||||
lastInGroupMap = make(map[string]int)
|
lastInGroupMap = make(map[string]int)
|
||||||
sort.Sort(ByPrivatePort(ports))
|
sort.Sort(byPrivatePort(ports))
|
||||||
for _, port := range ports {
|
for _, port := range ports {
|
||||||
var (
|
var (
|
||||||
current = port.PrivatePort
|
current = port.PrivatePort
|
||||||
@@ -69,18 +73,18 @@ func DisplayablePorts(ports []types.Port) string {
|
|||||||
lastInGroupMap[portKey] = current
|
lastInGroupMap[portKey] = current
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
result = append(result, FormGroup(portKey, firstInGroup, lastInGroup))
|
result = append(result, formGroup(portKey, firstInGroup, lastInGroup))
|
||||||
firstInGroupMap[portKey] = current
|
firstInGroupMap[portKey] = current
|
||||||
lastInGroupMap[portKey] = current
|
lastInGroupMap[portKey] = current
|
||||||
}
|
}
|
||||||
for portKey, firstInGroup := range firstInGroupMap {
|
for portKey, firstInGroup := range firstInGroupMap {
|
||||||
result = append(result, FormGroup(portKey, firstInGroup, lastInGroupMap[portKey]))
|
result = append(result, formGroup(portKey, firstInGroup, lastInGroupMap[portKey]))
|
||||||
}
|
}
|
||||||
result = append(result, hostMappings...)
|
result = append(result, hostMappings...)
|
||||||
return strings.Join(result, ", ")
|
return strings.Join(result, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormGroup(key string, start, last int) string {
|
func formGroup(key string, start, last int) string {
|
||||||
var (
|
var (
|
||||||
group string
|
group string
|
||||||
parts = strings.Split(key, "/")
|
parts = strings.Split(key, "/")
|
||||||
@@ -102,6 +106,7 @@ func FormGroup(key string, start, last int) string {
|
|||||||
return fmt.Sprintf("%s/%s", group, groupType)
|
return fmt.Sprintf("%s/%s", group, groupType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchesContentType validates the content type against the expected one
|
||||||
func MatchesContentType(contentType, expectedType string) bool {
|
func MatchesContentType(contentType, expectedType string) bool {
|
||||||
mimetype, _, err := mime.ParseMediaType(contentType)
|
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,282 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
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/udp, 1.2.3.4:8899->9988/tcp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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 folder do not exists.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
keyID: AWX2:I27X:WQFX:IOMK:CNAK:O7PW:VYNB:ZLKC:CVAE:YJP2:SI4A:XXAY
|
||||||
|
|
||||||
|
MHcCAQEEILHTRWdcpKWsnORxSFyBnndJ4ROU41hMtr/GCiLVvwBQoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAElpVFbQ2V2UQKajqdE3fVxJ+/pE/YuEFOxWbOxF2be19BY209/iky
|
||||||
|
NzeFFK7SLpQ4CBJ7zDVXOHsMzrkY/GquGA==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/cliconfig"
|
||||||
|
"github.com/docker/docker/pkg/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) postAuth(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
var config *cliconfig.AuthConfig
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&config)
|
||||||
|
r.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
status, err := s.daemon.RegistryService.Auth(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeJSON(w, http.StatusOK, &types.AuthResponse{
|
||||||
|
Status: status,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,535 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/websocket"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/daemon"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
"github.com/docker/docker/pkg/signal"
|
||||||
|
"github.com/docker/docker/pkg/version"
|
||||||
|
"github.com/docker/docker/runconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) getContainersByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
if version.LessThan("1.20") && runtime.GOOS != "windows" {
|
||||||
|
return getContainersByNameDownlevel(w, s, vars["name"])
|
||||||
|
}
|
||||||
|
|
||||||
|
containerJSON, err := s.daemon.ContainerInspect(vars["name"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeJSON(w, http.StatusOK, containerJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getContainersJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &daemon.ContainersConfig{
|
||||||
|
All: boolValue(r, "all"),
|
||||||
|
Size: boolValue(r, "size"),
|
||||||
|
Since: r.Form.Get("since"),
|
||||||
|
Before: r.Form.Get("before"),
|
||||||
|
Filters: r.Form.Get("filters"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
|
||||||
|
limit, err := strconv.Atoi(tmpLimit)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config.Limit = limit
|
||||||
|
}
|
||||||
|
|
||||||
|
containers, err := s.daemon.Containers(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusOK, containers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getContainersStats(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
stream := boolValueOrDefault(r, "stream", true)
|
||||||
|
var out io.Writer
|
||||||
|
if !stream {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
out = w
|
||||||
|
} else {
|
||||||
|
out = ioutils.NewWriteFlusher(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
var closeNotifier <-chan bool
|
||||||
|
if notifier, ok := w.(http.CloseNotifier); ok {
|
||||||
|
closeNotifier = notifier.CloseNotify()
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &daemon.ContainerStatsConfig{
|
||||||
|
Stream: stream,
|
||||||
|
OutStream: out,
|
||||||
|
Stop: closeNotifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.daemon.ContainerStats(vars["name"], config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getContainersLogs(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate args here, because we can't return not StatusOK after job.Run() call
|
||||||
|
stdout, stderr := boolValue(r, "stdout"), boolValue(r, "stderr")
|
||||||
|
if !(stdout || stderr) {
|
||||||
|
return fmt.Errorf("Bad parameters: you must choose at least one stream")
|
||||||
|
}
|
||||||
|
|
||||||
|
var since time.Time
|
||||||
|
if r.Form.Get("since") != "" {
|
||||||
|
s, err := strconv.ParseInt(r.Form.Get("since"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
since = time.Unix(s, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var closeNotifier <-chan bool
|
||||||
|
if notifier, ok := w.(http.CloseNotifier); ok {
|
||||||
|
closeNotifier = notifier.CloseNotify()
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := s.daemon.Get(vars["name"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
outStream := ioutils.NewWriteFlusher(w)
|
||||||
|
// write an empty chunk of data (this is to ensure that the
|
||||||
|
// HTTP Response is sent immediately, even if the container has
|
||||||
|
// not yet produced any data)
|
||||||
|
outStream.Write(nil)
|
||||||
|
|
||||||
|
logsConfig := &daemon.ContainerLogsConfig{
|
||||||
|
Follow: boolValue(r, "follow"),
|
||||||
|
Timestamps: boolValue(r, "timestamps"),
|
||||||
|
Since: since,
|
||||||
|
Tail: r.Form.Get("tail"),
|
||||||
|
UseStdout: stdout,
|
||||||
|
UseStderr: stderr,
|
||||||
|
OutStream: outStream,
|
||||||
|
Stop: closeNotifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.daemon.ContainerLogs(c, logsConfig); err != nil {
|
||||||
|
fmt.Fprintf(w, "Error running logs job: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getContainersExport(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.daemon.ContainerExport(vars["name"], w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainersStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If contentLength is -1, we can assumed chunked encoding
|
||||||
|
// or more technically that the length is unknown
|
||||||
|
// https://golang.org/src/pkg/net/http/request.go#L139
|
||||||
|
// net/http otherwise seems to swallow any headers related to chunked encoding
|
||||||
|
// including r.TransferEncoding
|
||||||
|
// allow a nil body for backwards compatibility
|
||||||
|
var hostConfig *runconfig.HostConfig
|
||||||
|
if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
|
||||||
|
if err := checkForJSON(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := runconfig.DecodeHostConfig(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
hostConfig = c
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.daemon.ContainerStart(vars["name"], hostConfig); err != nil {
|
||||||
|
if err.Error() == "Container already started" {
|
||||||
|
w.WriteHeader(http.StatusNotModified)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainersStop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
seconds, _ := strconv.Atoi(r.Form.Get("t"))
|
||||||
|
|
||||||
|
if err := s.daemon.ContainerStop(vars["name"], seconds); err != nil {
|
||||||
|
if err.Error() == "Container already stopped" {
|
||||||
|
w.WriteHeader(http.StatusNotModified)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainersKill(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var sig uint64
|
||||||
|
name := vars["name"]
|
||||||
|
|
||||||
|
// If we have a signal, look at it. Otherwise, do nothing
|
||||||
|
if sigStr := r.Form.Get("signal"); sigStr != "" {
|
||||||
|
// Check if we passed the signal as a number:
|
||||||
|
// The largest legal signal is 31, so let's parse on 5 bits
|
||||||
|
sigN, err := strconv.ParseUint(sigStr, 10, 5)
|
||||||
|
if err != nil {
|
||||||
|
// The signal is not a number, treat it as a string (either like
|
||||||
|
// "KILL" or like "SIGKILL")
|
||||||
|
syscallSig, ok := signal.SignalMap[strings.TrimPrefix(sigStr, "SIG")]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Invalid signal: %s", sigStr)
|
||||||
|
}
|
||||||
|
sig = uint64(syscallSig)
|
||||||
|
} else {
|
||||||
|
sig = sigN
|
||||||
|
}
|
||||||
|
|
||||||
|
if sig == 0 {
|
||||||
|
return fmt.Errorf("Invalid signal: %s", sigStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.daemon.ContainerKill(name, sig); err != nil {
|
||||||
|
_, isStopped := err.(daemon.ErrContainerNotRunning)
|
||||||
|
// 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.
|
||||||
|
if version.GreaterThanOrEqualTo("1.20") || !isStopped {
|
||||||
|
return fmt.Errorf("Cannot kill container %s: %v", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainersRestart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout, _ := strconv.Atoi(r.Form.Get("t"))
|
||||||
|
|
||||||
|
if err := s.daemon.ContainerRestart(vars["name"], timeout); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainersPause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.daemon.ContainerPause(vars["name"]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainersUnpause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.daemon.ContainerUnpause(vars["name"]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainersWait(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := s.daemon.ContainerWait(vars["name"], -1*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusOK, &types.ContainerWaitResponse{
|
||||||
|
StatusCode: status,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getContainersChanges(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
changes, err := s.daemon.ContainerChanges(vars["name"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusOK, changes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getContainersTop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
procList, err := s.daemon.ContainerTop(vars["name"], r.Form.Get("ps_args"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusOK, procList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainerRename(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := vars["name"]
|
||||||
|
newName := r.Form.Get("name")
|
||||||
|
if err := s.daemon.ContainerRename(name, newName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainersCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := checkForJSON(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
warnings []string
|
||||||
|
name = r.Form.Get("name")
|
||||||
|
)
|
||||||
|
|
||||||
|
config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
adjustCPUShares := version.LessThan("1.19")
|
||||||
|
|
||||||
|
container, warnings, err := s.daemon.ContainerCreate(name, config, hostConfig, adjustCPUShares)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusCreated, &types.ContainerCreateResponse{
|
||||||
|
ID: container.ID,
|
||||||
|
Warnings: warnings,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) deleteContainers(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := vars["name"]
|
||||||
|
config := &daemon.ContainerRmConfig{
|
||||||
|
ForceRemove: boolValue(r, "force"),
|
||||||
|
RemoveVolume: boolValue(r, "v"),
|
||||||
|
RemoveLink: boolValue(r, "link"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.daemon.ContainerRm(name, config); err != nil {
|
||||||
|
// Force a 404 for the empty string
|
||||||
|
if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") {
|
||||||
|
return fmt.Errorf("no such id: \"\"")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainersResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
height, err := strconv.Atoi(r.Form.Get("h"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
width, err := strconv.Atoi(r.Form.Get("w"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.daemon.ContainerResize(vars["name"], height, width)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
cont, err := s.daemon.Get(vars["name"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
inStream, outStream, err := hijackServer(w)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer closeStreams(inStream, outStream)
|
||||||
|
|
||||||
|
if _, ok := r.Header["Upgrade"]; ok {
|
||||||
|
fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
|
||||||
|
InStream: inStream,
|
||||||
|
OutStream: outStream,
|
||||||
|
UseStdin: boolValue(r, "stdin"),
|
||||||
|
UseStdout: boolValue(r, "stdout"),
|
||||||
|
UseStderr: boolValue(r, "stderr"),
|
||||||
|
Logs: boolValue(r, "logs"),
|
||||||
|
Stream: boolValue(r, "stream"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.daemon.ContainerAttachWithLogs(cont, attachWithLogsConfig); err != nil {
|
||||||
|
fmt.Fprintf(outStream, "Error attaching: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) wsContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
cont, err := s.daemon.Get(vars["name"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h := websocket.Handler(func(ws *websocket.Conn) {
|
||||||
|
defer ws.Close()
|
||||||
|
|
||||||
|
wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{
|
||||||
|
InStream: ws,
|
||||||
|
OutStream: ws,
|
||||||
|
ErrStream: ws,
|
||||||
|
Logs: boolValue(r, "logs"),
|
||||||
|
Stream: boolValue(r, "stream"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.daemon.ContainerWsAttachWithLogs(cont, wsAttachWithLogsConfig); err != nil {
|
||||||
|
logrus.Errorf("Error attaching websocket: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
ws := websocket.Server{Handler: h, Handshake: nil}
|
||||||
|
ws.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/pkg/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
// postContainersCopy is deprecated in favor of getContainersArchive.
|
||||||
|
func (s *Server) postContainersCopy(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := 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.daemon.ContainerCopy(vars["name"], cfg.Resource)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(strings.ToLower(err.Error()), "no such id") {
|
||||||
|
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 {
|
||||||
|
statJSON, err := json.Marshal(stat)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
header.Set(
|
||||||
|
"X-Docker-Container-Path-Stat",
|
||||||
|
base64.StdEncoding.EncodeToString(statJSON),
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) headContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
v, err := archiveFormValues(r, vars)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stat, err := s.daemon.ContainerStatPath(v.name, v.path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return setContainerPathStatHeader(stat, w.Header())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
v, err := archiveFormValues(r, vars)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tarArchive, stat, err := s.daemon.ContainerArchivePath(v.name, v.path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer tarArchive.Close()
|
||||||
|
|
||||||
|
if err := setContainerPathStatHeader(stat, w.Header()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/x-tar")
|
||||||
|
_, err = io.Copy(w, tarArchive)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) putContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
v, err := archiveFormValues(r, vars)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
noOverwriteDirNonDir := boolValue(r, "noOverwriteDirNonDir")
|
||||||
|
return s.daemon.ContainerExtractToDir(v.name, v.path, noOverwriteDirNonDir, r.Body)
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/api"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/autogen/dockerversion"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
|
"github.com/docker/docker/pkg/parsers/filters"
|
||||||
|
"github.com/docker/docker/pkg/parsers/kernel"
|
||||||
|
"github.com/docker/docker/pkg/version"
|
||||||
|
"github.com/docker/docker/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
v := &types.Version{
|
||||||
|
Version: dockerversion.VERSION,
|
||||||
|
APIVersion: api.Version,
|
||||||
|
GitCommit: dockerversion.GITCOMMIT,
|
||||||
|
GoVersion: runtime.Version(),
|
||||||
|
Os: runtime.GOOS,
|
||||||
|
Arch: runtime.GOARCH,
|
||||||
|
BuildTime: dockerversion.BUILDTIME,
|
||||||
|
}
|
||||||
|
|
||||||
|
if version.GreaterThanOrEqualTo("1.19") {
|
||||||
|
v.Experimental = utils.ExperimentalBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
||||||
|
v.KernelVersion = kernelVersion.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusOK, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getInfo(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
info, err := s.daemon.SystemInfo()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusOK, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getEvents(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var since int64 = -1
|
||||||
|
if r.Form.Get("since") != "" {
|
||||||
|
s, err := strconv.ParseInt(r.Form.Get("since"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
since = s
|
||||||
|
}
|
||||||
|
|
||||||
|
var until int64 = -1
|
||||||
|
if r.Form.Get("until") != "" {
|
||||||
|
u, err := strconv.ParseInt(r.Form.Get("until"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
until = u
|
||||||
|
}
|
||||||
|
|
||||||
|
timer := time.NewTimer(0)
|
||||||
|
timer.Stop()
|
||||||
|
if until > 0 {
|
||||||
|
dur := time.Unix(until, 0).Sub(time.Now())
|
||||||
|
timer = time.NewTimer(dur)
|
||||||
|
}
|
||||||
|
|
||||||
|
ef, err := filters.FromParam(r.Form.Get("filters"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
isFiltered := func(field string, filter []string) bool {
|
||||||
|
if len(field) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(filter) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, v := range filter {
|
||||||
|
if v == field {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if strings.Contains(field, ":") {
|
||||||
|
image := strings.Split(field, ":")
|
||||||
|
if image[0] == v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
d := s.daemon
|
||||||
|
es := d.EventsService
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
outStream := ioutils.NewWriteFlusher(w)
|
||||||
|
outStream.Write(nil) // make sure response is sent immediately
|
||||||
|
enc := json.NewEncoder(outStream)
|
||||||
|
|
||||||
|
getContainerID := func(cn string) string {
|
||||||
|
c, err := d.Get(cn)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return c.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
sendEvent := func(ev *jsonmessage.JSONMessage) error {
|
||||||
|
//incoming container filter can be name,id or partial id, convert and replace as a full container id
|
||||||
|
for i, cn := range ef["container"] {
|
||||||
|
ef["container"][i] = getContainerID(cn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isFiltered(ev.Status, ef["event"]) || (isFiltered(ev.ID, ef["image"]) &&
|
||||||
|
isFiltered(ev.From, ef["image"])) || isFiltered(ev.ID, ef["container"]) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return enc.Encode(ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
current, l := es.Subscribe()
|
||||||
|
if since == -1 {
|
||||||
|
current = nil
|
||||||
|
}
|
||||||
|
defer es.Evict(l)
|
||||||
|
for _, ev := range current {
|
||||||
|
if ev.Time < since {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := sendEvent(ev); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var closeNotify <-chan bool
|
||||||
|
if closeNotifier, ok := w.(http.CloseNotifier); ok {
|
||||||
|
closeNotify = closeNotifier.CloseNotify()
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case ev := <-l:
|
||||||
|
jev, ok := ev.(*jsonmessage.JSONMessage)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := sendEvent(jev); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-timer.C:
|
||||||
|
return nil
|
||||||
|
case <-closeNotify:
|
||||||
|
logrus.Debug("Client disconnected, stop sending events")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
|
"github.com/docker/docker/pkg/version"
|
||||||
|
"github.com/docker/docker/runconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) getExecByID(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter 'id'")
|
||||||
|
}
|
||||||
|
|
||||||
|
eConfig, err := s.daemon.ContainerExecInspect(vars["id"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusOK, eConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainerExecCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := checkForJSON(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name := vars["name"]
|
||||||
|
|
||||||
|
execConfig := &runconfig.ExecConfig{}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
execConfig.Container = name
|
||||||
|
|
||||||
|
if len(execConfig.Cmd) == 0 {
|
||||||
|
return fmt.Errorf("No exec command specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register an instance of Exec in container.
|
||||||
|
id, err := s.daemon.ContainerExecCreate(execConfig)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Error setting up exec command in container %s: %s", name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusCreated, &types.ContainerExecCreateResponse{
|
||||||
|
ID: id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
|
||||||
|
func (s *Server) postContainerExecStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
execName = vars["name"]
|
||||||
|
stdin, inStream io.ReadCloser
|
||||||
|
stdout, stderr, outStream io.Writer
|
||||||
|
)
|
||||||
|
|
||||||
|
execStartCheck := &types.ExecStartCheck{}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !execStartCheck.Detach {
|
||||||
|
var err error
|
||||||
|
// Setting up the streaming http interface.
|
||||||
|
inStream, outStream, err = hijackServer(w)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer closeStreams(inStream, outStream)
|
||||||
|
|
||||||
|
if _, ok := r.Header["Upgrade"]; ok {
|
||||||
|
fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
stdin = inStream
|
||||||
|
stdout = outStream
|
||||||
|
if !execStartCheck.Tty {
|
||||||
|
stderr = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
|
||||||
|
stdout = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outStream = w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now run the user process in container.
|
||||||
|
if err := s.daemon.ContainerExecStart(execName, stdin, stdout, stderr); err != nil {
|
||||||
|
fmt.Fprintf(outStream, "Error running exec in container: %v\n", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postContainerExecResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
height, err := strconv.Atoi(r.Form.Get("h"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
width, err := strconv.Atoi(r.Form.Get("w"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.daemon.ContainerExecResize(vars["name"], height, width)
|
||||||
|
}
|
||||||
+2
-1
@@ -3,6 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -43,7 +44,7 @@ func archiveFormValues(r *http.Request, vars map[string]string) (archiveOptions,
|
|||||||
}
|
}
|
||||||
|
|
||||||
name := vars["name"]
|
name := vars["name"]
|
||||||
path := r.Form.Get("path")
|
path := filepath.FromSlash(r.Form.Get("path"))
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case name == "":
|
case name == "":
|
||||||
|
|||||||
@@ -0,0 +1,425 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/builder"
|
||||||
|
"github.com/docker/docker/cliconfig"
|
||||||
|
"github.com/docker/docker/graph"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
"github.com/docker/docker/pkg/parsers"
|
||||||
|
"github.com/docker/docker/pkg/streamformatter"
|
||||||
|
"github.com/docker/docker/pkg/ulimit"
|
||||||
|
"github.com/docker/docker/pkg/version"
|
||||||
|
"github.com/docker/docker/runconfig"
|
||||||
|
"github.com/docker/docker/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) postCommit(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checkForJSON(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cname := r.Form.Get("container")
|
||||||
|
|
||||||
|
pause := boolValue(r, "pause")
|
||||||
|
if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
|
||||||
|
pause = true
|
||||||
|
}
|
||||||
|
|
||||||
|
c, _, err := runconfig.DecodeContainerConfig(r.Body)
|
||||||
|
if err != nil && err != io.EOF { //Do not fail if body is empty.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
commitCfg := &builder.CommitConfig{
|
||||||
|
Pause: pause,
|
||||||
|
Repo: r.Form.Get("repo"),
|
||||||
|
Tag: r.Form.Get("tag"),
|
||||||
|
Author: r.Form.Get("author"),
|
||||||
|
Comment: r.Form.Get("comment"),
|
||||||
|
Changes: r.Form["changes"],
|
||||||
|
Config: c,
|
||||||
|
}
|
||||||
|
|
||||||
|
imgID, err := builder.Commit(cname, s.daemon, commitCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusCreated, &types.ContainerCommitResponse{
|
||||||
|
ID: imgID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates an image from Pull or from Import
|
||||||
|
func (s *Server) postImagesCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
image = r.Form.Get("fromImage")
|
||||||
|
repo = r.Form.Get("repo")
|
||||||
|
tag = r.Form.Get("tag")
|
||||||
|
message = r.Form.Get("message")
|
||||||
|
)
|
||||||
|
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||||
|
authConfig := &cliconfig.AuthConfig{}
|
||||||
|
if authEncoded != "" {
|
||||||
|
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||||
|
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
|
||||||
|
// for a pull it is not an error if no auth was given
|
||||||
|
// to increase compatibility with the existing api it is defaulting to be empty
|
||||||
|
authConfig = &cliconfig.AuthConfig{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
output = ioutils.NewWriteFlusher(w)
|
||||||
|
)
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
if image != "" { //pull
|
||||||
|
if tag == "" {
|
||||||
|
image, tag = parsers.ParseRepositoryTag(image)
|
||||||
|
}
|
||||||
|
metaHeaders := map[string][]string{}
|
||||||
|
for k, v := range r.Header {
|
||||||
|
if strings.HasPrefix(k, "X-Meta-") {
|
||||||
|
metaHeaders[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imagePullConfig := &graph.ImagePullConfig{
|
||||||
|
MetaHeaders: metaHeaders,
|
||||||
|
AuthConfig: authConfig,
|
||||||
|
OutStream: output,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.daemon.Repositories().Pull(image, tag, imagePullConfig)
|
||||||
|
} else { //import
|
||||||
|
if tag == "" {
|
||||||
|
repo, tag = parsers.ParseRepositoryTag(repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
src := r.Form.Get("fromSrc")
|
||||||
|
|
||||||
|
// 'err' MUST NOT be defined within this block, we need any error
|
||||||
|
// generated from the download to be available to the output
|
||||||
|
// stream processing below
|
||||||
|
var newConfig *runconfig.Config
|
||||||
|
newConfig, err = builder.BuildFromConfig(s.daemon, &runconfig.Config{}, r.Form["changes"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.daemon.Repositories().Import(src, repo, tag, message, r.Body, output, newConfig)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if !output.Flushed() {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf := streamformatter.NewJSONStreamFormatter()
|
||||||
|
output.Write(sf.FormatError(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postImagesPush(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
metaHeaders := map[string][]string{}
|
||||||
|
for k, v := range r.Header {
|
||||||
|
if strings.HasPrefix(k, "X-Meta-") {
|
||||||
|
metaHeaders[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
authConfig := &cliconfig.AuthConfig{}
|
||||||
|
|
||||||
|
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||||
|
if authEncoded != "" {
|
||||||
|
// the new format is to handle the authConfig as a header
|
||||||
|
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||||
|
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
|
||||||
|
// to increase compatibility to existing api it is defaulting to be empty
|
||||||
|
authConfig = &cliconfig.AuthConfig{}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the old format is supported for compatibility if there was no authConfig header
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
|
||||||
|
return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name := vars["name"]
|
||||||
|
output := ioutils.NewWriteFlusher(w)
|
||||||
|
imagePushConfig := &graph.ImagePushConfig{
|
||||||
|
MetaHeaders: metaHeaders,
|
||||||
|
AuthConfig: authConfig,
|
||||||
|
Tag: r.Form.Get("tag"),
|
||||||
|
OutStream: output,
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
if err := s.daemon.Repositories().Push(name, imagePushConfig); err != nil {
|
||||||
|
if !output.Flushed() {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf := streamformatter.NewJSONStreamFormatter()
|
||||||
|
output.Write(sf.FormatError(err))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getImagesGet(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/x-tar")
|
||||||
|
|
||||||
|
output := ioutils.NewWriteFlusher(w)
|
||||||
|
var names []string
|
||||||
|
if name, ok := vars["name"]; ok {
|
||||||
|
names = []string{name}
|
||||||
|
} else {
|
||||||
|
names = r.Form["names"]
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.daemon.Repositories().ImageExport(names, output); err != nil {
|
||||||
|
if !output.Flushed() {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf := streamformatter.NewJSONStreamFormatter()
|
||||||
|
output.Write(sf.FormatError(err))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postImagesLoad(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
return s.daemon.Repositories().Load(r.Body, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) deleteImages(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := vars["name"]
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
return fmt.Errorf("image name cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
force := boolValue(r, "force")
|
||||||
|
prune := !boolValue(r, "noprune")
|
||||||
|
|
||||||
|
list, err := s.daemon.ImageDelete(name, force, prune)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusOK, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getImagesByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
imageInspect, err := s.daemon.Repositories().Lookup(vars["name"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusOK, imageInspect)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
var (
|
||||||
|
authConfigs = map[string]cliconfig.AuthConfig{}
|
||||||
|
authConfigsEncoded = r.Header.Get("X-Registry-Config")
|
||||||
|
buildConfig = builder.NewBuildConfig()
|
||||||
|
)
|
||||||
|
|
||||||
|
if authConfigsEncoded != "" {
|
||||||
|
authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
|
||||||
|
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.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") {
|
||||||
|
buildConfig.Remove = true
|
||||||
|
} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
|
||||||
|
buildConfig.Remove = true
|
||||||
|
} else {
|
||||||
|
buildConfig.Remove = boolValue(r, "rm")
|
||||||
|
}
|
||||||
|
if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") {
|
||||||
|
buildConfig.Pull = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output := ioutils.NewWriteFlusher(w)
|
||||||
|
buildConfig.Stdout = output
|
||||||
|
buildConfig.Context = r.Body
|
||||||
|
|
||||||
|
buildConfig.RemoteURL = r.FormValue("remote")
|
||||||
|
buildConfig.DockerfileName = r.FormValue("dockerfile")
|
||||||
|
buildConfig.RepoName = r.FormValue("t")
|
||||||
|
buildConfig.SuppressOutput = boolValue(r, "q")
|
||||||
|
buildConfig.NoCache = boolValue(r, "nocache")
|
||||||
|
buildConfig.ForceRemove = boolValue(r, "forcerm")
|
||||||
|
buildConfig.AuthConfigs = authConfigs
|
||||||
|
buildConfig.MemorySwap = int64ValueOrZero(r, "memswap")
|
||||||
|
buildConfig.Memory = int64ValueOrZero(r, "memory")
|
||||||
|
buildConfig.CPUShares = int64ValueOrZero(r, "cpushares")
|
||||||
|
buildConfig.CPUPeriod = int64ValueOrZero(r, "cpuperiod")
|
||||||
|
buildConfig.CPUQuota = int64ValueOrZero(r, "cpuquota")
|
||||||
|
buildConfig.CPUSetCpus = r.FormValue("cpusetcpus")
|
||||||
|
buildConfig.CPUSetMems = r.FormValue("cpusetmems")
|
||||||
|
buildConfig.CgroupParent = r.FormValue("cgroupparent")
|
||||||
|
|
||||||
|
var buildUlimits = []*ulimit.Ulimit{}
|
||||||
|
ulimitsJSON := r.FormValue("ulimits")
|
||||||
|
if ulimitsJSON != "" {
|
||||||
|
if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buildConfig.Ulimits = buildUlimits
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job cancellation. Note: not all job types support this.
|
||||||
|
if closeNotifier, ok := w.(http.CloseNotifier); ok {
|
||||||
|
finished := make(chan struct{})
|
||||||
|
defer close(finished)
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-finished:
|
||||||
|
case <-closeNotifier.CloseNotify():
|
||||||
|
logrus.Infof("Client disconnected, cancelling job: build")
|
||||||
|
buildConfig.Cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := builder.Build(s.daemon, buildConfig); err != nil {
|
||||||
|
// 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 interal error.
|
||||||
|
if !output.Flushed() {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf := streamformatter.NewJSONStreamFormatter()
|
||||||
|
w.Write(sf.FormatError(err))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getImagesJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: The filter parameter could just be a match filter
|
||||||
|
images, err := s.daemon.Repositories().Images(r.Form.Get("filters"), r.Form.Get("filter"), boolValue(r, "all"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusOK, images)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getImagesHistory(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := vars["name"]
|
||||||
|
history, err := s.daemon.Repositories().History(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeJSON(w, http.StatusOK, history)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postImagesTag(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
repo := r.Form.Get("repo")
|
||||||
|
tag := r.Form.Get("tag")
|
||||||
|
force := boolValue(r, "force")
|
||||||
|
name := vars["name"]
|
||||||
|
if err := s.daemon.Repositories().Tag(repo, tag, name, force); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.daemon.EventsService.Log("tag", utils.ImageReference(repo, tag), "")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getImagesSearch(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
config *cliconfig.AuthConfig
|
||||||
|
authEncoded = r.Header.Get("X-Registry-Auth")
|
||||||
|
headers = map[string][]string{}
|
||||||
|
)
|
||||||
|
|
||||||
|
if authEncoded != "" {
|
||||||
|
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||||
|
if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
|
||||||
|
// for a search it is not an error if no auth was given
|
||||||
|
// to increase compatibility with the existing api it is defaulting to be empty
|
||||||
|
config = &cliconfig.AuthConfig{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range r.Header {
|
||||||
|
if strings.HasPrefix(k, "X-Meta-") {
|
||||||
|
headers[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query, err := s.daemon.RegistryService.Search(r.Form.Get("term"), config, headers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeJSON(w, http.StatusOK, query.Results)
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProfilerSetup(mainRouter *mux.Router, path string) {
|
func profilerSetup(mainRouter *mux.Router, path string) {
|
||||||
var r = mainRouter.PathPrefix(path).Subrouter()
|
var r = mainRouter.PathPrefix(path).Subrouter()
|
||||||
r.HandleFunc("/vars", expVars)
|
r.HandleFunc("/vars", expVars)
|
||||||
r.HandleFunc("/pprof/", pprof.Index)
|
r.HandleFunc("/pprof/", pprof.Index)
|
||||||
|
|||||||
+34
-1353
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
|||||||
// +build experimental
|
// +build experimental,!windows
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
func (s *Server) registerSubRouter() {
|
func (s *Server) registerSubRouter() {
|
||||||
httpHandler := s.daemon.NetworkApiRouter()
|
httpHandler := s.daemon.NetworkAPIRouter()
|
||||||
|
|
||||||
subrouter := s.router.PathPrefix("/v{version:[0-9.]+}/networks").Subrouter()
|
subrouter := s.router.PathPrefix("/v{version:[0-9.]+}/networks").Subrouter()
|
||||||
subrouter.Methods("GET", "POST", "PUT", "DELETE").HandlerFunc(httpHandler)
|
subrouter.Methods("GET", "POST", "PUT", "DELETE").HandlerFunc(httpHandler)
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
// +build linux
|
|
||||||
|
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/version"
|
|
||||||
"github.com/docker/docker/runconfig"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAdjustCpuSharesOldApi(t *testing.T) {
|
|
||||||
apiVersion := version.Version("1.18")
|
|
||||||
hostConfig := &runconfig.HostConfig{
|
|
||||||
CpuShares: linuxMinCpuShares - 1,
|
|
||||||
}
|
|
||||||
adjustCpuShares(apiVersion, hostConfig)
|
|
||||||
if hostConfig.CpuShares != linuxMinCpuShares {
|
|
||||||
t.Errorf("Expected CpuShares to be %d", linuxMinCpuShares)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CpuShares = linuxMaxCpuShares + 1
|
|
||||||
adjustCpuShares(apiVersion, hostConfig)
|
|
||||||
if hostConfig.CpuShares != linuxMaxCpuShares {
|
|
||||||
t.Errorf("Expected CpuShares to be %d", linuxMaxCpuShares)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CpuShares = 0
|
|
||||||
adjustCpuShares(apiVersion, hostConfig)
|
|
||||||
if hostConfig.CpuShares != 0 {
|
|
||||||
t.Error("Expected CpuShares to be unchanged")
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CpuShares = 1024
|
|
||||||
adjustCpuShares(apiVersion, hostConfig)
|
|
||||||
if hostConfig.CpuShares != 1024 {
|
|
||||||
t.Error("Expected CpuShares to be unchanged")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAdjustCpuSharesNoAdjustment(t *testing.T) {
|
|
||||||
apiVersion := version.Version("1.19")
|
|
||||||
hostConfig := &runconfig.HostConfig{
|
|
||||||
CpuShares: linuxMinCpuShares - 1,
|
|
||||||
}
|
|
||||||
adjustCpuShares(apiVersion, hostConfig)
|
|
||||||
if hostConfig.CpuShares != linuxMinCpuShares-1 {
|
|
||||||
t.Errorf("Expected CpuShares to be %d", linuxMinCpuShares-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CpuShares = linuxMaxCpuShares + 1
|
|
||||||
adjustCpuShares(apiVersion, hostConfig)
|
|
||||||
if hostConfig.CpuShares != linuxMaxCpuShares+1 {
|
|
||||||
t.Errorf("Expected CpuShares to be %d", linuxMaxCpuShares+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CpuShares = 0
|
|
||||||
adjustCpuShares(apiVersion, hostConfig)
|
|
||||||
if hostConfig.CpuShares != 0 {
|
|
||||||
t.Error("Expected CpuShares to be unchanged")
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CpuShares = 1024
|
|
||||||
adjustCpuShares(apiVersion, hostConfig)
|
|
||||||
if hostConfig.CpuShares != 1024 {
|
|
||||||
t.Error("Expected CpuShares to be unchanged")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !experimental
|
// +build !experimental windows
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build linux
|
// +build freebsd linux
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
@@ -8,19 +8,12 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
"github.com/docker/docker/pkg/sockets"
|
"github.com/docker/docker/pkg/sockets"
|
||||||
"github.com/docker/docker/pkg/systemd"
|
|
||||||
"github.com/docker/docker/pkg/version"
|
|
||||||
"github.com/docker/docker/runconfig"
|
|
||||||
"github.com/docker/libnetwork/portallocator"
|
"github.com/docker/libnetwork/portallocator"
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
systemdActivation "github.com/coreos/go-systemd/activation"
|
||||||
// See http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269
|
systemdDaemon "github.com/coreos/go-systemd/daemon"
|
||||||
linuxMinCpuShares = 2
|
|
||||||
linuxMaxCpuShares = 262144
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// newServer sets up the required serverClosers and does protocol specific checking.
|
// newServer sets up the required serverClosers and does protocol specific checking.
|
||||||
@@ -31,7 +24,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
|||||||
)
|
)
|
||||||
switch proto {
|
switch proto {
|
||||||
case "fd":
|
case "fd":
|
||||||
ls, err = systemd.ListenFD(addr)
|
ls, err = listenFD(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -40,7 +33,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
|||||||
// won't be ready.
|
// won't be ready.
|
||||||
<-s.start
|
<-s.start
|
||||||
case "tcp":
|
case "tcp":
|
||||||
l, err := s.initTcpSocket(addr)
|
l, err := s.initTCPSocket(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -56,7 +49,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
|||||||
}
|
}
|
||||||
var res []serverCloser
|
var res []serverCloser
|
||||||
for _, l := range ls {
|
for _, l := range ls {
|
||||||
res = append(res, &HttpServer{
|
res = append(res, &HTTPServer{
|
||||||
&http.Server{
|
&http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: s.router,
|
Handler: s.router,
|
||||||
@@ -67,11 +60,14 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AcceptConnections allows clients to connect to the API server.
|
||||||
|
// Referenced Daemon is notified about this server, and waits for the
|
||||||
|
// daemon acknowledgement before the incoming connections are accepted.
|
||||||
func (s *Server) AcceptConnections(d *daemon.Daemon) {
|
func (s *Server) AcceptConnections(d *daemon.Daemon) {
|
||||||
// Tell the init daemon we are accepting requests
|
// Tell the init daemon we are accepting requests
|
||||||
s.daemon = d
|
s.daemon = d
|
||||||
s.registerSubRouter()
|
s.registerSubRouter()
|
||||||
go systemd.SdNotify("READY=1")
|
go systemdDaemon.SdNotify("READY=1")
|
||||||
// close the lock so the listeners start accepting connections
|
// close the lock so the listeners start accepting connections
|
||||||
select {
|
select {
|
||||||
case <-s.start:
|
case <-s.start:
|
||||||
@@ -107,17 +103,43 @@ func allocateDaemonPort(addr string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func adjustCpuShares(version version.Version, hostConfig *runconfig.HostConfig) {
|
// getContainersByNameDownlevel performs processing for pre 1.20 APIs. This
|
||||||
if version.LessThan("1.19") {
|
// is only relevant on non-Windows daemons.
|
||||||
if hostConfig != nil && hostConfig.CpuShares > 0 {
|
func getContainersByNameDownlevel(w http.ResponseWriter, s *Server, namevar string) error {
|
||||||
// Handle unsupported CpuShares
|
containerJSONRaw, err := s.daemon.ContainerInspectPre120(namevar)
|
||||||
if hostConfig.CpuShares < linuxMinCpuShares {
|
if err != nil {
|
||||||
logrus.Warnf("Changing requested CpuShares of %d to minimum allowed of %d", hostConfig.CpuShares, linuxMinCpuShares)
|
return err
|
||||||
hostConfig.CpuShares = linuxMinCpuShares
|
|
||||||
} else if hostConfig.CpuShares > linuxMaxCpuShares {
|
|
||||||
logrus.Warnf("Changing requested CpuShares of %d to maximum allowed of %d", hostConfig.CpuShares, linuxMaxCpuShares)
|
|
||||||
hostConfig.CpuShares = linuxMaxCpuShares
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return writeJSON(w, http.StatusOK, containerJSONRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// listenFD returns the specified socket activated files as a slice of
|
||||||
|
// net.Listeners or all of the activated files if "*" is given.
|
||||||
|
func listenFD(addr string) ([]net.Listener, error) {
|
||||||
|
// socket activation
|
||||||
|
listeners, err := systemdActivation.Listeners(false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if listeners == nil || len(listeners) == 0 {
|
||||||
|
return nil, fmt.Errorf("No sockets found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// default to all fds just like unix:// and tcp://
|
||||||
|
if addr == "" {
|
||||||
|
addr = "*"
|
||||||
|
}
|
||||||
|
|
||||||
|
fdNum, _ := strconv.Atoi(addr)
|
||||||
|
fdOffset := fdNum - 3
|
||||||
|
if (addr != "*") && (len(listeners) < int(fdOffset)+1) {
|
||||||
|
return nil, fmt.Errorf("Too few socket activated files passed in")
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr == "*" {
|
||||||
|
return listeners, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return []net.Listener{listeners[fdOffset]}, nil
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
"github.com/docker/docker/pkg/version"
|
|
||||||
"github.com/docker/docker/runconfig"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewServer sets up the required Server and does protocol specific checking.
|
// NewServer sets up the required Server and does protocol specific checking.
|
||||||
@@ -19,7 +17,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
|||||||
)
|
)
|
||||||
switch proto {
|
switch proto {
|
||||||
case "tcp":
|
case "tcp":
|
||||||
l, err := s.initTcpSocket(addr)
|
l, err := s.initTCPSocket(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -31,7 +29,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
|||||||
|
|
||||||
var res []serverCloser
|
var res []serverCloser
|
||||||
for _, l := range ls {
|
for _, l := range ls {
|
||||||
res = append(res, &HttpServer{
|
res = append(res, &HTTPServer{
|
||||||
&http.Server{
|
&http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: s.router,
|
Handler: s.router,
|
||||||
@@ -43,6 +41,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AcceptConnections allows router to start listening for the incoming requests.
|
||||||
func (s *Server) AcceptConnections(d *daemon.Daemon) {
|
func (s *Server) AcceptConnections(d *daemon.Daemon) {
|
||||||
s.daemon = d
|
s.daemon = d
|
||||||
s.registerSubRouter()
|
s.registerSubRouter()
|
||||||
@@ -58,5 +57,8 @@ func allocateDaemonPort(addr string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func adjustCpuShares(version version.Version, hostConfig *runconfig.HostConfig) {
|
// getContainersByNameDownlevel performs processing for pre 1.20 APIs. This
|
||||||
|
// is only relevant on non-Windows daemons.
|
||||||
|
func getContainersByNameDownlevel(w http.ResponseWriter, s *Server, namevar string) error {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/pkg/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) getVolumesList(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
volumes, err := s.daemon.Volumes(r.Form.Get("filters"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeJSON(w, http.StatusOK, &types.VolumesListResponse{Volumes: volumes})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getVolumeByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := s.daemon.VolumeInspect(vars["name"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeJSON(w, http.StatusOK, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) postVolumesCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checkForJSON(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req types.VolumeCreateRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
volume, err := s.daemon.VolumeCreate(req.Name, req.Driver, req.DriverOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeJSON(w, http.StatusCreated, volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) deleteVolumes(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.daemon.VolumeRm(vars["name"]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsTimeout takes an error returned from (generally) the http package and determines if it is a timeout error.
|
||||||
|
func IsTimeout(err error) bool {
|
||||||
|
switch e := err.(type) {
|
||||||
|
case net.Error:
|
||||||
|
return e.Timeout()
|
||||||
|
case *url.Error:
|
||||||
|
if t, ok := e.Err.(net.Error); ok {
|
||||||
|
return t.Timeout()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
-12
@@ -1,9 +1,10 @@
|
|||||||
// This package is used for API stability in the types and response to the
|
// Package types is used for API stability in the types and response to the
|
||||||
// consumers of the API stats endpoint.
|
// consumers of the API stats endpoint.
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
|
// ThrottlingData stores CPU throttling stats of one running container
|
||||||
type ThrottlingData struct {
|
type ThrottlingData struct {
|
||||||
// Number of periods with throttling active
|
// Number of periods with throttling active
|
||||||
Periods uint64 `json:"periods"`
|
Periods uint64 `json:"periods"`
|
||||||
@@ -13,8 +14,8 @@ type ThrottlingData struct {
|
|||||||
ThrottledTime uint64 `json:"throttled_time"`
|
ThrottledTime uint64 `json:"throttled_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// All CPU stats are aggregated since container inception.
|
// CPUUsage stores All CPU stats aggregated since container inception.
|
||||||
type CpuUsage struct {
|
type CPUUsage struct {
|
||||||
// Total CPU time consumed.
|
// Total CPU time consumed.
|
||||||
// Units: nanoseconds.
|
// Units: nanoseconds.
|
||||||
TotalUsage uint64 `json:"total_usage"`
|
TotalUsage uint64 `json:"total_usage"`
|
||||||
@@ -29,12 +30,14 @@ type CpuUsage struct {
|
|||||||
UsageInUsermode uint64 `json:"usage_in_usermode"`
|
UsageInUsermode uint64 `json:"usage_in_usermode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CpuStats struct {
|
// CPUStats aggregates and wraps all CPU related info of container
|
||||||
CpuUsage CpuUsage `json:"cpu_usage"`
|
type CPUStats struct {
|
||||||
|
CPUUsage CPUUsage `json:"cpu_usage"`
|
||||||
SystemUsage uint64 `json:"system_cpu_usage"`
|
SystemUsage uint64 `json:"system_cpu_usage"`
|
||||||
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MemoryStats aggregates All memory stats since container inception
|
||||||
type MemoryStats struct {
|
type MemoryStats struct {
|
||||||
// current res_counter usage for memory
|
// current res_counter usage for memory
|
||||||
Usage uint64 `json:"usage"`
|
Usage uint64 `json:"usage"`
|
||||||
@@ -48,6 +51,7 @@ type MemoryStats struct {
|
|||||||
Limit uint64 `json:"limit"`
|
Limit uint64 `json:"limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlkioStatEntry is one small entity to store a piece of Blkio stats
|
||||||
// TODO Windows: This can be factored out
|
// TODO Windows: This can be factored out
|
||||||
type BlkioStatEntry struct {
|
type BlkioStatEntry struct {
|
||||||
Major uint64 `json:"major"`
|
Major uint64 `json:"major"`
|
||||||
@@ -56,6 +60,7 @@ type BlkioStatEntry struct {
|
|||||||
Value uint64 `json:"value"`
|
Value uint64 `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlkioStats stores All IO service stats for data read and write
|
||||||
// TODO Windows: This can be factored out
|
// TODO Windows: This can be factored out
|
||||||
type BlkioStats struct {
|
type BlkioStats struct {
|
||||||
// number of bytes tranferred to and from the block device
|
// number of bytes tranferred to and from the block device
|
||||||
@@ -69,8 +74,9 @@ type BlkioStats struct {
|
|||||||
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"`
|
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NetworkStats aggregates All network stats of one container
|
||||||
// TODO Windows: This will require refactoring
|
// TODO Windows: This will require refactoring
|
||||||
type Network struct {
|
type NetworkStats struct {
|
||||||
RxBytes uint64 `json:"rx_bytes"`
|
RxBytes uint64 `json:"rx_bytes"`
|
||||||
RxPackets uint64 `json:"rx_packets"`
|
RxPackets uint64 `json:"rx_packets"`
|
||||||
RxErrors uint64 `json:"rx_errors"`
|
RxErrors uint64 `json:"rx_errors"`
|
||||||
@@ -81,11 +87,12 @@ type Network struct {
|
|||||||
TxDropped uint64 `json:"tx_dropped"`
|
TxDropped uint64 `json:"tx_dropped"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stats is Ultimate struct aggregating all types of stats of one container
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
Read time.Time `json:"read"`
|
Read time.Time `json:"read"`
|
||||||
Network Network `json:"network,omitempty"`
|
Network NetworkStats `json:"network,omitempty"`
|
||||||
PreCpuStats CpuStats `json:"precpu_stats,omitempty"`
|
PreCPUStats CPUStats `json:"precpu_stats,omitempty"`
|
||||||
CpuStats CpuStats `json:"cpu_stats,omitempty"`
|
CPUStats CPUStats `json:"cpu_stats,omitempty"`
|
||||||
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
||||||
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
+72
-25
@@ -19,35 +19,41 @@ type ContainerCreateResponse struct {
|
|||||||
Warnings []string `json:"Warnings"`
|
Warnings []string `json:"Warnings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST /containers/{name:.*}/exec
|
// ContainerExecCreateResponse contains response of Remote API:
|
||||||
|
// POST "/containers/{name:.*}/exec"
|
||||||
type ContainerExecCreateResponse struct {
|
type ContainerExecCreateResponse struct {
|
||||||
// ID is the exec ID.
|
// ID is the exec ID.
|
||||||
ID string `json:"Id"`
|
ID string `json:"Id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST /auth
|
// AuthResponse contains response of Remote API:
|
||||||
|
// POST "/auth"
|
||||||
type AuthResponse struct {
|
type AuthResponse struct {
|
||||||
// Status is the authentication status
|
// Status is the authentication status
|
||||||
Status string `json:"Status"`
|
Status string `json:"Status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerWaitResponse contains response of Remote API:
|
||||||
// POST "/containers/"+containerID+"/wait"
|
// POST "/containers/"+containerID+"/wait"
|
||||||
type ContainerWaitResponse struct {
|
type ContainerWaitResponse struct {
|
||||||
// StatusCode is the status code of the wait job
|
// StatusCode is the status code of the wait job
|
||||||
StatusCode int `json:"StatusCode"`
|
StatusCode int `json:"StatusCode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerCommitResponse contains response of Remote API:
|
||||||
// POST "/commit?container="+containerID
|
// POST "/commit?container="+containerID
|
||||||
type ContainerCommitResponse struct {
|
type ContainerCommitResponse struct {
|
||||||
ID string `json:"Id"`
|
ID string `json:"Id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerChange contains response of Remote API:
|
||||||
// GET "/containers/{name:.*}/changes"
|
// GET "/containers/{name:.*}/changes"
|
||||||
type ContainerChange struct {
|
type ContainerChange struct {
|
||||||
Kind int
|
Kind int
|
||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImageHistory contains response of Remote API:
|
||||||
// GET "/images/{name:.*}/history"
|
// GET "/images/{name:.*}/history"
|
||||||
type ImageHistory struct {
|
type ImageHistory struct {
|
||||||
ID string `json:"Id"`
|
ID string `json:"Id"`
|
||||||
@@ -58,32 +64,37 @@ type ImageHistory struct {
|
|||||||
Comment string
|
Comment string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImageDelete contains response of Remote API:
|
||||||
// DELETE "/images/{name:.*}"
|
// DELETE "/images/{name:.*}"
|
||||||
type ImageDelete struct {
|
type ImageDelete struct {
|
||||||
Untagged string `json:",omitempty"`
|
Untagged string `json:",omitempty"`
|
||||||
Deleted string `json:",omitempty"`
|
Deleted string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Image contains response of Remote API:
|
||||||
// GET "/images/json"
|
// GET "/images/json"
|
||||||
type Image struct {
|
type Image struct {
|
||||||
ID string `json:"Id"`
|
ID string `json:"Id"`
|
||||||
ParentId string
|
ParentID string `json:"ParentId"`
|
||||||
RepoTags []string
|
RepoTags []string
|
||||||
RepoDigests []string
|
RepoDigests []string
|
||||||
Created int
|
Created int64
|
||||||
Size int
|
Size int64
|
||||||
VirtualSize int
|
VirtualSize int64
|
||||||
Labels map[string]string
|
Labels map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GraphDriverData returns Image's graph driver config info
|
||||||
|
// when calling inspect command
|
||||||
type GraphDriverData struct {
|
type GraphDriverData struct {
|
||||||
Name string
|
Name string
|
||||||
Data map[string]string
|
Data map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImageInspect contains response of Remote API:
|
||||||
// GET "/images/{name:.*}/json"
|
// GET "/images/{name:.*}/json"
|
||||||
type ImageInspect struct {
|
type ImageInspect struct {
|
||||||
Id string
|
ID string `json:"Id"`
|
||||||
Parent string
|
Parent string
|
||||||
Comment string
|
Comment string
|
||||||
Created string
|
Created string
|
||||||
@@ -99,7 +110,8 @@ type ImageInspect struct {
|
|||||||
GraphDriver GraphDriverData
|
GraphDriver GraphDriverData
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET "/containers/json"
|
// Port stores open ports info of container
|
||||||
|
// e.g. {"PrivatePort": 8080, "PublicPort": 80, "Type": "tcp"}
|
||||||
type Port struct {
|
type Port struct {
|
||||||
IP string `json:",omitempty"`
|
IP string `json:",omitempty"`
|
||||||
PrivatePort int
|
PrivatePort int
|
||||||
@@ -107,15 +119,17 @@ type Port struct {
|
|||||||
Type string
|
Type string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Container contains response of Remote API:
|
||||||
|
// GET "/containers/json"
|
||||||
type Container struct {
|
type Container struct {
|
||||||
ID string `json:"Id"`
|
ID string `json:"Id"`
|
||||||
Names []string
|
Names []string
|
||||||
Image string
|
Image string
|
||||||
Command string
|
Command string
|
||||||
Created int
|
Created int64
|
||||||
Ports []Port
|
Ports []Port
|
||||||
SizeRw int `json:",omitempty"`
|
SizeRw int64 `json:",omitempty"`
|
||||||
SizeRootFs int `json:",omitempty"`
|
SizeRootFs int64 `json:",omitempty"`
|
||||||
Labels map[string]string
|
Labels map[string]string
|
||||||
Status string
|
Status string
|
||||||
HostConfig struct {
|
HostConfig struct {
|
||||||
@@ -123,14 +137,15 @@ type Container struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CopyConfig contains request body of Remote API:
|
||||||
// POST "/containers/"+containerID+"/copy"
|
// POST "/containers/"+containerID+"/copy"
|
||||||
type CopyConfig struct {
|
type CopyConfig struct {
|
||||||
Resource string
|
Resource string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerPathStat is used to encode the header from
|
// ContainerPathStat is used to encode the header from
|
||||||
// GET /containers/{name:.*}/archive
|
// GET "/containers/{name:.*}/archive"
|
||||||
// "name" is basename of the resource.
|
// "Name" is the file or directory name.
|
||||||
type ContainerPathStat struct {
|
type ContainerPathStat struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Size int64 `json:"size"`
|
Size int64 `json:"size"`
|
||||||
@@ -139,15 +154,18 @@ type ContainerPathStat struct {
|
|||||||
LinkTarget string `json:"linkTarget"`
|
LinkTarget string `json:"linkTarget"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerProcessList contains response of Remote API:
|
||||||
// GET "/containers/{name:.*}/top"
|
// GET "/containers/{name:.*}/top"
|
||||||
type ContainerProcessList struct {
|
type ContainerProcessList struct {
|
||||||
Processes [][]string
|
Processes [][]string
|
||||||
Titles []string
|
Titles []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version contains response of Remote API:
|
||||||
|
// GET "/version"
|
||||||
type Version struct {
|
type Version struct {
|
||||||
Version string
|
Version string
|
||||||
ApiVersion version.Version
|
APIVersion version.Version `json:"ApiVersion"`
|
||||||
GitCommit string
|
GitCommit string
|
||||||
GoVersion string
|
GoVersion string
|
||||||
Os string
|
Os string
|
||||||
@@ -157,6 +175,7 @@ type Version struct {
|
|||||||
BuildTime string `json:",omitempty"`
|
BuildTime string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Info contains response of Remote API:
|
||||||
// GET "/info"
|
// GET "/info"
|
||||||
type Info struct {
|
type Info struct {
|
||||||
ID string
|
ID string
|
||||||
@@ -166,11 +185,11 @@ type Info struct {
|
|||||||
DriverStatus [][2]string
|
DriverStatus [][2]string
|
||||||
MemoryLimit bool
|
MemoryLimit bool
|
||||||
SwapLimit bool
|
SwapLimit bool
|
||||||
CpuCfsPeriod bool
|
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
|
||||||
CpuCfsQuota bool
|
CPUCfsQuota bool `json:"CpuCfsQuota"`
|
||||||
IPv4Forwarding bool
|
IPv4Forwarding bool
|
||||||
BridgeNfIptables bool
|
BridgeNfIptables bool
|
||||||
BridgeNfIp6tables bool
|
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"`
|
||||||
Debug bool
|
Debug bool
|
||||||
NFd int
|
NFd int
|
||||||
OomKillDisable bool
|
OomKillDisable bool
|
||||||
@@ -188,15 +207,15 @@ type Info struct {
|
|||||||
NCPU int
|
NCPU int
|
||||||
MemTotal int64
|
MemTotal int64
|
||||||
DockerRootDir string
|
DockerRootDir string
|
||||||
HttpProxy string
|
HTTPProxy string `json:"HttpProxy"`
|
||||||
HttpsProxy string
|
HTTPSProxy string `json:"HttpsProxy"`
|
||||||
NoProxy string
|
NoProxy string
|
||||||
Name string
|
Name string
|
||||||
Labels []string
|
Labels []string
|
||||||
ExperimentalBuild bool
|
ExperimentalBuild bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// This struct is a temp struct used by execStart
|
// ExecStartCheck is a temp struct used by execStart
|
||||||
// Config fields is part of ExecConfig in runconfig package
|
// Config fields is part of ExecConfig in runconfig package
|
||||||
type ExecStartCheck struct {
|
type ExecStartCheck struct {
|
||||||
// ExecStart will first check if it's detached
|
// ExecStart will first check if it's detached
|
||||||
@@ -205,7 +224,10 @@ type ExecStartCheck struct {
|
|||||||
Tty bool
|
Tty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerState stores container's running state
|
||||||
|
// it's part of ContainerJSONBase and will return by "inspect" command
|
||||||
type ContainerState struct {
|
type ContainerState struct {
|
||||||
|
Status string
|
||||||
Running bool
|
Running bool
|
||||||
Paused bool
|
Paused bool
|
||||||
Restarting bool
|
Restarting bool
|
||||||
@@ -218,9 +240,10 @@ type ContainerState struct {
|
|||||||
FinishedAt string
|
FinishedAt string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerJSONBase contains response of Remote API:
|
||||||
// GET "/containers/{name:.*}/json"
|
// GET "/containers/{name:.*}/json"
|
||||||
type ContainerJSONBase struct {
|
type ContainerJSONBase struct {
|
||||||
Id string
|
ID string `json:"Id"`
|
||||||
Created string
|
Created string
|
||||||
Path string
|
Path string
|
||||||
Args []string
|
Args []string
|
||||||
@@ -243,13 +266,15 @@ type ContainerJSONBase struct {
|
|||||||
GraphDriver GraphDriverData
|
GraphDriver GraphDriverData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerJSON is newly used struct along with MountPoint
|
||||||
type ContainerJSON struct {
|
type ContainerJSON struct {
|
||||||
*ContainerJSONBase
|
*ContainerJSONBase
|
||||||
Mounts []MountPoint
|
Mounts []MountPoint
|
||||||
Config *runconfig.Config
|
Config *runconfig.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// backcompatibility struct along with ContainerConfig
|
// ContainerJSONPre120 is a backcompatibility struct along with ContainerConfig.
|
||||||
|
// Note this is not used by the Windows daemon.
|
||||||
type ContainerJSONPre120 struct {
|
type ContainerJSONPre120 struct {
|
||||||
*ContainerJSONBase
|
*ContainerJSONBase
|
||||||
Volumes map[string]string
|
Volumes map[string]string
|
||||||
@@ -257,14 +282,15 @@ type ContainerJSONPre120 struct {
|
|||||||
Config *ContainerConfig
|
Config *ContainerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerConfig is a backcompatibility struct used in ContainerJSONPre120
|
||||||
type ContainerConfig struct {
|
type ContainerConfig struct {
|
||||||
*runconfig.Config
|
*runconfig.Config
|
||||||
|
|
||||||
// backward compatibility, they now live in HostConfig
|
// backward compatibility, they now live in HostConfig
|
||||||
Memory int64
|
Memory int64
|
||||||
MemorySwap int64
|
MemorySwap int64
|
||||||
CpuShares int64
|
CPUShares int64 `json:"CpuShares"`
|
||||||
Cpuset string
|
CPUSet string `json:"CpuSet"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MountPoint represents a mount point configuration inside the container.
|
// MountPoint represents a mount point configuration inside the container.
|
||||||
@@ -273,6 +299,27 @@ type MountPoint struct {
|
|||||||
Source string
|
Source string
|
||||||
Destination string
|
Destination string
|
||||||
Driver string `json:",omitempty"`
|
Driver string `json:",omitempty"`
|
||||||
Mode string // this is internally named `Relabel`
|
Mode string
|
||||||
RW bool
|
RW bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Volume represents the configuration of a volume for the remote API
|
||||||
|
type Volume struct {
|
||||||
|
Name string // Name is the name of the volume
|
||||||
|
Driver string // Driver is the Driver name used to create the volume
|
||||||
|
Mountpoint string // Mountpoint is the location on disk of the volume
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumesListResponse contains the response for the remote API:
|
||||||
|
// GET "/volumes"
|
||||||
|
type VolumesListResponse struct {
|
||||||
|
Volumes []*Volume // Volumes is the list of volumes being returned
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeCreateRequest contains the response for the remote API:
|
||||||
|
// POST "/volumes"
|
||||||
|
type VolumeCreateRequest struct {
|
||||||
|
Name string // Name is the requested name of the volume
|
||||||
|
Driver string // Driver is the name of the driver that should be used to create the volume
|
||||||
|
DriverOpts map[string]string // DriverOpts holds the driver specific options to use for when creating the volume.
|
||||||
|
}
|
||||||
|
|||||||
+13
-1
@@ -38,6 +38,7 @@ func NewBFlags() *BFlags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddBool adds a bool flag to BFlags
|
// AddBool adds a bool flag to BFlags
|
||||||
|
// Note, any error will be generated when Parse() is called (see Parse).
|
||||||
func (bf *BFlags) AddBool(name string, def bool) *Flag {
|
func (bf *BFlags) AddBool(name string, def bool) *Flag {
|
||||||
flag := bf.addFlag(name, boolType)
|
flag := bf.addFlag(name, boolType)
|
||||||
if flag == nil {
|
if flag == nil {
|
||||||
@@ -52,6 +53,7 @@ func (bf *BFlags) AddBool(name string, def bool) *Flag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddString adds a string flag to BFlags
|
// AddString adds a string flag to BFlags
|
||||||
|
// Note, any error will be generated when Parse() is called (see Parse).
|
||||||
func (bf *BFlags) AddString(name string, def string) *Flag {
|
func (bf *BFlags) AddString(name string, def string) *Flag {
|
||||||
flag := bf.addFlag(name, stringType)
|
flag := bf.addFlag(name, stringType)
|
||||||
if flag == nil {
|
if flag == nil {
|
||||||
@@ -61,6 +63,9 @@ func (bf *BFlags) AddString(name string, def string) *Flag {
|
|||||||
return flag
|
return flag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addFlag is a generic func used by the other AddXXX() func
|
||||||
|
// to add a new flag to the BFlags struct.
|
||||||
|
// Note, any error will be generated when Parse() is called (see Parse).
|
||||||
func (bf *BFlags) addFlag(name string, flagType FlagType) *Flag {
|
func (bf *BFlags) addFlag(name string, flagType FlagType) *Flag {
|
||||||
if _, ok := bf.flags[name]; ok {
|
if _, ok := bf.flags[name]; ok {
|
||||||
bf.Err = fmt.Errorf("Duplicate flag defined: %s", name)
|
bf.Err = fmt.Errorf("Duplicate flag defined: %s", name)
|
||||||
@@ -94,7 +99,14 @@ func (fl *Flag) IsTrue() bool {
|
|||||||
return fl.Value == "true"
|
return fl.Value == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses and checks if the BFlags is valid
|
// Parse parses and checks if the BFlags is valid.
|
||||||
|
// Any error noticed during the AddXXX() funcs will be generated/returned
|
||||||
|
// here. We do this because an error during AddXXX() is more like a
|
||||||
|
// compile time error so it doesn't matter too much when we stop our
|
||||||
|
// processing as long as we do stop it, so this allows the code
|
||||||
|
// around AddXXX() to be just:
|
||||||
|
// defFlag := AddString("desription", "")
|
||||||
|
// w/o needing to add an if-statement around each one.
|
||||||
func (bf *BFlags) Parse() error {
|
func (bf *BFlags) Parse() error {
|
||||||
// If there was an error while defining the possible flags
|
// If there was an error while defining the possible flags
|
||||||
// go ahead and bubble it back up here since we didn't do it
|
// go ahead and bubble it back up here since we didn't do it
|
||||||
|
|||||||
+18
-36
@@ -10,7 +10,7 @@ package builder
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -20,6 +20,8 @@ import (
|
|||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
"github.com/docker/docker/pkg/nat"
|
"github.com/docker/docker/pkg/nat"
|
||||||
|
"github.com/docker/docker/pkg/stringutils"
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -194,7 +196,11 @@ func from(b *builder, args []string, attributes map[string]bool, original string
|
|||||||
|
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
|
// Windows cannot support a container with no base image.
|
||||||
if name == NoBaseImageSpecifier {
|
if name == NoBaseImageSpecifier {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return fmt.Errorf("Windows does not support FROM scratch")
|
||||||
|
}
|
||||||
b.image = ""
|
b.image = ""
|
||||||
b.noBaseImage = true
|
b.noBaseImage = true
|
||||||
return nil
|
return nil
|
||||||
@@ -267,39 +273,15 @@ func workdir(b *builder, args []string, attributes map[string]bool, original str
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that workdir passed comes from the Dockerfile. Hence it is in
|
// This is from the Dockerfile and will not necessarily be in platform
|
||||||
// Linux format using forward-slashes, even on Windows. However,
|
// specific semantics, hence ensure it is converted.
|
||||||
// b.Config.WorkingDir is in platform-specific notation (in other words
|
workdir := filepath.FromSlash(args[0])
|
||||||
// on Windows will use `\`
|
|
||||||
workdir := args[0]
|
|
||||||
|
|
||||||
isAbs := false
|
if !system.IsAbs(workdir) {
|
||||||
if runtime.GOOS == "windows" {
|
current := filepath.FromSlash(b.Config.WorkingDir)
|
||||||
// Alternate processing for Windows here is necessary as we can't call
|
workdir = filepath.Join(string(os.PathSeparator), current, workdir)
|
||||||
// filepath.IsAbs(workDir) as that would verify Windows style paths,
|
|
||||||
// along with drive-letters (eg c:\pathto\file.txt). We (arguably
|
|
||||||
// correctly or not) check for both forward and back slashes as this
|
|
||||||
// is what the 1.4.2 GoLang implementation of IsAbs() does in the
|
|
||||||
// isSlash() function.
|
|
||||||
isAbs = workdir[0] == '\\' || workdir[0] == '/'
|
|
||||||
} else {
|
|
||||||
isAbs = filepath.IsAbs(workdir)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isAbs {
|
|
||||||
current := b.Config.WorkingDir
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// Convert to Linux format before join
|
|
||||||
current = strings.Replace(current, "\\", "/", -1)
|
|
||||||
}
|
|
||||||
// Must use path.Join so works correctly on Windows, not filepath
|
|
||||||
workdir = path.Join("/", current, workdir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to platform specific format
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
workdir = strings.Replace(workdir, "/", "\\", -1)
|
|
||||||
}
|
|
||||||
b.Config.WorkingDir = workdir
|
b.Config.WorkingDir = workdir
|
||||||
|
|
||||||
return b.commit("", b.Config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
|
return b.commit("", b.Config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
|
||||||
@@ -348,7 +330,7 @@ func run(b *builder, args []string, attributes map[string]bool, original string)
|
|||||||
b.Config.Cmd = config.Cmd
|
b.Config.Cmd = config.Cmd
|
||||||
runconfig.Merge(b.Config, config)
|
runconfig.Merge(b.Config, config)
|
||||||
|
|
||||||
defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
|
defer func(cmd *stringutils.StrSlice) { b.Config.Cmd = cmd }(cmd)
|
||||||
|
|
||||||
logrus.Debugf("[BUILDER] Command to be executed: %v", b.Config.Cmd)
|
logrus.Debugf("[BUILDER] Command to be executed: %v", b.Config.Cmd)
|
||||||
|
|
||||||
@@ -401,7 +383,7 @@ func cmd(b *builder, args []string, attributes map[string]bool, original string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b.Config.Cmd = runconfig.NewCommand(cmdSlice...)
|
b.Config.Cmd = stringutils.NewStrSlice(cmdSlice...)
|
||||||
|
|
||||||
if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %q", cmdSlice)); err != nil {
|
if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %q", cmdSlice)); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -432,16 +414,16 @@ func entrypoint(b *builder, args []string, attributes map[string]bool, original
|
|||||||
switch {
|
switch {
|
||||||
case attributes["json"]:
|
case attributes["json"]:
|
||||||
// ENTRYPOINT ["echo", "hi"]
|
// ENTRYPOINT ["echo", "hi"]
|
||||||
b.Config.Entrypoint = runconfig.NewEntrypoint(parsed...)
|
b.Config.Entrypoint = stringutils.NewStrSlice(parsed...)
|
||||||
case len(parsed) == 0:
|
case len(parsed) == 0:
|
||||||
// ENTRYPOINT []
|
// ENTRYPOINT []
|
||||||
b.Config.Entrypoint = nil
|
b.Config.Entrypoint = nil
|
||||||
default:
|
default:
|
||||||
// ENTRYPOINT echo hi
|
// ENTRYPOINT echo hi
|
||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != "windows" {
|
||||||
b.Config.Entrypoint = runconfig.NewEntrypoint("/bin/sh", "-c", parsed[0])
|
b.Config.Entrypoint = stringutils.NewStrSlice("/bin/sh", "-c", parsed[0])
|
||||||
} else {
|
} else {
|
||||||
b.Config.Entrypoint = runconfig.NewEntrypoint("cmd", "/S /C", parsed[0])
|
b.Config.Entrypoint = stringutils.NewStrSlice("cmd", "/S /C", parsed[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-1
@@ -24,6 +24,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
@@ -281,11 +282,18 @@ func (b *builder) readDockerfile() error {
|
|||||||
// features.
|
// features.
|
||||||
func (b *builder) dispatch(stepN int, ast *parser.Node) error {
|
func (b *builder) dispatch(stepN int, ast *parser.Node) error {
|
||||||
cmd := ast.Value
|
cmd := ast.Value
|
||||||
|
|
||||||
|
// To ensure the user is give a decent error message if the platform
|
||||||
|
// on which the daemon is running does not support a builder command.
|
||||||
|
if err := platformSupports(strings.ToLower(cmd)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
attrs := ast.Attributes
|
attrs := ast.Attributes
|
||||||
original := ast.Original
|
original := ast.Original
|
||||||
flags := ast.Flags
|
flags := ast.Flags
|
||||||
strs := []string{}
|
strs := []string{}
|
||||||
msg := fmt.Sprintf("Step %d : %s", stepN, strings.ToUpper(cmd))
|
msg := fmt.Sprintf("Step %d : %s", stepN+1, strings.ToUpper(cmd))
|
||||||
|
|
||||||
if len(ast.Flags) > 0 {
|
if len(ast.Flags) > 0 {
|
||||||
msg += " " + strings.Join(ast.Flags, " ")
|
msg += " " + strings.Join(ast.Flags, " ")
|
||||||
@@ -349,3 +357,16 @@ func (b *builder) dispatch(stepN int, ast *parser.Node) error {
|
|||||||
|
|
||||||
return fmt.Errorf("Unknown instruction: %s", strings.ToUpper(cmd))
|
return fmt.Errorf("Unknown instruction: %s", strings.ToUpper(cmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// platformSupports is a short-term function to give users a quality error
|
||||||
|
// message if a Dockerfile uses a command not supported on the platform.
|
||||||
|
func platformSupports(command string) error {
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch command {
|
||||||
|
case "expose", "volume", "user":
|
||||||
|
return fmt.Errorf("The daemon on this platform does not support the command '%s'", command)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
+28
-30
@@ -33,6 +33,7 @@ import (
|
|||||||
"github.com/docker/docker/pkg/parsers"
|
"github.com/docker/docker/pkg/parsers"
|
||||||
"github.com/docker/docker/pkg/progressreader"
|
"github.com/docker/docker/pkg/progressreader"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
|
"github.com/docker/docker/pkg/stringutils"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/docker/docker/pkg/tarsum"
|
"github.com/docker/docker/pkg/tarsum"
|
||||||
"github.com/docker/docker/pkg/urlutil"
|
"github.com/docker/docker/pkg/urlutil"
|
||||||
@@ -73,7 +74,7 @@ func (b *builder) readContext(context io.Reader) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *builder) commit(id string, autoCmd *runconfig.Command, comment string) error {
|
func (b *builder) commit(id string, autoCmd *stringutils.StrSlice, comment string) error {
|
||||||
if b.disableCommit {
|
if b.disableCommit {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -84,11 +85,11 @@ func (b *builder) commit(id string, autoCmd *runconfig.Command, comment string)
|
|||||||
if id == "" {
|
if id == "" {
|
||||||
cmd := b.Config.Cmd
|
cmd := b.Config.Cmd
|
||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != "windows" {
|
||||||
b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", "#(nop) "+comment)
|
b.Config.Cmd = stringutils.NewStrSlice("/bin/sh", "-c", "#(nop) "+comment)
|
||||||
} else {
|
} else {
|
||||||
b.Config.Cmd = runconfig.NewCommand("cmd", "/S /C", "REM (nop) "+comment)
|
b.Config.Cmd = stringutils.NewStrSlice("cmd", "/S /C", "REM (nop) "+comment)
|
||||||
}
|
}
|
||||||
defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
|
defer func(cmd *stringutils.StrSlice) { b.Config.Cmd = cmd }(cmd)
|
||||||
|
|
||||||
hit, err := b.probeCache()
|
hit, err := b.probeCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -215,11 +216,11 @@ func (b *builder) runContextCommand(args []string, allowRemote bool, allowDecomp
|
|||||||
|
|
||||||
cmd := b.Config.Cmd
|
cmd := b.Config.Cmd
|
||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != "windows" {
|
||||||
b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest))
|
b.Config.Cmd = stringutils.NewStrSlice("/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest))
|
||||||
} else {
|
} else {
|
||||||
b.Config.Cmd = runconfig.NewCommand("cmd", "/S /C", fmt.Sprintf("REM (nop) %s %s in %s", cmdName, srcHash, dest))
|
b.Config.Cmd = stringutils.NewStrSlice("cmd", "/S /C", fmt.Sprintf("REM (nop) %s %s in %s", cmdName, srcHash, dest))
|
||||||
}
|
}
|
||||||
defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
|
defer func(cmd *stringutils.StrSlice) { b.Config.Cmd = cmd }(cmd)
|
||||||
|
|
||||||
hit, err := b.probeCache()
|
hit, err := b.probeCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -230,7 +231,7 @@ func (b *builder) runContextCommand(args []string, allowRemote bool, allowDecomp
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
container, _, err := b.Daemon.Create(b.Config, nil, "")
|
container, _, err := b.Daemon.ContainerCreate("", b.Config, nil, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -241,20 +242,12 @@ func (b *builder) runContextCommand(args []string, allowRemote bool, allowDecomp
|
|||||||
}
|
}
|
||||||
defer container.Unmount()
|
defer container.Unmount()
|
||||||
|
|
||||||
if err := container.PrepareStorage(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ci := range copyInfos {
|
for _, ci := range copyInfos {
|
||||||
if err := b.addContext(container, ci.origPath, ci.destPath, ci.decompress); err != nil {
|
if err := b.addContext(container, ci.origPath, ci.destPath, ci.decompress); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := container.CleanupStorage(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := b.commit(container.ID, cmd, fmt.Sprintf("%s %s in %s", cmdName, origPaths, dest)); err != nil {
|
if err := b.commit(container.ID, cmd, fmt.Sprintf("%s %s in %s", cmdName, origPaths, dest)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -277,7 +270,7 @@ func calcCopyInfo(b *builder, cmdName string, cInfos *[]*copyInfo, origPath stri
|
|||||||
|
|
||||||
// Twiddle the destPath when its a relative path - meaning, make it
|
// Twiddle the destPath when its a relative path - meaning, make it
|
||||||
// relative to the WORKINGDIR
|
// relative to the WORKINGDIR
|
||||||
if !filepath.IsAbs(destPath) {
|
if !system.IsAbs(destPath) {
|
||||||
hasSlash := strings.HasSuffix(destPath, string(os.PathSeparator))
|
hasSlash := strings.HasSuffix(destPath, string(os.PathSeparator))
|
||||||
destPath = filepath.Join(string(os.PathSeparator), filepath.FromSlash(b.Config.WorkingDir), destPath)
|
destPath = filepath.Join(string(os.PathSeparator), filepath.FromSlash(b.Config.WorkingDir), destPath)
|
||||||
|
|
||||||
@@ -330,7 +323,7 @@ func calcCopyInfo(b *builder, cmdName string, cInfos *[]*copyInfo, origPath stri
|
|||||||
In: resp.Body,
|
In: resp.Body,
|
||||||
Out: b.OutOld,
|
Out: b.OutOld,
|
||||||
Formatter: b.StreamFormatter,
|
Formatter: b.StreamFormatter,
|
||||||
Size: int(resp.ContentLength),
|
Size: resp.ContentLength,
|
||||||
NewLines: true,
|
NewLines: true,
|
||||||
ID: "",
|
ID: "",
|
||||||
Action: "Downloading",
|
Action: "Downloading",
|
||||||
@@ -355,8 +348,11 @@ func calcCopyInfo(b *builder, cmdName string, cInfos *[]*copyInfo, origPath stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := system.UtimesNano(tmpFileName, times); err != nil {
|
// Windows does not support UtimesNano.
|
||||||
return err
|
if runtime.GOOS != "windows" {
|
||||||
|
if err := system.UtimesNano(tmpFileName, times); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ci.origPath = filepath.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName))
|
ci.origPath = filepath.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName))
|
||||||
@@ -367,7 +363,7 @@ func calcCopyInfo(b *builder, cmdName string, cInfos *[]*copyInfo, origPath stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
path := u.Path
|
path := filepath.FromSlash(u.Path) // Ensure in platform semantics
|
||||||
if strings.HasSuffix(path, string(os.PathSeparator)) {
|
if strings.HasSuffix(path, string(os.PathSeparator)) {
|
||||||
path = path[:len(path)-1]
|
path = path[:len(path)-1]
|
||||||
}
|
}
|
||||||
@@ -539,7 +535,11 @@ func (b *builder) processImageFrom(img *image.Image) error {
|
|||||||
|
|
||||||
// Process ONBUILD triggers if they exist
|
// Process ONBUILD triggers if they exist
|
||||||
if nTriggers := len(b.Config.OnBuild); nTriggers != 0 {
|
if nTriggers := len(b.Config.OnBuild); nTriggers != 0 {
|
||||||
fmt.Fprintf(b.ErrStream, "# Executing %d build triggers\n", nTriggers)
|
word := "trigger"
|
||||||
|
if nTriggers > 1 {
|
||||||
|
word = "triggers"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(b.ErrStream, "# Executing %d build %s...\n", nTriggers, word)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the ONBUILD triggers, and remove them from the config, since the config will be committed.
|
// Copy the ONBUILD triggers, and remove them from the config, since the config will be committed.
|
||||||
@@ -547,7 +547,7 @@ func (b *builder) processImageFrom(img *image.Image) error {
|
|||||||
b.Config.OnBuild = []string{}
|
b.Config.OnBuild = []string{}
|
||||||
|
|
||||||
// parse the ONBUILD triggers by invoking the parser
|
// parse the ONBUILD triggers by invoking the parser
|
||||||
for stepN, step := range onBuildTriggers {
|
for _, step := range onBuildTriggers {
|
||||||
ast, err := parser.Parse(strings.NewReader(step))
|
ast, err := parser.Parse(strings.NewReader(step))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -561,8 +561,6 @@ func (b *builder) processImageFrom(img *image.Image) error {
|
|||||||
return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", n.Value)
|
return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", n.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(b.OutStream, "Trigger %d, %s\n", stepN, step)
|
|
||||||
|
|
||||||
if err := b.dispatch(i, n); err != nil {
|
if err := b.dispatch(i, n); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -607,9 +605,9 @@ func (b *builder) create() (*daemon.Container, error) {
|
|||||||
b.Config.Image = b.image
|
b.Config.Image = b.image
|
||||||
|
|
||||||
hostConfig := &runconfig.HostConfig{
|
hostConfig := &runconfig.HostConfig{
|
||||||
CpuShares: b.cpuShares,
|
CPUShares: b.cpuShares,
|
||||||
CpuPeriod: b.cpuPeriod,
|
CPUPeriod: b.cpuPeriod,
|
||||||
CpuQuota: b.cpuQuota,
|
CPUQuota: b.cpuQuota,
|
||||||
CpusetCpus: b.cpuSetCpus,
|
CpusetCpus: b.cpuSetCpus,
|
||||||
CpusetMems: b.cpuSetMems,
|
CpusetMems: b.cpuSetMems,
|
||||||
CgroupParent: b.cgroupParent,
|
CgroupParent: b.cgroupParent,
|
||||||
@@ -621,7 +619,7 @@ func (b *builder) create() (*daemon.Container, error) {
|
|||||||
config := *b.Config
|
config := *b.Config
|
||||||
|
|
||||||
// Create the container
|
// Create the container
|
||||||
c, warnings, err := b.Daemon.Create(b.Config, hostConfig, "")
|
c, warnings, err := b.Daemon.ContainerCreate("", b.Config, hostConfig, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -638,7 +636,7 @@ func (b *builder) create() (*daemon.Container, error) {
|
|||||||
c.Path = s[0]
|
c.Path = s[0]
|
||||||
c.Args = s[1:]
|
c.Args = s[1:]
|
||||||
} else {
|
} else {
|
||||||
config.Cmd = runconfig.NewCommand()
|
config.Cmd = stringutils.NewStrSlice()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build linux
|
// +build freebsd linux
|
||||||
|
|
||||||
package builder
|
package builder
|
||||||
|
|
||||||
+10
-4
@@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -139,7 +140,7 @@ func Build(d *daemon.Daemon, buildConfig *Config) error {
|
|||||||
}
|
}
|
||||||
defer f.Body.Close()
|
defer f.Body.Close()
|
||||||
ct := f.Header.Get("Content-Type")
|
ct := f.Header.Get("Content-Type")
|
||||||
clen := int(f.ContentLength)
|
clen := f.ContentLength
|
||||||
contentType, bodyReader, err := inspectResponse(ct, f.Body, clen)
|
contentType, bodyReader, err := inspectResponse(ct, f.Body, clen)
|
||||||
|
|
||||||
defer bodyReader.Close()
|
defer bodyReader.Close()
|
||||||
@@ -182,11 +183,11 @@ func Build(d *daemon.Daemon, buildConfig *Config) error {
|
|||||||
|
|
||||||
builder := &builder{
|
builder := &builder{
|
||||||
Daemon: d,
|
Daemon: d,
|
||||||
OutStream: &streamformatter.StdoutFormater{
|
OutStream: &streamformatter.StdoutFormatter{
|
||||||
Writer: buildConfig.Stdout,
|
Writer: buildConfig.Stdout,
|
||||||
StreamFormatter: sf,
|
StreamFormatter: sf,
|
||||||
},
|
},
|
||||||
ErrStream: &streamformatter.StderrFormater{
|
ErrStream: &streamformatter.StderrFormatter{
|
||||||
Writer: buildConfig.Stdout,
|
Writer: buildConfig.Stdout,
|
||||||
StreamFormatter: sf,
|
StreamFormatter: sf,
|
||||||
},
|
},
|
||||||
@@ -279,6 +280,11 @@ func Commit(name string, d *daemon.Daemon, c *CommitConfig) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It is not possible to commit a running container on Windows
|
||||||
|
if runtime.GOOS == "windows" && container.IsRunning() {
|
||||||
|
return "", fmt.Errorf("Windows does not support commit of a running container")
|
||||||
|
}
|
||||||
|
|
||||||
if c.Config == nil {
|
if c.Config == nil {
|
||||||
c.Config = &runconfig.Config{}
|
c.Config = &runconfig.Config{}
|
||||||
}
|
}
|
||||||
@@ -316,7 +322,7 @@ func Commit(name string, d *daemon.Daemon, c *CommitConfig) (string, error) {
|
|||||||
// - an io.Reader for the response body
|
// - an io.Reader for the response body
|
||||||
// - an error value which will be non-nil either when something goes wrong while
|
// - an error value which will be non-nil either when something goes wrong while
|
||||||
// reading bytes from r or when the detected content-type is not acceptable.
|
// reading bytes from r or when the detected content-type is not acceptable.
|
||||||
func inspectResponse(ct string, r io.ReadCloser, clen int) (string, io.ReadCloser, error) {
|
func inspectResponse(ct string, r io.ReadCloser, clen int64) (string, io.ReadCloser, error) {
|
||||||
plen := clen
|
plen := clen
|
||||||
if plen <= 0 || plen > maxPreambleLength {
|
if plen <= 0 || plen > maxPreambleLength {
|
||||||
plen = maxPreambleLength
|
plen = maxPreambleLength
|
||||||
|
|||||||
+4
-4
@@ -31,7 +31,7 @@ func TestInspectEmptyResponse(t *testing.T) {
|
|||||||
func TestInspectResponseBinary(t *testing.T) {
|
func TestInspectResponseBinary(t *testing.T) {
|
||||||
ct := "application/octet-stream"
|
ct := "application/octet-stream"
|
||||||
br := ioutil.NopCloser(bytes.NewReader(binaryContext))
|
br := ioutil.NopCloser(bytes.NewReader(binaryContext))
|
||||||
contentType, bReader, err := inspectResponse(ct, br, len(binaryContext))
|
contentType, bReader, err := inspectResponse(ct, br, int64(len(binaryContext)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ func TestResponseUnsupportedContentType(t *testing.T) {
|
|||||||
content := []byte(textPlainDockerfile)
|
content := []byte(textPlainDockerfile)
|
||||||
ct := "application/json"
|
ct := "application/json"
|
||||||
br := ioutil.NopCloser(bytes.NewReader(content))
|
br := ioutil.NopCloser(bytes.NewReader(content))
|
||||||
contentType, bReader, err := inspectResponse(ct, br, len(textPlainDockerfile))
|
contentType, bReader, err := inspectResponse(ct, br, int64(len(textPlainDockerfile)))
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Should have returned an error on content-type 'application/json'")
|
t.Fatal("Should have returned an error on content-type 'application/json'")
|
||||||
@@ -77,7 +77,7 @@ func TestInspectResponseTextSimple(t *testing.T) {
|
|||||||
content := []byte(textPlainDockerfile)
|
content := []byte(textPlainDockerfile)
|
||||||
ct := "text/plain"
|
ct := "text/plain"
|
||||||
br := ioutil.NopCloser(bytes.NewReader(content))
|
br := ioutil.NopCloser(bytes.NewReader(content))
|
||||||
contentType, bReader, err := inspectResponse(ct, br, len(content))
|
contentType, bReader, err := inspectResponse(ct, br, int64(len(content)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ func TestInspectResponseTextSimple(t *testing.T) {
|
|||||||
func TestInspectResponseEmptyContentType(t *testing.T) {
|
func TestInspectResponseEmptyContentType(t *testing.T) {
|
||||||
content := []byte(textPlainDockerfile)
|
content := []byte(textPlainDockerfile)
|
||||||
br := ioutil.NopCloser(bytes.NewReader(content))
|
br := ioutil.NopCloser(bytes.NewReader(content))
|
||||||
contentType, bodyReader, err := inspectResponse("", br, len(content))
|
contentType, bodyReader, err := inspectResponse("", br, int64(len(content)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,11 @@ func parseString(rest string) (*Node, map[string]bool, error) {
|
|||||||
|
|
||||||
// parseJSON converts JSON arrays to an AST.
|
// parseJSON converts JSON arrays to an AST.
|
||||||
func parseJSON(rest string) (*Node, map[string]bool, error) {
|
func parseJSON(rest string) (*Node, map[string]bool, error) {
|
||||||
|
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
||||||
|
if !strings.HasPrefix(rest, "[") {
|
||||||
|
return nil, nil, fmt.Errorf(`Error parsing "%s" as a JSON array`, rest)
|
||||||
|
}
|
||||||
|
|
||||||
var myJSON []interface{}
|
var myJSON []interface{}
|
||||||
if err := json.NewDecoder(strings.NewReader(rest)).Decode(&myJSON); err != nil {
|
if err := json.NewDecoder(strings.NewReader(rest)).Decode(&myJSON); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ var (
|
|||||||
func init() {
|
func init() {
|
||||||
// Dispatch Table. see line_parsers.go for the parse functions.
|
// Dispatch Table. see line_parsers.go for the parse functions.
|
||||||
// The command is parsed and mapped to the line parser. The line parser
|
// The command is parsed and mapped to the line parser. The line parser
|
||||||
// recieves the arguments but not the command, and returns an AST after
|
// receives the arguments but not the command, and returns an AST after
|
||||||
// reformulating the arguments according to the rules in the parser
|
// reformulating the arguments according to the rules in the parser
|
||||||
// functions. Errors are propagated up by Parse() and the resulting AST can
|
// functions. Errors are propagated up by Parse() and the resulting AST can
|
||||||
// be incorporated directly into the existing AST as a next.
|
// be incorporated directly into the existing AST as a next.
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ MAINTAINER Seongyeol Lim <seongyeol37@gmail.com>
|
|||||||
|
|
||||||
COPY . /go/src/github.com/docker/docker
|
COPY . /go/src/github.com/docker/docker
|
||||||
ADD . /
|
ADD . /
|
||||||
|
ADD null /
|
||||||
|
COPY nullfile /tmp
|
||||||
ADD [ "vimrc", "/tmp" ]
|
ADD [ "vimrc", "/tmp" ]
|
||||||
COPY [ "bashrc", "/tmp" ]
|
COPY [ "bashrc", "/tmp" ]
|
||||||
COPY [ "test file", "/tmp" ]
|
COPY [ "test file", "/tmp" ]
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
(maintainer "Seongyeol Lim <seongyeol37@gmail.com>")
|
(maintainer "Seongyeol Lim <seongyeol37@gmail.com>")
|
||||||
(copy "." "/go/src/github.com/docker/docker")
|
(copy "." "/go/src/github.com/docker/docker")
|
||||||
(add "." "/")
|
(add "." "/")
|
||||||
|
(add "null" "/")
|
||||||
|
(copy "nullfile" "/tmp")
|
||||||
(add "vimrc" "/tmp")
|
(add "vimrc" "/tmp")
|
||||||
(copy "bashrc" "/tmp")
|
(copy "bashrc" "/tmp")
|
||||||
(copy "test file" "/tmp")
|
(copy "test file" "/tmp")
|
||||||
|
|||||||
@@ -49,3 +49,45 @@ func TestShellParser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetEnv(t *testing.T) {
|
||||||
|
sw := &shellWord{
|
||||||
|
word: "",
|
||||||
|
envs: nil,
|
||||||
|
pos: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.envs = []string{}
|
||||||
|
if sw.getEnv("foo") != "" {
|
||||||
|
t.Fatalf("2 - 'foo' should map to ''")
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.envs = []string{"foo"}
|
||||||
|
if sw.getEnv("foo") != "" {
|
||||||
|
t.Fatalf("3 - 'foo' should map to ''")
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.envs = []string{"foo="}
|
||||||
|
if sw.getEnv("foo") != "" {
|
||||||
|
t.Fatalf("4 - 'foo' should map to ''")
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.envs = []string{"foo=bar"}
|
||||||
|
if sw.getEnv("foo") != "bar" {
|
||||||
|
t.Fatalf("5 - 'foo' should map to 'bar'")
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.envs = []string{"foo=bar", "car=hat"}
|
||||||
|
if sw.getEnv("foo") != "bar" {
|
||||||
|
t.Fatalf("6 - 'foo' should map to 'bar'")
|
||||||
|
}
|
||||||
|
if sw.getEnv("car") != "hat" {
|
||||||
|
t.Fatalf("7 - 'car' should map to 'hat'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we grab the first 'car' in the list
|
||||||
|
sw.envs = []string{"foo=bar", "car=hat", "car=bike"}
|
||||||
|
if sw.getEnv("car") != "hat" {
|
||||||
|
t.Fatalf("8 - 'car' should map to 'hat'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+106
-59
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -14,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ConfigFile is the name of config file
|
// ConfigFileName is the name of config file
|
||||||
ConfigFileName = "config.json"
|
ConfigFileName = "config.json"
|
||||||
oldConfigfile = ".dockercfg"
|
oldConfigfile = ".dockercfg"
|
||||||
|
|
||||||
@@ -70,6 +71,88 @@ func NewConfigFile(fn string) *ConfigFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LegacyLoadFromReader reads the non-nested configuration data given and sets up the
|
||||||
|
// auth config information with given directory and populates the receiver object
|
||||||
|
func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
|
||||||
|
b, err := ioutil.ReadAll(configData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
|
||||||
|
arr := strings.Split(string(b), "\n")
|
||||||
|
if len(arr) < 2 {
|
||||||
|
return fmt.Errorf("The Auth config file is empty")
|
||||||
|
}
|
||||||
|
authConfig := AuthConfig{}
|
||||||
|
origAuth := strings.Split(arr[0], " = ")
|
||||||
|
if len(origAuth) != 2 {
|
||||||
|
return fmt.Errorf("Invalid Auth config file")
|
||||||
|
}
|
||||||
|
authConfig.Username, authConfig.Password, err = DecodeAuth(origAuth[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
origEmail := strings.Split(arr[1], " = ")
|
||||||
|
if len(origEmail) != 2 {
|
||||||
|
return fmt.Errorf("Invalid Auth config file")
|
||||||
|
}
|
||||||
|
authConfig.Email = origEmail[1]
|
||||||
|
authConfig.ServerAddress = defaultIndexserver
|
||||||
|
configFile.AuthConfigs[defaultIndexserver] = authConfig
|
||||||
|
} else {
|
||||||
|
for k, authConfig := range configFile.AuthConfigs {
|
||||||
|
authConfig.Username, authConfig.Password, err = DecodeAuth(authConfig.Auth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
authConfig.Auth = ""
|
||||||
|
authConfig.ServerAddress = k
|
||||||
|
configFile.AuthConfigs[k] = authConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFromReader reads the configuration data given and sets up the auth config
|
||||||
|
// information with given directory and populates the receiver object
|
||||||
|
func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
|
||||||
|
if err := json.NewDecoder(configData).Decode(&configFile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
for addr, ac := range configFile.AuthConfigs {
|
||||||
|
ac.Username, ac.Password, err = DecodeAuth(ac.Auth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ac.Auth = ""
|
||||||
|
ac.ServerAddress = addr
|
||||||
|
configFile.AuthConfigs[addr] = ac
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from
|
||||||
|
// a non-nested reader
|
||||||
|
func LegacyLoadFromReader(configData io.Reader) (*ConfigFile, error) {
|
||||||
|
configFile := ConfigFile{
|
||||||
|
AuthConfigs: make(map[string]AuthConfig),
|
||||||
|
}
|
||||||
|
err := configFile.LegacyLoadFromReader(configData)
|
||||||
|
return &configFile, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFromReader is a convenience function that creates a ConfigFile object from
|
||||||
|
// a reader
|
||||||
|
func LoadFromReader(configData io.Reader) (*ConfigFile, error) {
|
||||||
|
configFile := ConfigFile{
|
||||||
|
AuthConfigs: make(map[string]AuthConfig),
|
||||||
|
}
|
||||||
|
err := configFile.LoadFromReader(configData)
|
||||||
|
return &configFile, err
|
||||||
|
}
|
||||||
|
|
||||||
// Load reads the configuration files in the given directory, and sets up
|
// Load reads the configuration files in the given directory, and sets up
|
||||||
// the auth config information and return values.
|
// the auth config information and return values.
|
||||||
// FIXME: use the internal golang config parser
|
// FIXME: use the internal golang config parser
|
||||||
@@ -90,22 +173,8 @@ func Load(configDir string) (*ConfigFile, error) {
|
|||||||
return &configFile, err
|
return &configFile, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
err = configFile.LoadFromReader(file)
|
||||||
if err := json.NewDecoder(file).Decode(&configFile); err != nil {
|
return &configFile, err
|
||||||
return &configFile, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for addr, ac := range configFile.AuthConfigs {
|
|
||||||
ac.Username, ac.Password, err = DecodeAuth(ac.Auth)
|
|
||||||
if err != nil {
|
|
||||||
return &configFile, err
|
|
||||||
}
|
|
||||||
ac.Auth = ""
|
|
||||||
ac.ServerAddress = addr
|
|
||||||
configFile.AuthConfigs[addr] = ac
|
|
||||||
}
|
|
||||||
|
|
||||||
return &configFile, nil
|
|
||||||
} else if !os.IsNotExist(err) {
|
} else if !os.IsNotExist(err) {
|
||||||
// if file is there but we can't stat it for any reason other
|
// if file is there but we can't stat it for any reason other
|
||||||
// than it doesn't exist then stop
|
// than it doesn't exist then stop
|
||||||
@@ -117,49 +186,18 @@ func Load(configDir string) (*ConfigFile, error) {
|
|||||||
if _, err := os.Stat(confFile); err != nil {
|
if _, err := os.Stat(confFile); err != nil {
|
||||||
return &configFile, nil //missing file is not an error
|
return &configFile, nil //missing file is not an error
|
||||||
}
|
}
|
||||||
|
file, err := os.Open(confFile)
|
||||||
b, err := ioutil.ReadFile(confFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &configFile, err
|
return &configFile, err
|
||||||
}
|
}
|
||||||
|
defer file.Close()
|
||||||
if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
|
err = configFile.LegacyLoadFromReader(file)
|
||||||
arr := strings.Split(string(b), "\n")
|
return &configFile, err
|
||||||
if len(arr) < 2 {
|
|
||||||
return &configFile, fmt.Errorf("The Auth config file is empty")
|
|
||||||
}
|
|
||||||
authConfig := AuthConfig{}
|
|
||||||
origAuth := strings.Split(arr[0], " = ")
|
|
||||||
if len(origAuth) != 2 {
|
|
||||||
return &configFile, fmt.Errorf("Invalid Auth config file")
|
|
||||||
}
|
|
||||||
authConfig.Username, authConfig.Password, err = DecodeAuth(origAuth[1])
|
|
||||||
if err != nil {
|
|
||||||
return &configFile, err
|
|
||||||
}
|
|
||||||
origEmail := strings.Split(arr[1], " = ")
|
|
||||||
if len(origEmail) != 2 {
|
|
||||||
return &configFile, fmt.Errorf("Invalid Auth config file")
|
|
||||||
}
|
|
||||||
authConfig.Email = origEmail[1]
|
|
||||||
authConfig.ServerAddress = defaultIndexserver
|
|
||||||
configFile.AuthConfigs[defaultIndexserver] = authConfig
|
|
||||||
} else {
|
|
||||||
for k, authConfig := range configFile.AuthConfigs {
|
|
||||||
authConfig.Username, authConfig.Password, err = DecodeAuth(authConfig.Auth)
|
|
||||||
if err != nil {
|
|
||||||
return &configFile, err
|
|
||||||
}
|
|
||||||
authConfig.Auth = ""
|
|
||||||
authConfig.ServerAddress = k
|
|
||||||
configFile.AuthConfigs[k] = authConfig
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &configFile, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save encodes and writes out all the authorization information
|
// SaveToWriter encodes and writes out all the authorization information to
|
||||||
func (configFile *ConfigFile) Save() error {
|
// the given writer
|
||||||
|
func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
|
||||||
// Encode sensitive data into a new/temp struct
|
// Encode sensitive data into a new/temp struct
|
||||||
tmpAuthConfigs := make(map[string]AuthConfig, len(configFile.AuthConfigs))
|
tmpAuthConfigs := make(map[string]AuthConfig, len(configFile.AuthConfigs))
|
||||||
for k, authConfig := range configFile.AuthConfigs {
|
for k, authConfig := range configFile.AuthConfigs {
|
||||||
@@ -180,16 +218,25 @@ func (configFile *ConfigFile) Save() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_, err = writer.Write(data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save encodes and writes out all the authorization information
|
||||||
|
func (configFile *ConfigFile) Save() error {
|
||||||
|
if configFile.Filename() == "" {
|
||||||
|
return fmt.Errorf("Can't save config with empty filename")
|
||||||
|
}
|
||||||
|
|
||||||
if err := system.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
|
if err := system.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
f, err := os.OpenFile(configFile.filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
if err := ioutil.WriteFile(configFile.filename, data, 0600); err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer f.Close()
|
||||||
return nil
|
return configFile.SaveToWriter(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filename returns the name of the configuration file
|
// Filename returns the name of the configuration file
|
||||||
|
|||||||
+331
-61
@@ -4,15 +4,41 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/homedir"
|
"github.com/docker/docker/pkg/homedir"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestEmptyConfigDir(t *testing.T) {
|
||||||
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
|
SetConfigDir(tmpHome)
|
||||||
|
|
||||||
|
config, err := Load("")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed loading on empty config dir: %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedConfigFilename := filepath.Join(tmpHome, ConfigFileName)
|
||||||
|
if config.Filename() != expectedConfigFilename {
|
||||||
|
t.Fatalf("Expected config filename %s, got %s", expectedConfigFilename, config.Filename())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now save it and make sure it shows up in new form
|
||||||
|
saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMissingFile(t *testing.T) {
|
func TestMissingFile(t *testing.T) {
|
||||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
config, err := Load(tmpHome)
|
config, err := Load(tmpHome)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -20,19 +46,15 @@ func TestMissingFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now save it and make sure it shows up in new form
|
// Now save it and make sure it shows up in new form
|
||||||
err = config.Save()
|
saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to save: %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
|
||||||
if !strings.Contains(string(buf), `"auths":`) {
|
|
||||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSaveFileToDirs(t *testing.T) {
|
func TestSaveFileToDirs(t *testing.T) {
|
||||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
tmpHome += "/.docker"
|
tmpHome += "/.docker"
|
||||||
|
|
||||||
@@ -42,32 +64,38 @@ func TestSaveFileToDirs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now save it and make sure it shows up in new form
|
// Now save it and make sure it shows up in new form
|
||||||
err = config.Save()
|
saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to save: %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
|
||||||
if !strings.Contains(string(buf), `"auths":`) {
|
|
||||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyFile(t *testing.T) {
|
func TestEmptyFile(t *testing.T) {
|
||||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
if err != nil {
|
||||||
ioutil.WriteFile(fn, []byte(""), 0600)
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
_, err := Load(tmpHome)
|
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||||
|
if err := ioutil.WriteFile(fn, []byte(""), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = Load(tmpHome)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Was supposed to fail")
|
t.Fatalf("Was supposed to fail")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyJson(t *testing.T) {
|
func TestEmptyJson(t *testing.T) {
|
||||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||||
ioutil.WriteFile(fn, []byte("{}"), 0600)
|
if err := ioutil.WriteFile(fn, []byte("{}"), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
config, err := Load(tmpHome)
|
config, err := Load(tmpHome)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -75,23 +103,119 @@ func TestEmptyJson(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now save it and make sure it shows up in new form
|
// Now save it and make sure it shows up in new form
|
||||||
err = config.Save()
|
saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||||
if err != nil {
|
}
|
||||||
t.Fatalf("Failed to save: %q", err)
|
|
||||||
|
func TestOldInvalidsAuth(t *testing.T) {
|
||||||
|
invalids := map[string]string{
|
||||||
|
`username = test`: "The Auth config file is empty",
|
||||||
|
`username
|
||||||
|
password
|
||||||
|
email`: "Invalid Auth config file",
|
||||||
|
`username = test
|
||||||
|
email`: "Invalid auth configuration file",
|
||||||
|
`username = am9lam9lOmhlbGxv
|
||||||
|
email`: "Invalid Auth config file",
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
if !strings.Contains(string(buf), `"auths":`) {
|
if err != nil {
|
||||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
|
homeKey := homedir.Key()
|
||||||
|
homeVal := homedir.Get()
|
||||||
|
|
||||||
|
defer func() { os.Setenv(homeKey, homeVal) }()
|
||||||
|
os.Setenv(homeKey, tmpHome)
|
||||||
|
|
||||||
|
for content, expectedError := range invalids {
|
||||||
|
fn := filepath.Join(tmpHome, oldConfigfile)
|
||||||
|
if err := ioutil.WriteFile(fn, []byte(content), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := Load(tmpHome)
|
||||||
|
if err == nil || err.Error() != expectedError {
|
||||||
|
t.Fatalf("Should have failed, got: %q, %q", config, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOldValidAuth(t *testing.T) {
|
||||||
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
|
homeKey := homedir.Key()
|
||||||
|
homeVal := homedir.Get()
|
||||||
|
|
||||||
|
defer func() { os.Setenv(homeKey, homeVal) }()
|
||||||
|
os.Setenv(homeKey, tmpHome)
|
||||||
|
|
||||||
|
fn := filepath.Join(tmpHome, oldConfigfile)
|
||||||
|
js := `username = am9lam9lOmhlbGxv
|
||||||
|
email = user@example.com`
|
||||||
|
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := Load(tmpHome)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultIndexserver is https://index.docker.io/v1/
|
||||||
|
ac := config.AuthConfigs["https://index.docker.io/v1/"]
|
||||||
|
if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
|
||||||
|
t.Fatalf("Missing data from parsing:\n%q", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now save it and make sure it shows up in new form
|
||||||
|
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||||
|
|
||||||
|
if !strings.Contains(configStr, "user@example.com") {
|
||||||
|
t.Fatalf("Should have save in new form: %s", configStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOldJsonInvalid(t *testing.T) {
|
||||||
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
|
homeKey := homedir.Key()
|
||||||
|
homeVal := homedir.Get()
|
||||||
|
|
||||||
|
defer func() { os.Setenv(homeKey, homeVal) }()
|
||||||
|
os.Setenv(homeKey, tmpHome)
|
||||||
|
|
||||||
|
fn := filepath.Join(tmpHome, oldConfigfile)
|
||||||
|
js := `{"https://index.docker.io/v1/":{"auth":"test","email":"user@example.com"}}`
|
||||||
|
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := Load(tmpHome)
|
||||||
|
if err == nil || err.Error() != "Invalid auth configuration file" {
|
||||||
|
t.Fatalf("Expected an error got : %v, %v", config, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOldJson(t *testing.T) {
|
func TestOldJson(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
return
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
|
||||||
defer os.RemoveAll(tmpHome)
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
homeKey := homedir.Key()
|
homeKey := homedir.Key()
|
||||||
@@ -102,7 +226,9 @@ func TestOldJson(t *testing.T) {
|
|||||||
|
|
||||||
fn := filepath.Join(tmpHome, oldConfigfile)
|
fn := filepath.Join(tmpHome, oldConfigfile)
|
||||||
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
|
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
|
||||||
ioutil.WriteFile(fn, []byte(js), 0600)
|
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
config, err := Load(tmpHome)
|
config, err := Load(tmpHome)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -115,23 +241,25 @@ func TestOldJson(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now save it and make sure it shows up in new form
|
// Now save it and make sure it shows up in new form
|
||||||
err = config.Save()
|
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to save: %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
if !strings.Contains(configStr, "user@example.com") {
|
||||||
if !strings.Contains(string(buf), `"auths":`) ||
|
t.Fatalf("Should have save in new form: %s", configStr)
|
||||||
!strings.Contains(string(buf), "user@example.com") {
|
|
||||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewJson(t *testing.T) {
|
func TestNewJson(t *testing.T) {
|
||||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||||
js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
|
js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
|
||||||
ioutil.WriteFile(fn, []byte(js), 0600)
|
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
config, err := Load(tmpHome)
|
config, err := Load(tmpHome)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -144,26 +272,28 @@ func TestNewJson(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now save it and make sure it shows up in new form
|
// Now save it and make sure it shows up in new form
|
||||||
err = config.Save()
|
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to save: %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
if !strings.Contains(configStr, "user@example.com") {
|
||||||
if !strings.Contains(string(buf), `"auths":`) ||
|
t.Fatalf("Should have save in new form: %s", configStr)
|
||||||
!strings.Contains(string(buf), "user@example.com") {
|
|
||||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJsonWithPsFormat(t *testing.T) {
|
func TestJsonWithPsFormat(t *testing.T) {
|
||||||
tmpHome, _ := ioutil.TempDir("", "config-test")
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
fn := filepath.Join(tmpHome, ConfigFileName)
|
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||||
js := `{
|
js := `{
|
||||||
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
|
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
|
||||||
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
|
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
|
||||||
}`
|
}`
|
||||||
ioutil.WriteFile(fn, []byte(js), 0600)
|
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
config, err := Load(tmpHome)
|
config, err := Load(tmpHome)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -175,14 +305,154 @@ func TestJsonWithPsFormat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now save it and make sure it shows up in new form
|
// Now save it and make sure it shows up in new form
|
||||||
err = config.Save()
|
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
|
||||||
|
if !strings.Contains(configStr, `"psFormat":`) ||
|
||||||
|
!strings.Contains(configStr, "{{.ID}}") {
|
||||||
|
t.Fatalf("Should have save in new form: %s", configStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save it and make sure it shows up in new form
|
||||||
|
func saveConfigAndValidateNewFormat(t *testing.T, config *ConfigFile, homeFolder string) string {
|
||||||
|
err := config.Save()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to save: %q", err)
|
t.Fatalf("Failed to save: %q", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf, err := ioutil.ReadFile(filepath.Join(homeFolder, ConfigFileName))
|
||||||
|
if !strings.Contains(string(buf), `"auths":`) {
|
||||||
|
t.Fatalf("Should have save in new form: %s", string(buf))
|
||||||
|
}
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigDir(t *testing.T) {
|
||||||
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
|
if ConfigDir() == tmpHome {
|
||||||
|
t.Fatalf("Expected ConfigDir to be different than %s by default, but was the same", tmpHome)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update configDir
|
||||||
|
SetConfigDir(tmpHome)
|
||||||
|
|
||||||
|
if ConfigDir() != tmpHome {
|
||||||
|
t.Fatalf("Expected ConfigDir to %s, but was %s", tmpHome, ConfigDir())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigFile(t *testing.T) {
|
||||||
|
configFilename := "configFilename"
|
||||||
|
configFile := NewConfigFile(configFilename)
|
||||||
|
|
||||||
|
if configFile.Filename() != configFilename {
|
||||||
|
t.Fatalf("Expected %s, got %s", configFilename, configFile.Filename())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonReaderNoFile(t *testing.T) {
|
||||||
|
js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
|
||||||
|
|
||||||
|
config, err := LoadFromReader(strings.NewReader(js))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed loading on empty json file: %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ac := config.AuthConfigs["https://index.docker.io/v1/"]
|
||||||
|
if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
|
||||||
|
t.Fatalf("Missing data from parsing:\n%q", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOldJsonReaderNoFile(t *testing.T) {
|
||||||
|
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
|
||||||
|
|
||||||
|
config, err := LegacyLoadFromReader(strings.NewReader(js))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed loading on empty json file: %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ac := config.AuthConfigs["https://index.docker.io/v1/"]
|
||||||
|
if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
|
||||||
|
t.Fatalf("Missing data from parsing:\n%q", config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonWithPsFormatNoFile(t *testing.T) {
|
||||||
|
js := `{
|
||||||
|
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
|
||||||
|
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
|
||||||
|
}`
|
||||||
|
config, err := LoadFromReader(strings.NewReader(js))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed loading on empty json file: %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` {
|
||||||
|
t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonSaveWithNoFile(t *testing.T) {
|
||||||
|
js := `{
|
||||||
|
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
|
||||||
|
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
|
||||||
|
}`
|
||||||
|
config, err := LoadFromReader(strings.NewReader(js))
|
||||||
|
err = config.Save()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error. File should not have been able to save with no file name.")
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create a temp dir: %q", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
|
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||||
|
f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
err = config.SaveToWriter(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed saving to file: %q", err)
|
||||||
|
}
|
||||||
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
||||||
if !strings.Contains(string(buf), `"psFormat":`) ||
|
if !strings.Contains(string(buf), `"auths":`) ||
|
||||||
!strings.Contains(string(buf), "{{.ID}}") {
|
!strings.Contains(string(buf), "user@example.com") {
|
||||||
|
t.Fatalf("Should have save in new form: %s", string(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func TestLegacyJsonSaveWithNoFile(t *testing.T) {
|
||||||
|
|
||||||
|
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
|
||||||
|
config, err := LegacyLoadFromReader(strings.NewReader(js))
|
||||||
|
err = config.Save()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error. File should not have been able to save with no file name.")
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpHome, err := ioutil.TempDir("", "config-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create a temp dir: %q", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpHome)
|
||||||
|
|
||||||
|
fn := filepath.Join(tmpHome, ConfigFileName)
|
||||||
|
f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
err = config.SaveToWriter(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed saving to file: %q", err)
|
||||||
|
}
|
||||||
|
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
|
||||||
|
if !strings.Contains(string(buf), `"auths":`) ||
|
||||||
|
!strings.Contains(string(buf), "user@example.com") {
|
||||||
t.Fatalf("Should have save in new form: %s", string(buf))
|
t.Fatalf("Should have save in new form: %s", string(buf))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ profile /usr/bin/docker (attach_disconnected, complain) {
|
|||||||
/sbin/iptables rCx,
|
/sbin/iptables rCx,
|
||||||
/sbin/modprobe rCx,
|
/sbin/modprobe rCx,
|
||||||
/sbin/auplink rCx,
|
/sbin/auplink rCx,
|
||||||
|
/sbin/mke2fs rCx,
|
||||||
|
/sbin/tune2fs rCx,
|
||||||
|
/sbin/blkid rCx,
|
||||||
/bin/kmod rCx,
|
/bin/kmod rCx,
|
||||||
/usr/bin/xz rCx,
|
/usr/bin/xz rCx,
|
||||||
/bin/ps rCx,
|
/bin/ps rCx,
|
||||||
@@ -148,4 +151,60 @@ profile /usr/bin/docker (attach_disconnected, complain) {
|
|||||||
file,
|
file,
|
||||||
capability,
|
capability,
|
||||||
}
|
}
|
||||||
|
profile /sbin/mke2fs (complain) {
|
||||||
|
/sbin/mke2fs rm,
|
||||||
|
|
||||||
|
/lib/** r,
|
||||||
|
|
||||||
|
/apparmor/.null w,
|
||||||
|
|
||||||
|
/etc/ld.so.cache r,
|
||||||
|
/etc/mke2fs.conf r,
|
||||||
|
/etc/mtab r,
|
||||||
|
|
||||||
|
/dev/dm-* rw,
|
||||||
|
/dev/urandom r,
|
||||||
|
/dev/null rw,
|
||||||
|
|
||||||
|
/proc/swaps r,
|
||||||
|
/proc/[0-9]*/mounts r,
|
||||||
|
}
|
||||||
|
profile /sbin/tune2fs (complain) {
|
||||||
|
/sbin/tune2fs rm,
|
||||||
|
|
||||||
|
/lib/** r,
|
||||||
|
|
||||||
|
/apparmor/.null w,
|
||||||
|
|
||||||
|
/etc/blkid.conf r,
|
||||||
|
/etc/mtab r,
|
||||||
|
/etc/ld.so.cache r,
|
||||||
|
|
||||||
|
/dev/null rw,
|
||||||
|
/dev/.blkid.tab r,
|
||||||
|
/dev/dm-* rw,
|
||||||
|
|
||||||
|
/proc/swaps r,
|
||||||
|
/proc/[0-9]*/mounts r,
|
||||||
|
}
|
||||||
|
profile /sbin/blkid (complain) {
|
||||||
|
/sbin/blkid rm,
|
||||||
|
|
||||||
|
/lib/** r,
|
||||||
|
/apparmor/.null w,
|
||||||
|
|
||||||
|
/etc/ld.so.cache r,
|
||||||
|
/etc/blkid.conf r,
|
||||||
|
|
||||||
|
/dev/null rw,
|
||||||
|
/dev/.blkid.tab rl,
|
||||||
|
/dev/.blkid.tab* rwl,
|
||||||
|
/dev/dm-* r,
|
||||||
|
|
||||||
|
/sys/devices/virtual/block/** r,
|
||||||
|
|
||||||
|
capability mknod,
|
||||||
|
|
||||||
|
mount -> @{DOCKER_GRAPH_PATH}/**,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||||
#
|
#
|
||||||
|
|
||||||
FROM debian:wheezy
|
FROM debian:wheezy-backports
|
||||||
RUN echo deb http://http.debian.net/debian wheezy-backports main > /etc/apt/sources.list.d/wheezy-backports.list
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion btrfs-tools/wheezy-backports build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
|
|||||||
@@ -22,6 +22,13 @@ for version in "${versions[@]}"; do
|
|||||||
suite="${version##*-}"
|
suite="${version##*-}"
|
||||||
from="${distro}:${suite}"
|
from="${distro}:${suite}"
|
||||||
|
|
||||||
|
case "$from" in
|
||||||
|
debian:wheezy)
|
||||||
|
# add -backports, like our users have to
|
||||||
|
from+='-backports'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
mkdir -p "$version"
|
mkdir -p "$version"
|
||||||
echo "$version -> FROM $from"
|
echo "$version -> FROM $from"
|
||||||
cat > "$version/Dockerfile" <<-EOF
|
cat > "$version/Dockerfile" <<-EOF
|
||||||
@@ -32,13 +39,6 @@ for version in "${versions[@]}"; do
|
|||||||
FROM $from
|
FROM $from
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
case "$from" in
|
|
||||||
debian:wheezy)
|
|
||||||
# add -backports, like our users have to
|
|
||||||
echo "RUN echo deb http://http.debian.net/debian $suite-backports main > /etc/apt/sources.list.d/$suite-backports.list" >> "$version/Dockerfile"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo >> "$version/Dockerfile"
|
echo >> "$version/Dockerfile"
|
||||||
|
|
||||||
extraBuildTags=
|
extraBuildTags=
|
||||||
@@ -73,6 +73,12 @@ for version in "${versions[@]}"; do
|
|||||||
extraBuildTags+=' exclude_graphdriver_btrfs'
|
extraBuildTags+=' exclude_graphdriver_btrfs'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$suite" = 'wheezy' ]; then
|
||||||
|
# pull btrfs-toold from backports
|
||||||
|
backports="/$suite-backports"
|
||||||
|
packages=( "${packages[@]/btrfs-tools/btrfs-tools$backports}" )
|
||||||
|
fi
|
||||||
|
|
||||||
echo "RUN apt-get update && apt-get install -y ${packages[*]} --no-install-recommends && rm -rf /var/lib/apt/lists/*" >> "$version/Dockerfile"
|
echo "RUN apt-get update && apt-get install -y ${packages[*]} --no-install-recommends && rm -rf /var/lib/apt/lists/*" >> "$version/Dockerfile"
|
||||||
|
|
||||||
echo >> "$version/Dockerfile"
|
echo >> "$version/Dockerfile"
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||||
#
|
#
|
||||||
|
|
||||||
FROM ubuntu-debootstrap:precise
|
FROM ubuntu:precise
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion build-essential curl ca-certificates debhelper git libapparmor-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion build-essential curl ca-certificates debhelper git libapparmor-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||||
#
|
#
|
||||||
|
|
||||||
FROM ubuntu-debootstrap:wily
|
FROM ubuntu:trusty
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||||
#
|
#
|
||||||
|
|
||||||
FROM ubuntu-debootstrap:vivid
|
FROM ubuntu:vivid
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"!
|
||||||
#
|
#
|
||||||
|
|
||||||
FROM ubuntu-debootstrap:trusty
|
FROM ubuntu:wily
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ FROM centos:7
|
|||||||
|
|
||||||
RUN yum groupinstall -y "Development Tools"
|
RUN yum groupinstall -y "Development Tools"
|
||||||
RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs
|
RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs
|
||||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
#
|
|
||||||
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/rpm/generate.sh"!
|
|
||||||
#
|
|
||||||
|
|
||||||
FROM fedora:20
|
|
||||||
|
|
||||||
RUN yum install -y @development-tools fedora-packager
|
|
||||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
|
||||||
ENV PATH $PATH:/usr/local/go/bin
|
|
||||||
|
|
||||||
ENV AUTO_GOPATH 1
|
|
||||||
ENV DOCKER_BUILDTAGS selinux
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
FROM fedora:21
|
FROM fedora:21
|
||||||
|
|
||||||
RUN yum install -y @development-tools fedora-packager
|
RUN yum install -y @development-tools fedora-packager
|
||||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
FROM fedora:22
|
FROM fedora:22
|
||||||
|
|
||||||
RUN yum install -y @development-tools fedora-packager
|
RUN yum install -y @development-tools fedora-packager
|
||||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ for version in "${versions[@]}"; do
|
|||||||
device-mapper-devel # for "libdevmapper.h"
|
device-mapper-devel # for "libdevmapper.h"
|
||||||
glibc-static
|
glibc-static
|
||||||
libselinux-devel # for "libselinux.so"
|
libselinux-devel # for "libselinux.so"
|
||||||
|
selinux-policy
|
||||||
|
selinux-policy-devel
|
||||||
sqlite-devel # for "sqlite3.h"
|
sqlite-devel # for "sqlite3.h"
|
||||||
tar # older versions of dev-tools do not have tar
|
tar # older versions of dev-tools do not have tar
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
FROM oraclelinux:6
|
FROM oraclelinux:6
|
||||||
|
|
||||||
RUN yum groupinstall -y "Development Tools"
|
RUN yum groupinstall -y "Development Tools"
|
||||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
FROM oraclelinux:7
|
FROM oraclelinux:7
|
||||||
|
|
||||||
RUN yum groupinstall -y "Development Tools"
|
RUN yum groupinstall -y "Development Tools"
|
||||||
RUN yum install -y --enablerepo=ol7_optional_latest btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
RUN yum install -y --enablerepo=ol7_optional_latest btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
|
||||||
|
|
||||||
ENV GO_VERSION 1.4.2
|
ENV GO_VERSION 1.4.2
|
||||||
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
|
||||||
|
|||||||
@@ -191,11 +191,17 @@ if [ "$kernelMajor" -lt 3 ] || [ "$kernelMajor" -eq 3 -a "$kernelMinor" -le 18 ]
|
|||||||
check_flags RESOURCE_COUNTERS
|
check_flags RESOURCE_COUNTERS
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$kernelMajor" -lt 3 ] || [ "$kernelMajor" -eq 3 -a "$kernelMinor" -le 13 ]; then
|
||||||
|
netprio=NETPRIO_CGROUP
|
||||||
|
else
|
||||||
|
netprio=CGROUP_NET_PRIO
|
||||||
|
fi
|
||||||
|
|
||||||
flags=(
|
flags=(
|
||||||
BLK_CGROUP IOSCHED_CFQ
|
BLK_CGROUP IOSCHED_CFQ BLK_DEV_THROTTLING
|
||||||
CGROUP_PERF
|
CGROUP_PERF
|
||||||
CGROUP_HUGETLB
|
CGROUP_HUGETLB
|
||||||
NET_CLS_CGROUP NETPRIO_CGROUP
|
NET_CLS_CGROUP $netprio
|
||||||
CFS_BANDWIDTH FAIR_GROUP_SCHED RT_GROUP_SCHED
|
CFS_BANDWIDTH FAIR_GROUP_SCHED RT_GROUP_SCHED
|
||||||
)
|
)
|
||||||
check_flags "${flags[@]}"
|
check_flags "${flags[@]}"
|
||||||
|
|||||||
Executable → Regular
+139
-30
@@ -14,6 +14,22 @@
|
|||||||
# - copy this file to e.g. ~/.docker-completion.sh and add the line
|
# - copy this file to e.g. ~/.docker-completion.sh and add the line
|
||||||
# below to your .bashrc after bash completion features are loaded
|
# below to your .bashrc after bash completion features are loaded
|
||||||
# . ~/.docker-completion.sh
|
# . ~/.docker-completion.sh
|
||||||
|
#
|
||||||
|
# Configuration:
|
||||||
|
#
|
||||||
|
# You can tailor completion for the "events", "history", "inspect", "run",
|
||||||
|
# "rmi" and "save" commands by settings the following environment
|
||||||
|
# variables:
|
||||||
|
#
|
||||||
|
# DOCKER_COMPLETION_SHOW_IMAGE_IDS
|
||||||
|
# "none" - Show names only (default)
|
||||||
|
# "non-intermediate" - Show names and ids, but omit intermediate image IDs
|
||||||
|
# "all" - Show names and ids, including intermediate image IDs
|
||||||
|
#
|
||||||
|
# DOCKER_COMPLETION_SHOW_TAGS
|
||||||
|
# "yes" - include tags in completion options (default)
|
||||||
|
# "no" - don't include tags in completion options
|
||||||
|
|
||||||
#
|
#
|
||||||
# Note:
|
# Note:
|
||||||
# Currently, the completions will not work if the docker daemon is not
|
# Currently, the completions will not work if the docker daemon is not
|
||||||
@@ -70,6 +86,40 @@ __docker_container_ids() {
|
|||||||
COMPREPLY=( $(compgen -W "${containers[*]}" -- "$cur") )
|
COMPREPLY=( $(compgen -W "${containers[*]}" -- "$cur") )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__docker_images() {
|
||||||
|
local images_args=""
|
||||||
|
|
||||||
|
case "$DOCKER_COMPLETION_SHOW_IMAGE_IDS" in
|
||||||
|
all)
|
||||||
|
images_args="--no-trunc -a"
|
||||||
|
;;
|
||||||
|
non-intermediate)
|
||||||
|
images_args="--no-trunc"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
local repo_print_command
|
||||||
|
if [ "${DOCKER_COMPLETION_SHOW_TAGS:-yes}" = "yes" ]; then
|
||||||
|
repo_print_command='print $1; print $1":"$2'
|
||||||
|
else
|
||||||
|
repo_print_command='print $1'
|
||||||
|
fi
|
||||||
|
|
||||||
|
local awk_script
|
||||||
|
case "$DOCKER_COMPLETION_SHOW_IMAGE_IDS" in
|
||||||
|
all|non-intermediate)
|
||||||
|
awk_script='NR>1 { print $3; if ($1 != "<none>") { '"$repo_print_command"' } }'
|
||||||
|
;;
|
||||||
|
none|*)
|
||||||
|
awk_script='NR>1 && $1 != "<none>" { '"$repo_print_command"' }'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
local images=$(__docker_q images $images_args | awk "$awk_script")
|
||||||
|
COMPREPLY=( $(compgen -W "$images" -- "$cur") )
|
||||||
|
__ltrim_colon_completions "$cur"
|
||||||
|
}
|
||||||
|
|
||||||
__docker_image_repos() {
|
__docker_image_repos() {
|
||||||
local repos="$(__docker_q images | awk 'NR>1 && $1 != "<none>" { print $1 }')"
|
local repos="$(__docker_q images | awk 'NR>1 && $1 != "<none>" { print $1 }')"
|
||||||
COMPREPLY=( $(compgen -W "$repos" -- "$cur") )
|
COMPREPLY=( $(compgen -W "$repos" -- "$cur") )
|
||||||
@@ -81,16 +131,10 @@ __docker_image_repos_and_tags() {
|
|||||||
__ltrim_colon_completions "$cur"
|
__ltrim_colon_completions "$cur"
|
||||||
}
|
}
|
||||||
|
|
||||||
__docker_image_repos_and_tags_and_ids() {
|
|
||||||
local images="$(__docker_q images -a --no-trunc | awk 'NR>1 { print $3; if ($1 != "<none>") { print $1; print $1":"$2 } }')"
|
|
||||||
COMPREPLY=( $(compgen -W "$images" -- "$cur") )
|
|
||||||
__ltrim_colon_completions "$cur"
|
|
||||||
}
|
|
||||||
|
|
||||||
__docker_containers_and_images() {
|
__docker_containers_and_images() {
|
||||||
__docker_containers_all
|
__docker_containers_all
|
||||||
local containers=( "${COMPREPLY[@]}" )
|
local containers=( "${COMPREPLY[@]}" )
|
||||||
__docker_image_repos_and_tags_and_ids
|
__docker_images
|
||||||
COMPREPLY+=( "${containers[@]}" )
|
COMPREPLY+=( "${containers[@]}" )
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +183,7 @@ __docker_value_of_option() {
|
|||||||
local counter=$((command_pos + 1))
|
local counter=$((command_pos + 1))
|
||||||
while [ $counter -lt $cword ]; do
|
while [ $counter -lt $cword ]; do
|
||||||
case ${words[$counter]} in
|
case ${words[$counter]} in
|
||||||
$option_glob )
|
@($option_glob) )
|
||||||
echo ${words[$counter + 1]}
|
echo ${words[$counter + 1]}
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
@@ -229,11 +273,12 @@ __docker_log_driver_options() {
|
|||||||
# see docs/reference/logging/index.md
|
# see docs/reference/logging/index.md
|
||||||
local fluentd_options="fluentd-address fluentd-tag"
|
local fluentd_options="fluentd-address fluentd-tag"
|
||||||
local gelf_options="gelf-address gelf-tag"
|
local gelf_options="gelf-address gelf-tag"
|
||||||
|
local json_file_options="max-file max-size"
|
||||||
local syslog_options="syslog-address syslog-facility syslog-tag"
|
local syslog_options="syslog-address syslog-facility syslog-tag"
|
||||||
|
|
||||||
case $(__docker_value_of_option --log-driver) in
|
case $(__docker_value_of_option --log-driver) in
|
||||||
'')
|
'')
|
||||||
COMPREPLY=( $( compgen -W "$fluentd_options $gelf_options $syslog_options" -S = -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$fluentd_options $gelf_options $json_file_options $syslog_options" -S = -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
fluentd)
|
fluentd)
|
||||||
COMPREPLY=( $( compgen -W "$fluentd_options" -S = -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$fluentd_options" -S = -- "$cur" ) )
|
||||||
@@ -241,6 +286,9 @@ __docker_log_driver_options() {
|
|||||||
gelf)
|
gelf)
|
||||||
COMPREPLY=( $( compgen -W "$gelf_options" -S = -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$gelf_options" -S = -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
|
json-file)
|
||||||
|
COMPREPLY=( $( compgen -W "$json_file_options" -S = -- "$cur" ) )
|
||||||
|
;;
|
||||||
syslog)
|
syslog)
|
||||||
COMPREPLY=( $( compgen -W "$syslog_options" -S = -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$syslog_options" -S = -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
@@ -320,7 +368,7 @@ __docker_signals() {
|
|||||||
_docker_docker() {
|
_docker_docker() {
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
$global_boolean_options
|
$global_boolean_options
|
||||||
--help -h
|
--help
|
||||||
--version -v
|
--version -v
|
||||||
"
|
"
|
||||||
|
|
||||||
@@ -338,8 +386,6 @@ _docker_docker() {
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
__docker_complete_log_driver_options && return
|
|
||||||
|
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
-*)
|
-*)
|
||||||
COMPREPLY=( $( compgen -W "$boolean_options $global_options_with_args" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$boolean_options $global_options_with_args" -- "$cur" ) )
|
||||||
@@ -460,7 +506,7 @@ _docker_create() {
|
|||||||
_docker_daemon() {
|
_docker_daemon() {
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
$global_boolean_options
|
$global_boolean_options
|
||||||
--help -h
|
--help
|
||||||
--icc=false
|
--icc=false
|
||||||
--ip-forward=false
|
--ip-forward=false
|
||||||
--ip-masq=false
|
--ip-masq=false
|
||||||
@@ -512,7 +558,39 @@ _docker_daemon() {
|
|||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
--storage-driver|-s)
|
--storage-driver|-s)
|
||||||
COMPREPLY=( $( compgen -W "aufs devicemapper btrfs overlay" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
|
COMPREPLY=( $( compgen -W "aufs btrfs devicemapper overlay vfs zfs" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--storage-opt)
|
||||||
|
local devicemapper_options="
|
||||||
|
dm.basesize
|
||||||
|
dm.blkdiscard
|
||||||
|
dm.blocksize
|
||||||
|
dm.fs
|
||||||
|
dm.loopdatasize
|
||||||
|
dm.loopmetadatasize
|
||||||
|
dm.mkfsarg
|
||||||
|
dm.mountopt
|
||||||
|
dm.override_udev_sync_check
|
||||||
|
dm.thinpooldev
|
||||||
|
"
|
||||||
|
local zfs_options="zfs.fsname"
|
||||||
|
|
||||||
|
case $(__docker_value_of_option '--storage-driver|-s') in
|
||||||
|
'')
|
||||||
|
COMPREPLY=( $( compgen -W "$devicemapper_options $zfs_options" -S = -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
devicemapper)
|
||||||
|
COMPREPLY=( $( compgen -W "$devicemapper_options" -S = -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
zfs)
|
||||||
|
COMPREPLY=( $( compgen -W "$zfs_options" -S = -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
compopt -o nospace
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
--log-level|-l)
|
--log-level|-l)
|
||||||
@@ -528,6 +606,28 @@ _docker_daemon() {
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
__docker_complete_log_driver_options && return
|
||||||
|
|
||||||
|
# completions for --storage-opt
|
||||||
|
case "${words[$cword-2]}$prev=" in
|
||||||
|
*dm.blkdiscard=*)
|
||||||
|
COMPREPLY=( $( compgen -W "false true" -- "${cur#=}" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*dm.fs=*)
|
||||||
|
COMPREPLY=( $( compgen -W "ext4 xfs" -- "${cur#=}" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*dm.override_udev_sync_check=*)
|
||||||
|
COMPREPLY=( $( compgen -W "false true" -- "${cur#=}" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*dm.thinpooldev=*)
|
||||||
|
_filedir
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
-*)
|
-*)
|
||||||
COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
|
||||||
@@ -599,7 +699,7 @@ _docker_events() {
|
|||||||
;;
|
;;
|
||||||
*image=*)
|
*image=*)
|
||||||
cur="${cur#=}"
|
cur="${cur#=}"
|
||||||
__docker_image_repos_and_tags_and_ids
|
__docker_images
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -620,7 +720,7 @@ _docker_exec() {
|
|||||||
|
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
-*)
|
-*)
|
||||||
COMPREPLY=( $( compgen -W "--detach -d --help --interactive -i -t --tty -u --user" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "--detach -d --help --interactive -i --privileged -t --tty -u --user" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
__docker_containers_running
|
__docker_containers_running
|
||||||
@@ -657,7 +757,7 @@ _docker_history() {
|
|||||||
*)
|
*)
|
||||||
local counter=$(__docker_pos_first_nonflag)
|
local counter=$(__docker_pos_first_nonflag)
|
||||||
if [ $cword -eq $counter ]; then
|
if [ $cword -eq $counter ]; then
|
||||||
__docker_image_repos_and_tags_and_ids
|
__docker_images
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -750,7 +850,7 @@ _docker_inspect() {
|
|||||||
__docker_containers_all
|
__docker_containers_all
|
||||||
;;
|
;;
|
||||||
image)
|
image)
|
||||||
__docker_image_repos_and_tags_and_ids
|
__docker_images
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
esac
|
esac
|
||||||
@@ -865,16 +965,21 @@ _docker_ps() {
|
|||||||
__docker_containers_all
|
__docker_containers_all
|
||||||
;;
|
;;
|
||||||
--filter|-f)
|
--filter|-f)
|
||||||
COMPREPLY=( $( compgen -S = -W "exited id label name status" -- "$cur" ) )
|
COMPREPLY=( $( compgen -S = -W "ancestor exited id label name status" -- "$cur" ) )
|
||||||
compopt -o nospace
|
compopt -o nospace
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
-n)
|
--format|-n)
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case "${words[$cword-2]}$prev=" in
|
case "${words[$cword-2]}$prev=" in
|
||||||
|
*ancestor=*)
|
||||||
|
cur="${cur#=}"
|
||||||
|
__docker_images
|
||||||
|
return
|
||||||
|
;;
|
||||||
*id=*)
|
*id=*)
|
||||||
cur="${cur#=}"
|
cur="${cur#=}"
|
||||||
__docker_container_ids
|
__docker_container_ids
|
||||||
@@ -893,7 +998,7 @@ _docker_ps() {
|
|||||||
|
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
-*)
|
-*)
|
||||||
COMPREPLY=( $( compgen -W "--all -a --before --filter -f --help --latest -l -n --no-trunc --quiet -q --size -s --since" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "--all -a --before --filter -f --format --help --latest -l -n --no-trunc --quiet -q --size -s --since" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -990,7 +1095,7 @@ _docker_rmi() {
|
|||||||
COMPREPLY=( $( compgen -W "--force -f --help --no-prune" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "--force -f --help --no-prune" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
__docker_image_repos_and_tags_and_ids
|
__docker_images
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -998,15 +1103,16 @@ _docker_rmi() {
|
|||||||
_docker_run() {
|
_docker_run() {
|
||||||
local options_with_args="
|
local options_with_args="
|
||||||
--add-host
|
--add-host
|
||||||
--blkio-weight
|
|
||||||
--attach -a
|
--attach -a
|
||||||
|
--blkio-weight
|
||||||
--cap-add
|
--cap-add
|
||||||
--cap-drop
|
--cap-drop
|
||||||
--cgroup-parent
|
--cgroup-parent
|
||||||
--cidfile
|
--cidfile
|
||||||
--cpuset
|
|
||||||
--cpu-period
|
--cpu-period
|
||||||
--cpu-quota
|
--cpu-quota
|
||||||
|
--cpuset-cpus
|
||||||
|
--cpuset-mems
|
||||||
--cpu-shares -c
|
--cpu-shares -c
|
||||||
--device
|
--device
|
||||||
--dns
|
--dns
|
||||||
@@ -1018,8 +1124,9 @@ _docker_run() {
|
|||||||
--group-add
|
--group-add
|
||||||
--hostname -h
|
--hostname -h
|
||||||
--ipc
|
--ipc
|
||||||
--label -l
|
--kernel-memory
|
||||||
--label-file
|
--label-file
|
||||||
|
--label -l
|
||||||
--link
|
--link
|
||||||
--log-driver
|
--log-driver
|
||||||
--log-opt
|
--log-opt
|
||||||
@@ -1027,14 +1134,15 @@ _docker_run() {
|
|||||||
--mac-address
|
--mac-address
|
||||||
--memory -m
|
--memory -m
|
||||||
--memory-swap
|
--memory-swap
|
||||||
|
--memory-swappiness
|
||||||
--name
|
--name
|
||||||
--net
|
--net
|
||||||
--pid
|
--pid
|
||||||
--publish -p
|
--publish -p
|
||||||
--restart
|
--restart
|
||||||
--security-opt
|
--security-opt
|
||||||
--user -u
|
|
||||||
--ulimit
|
--ulimit
|
||||||
|
--user -u
|
||||||
--uts
|
--uts
|
||||||
--volumes-from
|
--volumes-from
|
||||||
--volume -v
|
--volume -v
|
||||||
@@ -1042,8 +1150,10 @@ _docker_run() {
|
|||||||
"
|
"
|
||||||
|
|
||||||
local all_options="$options_with_args
|
local all_options="$options_with_args
|
||||||
|
--disable-content-trust=false
|
||||||
--help
|
--help
|
||||||
--interactive -i
|
--interactive -i
|
||||||
|
--oom-kill-disable
|
||||||
--privileged
|
--privileged
|
||||||
--publish-all -P
|
--publish-all -P
|
||||||
--read-only
|
--read-only
|
||||||
@@ -1053,7 +1163,7 @@ _docker_run() {
|
|||||||
[ "$command" = "run" ] && all_options="$all_options
|
[ "$command" = "run" ] && all_options="$all_options
|
||||||
--detach -d
|
--detach -d
|
||||||
--rm
|
--rm
|
||||||
--sig-proxy
|
--sig-proxy=false
|
||||||
"
|
"
|
||||||
|
|
||||||
local options_with_args_glob=$(__docker_to_extglob "$options_with_args")
|
local options_with_args_glob=$(__docker_to_extglob "$options_with_args")
|
||||||
@@ -1195,9 +1305,8 @@ _docker_run() {
|
|||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
|
local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
|
||||||
|
|
||||||
if [ $cword -eq $counter ]; then
|
if [ $cword -eq $counter ]; then
|
||||||
__docker_image_repos_and_tags_and_ids
|
__docker_images
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -1216,7 +1325,7 @@ _docker_save() {
|
|||||||
COMPREPLY=( $( compgen -W "--help --output -o" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "--help --output -o" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
__docker_image_repos_and_tags_and_ids
|
__docker_images
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,9 +134,9 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s i -l interac
|
|||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l ipc -d 'Default is to create a private IPC namespace (POSIX SysV IPC) for the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l ipc -d 'Default is to create a private IPC namespace (POSIX SysV IPC) for the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l link -d 'Add link to another container in the form of <name|id>:alias'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l link -d 'Add link to another container in the form of <name|id>:alias'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l lxc-conf -d '(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l lxc-conf -d '(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s m -l memory -d 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s m -l memory -d 'Memory limit (format: <number>[<unit>], where unit = b, k, m or g)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l mac-address -d 'Container MAC address (e.g. 92:d0:c6:0a:29:33)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l mac-address -d 'Container MAC address (e.g. 92:d0:c6:0a:29:33)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number><optional unit>, where unit = b, k, m or g)"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number>[<unit>], where unit = b, k, m or g)"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l name -d 'Assign a name to the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l name -d 'Assign a name to the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l net -d 'Set the Network mode for the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l net -d 'Set the Network mode for the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'
|
||||||
@@ -321,9 +321,9 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s i -l interactiv
|
|||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l ipc -d 'Default is to create a private IPC namespace (POSIX SysV IPC) for the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l ipc -d 'Default is to create a private IPC namespace (POSIX SysV IPC) for the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l link -d 'Add link to another container in the form of <name|id>:alias'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l link -d 'Add link to another container in the form of <name|id>:alias'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l lxc-conf -d '(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l lxc-conf -d '(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s m -l memory -d 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s m -l memory -d 'Memory limit (format: <number>[<unit>], where unit = b, k, m or g)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l mac-address -d 'Container MAC address (e.g. 92:d0:c6:0a:29:33)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l mac-address -d 'Container MAC address (e.g. 92:d0:c6:0a:29:33)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number><optional unit>, where unit = b, k, m or g)"
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number>[<unit>], where unit = b, k, m or g)"
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l net -d 'Set the Network mode for the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l net -d 'Set the Network mode for the container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'
|
||||||
|
|||||||
@@ -0,0 +1,502 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that
|
||||||
|
there is no warranty for the free library. Also, if the library is
|
||||||
|
modified by someone else and passed on, the recipients should know
|
||||||
|
that what they have is not the original version, so that the original
|
||||||
|
author's reputation will not be affected by problems that might be
|
||||||
|
introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using
|
||||||
|
a shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it becomes
|
||||||
|
a de-facto standard. To achieve this, non-free programs must be
|
||||||
|
allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (1) uses at run time a
|
||||||
|
copy of the library already present on the user's computer system,
|
||||||
|
rather than copying library functions into the executable, and (2)
|
||||||
|
will operate properly with a modified version of the library, if
|
||||||
|
the user installs one, as long as the modified version is
|
||||||
|
interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at
|
||||||
|
least three years, to give the same user the materials
|
||||||
|
specified in Subsection 6a, above, for a charge no more
|
||||||
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License may add
|
||||||
|
an explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms of the
|
||||||
|
ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is
|
||||||
|
safest to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least the
|
||||||
|
"copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the library's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||||
|
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
TARGETS?=docker
|
||||||
|
MODULES?=${TARGETS:=.pp.bz2}
|
||||||
|
SHAREDIR?=/usr/share
|
||||||
|
|
||||||
|
all: ${TARGETS:=.pp.bz2}
|
||||||
|
|
||||||
|
%.pp.bz2: %.pp
|
||||||
|
@echo Compressing $^ -\> $@
|
||||||
|
bzip2 -9 $^
|
||||||
|
|
||||||
|
%.pp: %.te
|
||||||
|
make -f ${SHAREDIR}/selinux/devel/Makefile $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *~ *.tc *.pp *.pp.bz2
|
||||||
|
rm -rf tmp *.tar.gz
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/root/\.docker gen_context(system_u:object_r:docker_home_t,s0)
|
||||||
|
|
||||||
|
/usr/bin/docker -- gen_context(system_u:object_r:docker_exec_t,s0)
|
||||||
|
|
||||||
|
/usr/lib/systemd/system/docker.service -- gen_context(system_u:object_r:docker_unit_file_t,s0)
|
||||||
|
|
||||||
|
/etc/docker(/.*)? gen_context(system_u:object_r:docker_config_t,s0)
|
||||||
|
|
||||||
|
/var/lib/docker(/.*)? gen_context(system_u:object_r:docker_var_lib_t,s0)
|
||||||
|
/var/lib/kublet(/.*)? gen_context(system_u:object_r:docker_var_lib_t,s0)
|
||||||
|
/var/lib/docker/vfs(/.*)? gen_context(system_u:object_r:svirt_sandbox_file_t,s0)
|
||||||
|
|
||||||
|
/var/run/docker\.pid -- gen_context(system_u:object_r:docker_var_run_t,s0)
|
||||||
|
/var/run/docker\.sock -s gen_context(system_u:object_r:docker_var_run_t,s0)
|
||||||
|
/var/run/docker-client(/.*)? gen_context(system_u:object_r:docker_var_run_t,s0)
|
||||||
|
|
||||||
|
/var/lock/lxc(/.*)? gen_context(system_u:object_r:docker_lock_t,s0)
|
||||||
|
|
||||||
|
/var/log/lxc(/.*)? gen_context(system_u:object_r:docker_log_t,s0)
|
||||||
|
|
||||||
|
/var/lib/docker/init(/.*)? gen_context(system_u:object_r:docker_share_t,s0)
|
||||||
|
/var/lib/docker/containers/.*/hosts gen_context(system_u:object_r:docker_share_t,s0)
|
||||||
|
/var/lib/docker/containers/.*/hostname gen_context(system_u:object_r:docker_share_t,s0)
|
||||||
|
/var/lib/docker/.*/config\.env gen_context(system_u:object_r:docker_share_t,s0)
|
||||||
@@ -0,0 +1,467 @@
|
|||||||
|
|
||||||
|
## <summary>The open-source application container engine.</summary>
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Execute docker in the docker domain.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed to transition.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_domtrans',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_t, docker_exec_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
corecmd_search_bin($1)
|
||||||
|
domtrans_pattern($1, docker_exec_t, docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Execute docker in the caller domain.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed to transition.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_exec',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_exec_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
corecmd_search_bin($1)
|
||||||
|
can_exec($1, docker_exec_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Search docker lib directories.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_search_lib',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_var_lib_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
allow $1 docker_var_lib_t:dir search_dir_perms;
|
||||||
|
files_search_var_lib($1)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Execute docker lib directories.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_exec_lib',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_var_lib_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
allow $1 docker_var_lib_t:dir search_dir_perms;
|
||||||
|
can_exec($1, docker_var_lib_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Read docker lib files.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_read_lib_files',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_var_lib_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
files_search_var_lib($1)
|
||||||
|
read_files_pattern($1, docker_var_lib_t, docker_var_lib_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Read docker share files.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_read_share_files',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_share_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
files_search_var_lib($1)
|
||||||
|
read_files_pattern($1, docker_share_t, docker_share_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Manage docker lib files.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_manage_lib_files',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_var_lib_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
files_search_var_lib($1)
|
||||||
|
manage_files_pattern($1, docker_var_lib_t, docker_var_lib_t)
|
||||||
|
manage_lnk_files_pattern($1, docker_var_lib_t, docker_var_lib_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Manage docker lib directories.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_manage_lib_dirs',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_var_lib_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
files_search_var_lib($1)
|
||||||
|
manage_dirs_pattern($1, docker_var_lib_t, docker_var_lib_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Create objects in a docker var lib directory
|
||||||
|
## with an automatic type transition to
|
||||||
|
## a specified private type.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
## <param name="private_type">
|
||||||
|
## <summary>
|
||||||
|
## The type of the object to create.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
## <param name="object_class">
|
||||||
|
## <summary>
|
||||||
|
## The class of the object to be created.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
## <param name="name" optional="true">
|
||||||
|
## <summary>
|
||||||
|
## The name of the object being created.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_lib_filetrans',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_var_lib_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
filetrans_pattern($1, docker_var_lib_t, $2, $3, $4)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Read docker PID files.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_read_pid_files',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_var_run_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
files_search_pids($1)
|
||||||
|
read_files_pattern($1, docker_var_run_t, docker_var_run_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Execute docker server in the docker domain.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed to transition.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_systemctl',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_t;
|
||||||
|
type docker_unit_file_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
systemd_exec_systemctl($1)
|
||||||
|
init_reload_services($1)
|
||||||
|
systemd_read_fifo_file_passwd_run($1)
|
||||||
|
allow $1 docker_unit_file_t:file read_file_perms;
|
||||||
|
allow $1 docker_unit_file_t:service manage_service_perms;
|
||||||
|
|
||||||
|
ps_process_pattern($1, docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Read and write docker shared memory.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_rw_sem',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
allow $1 docker_t:sem rw_sem_perms;
|
||||||
|
')
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
## <summary>
|
||||||
|
## Read and write the docker pty type.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_use_ptys',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_devpts_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
allow $1 docker_devpts_t:chr_file rw_term_perms;
|
||||||
|
')
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
## <summary>
|
||||||
|
## Allow domain to create docker content
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_filetrans_named_content',`
|
||||||
|
|
||||||
|
gen_require(`
|
||||||
|
type docker_var_lib_t;
|
||||||
|
type docker_share_t;
|
||||||
|
type docker_log_t;
|
||||||
|
type docker_var_run_t;
|
||||||
|
type docker_home_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
files_pid_filetrans($1, docker_var_run_t, file, "docker.pid")
|
||||||
|
files_pid_filetrans($1, docker_var_run_t, sock_file, "docker.sock")
|
||||||
|
files_pid_filetrans($1, docker_var_run_t, dir, "docker-client")
|
||||||
|
logging_log_filetrans($1, docker_log_t, dir, "lxc")
|
||||||
|
files_var_lib_filetrans($1, docker_var_lib_t, dir, "docker")
|
||||||
|
filetrans_pattern($1, docker_var_lib_t, docker_share_t, file, "config.env")
|
||||||
|
filetrans_pattern($1, docker_var_lib_t, docker_share_t, file, "hosts")
|
||||||
|
filetrans_pattern($1, docker_var_lib_t, docker_share_t, file, "hostname")
|
||||||
|
filetrans_pattern($1, docker_var_lib_t, docker_share_t, file, "resolv.conf")
|
||||||
|
filetrans_pattern($1, docker_var_lib_t, docker_share_t, dir, "init")
|
||||||
|
userdom_admin_home_dir_filetrans($1, docker_home_t, dir, ".docker")
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Connect to docker over a unix stream socket.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_stream_connect',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_t, docker_var_run_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
files_search_pids($1)
|
||||||
|
stream_connect_pattern($1, docker_var_run_t, docker_var_run_t, docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## Connect to SPC containers over a unix stream socket.
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_spc_stream_connect',`
|
||||||
|
gen_require(`
|
||||||
|
type spc_t, spc_var_run_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
files_search_pids($1)
|
||||||
|
files_write_all_pid_sockets($1)
|
||||||
|
allow $1 spc_t:unix_stream_socket connectto;
|
||||||
|
')
|
||||||
|
|
||||||
|
|
||||||
|
########################################
|
||||||
|
## <summary>
|
||||||
|
## All of the rules required to administrate
|
||||||
|
## an docker environment
|
||||||
|
## </summary>
|
||||||
|
## <param name="domain">
|
||||||
|
## <summary>
|
||||||
|
## Domain allowed access.
|
||||||
|
## </summary>
|
||||||
|
## </param>
|
||||||
|
#
|
||||||
|
interface(`docker_admin',`
|
||||||
|
gen_require(`
|
||||||
|
type docker_t;
|
||||||
|
type docker_var_lib_t, docker_var_run_t;
|
||||||
|
type docker_unit_file_t;
|
||||||
|
type docker_lock_t;
|
||||||
|
type docker_log_t;
|
||||||
|
type docker_config_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
allow $1 docker_t:process { ptrace signal_perms };
|
||||||
|
ps_process_pattern($1, docker_t)
|
||||||
|
|
||||||
|
admin_pattern($1, docker_config_t)
|
||||||
|
|
||||||
|
files_search_var_lib($1)
|
||||||
|
admin_pattern($1, docker_var_lib_t)
|
||||||
|
|
||||||
|
files_search_pids($1)
|
||||||
|
admin_pattern($1, docker_var_run_t)
|
||||||
|
|
||||||
|
files_search_locks($1)
|
||||||
|
admin_pattern($1, docker_lock_t)
|
||||||
|
|
||||||
|
logging_search_logs($1)
|
||||||
|
admin_pattern($1, docker_log_t)
|
||||||
|
|
||||||
|
docker_systemctl($1)
|
||||||
|
admin_pattern($1, docker_unit_file_t)
|
||||||
|
allow $1 docker_unit_file_t:service all_service_perms;
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
systemd_passwd_agent_exec($1)
|
||||||
|
systemd_read_fifo_file_passwd_run($1)
|
||||||
|
')
|
||||||
|
')
|
||||||
|
|
||||||
|
interface(`domain_stub_named_filetrans_domain',`
|
||||||
|
gen_require(`
|
||||||
|
attribute named_filetrans_domain;
|
||||||
|
')
|
||||||
|
')
|
||||||
|
|
||||||
|
interface(`lvm_stub',`
|
||||||
|
gen_require(`
|
||||||
|
type lvm_t;
|
||||||
|
')
|
||||||
|
')
|
||||||
|
interface(`staff_stub',`
|
||||||
|
gen_require(`
|
||||||
|
type staff_t;
|
||||||
|
')
|
||||||
|
')
|
||||||
|
interface(`virt_stub_lxc',`
|
||||||
|
gen_require(`
|
||||||
|
type virtd_lxc_t;
|
||||||
|
')
|
||||||
|
')
|
||||||
|
interface(`virt_stub_svirt_sandbox_domain',`
|
||||||
|
gen_require(`
|
||||||
|
attribute svirt_sandbox_domain;
|
||||||
|
')
|
||||||
|
')
|
||||||
|
interface(`virt_stub_svirt_sandbox_file',`
|
||||||
|
gen_require(`
|
||||||
|
type svirt_sandbox_file_t;
|
||||||
|
')
|
||||||
|
')
|
||||||
|
interface(`fs_dontaudit_remount_tmpfs',`
|
||||||
|
gen_require(`
|
||||||
|
type tmpfs_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
dontaudit $1 tmpfs_t:filesystem remount;
|
||||||
|
')
|
||||||
|
interface(`dev_dontaudit_list_all_dev_nodes',`
|
||||||
|
gen_require(`
|
||||||
|
type device_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
dontaudit $1 device_t:dir list_dir_perms;
|
||||||
|
')
|
||||||
|
interface(`kernel_unlabeled_entry_type',`
|
||||||
|
gen_require(`
|
||||||
|
type unlabeled_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
domain_entry_file($1, unlabeled_t)
|
||||||
|
')
|
||||||
|
interface(`kernel_unlabeled_domtrans',`
|
||||||
|
gen_require(`
|
||||||
|
type unlabeled_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
read_lnk_files_pattern($1, unlabeled_t, unlabeled_t)
|
||||||
|
domain_transition_pattern($1, unlabeled_t, $2)
|
||||||
|
type_transition $1 unlabeled_t:process $2;
|
||||||
|
')
|
||||||
|
interface(`files_write_all_pid_sockets',`
|
||||||
|
gen_require(`
|
||||||
|
attribute pidfile;
|
||||||
|
')
|
||||||
|
|
||||||
|
allow $1 pidfile:sock_file write_sock_file_perms;
|
||||||
|
')
|
||||||
|
interface(`dev_dontaudit_mounton_sysfs',`
|
||||||
|
gen_require(`
|
||||||
|
type sysfs_t;
|
||||||
|
')
|
||||||
|
|
||||||
|
dontaudit $1 sysfs_t:dir mounton;
|
||||||
|
')
|
||||||
@@ -0,0 +1,418 @@
|
|||||||
|
policy_module(docker, 1.0.0)
|
||||||
|
|
||||||
|
########################################
|
||||||
|
#
|
||||||
|
# Declarations
|
||||||
|
#
|
||||||
|
|
||||||
|
## <desc>
|
||||||
|
## <p>
|
||||||
|
## Allow sandbox containers manage fuse files
|
||||||
|
## </p>
|
||||||
|
## </desc>
|
||||||
|
gen_tunable(virt_sandbox_use_fusefs, false)
|
||||||
|
|
||||||
|
## <desc>
|
||||||
|
## <p>
|
||||||
|
## Determine whether docker can
|
||||||
|
## connect to all TCP ports.
|
||||||
|
## </p>
|
||||||
|
## </desc>
|
||||||
|
gen_tunable(docker_connect_any, false)
|
||||||
|
|
||||||
|
type docker_t;
|
||||||
|
type docker_exec_t;
|
||||||
|
init_daemon_domain(docker_t, docker_exec_t)
|
||||||
|
domain_subj_id_change_exemption(docker_t)
|
||||||
|
domain_role_change_exemption(docker_t)
|
||||||
|
|
||||||
|
type spc_t;
|
||||||
|
domain_type(spc_t)
|
||||||
|
role system_r types spc_t;
|
||||||
|
|
||||||
|
type spc_var_run_t;
|
||||||
|
files_pid_file(spc_var_run_t)
|
||||||
|
|
||||||
|
type docker_var_lib_t;
|
||||||
|
files_type(docker_var_lib_t)
|
||||||
|
|
||||||
|
type docker_home_t;
|
||||||
|
userdom_user_home_content(docker_home_t)
|
||||||
|
|
||||||
|
type docker_config_t;
|
||||||
|
files_config_file(docker_config_t)
|
||||||
|
|
||||||
|
type docker_lock_t;
|
||||||
|
files_lock_file(docker_lock_t)
|
||||||
|
|
||||||
|
type docker_log_t;
|
||||||
|
logging_log_file(docker_log_t)
|
||||||
|
|
||||||
|
type docker_tmp_t;
|
||||||
|
files_tmp_file(docker_tmp_t)
|
||||||
|
|
||||||
|
type docker_tmpfs_t;
|
||||||
|
files_tmpfs_file(docker_tmpfs_t)
|
||||||
|
|
||||||
|
type docker_var_run_t;
|
||||||
|
files_pid_file(docker_var_run_t)
|
||||||
|
|
||||||
|
type docker_unit_file_t;
|
||||||
|
systemd_unit_file(docker_unit_file_t)
|
||||||
|
|
||||||
|
type docker_devpts_t;
|
||||||
|
term_pty(docker_devpts_t)
|
||||||
|
|
||||||
|
type docker_share_t;
|
||||||
|
files_type(docker_share_t)
|
||||||
|
|
||||||
|
########################################
|
||||||
|
#
|
||||||
|
# docker local policy
|
||||||
|
#
|
||||||
|
allow docker_t self:capability { chown kill fowner fsetid mknod net_admin net_bind_service net_raw setfcap };
|
||||||
|
allow docker_t self:tun_socket relabelto;
|
||||||
|
allow docker_t self:process { getattr signal_perms setrlimit setfscreate };
|
||||||
|
allow docker_t self:fifo_file rw_fifo_file_perms;
|
||||||
|
allow docker_t self:unix_stream_socket create_stream_socket_perms;
|
||||||
|
allow docker_t self:tcp_socket create_stream_socket_perms;
|
||||||
|
allow docker_t self:udp_socket create_socket_perms;
|
||||||
|
allow docker_t self:capability2 block_suspend;
|
||||||
|
|
||||||
|
manage_files_pattern(docker_t, docker_home_t, docker_home_t)
|
||||||
|
manage_dirs_pattern(docker_t, docker_home_t, docker_home_t)
|
||||||
|
manage_lnk_files_pattern(docker_t, docker_home_t, docker_home_t)
|
||||||
|
userdom_admin_home_dir_filetrans(docker_t, docker_home_t, dir, ".docker")
|
||||||
|
|
||||||
|
manage_dirs_pattern(docker_t, docker_config_t, docker_config_t)
|
||||||
|
manage_files_pattern(docker_t, docker_config_t, docker_config_t)
|
||||||
|
files_etc_filetrans(docker_t, docker_config_t, dir, "docker")
|
||||||
|
|
||||||
|
manage_dirs_pattern(docker_t, docker_lock_t, docker_lock_t)
|
||||||
|
manage_files_pattern(docker_t, docker_lock_t, docker_lock_t)
|
||||||
|
files_lock_filetrans(docker_t, docker_lock_t, { dir file }, "lxc")
|
||||||
|
|
||||||
|
manage_dirs_pattern(docker_t, docker_log_t, docker_log_t)
|
||||||
|
manage_files_pattern(docker_t, docker_log_t, docker_log_t)
|
||||||
|
manage_lnk_files_pattern(docker_t, docker_log_t, docker_log_t)
|
||||||
|
logging_log_filetrans(docker_t, docker_log_t, { dir file lnk_file })
|
||||||
|
allow docker_t docker_log_t:dir_file_class_set { relabelfrom relabelto };
|
||||||
|
|
||||||
|
manage_dirs_pattern(docker_t, docker_tmp_t, docker_tmp_t)
|
||||||
|
manage_files_pattern(docker_t, docker_tmp_t, docker_tmp_t)
|
||||||
|
manage_lnk_files_pattern(docker_t, docker_tmp_t, docker_tmp_t)
|
||||||
|
files_tmp_filetrans(docker_t, docker_tmp_t, { dir file lnk_file })
|
||||||
|
|
||||||
|
manage_dirs_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||||
|
manage_files_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||||
|
manage_lnk_files_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||||
|
manage_fifo_files_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||||
|
manage_chr_files_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||||
|
manage_blk_files_pattern(docker_t, docker_tmpfs_t, docker_tmpfs_t)
|
||||||
|
allow docker_t docker_tmpfs_t:dir relabelfrom;
|
||||||
|
can_exec(docker_t, docker_tmpfs_t)
|
||||||
|
fs_tmpfs_filetrans(docker_t, docker_tmpfs_t, { dir file })
|
||||||
|
allow docker_t docker_tmpfs_t:chr_file mounton;
|
||||||
|
|
||||||
|
manage_dirs_pattern(docker_t, docker_share_t, docker_share_t)
|
||||||
|
manage_files_pattern(docker_t, docker_share_t, docker_share_t)
|
||||||
|
manage_lnk_files_pattern(docker_t, docker_share_t, docker_share_t)
|
||||||
|
allow docker_t docker_share_t:dir_file_class_set { relabelfrom relabelto };
|
||||||
|
|
||||||
|
can_exec(docker_t, docker_share_t)
|
||||||
|
#docker_filetrans_named_content(docker_t)
|
||||||
|
|
||||||
|
manage_dirs_pattern(docker_t, docker_var_lib_t, docker_var_lib_t)
|
||||||
|
manage_chr_files_pattern(docker_t, docker_var_lib_t, docker_var_lib_t)
|
||||||
|
manage_blk_files_pattern(docker_t, docker_var_lib_t, docker_var_lib_t)
|
||||||
|
manage_files_pattern(docker_t, docker_var_lib_t, docker_var_lib_t)
|
||||||
|
manage_lnk_files_pattern(docker_t, docker_var_lib_t, docker_var_lib_t)
|
||||||
|
allow docker_t docker_var_lib_t:dir_file_class_set { relabelfrom relabelto };
|
||||||
|
files_var_lib_filetrans(docker_t, docker_var_lib_t, { dir file lnk_file })
|
||||||
|
|
||||||
|
manage_dirs_pattern(docker_t, docker_var_run_t, docker_var_run_t)
|
||||||
|
manage_files_pattern(docker_t, docker_var_run_t, docker_var_run_t)
|
||||||
|
manage_sock_files_pattern(docker_t, docker_var_run_t, docker_var_run_t)
|
||||||
|
manage_lnk_files_pattern(docker_t, docker_var_run_t, docker_var_run_t)
|
||||||
|
files_pid_filetrans(docker_t, docker_var_run_t, { dir file lnk_file sock_file })
|
||||||
|
|
||||||
|
allow docker_t docker_devpts_t:chr_file { relabelfrom rw_chr_file_perms setattr_chr_file_perms };
|
||||||
|
term_create_pty(docker_t, docker_devpts_t)
|
||||||
|
|
||||||
|
kernel_read_system_state(docker_t)
|
||||||
|
kernel_read_network_state(docker_t)
|
||||||
|
kernel_read_all_sysctls(docker_t)
|
||||||
|
kernel_rw_net_sysctls(docker_t)
|
||||||
|
kernel_setsched(docker_t)
|
||||||
|
kernel_read_all_proc(docker_t)
|
||||||
|
|
||||||
|
domain_use_interactive_fds(docker_t)
|
||||||
|
domain_dontaudit_read_all_domains_state(docker_t)
|
||||||
|
|
||||||
|
corecmd_exec_bin(docker_t)
|
||||||
|
corecmd_exec_shell(docker_t)
|
||||||
|
|
||||||
|
corenet_tcp_bind_generic_node(docker_t)
|
||||||
|
corenet_tcp_sendrecv_generic_if(docker_t)
|
||||||
|
corenet_tcp_sendrecv_generic_node(docker_t)
|
||||||
|
corenet_tcp_sendrecv_generic_port(docker_t)
|
||||||
|
corenet_tcp_bind_all_ports(docker_t)
|
||||||
|
corenet_tcp_connect_http_port(docker_t)
|
||||||
|
corenet_tcp_connect_commplex_main_port(docker_t)
|
||||||
|
corenet_udp_sendrecv_generic_if(docker_t)
|
||||||
|
corenet_udp_sendrecv_generic_node(docker_t)
|
||||||
|
corenet_udp_sendrecv_all_ports(docker_t)
|
||||||
|
corenet_udp_bind_generic_node(docker_t)
|
||||||
|
corenet_udp_bind_all_ports(docker_t)
|
||||||
|
|
||||||
|
files_read_config_files(docker_t)
|
||||||
|
files_dontaudit_getattr_all_dirs(docker_t)
|
||||||
|
files_dontaudit_getattr_all_files(docker_t)
|
||||||
|
|
||||||
|
fs_read_cgroup_files(docker_t)
|
||||||
|
fs_read_tmpfs_symlinks(docker_t)
|
||||||
|
fs_search_all(docker_t)
|
||||||
|
fs_getattr_all_fs(docker_t)
|
||||||
|
|
||||||
|
storage_raw_rw_fixed_disk(docker_t)
|
||||||
|
|
||||||
|
auth_use_nsswitch(docker_t)
|
||||||
|
auth_dontaudit_getattr_shadow(docker_t)
|
||||||
|
|
||||||
|
init_read_state(docker_t)
|
||||||
|
init_status(docker_t)
|
||||||
|
|
||||||
|
logging_send_audit_msgs(docker_t)
|
||||||
|
logging_send_syslog_msg(docker_t)
|
||||||
|
|
||||||
|
miscfiles_read_localization(docker_t)
|
||||||
|
|
||||||
|
mount_domtrans(docker_t)
|
||||||
|
|
||||||
|
seutil_read_default_contexts(docker_t)
|
||||||
|
seutil_read_config(docker_t)
|
||||||
|
|
||||||
|
sysnet_dns_name_resolve(docker_t)
|
||||||
|
sysnet_exec_ifconfig(docker_t)
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
rpm_exec(docker_t)
|
||||||
|
rpm_read_db(docker_t)
|
||||||
|
rpm_exec(docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
fstools_domtrans(docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
iptables_domtrans(docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
openvswitch_stream_connect(docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
#
|
||||||
|
# lxc rules
|
||||||
|
#
|
||||||
|
|
||||||
|
allow docker_t self:capability { dac_override setgid setpcap setuid sys_admin sys_boot sys_chroot sys_ptrace };
|
||||||
|
|
||||||
|
allow docker_t self:process { getcap setcap setexec setpgid setsched signal_perms };
|
||||||
|
|
||||||
|
allow docker_t self:netlink_route_socket rw_netlink_socket_perms;;
|
||||||
|
allow docker_t self:netlink_audit_socket create_netlink_socket_perms;
|
||||||
|
allow docker_t self:unix_dgram_socket { create_socket_perms sendto };
|
||||||
|
allow docker_t self:unix_stream_socket { create_stream_socket_perms connectto };
|
||||||
|
|
||||||
|
allow docker_t docker_var_lib_t:dir mounton;
|
||||||
|
allow docker_t docker_var_lib_t:chr_file mounton;
|
||||||
|
can_exec(docker_t, docker_var_lib_t)
|
||||||
|
|
||||||
|
kernel_dontaudit_setsched(docker_t)
|
||||||
|
kernel_get_sysvipc_info(docker_t)
|
||||||
|
kernel_request_load_module(docker_t)
|
||||||
|
kernel_mounton_messages(docker_t)
|
||||||
|
kernel_mounton_all_proc(docker_t)
|
||||||
|
kernel_mounton_all_sysctls(docker_t)
|
||||||
|
kernel_unlabeled_entry_type(spc_t)
|
||||||
|
kernel_unlabeled_domtrans(docker_t, spc_t)
|
||||||
|
|
||||||
|
dev_getattr_all(docker_t)
|
||||||
|
dev_getattr_sysfs_fs(docker_t)
|
||||||
|
dev_read_urand(docker_t)
|
||||||
|
dev_read_lvm_control(docker_t)
|
||||||
|
dev_rw_sysfs(docker_t)
|
||||||
|
dev_rw_loop_control(docker_t)
|
||||||
|
dev_rw_lvm_control(docker_t)
|
||||||
|
|
||||||
|
files_getattr_isid_type_dirs(docker_t)
|
||||||
|
files_manage_isid_type_dirs(docker_t)
|
||||||
|
files_manage_isid_type_files(docker_t)
|
||||||
|
files_manage_isid_type_symlinks(docker_t)
|
||||||
|
files_manage_isid_type_chr_files(docker_t)
|
||||||
|
files_manage_isid_type_blk_files(docker_t)
|
||||||
|
files_exec_isid_files(docker_t)
|
||||||
|
files_mounton_isid(docker_t)
|
||||||
|
files_mounton_non_security(docker_t)
|
||||||
|
files_mounton_isid_type_chr_file(docker_t)
|
||||||
|
|
||||||
|
fs_mount_all_fs(docker_t)
|
||||||
|
fs_unmount_all_fs(docker_t)
|
||||||
|
fs_remount_all_fs(docker_t)
|
||||||
|
files_mounton_isid(docker_t)
|
||||||
|
fs_manage_cgroup_dirs(docker_t)
|
||||||
|
fs_manage_cgroup_files(docker_t)
|
||||||
|
fs_relabelfrom_xattr_fs(docker_t)
|
||||||
|
fs_relabelfrom_tmpfs(docker_t)
|
||||||
|
fs_read_tmpfs_symlinks(docker_t)
|
||||||
|
fs_list_hugetlbfs(docker_t)
|
||||||
|
|
||||||
|
term_use_generic_ptys(docker_t)
|
||||||
|
term_use_ptmx(docker_t)
|
||||||
|
term_getattr_pty_fs(docker_t)
|
||||||
|
term_relabel_pty_fs(docker_t)
|
||||||
|
term_mounton_unallocated_ttys(docker_t)
|
||||||
|
|
||||||
|
modutils_domtrans_insmod(docker_t)
|
||||||
|
|
||||||
|
systemd_status_all_unit_files(docker_t)
|
||||||
|
systemd_start_systemd_services(docker_t)
|
||||||
|
|
||||||
|
userdom_stream_connect(docker_t)
|
||||||
|
userdom_search_user_home_content(docker_t)
|
||||||
|
userdom_read_all_users_state(docker_t)
|
||||||
|
userdom_relabel_user_home_files(docker_t)
|
||||||
|
userdom_relabel_user_tmp_files(docker_t)
|
||||||
|
userdom_relabel_user_tmp_dirs(docker_t)
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
gpm_getattr_gpmctl(docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
dbus_system_bus_client(docker_t)
|
||||||
|
init_dbus_chat(docker_t)
|
||||||
|
init_start_transient_unit(docker_t)
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
systemd_dbus_chat_logind(docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
firewalld_dbus_chat(docker_t)
|
||||||
|
')
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
udev_read_db(docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
virt_read_config(docker_t)
|
||||||
|
virt_exec(docker_t)
|
||||||
|
virt_stream_connect(docker_t)
|
||||||
|
virt_stream_connect_sandbox(docker_t)
|
||||||
|
virt_exec_sandbox_files(docker_t)
|
||||||
|
virt_manage_sandbox_files(docker_t)
|
||||||
|
virt_relabel_sandbox_filesystem(docker_t)
|
||||||
|
# for lxc
|
||||||
|
virt_transition_svirt_sandbox(docker_t, system_r)
|
||||||
|
virt_mounton_sandbox_file(docker_t)
|
||||||
|
# virt_attach_sandbox_tun_iface(docker_t)
|
||||||
|
allow docker_t svirt_sandbox_domain:tun_socket relabelfrom;
|
||||||
|
')
|
||||||
|
|
||||||
|
tunable_policy(`docker_connect_any',`
|
||||||
|
corenet_tcp_connect_all_ports(docker_t)
|
||||||
|
corenet_sendrecv_all_packets(docker_t)
|
||||||
|
corenet_tcp_sendrecv_all_ports(docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
#
|
||||||
|
# spc local policy
|
||||||
|
#
|
||||||
|
domain_entry_file(spc_t, docker_share_t)
|
||||||
|
domain_entry_file(spc_t, docker_var_lib_t)
|
||||||
|
role system_r types spc_t;
|
||||||
|
|
||||||
|
domain_entry_file(spc_t, docker_share_t)
|
||||||
|
domain_entry_file(spc_t, docker_var_lib_t)
|
||||||
|
domtrans_pattern(docker_t, docker_share_t, spc_t)
|
||||||
|
domtrans_pattern(docker_t, docker_var_lib_t, spc_t)
|
||||||
|
allow docker_t spc_t:process { setsched signal_perms };
|
||||||
|
ps_process_pattern(docker_t, spc_t)
|
||||||
|
allow docker_t spc_t:socket_class_set { relabelto relabelfrom };
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
dbus_chat_system_bus(spc_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
unconfined_domain_noaudit(spc_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
unconfined_domain(docker_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
virt_transition_svirt_sandbox(spc_t, system_r)
|
||||||
|
')
|
||||||
|
|
||||||
|
########################################
|
||||||
|
#
|
||||||
|
# docker upstream policy
|
||||||
|
#
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
# domain_stub_named_filetrans_domain()
|
||||||
|
gen_require(`
|
||||||
|
attribute named_filetrans_domain;
|
||||||
|
')
|
||||||
|
|
||||||
|
docker_filetrans_named_content(named_filetrans_domain)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
lvm_stub()
|
||||||
|
docker_rw_sem(lvm_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
staff_stub()
|
||||||
|
docker_stream_connect(staff_t)
|
||||||
|
docker_exec(staff_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
virt_stub_lxc()
|
||||||
|
docker_exec_lib(virtd_lxc_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
optional_policy(`
|
||||||
|
virt_stub_svirt_sandbox_domain()
|
||||||
|
virt_stub_svirt_sandbox_file()
|
||||||
|
allow svirt_sandbox_domain self:netlink_kobject_uevent_socket create_socket_perms;
|
||||||
|
docker_read_share_files(svirt_sandbox_domain)
|
||||||
|
docker_lib_filetrans(svirt_sandbox_domain,svirt_sandbox_file_t, sock_file)
|
||||||
|
docker_use_ptys(svirt_sandbox_domain)
|
||||||
|
docker_spc_stream_connect(svirt_sandbox_domain)
|
||||||
|
fs_list_tmpfs(svirt_sandbox_domain)
|
||||||
|
fs_rw_hugetlbfs_files(svirt_sandbox_domain)
|
||||||
|
fs_dontaudit_remount_tmpfs(svirt_sandbox_domain)
|
||||||
|
dev_dontaudit_mounton_sysfs(svirt_sandbox_domain)
|
||||||
|
|
||||||
|
tunable_policy(`virt_sandbox_use_fusefs',`
|
||||||
|
fs_manage_fusefs_dirs(svirt_sandbox_domain)
|
||||||
|
fs_manage_fusefs_files(svirt_sandbox_domain)
|
||||||
|
fs_manage_fusefs_symlinks(svirt_sandbox_domain)
|
||||||
|
')
|
||||||
|
gen_require(`
|
||||||
|
attribute domain;
|
||||||
|
')
|
||||||
|
|
||||||
|
dontaudit svirt_sandbox_domain domain:key {search link};
|
||||||
|
')
|
||||||
Binary file not shown.
Executable → Regular
+9
-24
@@ -1,34 +1,19 @@
|
|||||||
#!/sbin/runscript
|
#!/sbin/openrc-run
|
||||||
# Copyright 1999-2013 Gentoo Foundation
|
# Copyright 1999-2013 Gentoo Foundation
|
||||||
# Distributed under the terms of the GNU General Public License v2
|
# Distributed under the terms of the GNU General Public License v2
|
||||||
# $Header: $
|
|
||||||
|
|
||||||
DOCKER_LOGFILE=${DOCKER_LOGFILE:-/var/log/${SVCNAME}.log}
|
command="${DOCKER_BINARY:-/usr/bin/docker}"
|
||||||
DOCKER_PIDFILE=${DOCKER_PIDFILE:-/run/${SVCNAME}.pid}
|
pidfile="${DOCKER_PIDFILE:-/run/${RC_SVCNAME}.pid}"
|
||||||
DOCKER_BINARY=${DOCKER_BINARY:-/usr/bin/docker}
|
command_args="daemon -p \"${pidfile}\" ${DOCKER_OPTS}"
|
||||||
DOCKER_OPTS=${DOCKER_OPTS:-}
|
DOCKER_LOGFILE="${DOCKER_LOGFILE:-/var/log/${RC_SVCNAME}.log}"
|
||||||
|
start_stop_daemon_args="--background \
|
||||||
|
--stderr \"${DOCKER_LOGFILE}\" --stdout \"${DOCKER_LOGFILE}\""
|
||||||
|
|
||||||
start() {
|
start_pre() {
|
||||||
checkpath -f -m 0644 -o root:docker "$DOCKER_LOGFILE"
|
checkpath -f -m 0644 -o root:docker "$DOCKER_LOGFILE"
|
||||||
|
|
||||||
ulimit -n 1048576
|
ulimit -n 1048576
|
||||||
ulimit -u 1048576
|
ulimit -u 1048576
|
||||||
|
|
||||||
ebegin "Starting docker daemon"
|
return 0
|
||||||
start-stop-daemon --start --background \
|
|
||||||
--exec "$DOCKER_BINARY" \
|
|
||||||
--pidfile "$DOCKER_PIDFILE" \
|
|
||||||
--stdout "$DOCKER_LOGFILE" \
|
|
||||||
--stderr "$DOCKER_LOGFILE" \
|
|
||||||
-- daemon -p "$DOCKER_PIDFILE" \
|
|
||||||
$DOCKER_OPTS
|
|
||||||
eend $?
|
|
||||||
}
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
ebegin "Stopping docker daemon"
|
|
||||||
start-stop-daemon --stop \
|
|
||||||
--exec "$DOCKER_BINARY" \
|
|
||||||
--pidfile "$DOCKER_PIDFILE"
|
|
||||||
eend $?
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,14 @@ prestart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
[ -x $exec ] || exit 5
|
if [ ! -x $exec ]; then
|
||||||
|
if [ ! -e $exec ]; then
|
||||||
|
echo "Docker executable $exec not found"
|
||||||
|
else
|
||||||
|
echo "You do not have permission to execute the Docker executable $exec"
|
||||||
|
fi
|
||||||
|
exit 5
|
||||||
|
fi
|
||||||
|
|
||||||
check_for_cleanup
|
check_for_cleanup
|
||||||
|
|
||||||
@@ -58,7 +65,13 @@ start() {
|
|||||||
while [ ! -f $pidfile -a $tries -lt 10 ]; do
|
while [ ! -f $pidfile -a $tries -lt 10 ]; do
|
||||||
sleep 1
|
sleep 1
|
||||||
tries=$((tries + 1))
|
tries=$((tries + 1))
|
||||||
|
echo -n '.'
|
||||||
done
|
done
|
||||||
|
if [ ! -f $pidfile ]; then
|
||||||
|
failure
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
success
|
success
|
||||||
echo
|
echo
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
*
|
*
|
||||||
* This sample list was compiled as a combination of all the syscalls
|
* This sample list was compiled as a combination of all the syscalls
|
||||||
* available on i386 and amd64 on Ubuntu Precise, as such it may not contain
|
* available on i386 and amd64 on Ubuntu Precise, as such it may not contain
|
||||||
* everything and not everything may be relevent for your system. This
|
* everything and not everything may be relevant for your system. This
|
||||||
* shouldn't be a problem.
|
* shouldn't be a problem.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ targets_from() {
|
|||||||
git ls-tree -r --name-only origin/master contrib/builder/deb | grep '/Dockerfile$' | sed -r 's!^contrib/builder/deb/|-debootstrap|/Dockerfile$!!g'
|
git ls-tree -r --name-only origin/master contrib/builder/deb | grep '/Dockerfile$' | sed -r 's!^contrib/builder/deb/|-debootstrap|/Dockerfile$!!g'
|
||||||
}
|
}
|
||||||
|
|
||||||
{ targets_from master; targets_from release; } | sort -u
|
release_branch=$(git ls-remote --heads https://github.com/docker/docker.git | awk -F 'refs/heads/' '$2 ~ /^release/ { print $2 }' | sort -V | tail -1)
|
||||||
|
{ targets_from master; targets_from "$release_branch"; } | sort -u
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ stop on runlevel [!2345]
|
|||||||
respawn
|
respawn
|
||||||
|
|
||||||
script
|
script
|
||||||
/usr/bin/docker -d -H=tcp://0.0.0.0:2375
|
/usr/bin/docker daemon -H=tcp://0.0.0.0:2375
|
||||||
end script
|
end script
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ This directory contains code pertaining to running containers and storing images
|
|||||||
Code pertaining to running containers:
|
Code pertaining to running containers:
|
||||||
|
|
||||||
- execdriver
|
- execdriver
|
||||||
- networkdriver
|
|
||||||
|
|
||||||
Code pertaining to storing images:
|
Code pertaining to storing images:
|
||||||
|
|
||||||
|
|||||||
+28
-28
@@ -5,6 +5,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
@@ -17,7 +18,7 @@ import (
|
|||||||
// path does not refer to a directory.
|
// path does not refer to a directory.
|
||||||
var ErrExtractPointNotDirectory = errors.New("extraction point is not a directory")
|
var ErrExtractPointNotDirectory = errors.New("extraction point is not a directory")
|
||||||
|
|
||||||
// ContainerCopy performs a depracated operation of archiving the resource at
|
// ContainerCopy performs a deprecated operation of archiving the resource at
|
||||||
// the specified path in the conatiner identified by the given name.
|
// the specified path in the conatiner identified by the given name.
|
||||||
func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, error) {
|
func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, error) {
|
||||||
container, err := daemon.Get(name)
|
container, err := daemon.Get(name)
|
||||||
@@ -25,11 +26,11 @@ func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if res[0] == '/' {
|
if res[0] == '/' || res[0] == '\\' {
|
||||||
res = res[1:]
|
res = res[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return container.Copy(res)
|
return container.copy(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerStatPath stats the filesystem resource at the specified path in the
|
// ContainerStatPath stats the filesystem resource at the specified path in the
|
||||||
@@ -95,7 +96,7 @@ func (container *Container) resolvePath(path string) (resolvedPath, absPath stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// statPath is the unexported version of StatPath. Locks and mounts should
|
// statPath is the unexported version of StatPath. Locks and mounts should
|
||||||
// be aquired before calling this method and the given path should be fully
|
// be acquired before calling this method and the given path should be fully
|
||||||
// resolved to a path on the host corresponding to the given absolute path
|
// resolved to a path on the host corresponding to the given absolute path
|
||||||
// inside the container.
|
// inside the container.
|
||||||
func (container *Container) statPath(resolvedPath, absPath string) (stat *types.ContainerPathStat, err error) {
|
func (container *Container) statPath(resolvedPath, absPath string) (stat *types.ContainerPathStat, err error) {
|
||||||
@@ -142,7 +143,7 @@ func (container *Container) StatPath(path string) (stat *types.ContainerPathStat
|
|||||||
defer container.Unmount()
|
defer container.Unmount()
|
||||||
|
|
||||||
err = container.mountVolumes()
|
err = container.mountVolumes()
|
||||||
defer container.UnmountVolumes(true)
|
defer container.unmountVolumes(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -177,7 +178,7 @@ func (container *Container) ArchivePath(path string) (content io.ReadCloser, sta
|
|||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// unmount any volumes
|
// unmount any volumes
|
||||||
container.UnmountVolumes(true)
|
container.unmountVolumes(true)
|
||||||
// unmount the container's rootfs
|
// unmount the container's rootfs
|
||||||
container.Unmount()
|
container.Unmount()
|
||||||
}
|
}
|
||||||
@@ -212,13 +213,13 @@ func (container *Container) ArchivePath(path string) (content io.ReadCloser, sta
|
|||||||
|
|
||||||
content = ioutils.NewReadCloserWrapper(data, func() error {
|
content = ioutils.NewReadCloserWrapper(data, func() error {
|
||||||
err := data.Close()
|
err := data.Close()
|
||||||
container.UnmountVolumes(true)
|
container.unmountVolumes(true)
|
||||||
container.Unmount()
|
container.Unmount()
|
||||||
container.Unlock()
|
container.Unlock()
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
container.LogEvent("archive-path")
|
container.logEvent("archive-path")
|
||||||
|
|
||||||
return content, stat, nil
|
return content, stat, nil
|
||||||
}
|
}
|
||||||
@@ -239,7 +240,7 @@ func (container *Container) ExtractToDir(path string, noOverwriteDirNonDir bool,
|
|||||||
defer container.Unmount()
|
defer container.Unmount()
|
||||||
|
|
||||||
err = container.mountVolumes()
|
err = container.mountVolumes()
|
||||||
defer container.UnmountVolumes(true)
|
defer container.unmountVolumes(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -275,7 +276,22 @@ func (container *Container) ExtractToDir(path string, noOverwriteDirNonDir bool,
|
|||||||
// Use the resolved path relative to the container rootfs as the new
|
// Use the resolved path relative to the container rootfs as the new
|
||||||
// absPath. This way we fully follow any symlinks in a volume that may
|
// absPath. This way we fully follow any symlinks in a volume that may
|
||||||
// lead back outside the volume.
|
// lead back outside the volume.
|
||||||
baseRel, err := filepath.Rel(container.basefs, resolvedPath)
|
//
|
||||||
|
// The Windows implementation of filepath.Rel in golang 1.4 does not
|
||||||
|
// support volume style file path semantics. On Windows when using the
|
||||||
|
// filter driver, we are guaranteed that the path will always be
|
||||||
|
// a volume file path.
|
||||||
|
var baseRel string
|
||||||
|
if strings.HasPrefix(resolvedPath, `\\?\Volume{`) {
|
||||||
|
if strings.HasPrefix(resolvedPath, container.basefs) {
|
||||||
|
baseRel = resolvedPath[len(container.basefs):]
|
||||||
|
if baseRel[:1] == `\` {
|
||||||
|
baseRel = baseRel[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
baseRel, err = filepath.Rel(container.basefs, resolvedPath)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -288,7 +304,7 @@ func (container *Container) ExtractToDir(path string, noOverwriteDirNonDir bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !toVolume && container.hostConfig.ReadonlyRootfs {
|
if !toVolume && container.hostConfig.ReadonlyRootfs {
|
||||||
return ErrContainerRootfsReadonly
|
return ErrRootFSReadOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
options := &archive.TarOptions{
|
options := &archive.TarOptions{
|
||||||
@@ -302,23 +318,7 @@ func (container *Container) ExtractToDir(path string, noOverwriteDirNonDir bool,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
container.LogEvent("extract-to-dir")
|
container.logEvent("extract-to-dir")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
|
|
||||||
// cannot be in a read-only volume. If it is not in a volume, the container
|
|
||||||
// cannot be configured with a read-only rootfs.
|
|
||||||
func checkIfPathIsInAVolume(container *Container, absPath string) (bool, error) {
|
|
||||||
var toVolume bool
|
|
||||||
for _, mnt := range container.MountPoints {
|
|
||||||
if toVolume = mnt.hasResource(absPath); toVolume {
|
|
||||||
if mnt.RW {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return false, ErrVolumeReadonly
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return toVolume, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
|
||||||
|
// cannot be in a read-only volume. If it is not in a volume, the container
|
||||||
|
// cannot be configured with a read-only rootfs.
|
||||||
|
func checkIfPathIsInAVolume(container *Container, absPath string) (bool, error) {
|
||||||
|
var toVolume bool
|
||||||
|
for _, mnt := range container.MountPoints {
|
||||||
|
if toVolume = mnt.hasResource(absPath); toVolume {
|
||||||
|
if mnt.RW {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return false, ErrVolumeReadonly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toVolume, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
|
||||||
|
// cannot be in a read-only volume. If it is not in a volume, the container
|
||||||
|
// cannot be configured with a read-only rootfs.
|
||||||
|
//
|
||||||
|
// This is a no-op on Windows which does not support volumes.
|
||||||
|
func checkIfPathIsInAVolume(container *Container, absPath string) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
+7
-2
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/docker/docker/pkg/stdcopy"
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ContainerAttachWithLogsConfig holds the streams to use when connecting to a container to view logs.
|
||||||
type ContainerAttachWithLogsConfig struct {
|
type ContainerAttachWithLogsConfig struct {
|
||||||
InStream io.ReadCloser
|
InStream io.ReadCloser
|
||||||
OutStream io.Writer
|
OutStream io.Writer
|
||||||
@@ -13,6 +14,7 @@ type ContainerAttachWithLogsConfig struct {
|
|||||||
Logs, Stream bool
|
Logs, Stream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerAttachWithLogs attaches to logs according to the config passed in. See ContainerAttachWithLogsConfig.
|
||||||
func (daemon *Daemon) ContainerAttachWithLogs(container *Container, c *ContainerAttachWithLogsConfig) error {
|
func (daemon *Daemon) ContainerAttachWithLogs(container *Container, c *ContainerAttachWithLogsConfig) error {
|
||||||
var errStream io.Writer
|
var errStream io.Writer
|
||||||
|
|
||||||
@@ -36,15 +38,18 @@ func (daemon *Daemon) ContainerAttachWithLogs(container *Container, c *Container
|
|||||||
stderr = errStream
|
stderr = errStream
|
||||||
}
|
}
|
||||||
|
|
||||||
return container.AttachWithLogs(stdin, stdout, stderr, c.Logs, c.Stream)
|
return container.attachWithLogs(stdin, stdout, stderr, c.Logs, c.Stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerWsAttachWithLogsConfig attach with websockets, since all
|
||||||
|
// stream data is delegated to the websocket to handle, there
|
||||||
type ContainerWsAttachWithLogsConfig struct {
|
type ContainerWsAttachWithLogsConfig struct {
|
||||||
InStream io.ReadCloser
|
InStream io.ReadCloser
|
||||||
OutStream, ErrStream io.Writer
|
OutStream, ErrStream io.Writer
|
||||||
Logs, Stream bool
|
Logs, Stream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerWsAttachWithLogs websocket connection
|
||||||
func (daemon *Daemon) ContainerWsAttachWithLogs(container *Container, c *ContainerWsAttachWithLogsConfig) error {
|
func (daemon *Daemon) ContainerWsAttachWithLogs(container *Container, c *ContainerWsAttachWithLogsConfig) error {
|
||||||
return container.AttachWithLogs(c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream)
|
return container.attachWithLogs(c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream)
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user