Compare commits
617 Commits
master
...
release-3.
Author | SHA1 | Date |
---|---|---|
Benjamin Wang | fee612d900 | |
Benjamin Wang | d897e4f555 | |
Benjamin Wang | a8d4009a94 | |
James Blair | f0a1499ce9 | |
Thomas Jungblut | afa0167538 | |
kkkkun | bce0d0b799 | |
Benjamin Wang | ca4a717def | |
Daljit Singh | 7b7140bd51 | |
Thomas Jungblut | 96d0831770 | |
Benjamin Wang | a603c07989 | |
Benjamin Wang | 3f78c423b5 | |
Benjamin Wang | 2db96e817f | |
Marek Siarkowicz | 6796a50397 | |
Hitoshi Mitake | c62b5db79d | |
Hitoshi Mitake | 71e85e9ded | |
Benjamin Wang | 27d362ae94 | |
James Blair | 9925f90161 | |
James Blair | 2ce1c37160 | |
Benjamin Wang | 392144d73a | |
sharathsivakumar | 7fa519fa24 | |
Benjamin Wang | 94593e63d4 | |
Benjamin Wang | 46c6ea552e | |
Benjamin Wang | bc19b67f16 | |
Benjamin Wang | b56268ac48 | |
Marek Siarkowicz | 1d759fc8bd | |
Benjamin Wang | 90e4d04c8e | |
Benjamin Wang | 881147f5d8 | |
James Blair | 8f0a8a1271 | |
Benjamin Wang | abdc3cc41f | |
Prasad Chandrasekaran | 4a826042f1 | |
Benjamin Wang | b000f15049 | |
Marek Siarkowicz | 4b91b6d800 | |
Benjamin Wang | b48cf63488 | |
Benjamin Wang | b364b48475 | |
Benjamin Wang | 3618ab4b07 | |
Benjamin Wang | 1f746597ea | |
Benjamin Wang | 584576d672 | |
Benjamin Wang | 78a898a903 | |
Benjamin Wang | ab64d49a13 | |
Hitoshi Mitake | 442de314a2 | |
J. David Lowe | cee78aca75 | |
Marek Siarkowicz | a1a37492f5 | |
Marek Siarkowicz | 47d4ff2e36 | |
Marek Siarkowicz | 75675cd464 | |
Marek Siarkowicz | 8dc1244179 | |
Marek Siarkowicz | dd0bc66478 | |
Marek Siarkowicz | a4ac849ec1 | |
Marek Siarkowicz | 66704b4c59 | |
Marek Siarkowicz | 6de105e89b | |
Benjamin Wang | 9ba5c28404 | |
Benjamin Wang | 49d05f88c3 | |
Marek Siarkowicz | f9a4a471a0 | |
Marek Siarkowicz | 7d62b4d64a | |
Marek Siarkowicz | 7bb5f1f58c | |
Marek Siarkowicz | c4a0bac555 | |
Marek Siarkowicz | ec9221f42a | |
Marek Siarkowicz | 9e912ba3ed | |
Marek Siarkowicz | 063d3ceed6 | |
Marek Siarkowicz | cee9d4c0f1 | |
Marek Siarkowicz | 1bafc86b42 | |
Benjamin Wang | bf22b350b0 | |
Hitoshi Mitake | 01c0d8b309 | |
Marek Siarkowicz | 2b189d8638 | |
Marek Siarkowicz | 3f6429d702 | |
Marek Siarkowicz | 63c7a038eb | |
Marek Siarkowicz | 73f152e61e | |
Marek Siarkowicz | e0fcb9e637 | |
Benjamin Wang | 82de82ee80 | |
Wei Fu | 3fc5fbeaa0 | |
Benjamin Wang | 284c312fd4 | |
Benjamin Wang | 336ac78ebe | |
Hitoshi Mitake | be808bde23 | |
Hitoshi Mitake | c8f890cde1 | |
Benjamin Wang | 46ae7ebd96 | |
Marek Siarkowicz | 29ecfc0185 | |
Bogdan Kanivets | 8160d9aea5 | |
Wei Fu | 303519c7b8 | |
James Blair | d8f7cfe28d | |
Marek Siarkowicz | 2eabc0bc70 | |
Benjamin Wang | 7c6b0882fd | |
Marek Siarkowicz | 08a42e65a8 | |
Marek Siarkowicz | 60e381aaa9 | |
Marek Siarkowicz | e818b5fac8 | |
Marek Siarkowicz | 6025355ce0 | |
Benjamin Wang | 4cdb91db15 | |
James Blair | 51ea1c0abe | |
Piotr Tabor | 20eee55557 | |
James Blair | a91bacf567 | |
James Blair | 7318f5dd0c | |
James Blair | 9570978e93 | |
Benjamin Wang | 6d1bfe4f99 | |
Benjamin Wang | 9c81b86e90 | |
Benjamin Wang | ed529ab0e5 | |
James Blair | d32dceb8a6 | |
Marek Siarkowicz | fb7a8973bd | |
Benjamin Wang | 109873dcb6 | |
Benjamin Wang | b4e3ed72e3 | |
Wilson Wang | 2f8158650f | |
kidsan | c5347cb0c6 | |
Iavael | d2fc8dbeeb | |
Benjamin Wang | e4b154231c | |
Wei Fu | 931cf9a814 | |
Marek Siarkowicz | 1246c52d04 | |
tangcong | d48f7ad7c1 | |
Benjamin Wang | a1d1af5774 | |
Piotr Tabor | 4be8c0e5a5 | |
Benjamin Wang | 00b31512a1 | |
Wei Fu | 10c080dc5e | |
tangcong | 2070f55aab | |
tangcong | 00a005c300 | |
mlmhl | 841f3bd2be | |
Benjamin Wang | a577940b4e | |
Wei Fu | c320f75a15 | |
Benjamin Wang | 46511ab96e | |
Benjamin Wang | 58c2f5f228 | |
Benjamin Wang | 283e447df5 | |
Benjamin Wang | 8aace73c77 | |
Benjamin Wang | c8b7831967 | |
Benjamin Wang | 8119eb3951 | |
Benjamin Wang | 5413ce46dc | |
Benjamin Wang | 86479c5ba9 | |
Benjamin Wang | 68a55439e1 | |
Benjamin Wang | 40566d943a | |
Benjamin Wang | fcb048dd67 | |
Benjamin Wang | f318a39998 | |
Benjamin Wang | c1bec6bd97 | |
Benjamin Wang | 9d37e7626a | |
Mukul Kolpe | fb07cf843a | |
Benjamin Wang | e03c62d5e7 | |
ArkaSaha30 | 7450bcfc49 | |
Benjamin Wang | 593711848e | |
Benjamin Wang | acca4fa93e | |
Benjamin Wang | c619e2705e | |
Benjamin Wang | 2f4f7328d0 | |
Benjamin Wang | f4bf538781 | |
Benjamin Wang | 90585e03a0 | |
Benjamin Wang | 8b4405b276 | |
Benjamin Wang | 8ca42a7ae4 | |
Benjamin Wang | 1f054980bc | |
Benjamin Wang | c9cf4db813 | |
Cenk Alti | 7a4a3ad8db | |
Benjamin Wang | 7c1499d3bb | |
Hitoshi Mitake | b7a23311e6 | |
Hitoshi Mitake | 0b3ff06868 | |
Benjamin Wang | ce1630f68f | |
Allen Ray | 9254f8f05b | |
Benjamin Wang | b058374fbd | |
王霄霄 | dcebdf7958 | |
Benjamin Wang | 5b764d8771 | |
Hisanobu Tomari | 7b7fbbf8b8 | |
Sahdev Zala | 429fcb98ab | |
Benjamin Wang | 1d7639f796 | |
Benjamin Wang | 5b3ac7da6b | |
Sergey Kacheev | 5381dafaae | |
Sergey Kacheev | 90e7e254ae | |
Sergey Kacheev | abb019a51e | |
Hitoshi Mitake | 57a27de189 | |
Kafuu Chino | ed10ca13f4 | |
Benjamin Wang | de11726a8a | |
Hitoshi Mitake | 91365174b3 | |
Hitoshi Mitake | 0c6e466024 | |
Marek Siarkowicz | d0a732f96d | |
Benjamin Wang | 29911e9a5b | |
Benjamin Wang | 85b640cee7 | |
Marek Siarkowicz | 1a05326fae | |
Benjamin Wang | b8bea91f22 | |
Benjamin Wang | 6730ed8477 | |
Benjamin Wang | a55a9f5e07 | |
Thomas Jungblut | 86bc0a25c4 | |
Benjamin Wang | dd743eea81 | |
Vladimir Sokolov | 1ed5dfc20e | |
Benjamin Wang | b2b7b9d535 | |
Benjamin Wang | 119e4dda19 | |
Benjamin Wang | 9d5ae56764 | |
Vladimir Sokolov | 38342e88da | |
vivekpatani | c0ef7d52e0 | |
Benjamin Wang | 1e2682301c | |
Sahdev Zala | ee366151c6 | |
Benjamin Wang | 095bbfc4ed | |
Benjamin Wang | cc1b0e6a44 | |
Benjamin Wang | 314dcbf6f5 | |
Bogdan Kanivets | 6f483a649e | |
Benjamin Wang | ce539a960c | |
SimFG | 04e5e5516e | |
Benjamin Wang | 2c778eebf7 | |
Benjamin Wang | f53db9b246 | |
Benjamin Wang | e2b36f8879 | |
Benjamin Wang | de2e8ccc78 | |
Marek Siarkowicz | 783e99cbfe | |
Marek Siarkowicz | 8f4735dfd4 | |
Benjamin Wang | 9c9148c4cd | |
Benjamin Wang | f18d074866 | |
Benjamin Wang | aca5cd1717 | |
vivekpatani | e4deb09c9e | |
Chao Chen | 96f69dee47 | |
Michał Jasionowski | 8d83691d53 | |
Michał Jasionowski | a30aba8fc2 | |
Benjamin Wang | 7ee7029c08 | |
Benjamin Wang | 6071b1c523 | |
Benjamin Wang | 40ccb8b454 | |
Chao Chen | 864006b72d | |
Pierre Zemb | 3f9fba9112 | |
Benjamin Wang | fc76e90cf2 | |
Benjamin Wang | 3ea12d352e | |
Benjamin Wang | 6313502fb4 | |
Benjamin Wang | b0e1aaef69 | |
Chris Ayoub | 36a76e8531 | |
vivekpatani | 4fef7fcb90 | |
Chao Chen | fd51434b54 | |
Benjamin Wang | d58a0c0434 | |
Hitoshi Mitake | ecd91da40d | |
Benjamin Wang | 07d2b1d626 | |
Benjamin Wang | 4636a5fab4 | |
Benjamin Wang | 06561ae4bf | |
Benjamin Wang | be0ce4f15b | |
Benjamin Wang | d3dfc9b796 | |
Bogdan Kanivets | 185f203528 | |
Benjamin Wang | 7de53273dd | |
Benjamin Wang | 6cc9416ae5 | |
Benjamin Wang | e6b3d97712 | |
Marek Siarkowicz | 852ac37bc0 | |
Benjamin Wang | 8c1c5fefdb | |
Marek Siarkowicz | 0c6063fa82 | |
Benjamin Wang | 860dc149b2 | |
Marek Siarkowicz | f0256eeec9 | |
Bogdan Kanivets | 576a798bf9 | |
Benjamin Wang | bae61786fc | |
Benjamin Wang | 8160e9ebe2 | |
Benjamin Wang | 5b3f269159 | |
Benjamin Wang | bb9113097a | |
Benjamin Wang | f169e5dcba | |
Benjamin Wang | 6958ee8ff2 | |
Marek Siarkowicz | f1c59dcfac | |
Benjamin Wang | 1c9fa07cd7 | |
Benjamin Wang | 4e88cce06c | |
Bogdan Kanivets | 2d99b341ad | |
Marek Siarkowicz | 17fc680454 | |
Benjamin Wang | f036529b5d | |
Benjamin Wang | 953376e666 | |
Benjamin Wang | 1abf085cfb | |
Benjamin Wang | c2c9e7de01 | |
Bogdan Kanivets | ceed023f7c | |
Benjamin Wang | 5505d7a95b | |
Piotr Tabor | 76147c9c79 | |
cfz | 23e79dbf19 | |
Hitoshi Mitake | 757a8e8f5b | |
Ashish Ranjan | 9bbdeb4a64 | |
Marek Siarkowicz | c50b7260cc | |
Bogdan Kanivets | d30a4fbf0c | |
richkun | a905430d27 | |
Sam Batschelet | 161bf7e7be | |
Chao Chen | 04d47a93f9 | |
Sam Batschelet | 72d3e382e7 | |
Piotr Tabor | eb9cee9ee3 | |
Geeta Gharpure | 85abf6e46d | |
Piotr Tabor | 1eac258f58 | |
Sam Batschelet | 91da298560 | |
Sam Batschelet | 19e2e70e4f | |
Sam Batschelet | 8ea187e2cf | |
Yusuke Suzuki | e63d058247 | |
Yusuke Suzuki | 1558ede7f8 | |
Sam Batschelet | 41061e56ad | |
Sam Batschelet | 501d8f01ea | |
Sam Batschelet | 38669a0709 | |
Sam Batschelet | 7489911d51 | |
Sam Batschelet | 15b7954d03 | |
Marek Siarkowicz | 6cc1345a0b | |
Sam Batschelet | 589a6993b8 | |
Saeid Bostandoust | 4bacd21e20 | |
Sam Batschelet | 0ecc337028 | |
spacewander | 628fa1818e | |
Gyuho Lee | d19fbe541b | |
Piotr Tabor | 6bbc85827b | |
Chao Chen | dbde4f2d5e | |
Gyuho Lee | 15715dcf1a | |
makdon | 963d3b9369 | |
Piotr Tabor | ba829044f5 | |
Chao Chen | c4eb81af99 | |
Piotr Tabor | ceafa1b33e | |
Lili Cosic | 5890bc8bd6 | |
Piotr Tabor | c274aa5ea4 | |
Piotr Tabor | 276ee962ec | |
Lili Cosic | 8d1b8335e3 | |
Piotr Tabor | c3f447a698 | |
Lili Cosic | 85e037d9c6 | |
Lili Cosic | a1691be1bd | |
Vimal K | df35086b6a | |
Lili Cosic | eeefd614c8 | |
Lili Cosic | 4276c33026 | |
Lili Cosic | cfc08e5f06 | |
Lili Cosic | 0b7e4184e8 | |
Lili Cosic | 35bd924596 | |
Lili Cosic | 62596faeed | |
Piotr Tabor | b7e5f5bc12 | |
Lili Cosic | 91bed2e01f | |
Lili Cosic | b19eb0f339 | |
Lili Cosic | 8557cb29ba | |
Lili Cosic | ef415e3fe1 | |
Sam Batschelet | 82eae9227c | |
Chris Wedgwood | 656dc63eab | |
Piotr Tabor | 30799c97be | |
Piotr Tabor | 16fe9a89ff | |
Chris Wedgwood | c499d9b047 | |
Piotr Tabor | 2702f9e5f2 | |
Chris Wedgwood | 94634fc258 | |
Sam Batschelet | afd6d8a40d | |
Sam Batschelet | 9aeabe447d | |
Gyuho Lee | aa7126864d | |
Gyuho Lee | 3be9460ddc | |
Chao Chen | f27ef4d343 | |
Piotr Tabor | a1c5f59b59 | |
Vitaliy Filippov | a40f14d92c | |
Sam Batschelet | d51c6c689b | |
Sam Batschelet | becc228c5a | |
Piotr Tabor | 0880605772 | |
Kir Kolyshkin | bea35fd2c6 | |
Gyuho Lee | 8a03d2e961 | |
Gyuho Lee | a4b43b388d | |
Gyuho Lee | e3b29b66a4 | |
Gyuho Lee | eb0fb0e799 | |
CFC4N | 40b71074e8 | |
Jingyi Hu | 7e2d426ec0 | |
galal-hussein | 3019246742 | |
Joe Betz | dd1b699fc4 | |
jingyih | f44aaf8248 | |
Gyuho Lee | ae9734ed27 | |
Gyuho Lee | 781bde75e2 | |
Sahdev P. Zala | d5ebbbceb8 | |
Sam Batschelet | 7cd5872656 | |
Sam Batschelet | 46a0a44f95 | |
Gyuho Lee | 17cef6e3e9 | |
Gyuho Lee | c07cba001b | |
Jordan Liggitt | b8878eac45 | |
Gyuho Lee | e71e0c5c88 | |
Gyuho Lee | bc44e367c3 | |
Gyuho Lee | 299e0f17aa | |
jingyih | 75d5e78d1f | |
jingyih | c60dabf2f3 | |
Gyuho Lee | 8a4afdbcc2 | |
jingyih | 6fcab5af9f | |
Gyuho Lee | 008074187c | |
Gyuho Lee | cf558ee8b7 | |
Ted Yu | e800c62eca | |
Gyuho Lee | 0372cfc7ab | |
Gyuho Lee | 18dfb9cca3 | |
Jingyi Hu | 7b8270416d | |
Sahdev Zala | a2c37485dd | |
Hitoshi Mitake | 67bfc310f0 | |
Yuchen Zhou | ed28c768a3 | |
Gyuho Lee | d3a702a09d | |
Sahdev P. Zala | 319331192e | |
Gyuho Lee | 2acdf88406 | |
Gyuho Lee | a8454e453f | |
Gyuho Lee | 32583af167 | |
Sahdev Zala | 85cc4deae6 | |
Hitoshi Mitake | 7dec4c412c | |
tangcong | a4667f596a | |
tangcong | 0207d1df66 | |
Gyuho Lee | 99e893d285 | |
Gyuho Lee | d5dec731db | |
Gyuho Lee | 81a2edc365 | |
Changxin Miao | e5424fc474 | |
cfc4n | 4488595e05 | |
cfc4n | 7b99863e02 | |
cfc4n | 490c6139ac | |
Gyuho Lee | 31e49a4df3 | |
Gyuho Lee | 83fc96df0c | |
Gyuho Lee | 45192cf62b | |
Gyuho Lee | 1a1281005c | |
Gyuho Lee | a4f42948e8 | |
Gyuho Lee | 2212a84adb | |
tangcong | e42d7b5248 | |
Xiang Li | b86bb615ff | |
cfc4n | ee963470f4 | |
Jack Kleeman | 36452a1c1d | |
Gyuho Lee | 4571e528f4 | |
Gyuho Lee | 37ac22205b | |
Gyuho Lee | 493f15c156 | |
Gyuho Lee | c8b3c6f54c | |
Gyuho Lee | 368ff75a10 | |
Gyuho Lee | 7adbfa1144 | |
Gyuho Lee | e151faf3cc | |
Gyuho Lee | 8292fd5051 | |
Gyuho Lee | c37245ed4b | |
Gyuho Lee | 6dab8aff66 | |
Hitoshi Mitake | c69efda350 | |
Hitoshi Mitake | 3d8e9a323d | |
Hitoshi Mitake | 963b242846 | |
Hitoshi Mitake | 6f011ce524 | |
Hitoshi Mitake | 36f8dee003 | |
Xiang Li | 47001f28bd | |
Sahdev P. Zala | 9a24f73f7b | |
Sahdev P. Zala | 7d1cf64049 | |
Sahdev P. Zala | 05c441f92f | |
Sahdev P. Zala | 434f7e83f0 | |
Gyuho Lee | 91b1a9182a | |
David Crawshaw | 78f67988aa | |
Gyuho Lee | 54ba958911 | |
Gyuho Lee | 609e844f86 | |
tangcong | 166b4473fa | |
tangcong | ed231df7c0 | |
Gyuho Lee | cfe37de6c0 | |
Gyuho Lee | 0de2b1f860 | |
Gyuho Lee | a668adba78 | |
Gyuho Lee | 9bad82fee5 | |
Gyuho Lee | f1ea03a7c8 | |
Ted Yu | 4079deadb4 | |
Viacheslav Biriukov | 87fc3c9e57 | |
Gary Belvin | e048e166ab | |
Gyuho Lee | 2333c727a2 | |
tangcong | aa75e90ac8 | |
shawwang | f18976f4b8 | |
Jingyi Hu | f1eca4e1fa | |
tangcong | b733b22712 | |
tangcong | eb80716532 | |
tangcong | e2abd97659 | |
tangcong | 716821b9b5 | |
shawwang | 63116ffdb4 | |
shawwang | b3d54def77 | |
tangcong | 347c8dac3b | |
Sahdev Zala | e2ae6013a4 | |
Changxin Miao | 9c8554573f | |
Gyuho Lee | e694b7bb08 | |
Gyuho Lee | e99399d0dc | |
Gyuho Lee | b68f8ff31d | |
Gyuho Lee | 857dffa386 | |
jingyih | 5f17aa2c8b | |
shenjiangc | 89b10cf967 | |
Gyuho Lee | bdc9bc1d81 | |
tangcong | b0bdaaa449 | |
Gyuho Lee | e784ba73c2 | |
Gyuho Lee | 35dc623a98 | |
Gyuho Lee | 130342152a | |
Gyuho Lee | fc93fbf9de | |
Gyuho Lee | 0dc5d577fc | |
Gyuho Lee | e63db56cc9 | |
Gyuho Lee | 1471e12108 | |
Gyuho Lee | 0b9cfa8677 | |
Sam Batschelet | b66c53ff5f | |
Sahdev Zala | 8f6c3f4d09 | |
jingyih | 379d01a0d2 | |
Gyuho Lee | c65a9e2dd1 | |
Jingyi Hu | 7862f6ed2c | |
Rafael Fernández López | 257319fb18 | |
Jingyi Hu | cdb2dc11b8 | |
jingyih | 770674e4a6 | |
Jingyi Hu | c10168f718 | |
jingyih | 94673a6ba4 | |
Joe Betz | a1bf5574fc | |
Joe Betz | 6d646c442a | |
Jingyi Hu | 1226686cf3 | |
jingyih | 50e12328ac | |
Jingyi Hu | 0dc78a144b | |
yoyinzyc | 7cf32c262c | |
yoyinzyc | 4a9247a47e | |
Jingyi Hu | ac63c2fbd0 | |
yoyinzyc | ae5bd3c268 | |
Jingyi Hu | 94e46ba0d7 | |
宇慕 | 8c10973820 | |
Wenjia | 1af0b51537 | |
yoyinzyc | f4669c3b62 | |
yoyinzyc | 55c3476abc | |
Jingyi Hu | b66203c0a1 | |
Gyuho Lee | 4388404f56 | |
Gyuho Lee | a447d51f23 | |
Jingyi Hu | 4f3c81d81d | |
Jingyi Hu | 478da3bf24 | |
Jingyi Hu | d6b30e43cd | |
Gyuho Lee | 1e98c9642e | |
Gyuho Lee | 3cf2f69b57 | |
Gyuho Lee | d617055284 | |
Gyuho Lee | 84db9b0878 | |
Gyuho Lee | 6cf418ff6d | |
yoyinzyc | 97e68cf4e7 | |
Gyuho Lee | 90556d550d | |
Jingyi Hu | a00abf5f2a | |
Gyuho Lee | b3329ebcd2 | |
Jingyi Hu | b67862c0a6 | |
Jingyi Hu | 6a699b6b7f | |
Joe Betz | bb5ba14aac | |
Gyuho Lee | c3dc994567 | |
Yuchen Zhou | f3fbed5b72 | |
yoyinzyc | e2547907c5 | |
Gyuho Lee | 14c239030f | |
yoyinzyc | 7b67e8a5c5 | |
Joe Betz | bbe86b066c | |
Gyuho Lee | 2c36cab87d | |
yoyinzyc | 480d5510f9 | |
yoyinzyc | 9245518363 | |
yoyinzyc | daa432cfa7 | |
yoyinzyc | 8717327697 | |
yoyinzyc | 4f1bbff888 | |
yoyinzyc | 28bb8037d9 | |
Joe Betz | 03b5e7229b | |
Joe Betz | 0781c0327d | |
Joe Betz | 99774d8ed4 | |
Joe Betz | c454344f14 | |
Gyuho Lee | dae0a72a42 | |
Gyuho Lee | c91a6bf14f | |
Jingyi Hu | b7ff97f54e | |
Gyuho Lee | d08bb07d6d | |
Gyuho Lee | 3a736a81e8 | |
Gyuho Lee | a14579fbfb | |
Gyuho Lee | ade66a5722 | |
Guangming Wang | 67cc70926d | |
Debabrata Banerjee | 3b8f812955 | |
Gyuho Lee | 21dcadc83c | |
chris | c7c379e52e | |
Gyuho Lee | 9ed5f76dc0 | |
Jingyi Hu | 994865c89e | |
Jingyi Hu | ccbbb2f8d6 | |
zhangjianweibj | d5f79adc9c | |
Gyuho Lee | 8b053b0f44 | |
Manuel Rüger | 11980f8165 | |
Brandon Philips | 41d4e2b276 | |
Gyuho Lee | 898bd1351f | |
Gyuho Lee | d04d96c9ac | |
keepCaim | 21edf98fdb | |
Carlos de Paula | a4f7c65ef8 | |
Gyuho Lee | c3a9eec843 | |
Gyuho Lee | e5528acf57 | |
Gyuho Lee | 9977550ae9 | |
Sam Batschelet | 4d7a6e2755 | |
vimalk78 | 5e8757c3c5 | |
Gyuho Lee | 012e38fef3 | |
Gyuho Lee | 41a2cfa122 | |
Gyuho Lee | 9f8a1edf38 | |
Wine93 | 165ba72593 | |
Wine93 | 9c850ccef0 | |
Raphael Westphal | 61d6efda4c | |
Gyuho Lee | b76f149c35 | |
vimalk78 | 5e33bb1a95 | |
vimalk78 | 83bf125d93 | |
Gyuho Lee | d23af41bca | |
Gyuho Lee | 67d0c21bb0 | |
nilsocket | 18a077d3d3 | |
Xiang Li | fb6d870e89 | |
Jingyi Hu | e00224f87e | |
Wenjia | 2af1caf1a5 | |
Gyuho Lee | 0777eab766 | |
Jingyi Hu | 0ecc0d0542 | |
Tobias Schottdorf | 982a8c9bc3 | |
Tobias Schottdorf | b8e3e4e7cb | |
Tobias Schottdorf | 4090edfb5b | |
Tobias Schottdorf | 078caccce5 | |
Tobias Schottdorf | d177b7f6b4 | |
Tobias Schottdorf | 2c1a1d8c32 | |
Tobias Schottdorf | 0fc108428e | |
Tobias Schottdorf | df489e7a2c | |
Gyuho Lee | f13a5102ec | |
Gyuho Lee | c9465f51d2 | |
Gyuho Lee | 8f85f0dc26 | |
Gyuho Lee | 0161e72d8d | |
Gyuho Lee | 1691eec2db | |
Joe Betz | 1e213b7ab6 | |
Joe Betz | b30c1eb2c8 | |
Gyuho Lee | a0be90f450 | |
Gyuho Lee | 8110a96f69 | |
Gyuho Lee | 8e05c73fa7 | |
Gyuho Lee | 970ca9fa43 | |
Gyuho Lee | a481ee809f | |
Gyuho Lee | 4d06d3b498 | |
Gyuho Lee | 98462b52d1 | |
Gyuho Lee | 2a8d09b83b | |
Gyuho Lee | 49c6e87f74 | |
Gyuho Lee | 84ed0f7f87 | |
Gyuho Lee | 52d34298ab | |
Gyuho Lee | 9c1d2eaee4 | |
Gyuho Lee | 547631a492 | |
Gyuho Lee | 802e01a0d8 | |
Gyuho Lee | 1dff1c869f | |
Tobias Schottdorf | ac6b604bb8 | |
Tobias Schottdorf | 69c97cdc8f | |
ethan | faa71d89d4 | |
Gyuho Lee | 64c16779c0 | |
Hanaasagi | 8ff71c52db | |
Tobias Schottdorf | dbe5198c45 | |
Tobias Schottdorf | 39d0f4e53c | |
nilsocket | a8b4213ec0 | |
Tobias Schottdorf | a945379ce4 | |
Tobias Schottdorf | 7a50cd7074 | |
Gyuho Lee | f786b6ba16 | |
Gyuho Lee | 1c8ab76333 | |
Gyuho Lee | abdb7ca17b | |
Zeming YU | 629cb7aa5e | |
Gyuho Lee | 89e102365d | |
Tobias Schottdorf | 9018b3dc4d | |
Gyuho Lee | b9bea9def7 | |
Gyuho Lee | d2675c13f4 | |
Gyuho Lee | 8230536171 | |
lzhfromustc | 524278c187 | |
Gyuho Lee | 29cdc9abfc | |
Zeming YU | a6a9a71b6a | |
Gyuho Lee | 8c8f6f4b01 | |
Zeming YU | b6cfaf883b | |
Gyuho Lee | b522281a98 | |
Gyuho Lee | a78793e6bf | |
Gyuho Lee | e09528aa06 | |
Wenjia Zhang | cb4507d15b | |
Gyuho Lee | 4cead3c25c | |
Wenjia | 3ac41644cc | |
Gyuho Lee | 0564743c9b | |
Gyuho Lee | 9d927afead | |
Gyuho Lee | 5d19b96341 | |
Gyuho Lee | faa1d9d206 | |
Gyuho Lee | ab1db0dfd8 | |
Gyuho Lee | 1c312cefbd | |
Gyuho Lee | b4fcaad87d | |
Gyuho Lee | 3468505e38 | |
Gyuho Lee | a2d68dd389 | |
Gyuho Lee | c6e9699960 | |
Gyuho Lee | b05dfeb15e | |
xkey | bb7df24af4 | |
Gyuho Lee | 9ff86fe516 | |
Gyuho Lee | bc9a54beae | |
Gyuho Lee | df1d3f7c6e | |
Gyuho Lee | 14053ba7f7 |
|
@ -0,0 +1,32 @@
|
|||
name: Release
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: goversion
|
||||
run: echo "goversion=$(cat .go-version)" >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ steps.goversion.outputs.goversion }}
|
||||
- name: release
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
git config --global user.email "github-action@etcd.io"
|
||||
git config --global user.name "Github Action"
|
||||
gpg --batch --gen-key <<EOF
|
||||
%no-protection
|
||||
Key-Type: 1
|
||||
Key-Length: 2048
|
||||
Subkey-Type: 1
|
||||
Subkey-Length: 2048
|
||||
Name-Real: Github Action
|
||||
Name-Email: github-action@etcd.io
|
||||
Expire-Date: 0
|
||||
EOF
|
||||
DRY_RUN=true ./scripts/release.sh --no-upload --no-docker-push --in-place 3.4.99
|
||||
- name: test-image
|
||||
run: |
|
||||
VERSION=3.4.99 ./scripts/test_images.sh
|
|
@ -0,0 +1,78 @@
|
|||
name: Tests
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- linux-amd64-fmt
|
||||
- linux-amd64-integration-1-cpu
|
||||
- linux-amd64-integration-2-cpu
|
||||
- linux-amd64-integration-4-cpu
|
||||
- linux-amd64-functional
|
||||
- linux-amd64-unit-4-cpu-race
|
||||
- all-build
|
||||
- linux-amd64-grpcproxy
|
||||
- linux-amd64-e2e
|
||||
- linux-386-unit
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: goversion
|
||||
run: echo "goversion=$(cat .go-version)" >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ steps.goversion.outputs.goversion }}
|
||||
- run: date
|
||||
- env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
go version
|
||||
echo ${GOROOT}
|
||||
echo "${TARGET}"
|
||||
case "${TARGET}" in
|
||||
linux-amd64-fmt)
|
||||
GOARCH=amd64 PASSES='fmt bom dep' ./test
|
||||
;;
|
||||
linux-amd64-integration-1-cpu)
|
||||
GOARCH=amd64 CPU=1 PASSES='integration' RACE='false' ./test
|
||||
;;
|
||||
linux-amd64-integration-2-cpu)
|
||||
GOARCH=amd64 CPU=2 PASSES='integration' RACE='false' ./test
|
||||
;;
|
||||
linux-amd64-integration-4-cpu)
|
||||
GOARCH=amd64 CPU=4 PASSES='integration' RACE='false' ./test
|
||||
;;
|
||||
linux-amd64-functional)
|
||||
./build && GOARCH=amd64 PASSES='functional' ./test
|
||||
;;
|
||||
linux-amd64-unit-4-cpu-race)
|
||||
GOARCH=amd64 PASSES='unit' RACE='true' CPU='4' ./test -p=2
|
||||
;;
|
||||
all-build)
|
||||
GOARCH=amd64 PASSES='build' ./test
|
||||
GOARCH=386 PASSES='build' ./test
|
||||
GO_BUILD_FLAGS='-v' GOOS=darwin GOARCH=amd64 ./build
|
||||
GO_BUILD_FLAGS='-v' GOOS=windows GOARCH=amd64 ./build
|
||||
GO_BUILD_FLAGS='-v' GOARCH=arm ./build
|
||||
GO_BUILD_FLAGS='-v' GOARCH=arm64 ./build
|
||||
GO_BUILD_FLAGS='-v' GOARCH=ppc64le ./build
|
||||
GO_BUILD_FLAGS='-v' GOARCH=s390x ./build
|
||||
;;
|
||||
linux-amd64-grpcproxy)
|
||||
PASSES='build grpcproxy' CPU='4' RACE='true' ./test
|
||||
;;
|
||||
linux-amd64-e2e)
|
||||
GOARCH=amd64 PASSES='build release e2e' ./test
|
||||
;;
|
||||
linux-386-unit)
|
||||
GOARCH=386 PASSES='unit' ./test
|
||||
;;
|
||||
*)
|
||||
echo "Failed to find target"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
|
@ -0,0 +1,37 @@
|
|||
name: Trivy Nightly Scan
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||
|
||||
permissions: read-all
|
||||
jobs:
|
||||
nightly-scan:
|
||||
name: Trivy Scan nightly
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# maintain the versions of etcd that need to be actively
|
||||
# security scanned
|
||||
versions: [v3.4.22]
|
||||
permissions:
|
||||
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
with:
|
||||
ref: release-3.4
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@9ab158e8597f3b310480b9a69402b419bc03dbd5 # master
|
||||
with:
|
||||
image-ref: 'gcr.io/etcd-development/etcd:${{ matrix.versions }}'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
format: 'template'
|
||||
template: '@/contrib/sarif.tpl'
|
||||
output: 'trivy-results-3-4.sarif'
|
||||
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2.1.36
|
||||
with:
|
||||
sarif_file: 'trivy-results-3-4.sarif'
|
|
@ -31,6 +31,7 @@ vendor/**/*
|
|||
!vendor/**/License*
|
||||
!vendor/**/LICENCE*
|
||||
!vendor/**/LICENSE*
|
||||
!vendor/modules.txt
|
||||
vendor/**/*_test.go
|
||||
|
||||
*.bak
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
1.19.10
|
132
.travis.yml
132
.travis.yml
|
@ -1,132 +0,0 @@
|
|||
language: go
|
||||
go_import_path: go.etcd.io/etcd
|
||||
|
||||
sudo: required
|
||||
|
||||
services: docker
|
||||
|
||||
go:
|
||||
- 1.12.7
|
||||
- tip
|
||||
|
||||
notifications:
|
||||
on_success: never
|
||||
on_failure: never
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- TARGET=linux-amd64-fmt
|
||||
- TARGET=linux-amd64-integration-1-cpu
|
||||
- TARGET=linux-amd64-integration-2-cpu
|
||||
- TARGET=linux-amd64-integration-4-cpu
|
||||
- TARGET=linux-amd64-functional
|
||||
- TARGET=linux-amd64-unit
|
||||
- TARGET=all-build
|
||||
- TARGET=linux-amd64-grpcproxy
|
||||
- TARGET=linux-amd64-coverage
|
||||
- TARGET=linux-amd64-fmt-unit-go-tip
|
||||
- TARGET=linux-386-unit
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- go: 1.12.7
|
||||
env: TARGET=linux-amd64-integration-4-cpu
|
||||
- go: 1.12.7
|
||||
env: TARGET=linux-amd64-grpcproxy
|
||||
- go: 1.12.7
|
||||
env: TARGET=linux-amd64-coverage
|
||||
- go: tip
|
||||
env: TARGET=linux-amd64-fmt-unit-go-tip
|
||||
- go: 1.12.7
|
||||
env: TARGET=linux-386-unit
|
||||
exclude:
|
||||
- go: tip
|
||||
env: TARGET=linux-amd64-fmt
|
||||
- go: tip
|
||||
env: TARGET=linux-amd64-integration-1-cpu
|
||||
- go: tip
|
||||
env: TARGET=linux-amd64-integration-2-cpu
|
||||
- go: tip
|
||||
env: TARGET=linux-amd64-integration-4-cpu
|
||||
- go: tip
|
||||
env: TARGET=linux-amd64-functional
|
||||
- go: tip
|
||||
env: TARGET=linux-amd64-unit
|
||||
- go: tip
|
||||
env: TARGET=all-build
|
||||
- go: tip
|
||||
env: TARGET=linux-amd64-grpcproxy
|
||||
- go: tip
|
||||
env: TARGET=linux-amd64-coverage
|
||||
- go: 1.12.7
|
||||
env: TARGET=linux-amd64-fmt-unit-go-tip
|
||||
- go: tip
|
||||
env: TARGET=linux-386-unit
|
||||
|
||||
before_install:
|
||||
- if [[ $TRAVIS_GO_VERSION == 1.* ]]; then docker pull gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION}; fi
|
||||
|
||||
install:
|
||||
- go get -t -v -d ./...
|
||||
|
||||
script:
|
||||
- echo "TRAVIS_GO_VERSION=${TRAVIS_GO_VERSION}"
|
||||
- >
|
||||
case "${TARGET}" in
|
||||
linux-amd64-fmt)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/go.etcd.io/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=amd64 PASSES='fmt bom dep' ./test"
|
||||
;;
|
||||
linux-amd64-integration-1-cpu)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/go.etcd.io/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=amd64 CPU=1 PASSES='integration' ./test"
|
||||
;;
|
||||
linux-amd64-integration-2-cpu)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/go.etcd.io/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=amd64 CPU=2 PASSES='integration' ./test"
|
||||
;;
|
||||
linux-amd64-integration-4-cpu)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/go.etcd.io/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=amd64 CPU=4 PASSES='integration' ./test"
|
||||
;;
|
||||
linux-amd64-functional)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/go.etcd.io/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "./build && GOARCH=amd64 PASSES='functional' ./test"
|
||||
;;
|
||||
linux-amd64-unit)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/go.etcd.io/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=amd64 PASSES='unit' ./test"
|
||||
;;
|
||||
all-build)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/go.etcd.io/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=amd64 PASSES='build' ./test \
|
||||
&& GOARCH=386 PASSES='build' ./test \
|
||||
&& GO_BUILD_FLAGS='-v' GOOS=darwin GOARCH=amd64 ./build \
|
||||
&& GO_BUILD_FLAGS='-v' GOOS=windows GOARCH=amd64 ./build \
|
||||
&& GO_BUILD_FLAGS='-v' GOARCH=arm ./build \
|
||||
&& GO_BUILD_FLAGS='-v' GOARCH=arm64 ./build \
|
||||
&& GO_BUILD_FLAGS='-v' GOARCH=ppc64le ./build"
|
||||
;;
|
||||
linux-amd64-grpcproxy)
|
||||
sudo HOST_TMP_DIR=/tmp TEST_OPTS="PASSES='build grpcproxy'" make docker-test
|
||||
;;
|
||||
linux-amd64-coverage)
|
||||
sudo HOST_TMP_DIR=/tmp make docker-test-coverage
|
||||
;;
|
||||
linux-amd64-fmt-unit-go-tip)
|
||||
GOARCH=amd64 PASSES='fmt unit' ./test
|
||||
;;
|
||||
linux-386-unit)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/go.etcd.io/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=386 PASSES='unit' ./test"
|
||||
;;
|
||||
esac
|
4
.words
4
.words
|
@ -36,6 +36,8 @@ iff
|
|||
inflight
|
||||
keepalive
|
||||
keepalives
|
||||
hasleader
|
||||
racey
|
||||
keyspace
|
||||
linearization
|
||||
liveness
|
||||
|
@ -74,6 +76,7 @@ consistentIndex
|
|||
todo
|
||||
saveWALAndSnap
|
||||
|
||||
SHA
|
||||
subconns
|
||||
nop
|
||||
SubConns
|
||||
|
@ -95,6 +98,7 @@ jitter
|
|||
WithBackoff
|
||||
BackoffLinearWithJitter
|
||||
jitter
|
||||
WithDialer
|
||||
WithMax
|
||||
ServerStreams
|
||||
BidiStreams
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v2.3.8](https://github.com/etcd-io/etcd/releases/tag/v2.3.8) (2017-02-17)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v2.3.7...v2.3.8).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
|
||||
|
||||
|
||||
<hr>
|
||||
|
291
CHANGELOG-3.0.md
291
CHANGELOG-3.0.md
|
@ -1,291 +0,0 @@
|
|||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.16](https://github.com/etcd-io/etcd/releases/tag/v3.0.16) (2016-11-13)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.15...v3.0.16) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.4*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.15](https://github.com/etcd-io/etcd/releases/tag/v3.0.15) (2016-11-11)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.14...v3.0.15) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix cancel watch request with wrong range end.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.14](https://github.com/etcd-io/etcd/releases/tag/v3.0.14) (2016-11-04)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.13...v3.0.14) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Added
|
||||
|
||||
- v3 `etcdctl migrate` command now supports `--no-ttl` flag to discard keys on transform.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.13](https://github.com/etcd-io/etcd/releases/tag/v3.0.13) (2016-10-24)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.12...v3.0.13) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.12](https://github.com/etcd-io/etcd/releases/tag/v3.0.12) (2016-10-07)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.11...v3.0.12) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.11](https://github.com/etcd-io/etcd/releases/tag/v3.0.11) (2016-10-07)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.10...v3.0.11) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Added
|
||||
|
||||
- Server returns previous key-value (optional)
|
||||
- `clientv3.WithPrevKV` option
|
||||
- v3 etcdctl `put,watch,del --prev-kv` flag
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.10](https://github.com/etcd-io/etcd/releases/tag/v3.0.10) (2016-09-23)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.9...v3.0.10) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.9](https://github.com/etcd-io/etcd/releases/tag/v3.0.9) (2016-09-15)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.8...v3.0.9) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Added
|
||||
|
||||
- Warn on domain names on listen URLs (v3.2 will reject domain names).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.8](https://github.com/etcd-io/etcd/releases/tag/v3.0.8) (2016-09-09)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.7...v3.0.8) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Other
|
||||
|
||||
- Allow only IP addresses in listen URLs (domain names are rejected).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.7](https://github.com/etcd-io/etcd/releases/tag/v3.0.7) (2016-08-31)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.6...v3.0.7) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Other
|
||||
|
||||
- SRV records only allow A records (RFC 2052).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.6](https://github.com/etcd-io/etcd/releases/tag/v3.0.6) (2016-08-19)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.5...v3.0.6) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.5](https://github.com/etcd-io/etcd/releases/tag/v3.0.5) (2016-08-19)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.4...v3.0.5) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Other
|
||||
|
||||
- SRV records (e.g., infra1.example.com) must match the discovery domain (i.e., example.com) if no custom certificate authority is given.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.4](https://github.com/etcd-io/etcd/releases/tag/v3.0.4) (2016-07-27)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.3...v3.0.4) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Added
|
||||
|
||||
- v2 `etcdctl ls` command now supports `--output=json`.
|
||||
- Add /var/lib/etcd directory to etcd official Docker image.
|
||||
|
||||
### Other
|
||||
|
||||
- v2 auth can now use common name from TLS certificate when `--client-cert-auth` is enabled.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.3*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.3](https://github.com/etcd-io/etcd/releases/tag/v3.0.3) (2016-07-15)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.2...v3.0.3) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Other
|
||||
|
||||
- Revert Dockerfile to use `CMD`, instead of `ENTRYPOINT`, to support `etcdctl` run.
|
||||
- Docker commands for v3.0.2 won't work without specifying executable binary paths.
|
||||
- v3 etcdctl default endpoints are now `127.0.0.1:2379`.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.2*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.2](https://github.com/etcd-io/etcd/releases/tag/v3.0.2) (2016-07-08)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.1...v3.0.2) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Other
|
||||
|
||||
- Dockerfile uses `ENTRYPOINT`, instead of `CMD`, to run etcd without binary path specified.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.2*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.1](https://github.com/etcd-io/etcd/releases/tag/v3.0.1) (2016-07-01)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.0...v3.0.1) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.2*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.0.0](https://github.com/etcd-io/etcd/releases/tag/v3.0.0) (2016-06-30)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v2.3.0...v3.0.0) and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).**
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.6.2*](https://golang.org/doc/devel/release.html#go1.6).
|
||||
|
||||
|
||||
<hr>
|
||||
|
574
CHANGELOG-3.1.md
574
CHANGELOG-3.1.md
|
@ -1,574 +0,0 @@
|
|||
|
||||
|
||||
Previous change logs can be found at [CHANGELOG-3.0](https://github.com/etcd-io/etcd/blob/master/CHANGELOG-3.0.md).
|
||||
|
||||
|
||||
The minimum recommended etcd versions to run in **production** are 3.1.11+, 3.2.26+, and 3.3.11+.
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
## [v3.1.21](https://github.com/etcd-io/etcd/releases/tag/v3.1.21) (2019-TBD)
|
||||
|
||||
### etcdctl
|
||||
|
||||
- [Strip out insecure endpoints from DNS SRV records when using discovery](https://github.com/etcd-io/etcd/pull/10443) with etcdctl v2
|
||||
- Add [`etcdctl endpoint health --write-out` support](https://github.com/etcd-io/etcd/pull/9540).
|
||||
- Previously, [`etcdctl endpoint health --write-out json` did not work](https://github.com/etcd-io/etcd/issues/9532).
|
||||
- The command output is changed. Previously, if endpoint is unreachable, the command output is
|
||||
"\<endpoint\> is unhealthy: failed to connect: \<error message\>". This change unified the error message, all error types
|
||||
now have the same output "\<endpoint\> is unhealthy: failed to commit proposal: \<error message\>".
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
- Fix bug where [db_compaction_total_duration_milliseconds metric incorrectly measured duration as 0](https://github.com/etcd-io/etcd/pull/10646).
|
||||
|
||||
<hr>
|
||||
|
||||
## [v3.1.20](https://github.com/etcd-io/etcd/releases/tag/v3.1.20) (2018-10-10)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.19...v3.1.20) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Improve ["became inactive" warning log](https://github.com/etcd-io/etcd/pull/10024), which indicates message send to a peer failed.
|
||||
- Improve [read index wait timeout warning log](https://github.com/etcd-io/etcd/pull/10026), which indicates that local node might have slow network.
|
||||
- Add [gRPC interceptor for debugging logs](https://github.com/etcd-io/etcd/pull/9990); enable `etcd --debug` flag to see per-request debug information.
|
||||
- Add [consistency check in snapshot status](https://github.com/etcd-io/etcd/pull/10109). If consistency check on snapshot file fails, `snapshot status` returns `"snapshot file integrity check failed..."` error.
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.1.12/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Improve [`etcd_network_peer_round_trip_time_seconds`](https://github.com/etcd-io/etcd/pull/10155) Prometheus metric to track leader heartbeats.
|
||||
- Previously, it only samples the TCP connection for snapshot messages.
|
||||
- Display all registered [gRPC metrics at start](https://github.com/etcd-io/etcd/pull/10034).
|
||||
- Add [`etcd_snap_db_fsync_duration_seconds_count`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_snap_db_save_total_duration_seconds_bucket`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_server_id`](https://github.com/etcd-io/etcd/pull/9998) Prometheus metric.
|
||||
- Add [`etcd_server_health_success`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
|
||||
- Add [`etcd_server_health_failures`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
|
||||
- Add [`etcd_server_read_indexes_failed_total`](https://github.com/etcd-io/etcd/pull/10094) Prometheus metric.
|
||||
|
||||
### client v3
|
||||
|
||||
- Fix logic on [release lock key if cancelled](https://github.com/etcd-io/etcd/pull/10153) in `clientv3/concurrency` package.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.19](https://github.com/etcd-io/etcd/releases/tag/v3.1.19) (2018-07-24)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.18...v3.1.19) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Improve [Raft Read Index timeout warning messages](https://github.com/etcd-io/etcd/pull/9897).
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.1.12/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add [`etcd_server_go_version`](https://github.com/etcd-io/etcd/pull/9957) Prometheus metric.
|
||||
- Add [`etcd_server_slow_read_indexes_total`](https://github.com/etcd-io/etcd/pull/9897) Prometheus metric.
|
||||
- Add [`etcd_server_quota_backend_bytes`](https://github.com/etcd-io/etcd/pull/9820) Prometheus metric.
|
||||
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
|
||||
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
|
||||
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
|
||||
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
|
||||
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
|
||||
- Add [`etcd_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819) Prometheus metric.
|
||||
- In addition to [`etcd_debugging_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819).
|
||||
- Add [`etcd_mvcc_db_total_size_in_use_in_bytes`](https://github.com/etcd-io/etcd/pull/9256) Prometheus metric.
|
||||
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
|
||||
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
|
||||
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
|
||||
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
|
||||
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
|
||||
|
||||
### client v3
|
||||
|
||||
- Fix [lease keepalive interval updates when response queue is full](https://github.com/etcd-io/etcd/pull/9952).
|
||||
- If `<-chan *clientv3LeaseKeepAliveResponse` from `clientv3.Lease.KeepAlive` was never consumed or channel is full, client was [sending keepalive request every 500ms](https://github.com/etcd-io/etcd/issues/9911) instead of expected rate of every "TTL / 3" duration.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.18](https://github.com/etcd-io/etcd/releases/tag/v3.1.18) (2018-06-15)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.17...v3.1.18) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.1.12/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add [`etcd_server_version`](https://github.com/etcd-io/etcd/pull/8960) Prometheus metric.
|
||||
- To replace [Kubernetes `etcd-version-monitor`](https://github.com/etcd-io/etcd/issues/8948).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.17](https://github.com/etcd-io/etcd/releases/tag/v3.1.17) (2018-06-06)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.16...v3.1.17) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [v3 snapshot recovery](https://github.com/etcd-io/etcd/issues/7628).
|
||||
- A follower receives a leader snapshot to be persisted as a `[SNAPSHOT-INDEX].snap.db` file on disk.
|
||||
- Now, server [ensures that the incoming snapshot be persisted on disk before loading it](https://github.com/etcd-io/etcd/pull/7876).
|
||||
- Otherwise, index mismatch happens and triggers server-side panic (e.g. newer WAL entry with outdated snapshot index).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.16](https://github.com/etcd-io/etcd/releases/tag/v3.1.16) (2018-05-31)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.15...v3.1.16) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [`mvcc` server panic from restore operation](https://github.com/etcd-io/etcd/pull/9775).
|
||||
- Let's assume that a watcher had been requested with a future revision X and sent to node A that became network-partitioned thereafter. Meanwhile, cluster makes progress. Then when the partition gets removed, the leader sends a snapshot to node A. Previously if the snapshot's latest revision is still lower than the watch revision X, **etcd server panicked** during snapshot restore operation.
|
||||
- Now, this server-side panic has been fixed.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.15](https://github.com/etcd-io/etcd/releases/tag/v3.1.15) (2018-05-09)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.14...v3.1.15) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Purge old [`*.snap.db` snapshot files](https://github.com/etcd-io/etcd/pull/7967).
|
||||
- Previously, etcd did not respect `--max-snapshots` flag to purge old `*.snap.db` files.
|
||||
- Now, etcd purges old `*.snap.db` files to keep maximum `--max-snapshots` number of files on disk.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.14](https://github.com/etcd-io/etcd/releases/tag/v3.1.14) (2018-04-24)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.13...v3.1.14) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.1.12/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add [`etcd_server_is_leader`](https://github.com/etcd-io/etcd/pull/9587) Prometheus metric.
|
||||
|
||||
### etcd server
|
||||
|
||||
- Add [`--initial-election-tick-advance`](https://github.com/etcd-io/etcd/pull/9591) flag to configure initial election tick fast-forward.
|
||||
- By default, `--initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
|
||||
- This benefits the case of larger election ticks. For instance, cross datacenter deployment may require longer election timeout of 10-second. If true, local node does not need wait up to 10-second. Instead, forwards its election ticks to 8-second, and have only 2-second left before leader election.
|
||||
- Major assumptions are that: cluster has no active leader thus advancing ticks enables faster leader election. Or cluster already has an established leader, and rejoining follower is likely to receive heartbeats from the leader after tick advance and before election timeout.
|
||||
- However, when network from leader to rejoining follower is congested, and the follower does not receive leader heartbeat within left election ticks, disruptive election has to happen thus affecting cluster availabilities.
|
||||
- Now, this can be disabled by setting `--initial-election-tick-advance=false`.
|
||||
- Disabling this would slow down initial bootstrap process for cross datacenter deployments. Make tradeoffs by configuring `--initial-election-tick-advance` at the cost of slow initial bootstrap.
|
||||
- If single-node, it advances ticks regardless.
|
||||
- Address [disruptive rejoining follower node](https://github.com/etcd-io/etcd/issues/9333).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.13](https://github.com/etcd-io/etcd/releases/tag/v3.1.13) (2018-03-29)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.12...v3.1.13) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Adjust [election timeout on server restart](https://github.com/etcd-io/etcd/pull/9415) to reduce [disruptive rejoining servers](https://github.com/etcd-io/etcd/issues/9333).
|
||||
- Previously, etcd fast-forwards election ticks on server start, with only one tick left for leader election. This is to speed up start phase, without having to wait until all election ticks elapse. Advancing election ticks is useful for cross datacenter deployments with larger election timeouts. However, it was affecting cluster availability if the last tick elapses before leader contacts the restarted node.
|
||||
- Now, when etcd restarts, it adjusts election ticks with more than one tick left, thus more time for leader to prevent disruptive restart.
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.1.12/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add missing [`etcd_network_peer_sent_failures_total` count](https://github.com/etcd-io/etcd/pull/9437).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.12](https://github.com/etcd-io/etcd/releases/tag/v3.1.12) (2018-03-08)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.11...v3.1.12) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [`mvcc` "unsynced" watcher restore operation](https://github.com/etcd-io/etcd/pull/9297).
|
||||
- "unsynced" watcher is watcher that needs to be in sync with events that have happened.
|
||||
- That is, "unsynced" watcher is the slow watcher that was requested on old revision.
|
||||
- "unsynced" watcher restore operation was not correctly populating its underlying watcher group.
|
||||
- Which possibly causes [missing events from "unsynced" watchers](https://github.com/etcd-io/etcd/issues/9086).
|
||||
- A node gets network partitioned with a watcher on a future revision, and falls behind receiving a leader snapshot after partition gets removed. When applying this snapshot, etcd watch storage moves current synced watchers to unsynced since sync watchers might have become stale during network partition. And reset synced watcher group to restart watcher routines. Previously, there was a bug when moving from synced watcher group to unsynced, thus client would miss events when the watcher was requested to the network-partitioned node.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.11](https://github.com/etcd-io/etcd/releases/tag/v3.1.11) (2017-11-28)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.10...v3.1.11) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- [#8411](https://github.com/etcd-io/etcd/issues/8411),[#8806](https://github.com/etcd-io/etcd/pull/8806) backport "mvcc: sending events after restore"
|
||||
- [#8009](https://github.com/etcd-io/etcd/issues/8009),[#8902](https://github.com/etcd-io/etcd/pull/8902) backport coreos/bbolt v1.3.1-coreos.5
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.10](https://github.com/etcd-io/etcd/releases/tag/v3.1.10) (2017-07-14)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.9...v3.1.10) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### Added
|
||||
|
||||
- Tag docker images with minor versions.
|
||||
- e.g. `docker pull quay.io/coreos/etcd:v3.1` to fetch latest v3.1 versions.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
- Fix panic on `net/http.CloseNotify`
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.9](https://github.com/etcd-io/etcd/releases/tag/v3.1.9) (2017-06-09)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.8...v3.1.9) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Allow v2 snapshot over 512MB.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.7.6*](https://golang.org/doc/devel/release.html#go1.7).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.8](https://github.com/etcd-io/etcd/releases/tag/v3.1.8) (2017-05-19)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.7...v3.1.8) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.7](https://github.com/etcd-io/etcd/releases/tag/v3.1.7) (2017-04-28)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.6...v3.1.7) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.6](https://github.com/etcd-io/etcd/releases/tag/v3.1.6) (2017-04-19)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.5...v3.1.6) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fill in Auth API response header.
|
||||
- Remove auth check in Status API.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.5](https://github.com/etcd-io/etcd/releases/tag/v3.1.5) (2017-03-27)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.4...v3.1.5) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix raft memory leak issue.
|
||||
- Fix Windows file path issues.
|
||||
|
||||
### Other
|
||||
|
||||
- Add `/etc/nsswitch.conf` file to alpine-based Docker image.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.4](https://github.com/etcd-io/etcd/releases/tag/v3.1.4) (2017-03-22)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.3...v3.1.4) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.3](https://github.com/etcd-io/etcd/releases/tag/v3.1.3) (2017-03-10)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.2...v3.1.3) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### etcd gateway
|
||||
|
||||
- Fix `etcd gateway` schema handling in DNS discovery.
|
||||
- Fix sd_notify behaviors in `gateway`, `grpc-proxy`.
|
||||
|
||||
### gRPC Proxy
|
||||
|
||||
- Fix sd_notify behaviors in `gateway`, `grpc-proxy`.
|
||||
|
||||
### Other
|
||||
|
||||
- Use machine default host when advertise URLs are default values(`localhost:2379,2380`) AND if listen URL is `0.0.0.0`.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.2](https://github.com/etcd-io/etcd/releases/tag/v3.1.2) (2017-02-24)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.1...v3.1.2) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### etcd gateway
|
||||
|
||||
- Fix `etcd gateway` with multiple endpoints.
|
||||
|
||||
### Other
|
||||
|
||||
- Use IPv4 default host, by default (when IPv4 and IPv6 are available).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.1](https://github.com/etcd-io/etcd/releases/tag/v3.1.1) (2017-02-17)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.0...v3.1.1) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.7.5*](https://golang.org/doc/devel/release.html#go1.7).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.1.0](https://github.com/etcd-io/etcd/releases/tag/v3.1.0) (2017-01-20)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.0.0...v3.1.0) and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.1 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Faster linearizable reads (implements Raft [read-index](https://github.com/etcd-io/etcd/pull/6212)).
|
||||
- v3 authentication API is now stable.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Deprecated following gRPC metrics in favor of [go-grpc-prometheus](https://github.com/grpc-ecosystem/go-grpc-prometheus).
|
||||
- `etcd_grpc_requests_total`
|
||||
- `etcd_grpc_requests_failed_total`
|
||||
- `etcd_grpc_active_streams`
|
||||
- `etcd_grpc_unary_requests_duration_seconds`
|
||||
|
||||
### Dependency
|
||||
|
||||
- Upgrade [`github.com/ugorji/go/codec`](https://github.com/ugorji/go) to [**`ugorji/go@9c7f9b7`**](https://github.com/ugorji/go/commit/9c7f9b7a2bc3a520f7c7b30b34b7f85f47fe27b6), and [regenerate v2 `client`](https://github.com/etcd-io/etcd/pull/6945).
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
See [security doc](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/security.md) for more details.
|
||||
|
||||
- SRV records (e.g., infra1.example.com) must match the discovery domain (i.e., example.com) if no custom certificate authority is given.
|
||||
- `TLSConfig.ServerName` is ignored with user-provided certificates for backwards compatibility; to be deprecated.
|
||||
- For example, `etcd --discovery-srv=example.com` will only authenticate peers/clients when the provided certs have root domain `example.com` as an entry in Subject Alternative Name (SAN) field.
|
||||
|
||||
### etcd server
|
||||
|
||||
- Automatic leadership transfer when leader steps down.
|
||||
- etcd flags
|
||||
- `--strict-reconfig-check` flag is set by default.
|
||||
- Add `--log-output` flag.
|
||||
- Add `--metrics` flag.
|
||||
- etcd uses default route IP if advertise URL is not given.
|
||||
- Cluster rejects removing members if quorum will be lost.
|
||||
- Discovery now has upper limit for waiting on retries.
|
||||
- Warn on binding listeners through domain names; to be deprecated.
|
||||
- v3.0 and v3.1 with `--auto-compaction-retention=10` run periodic compaction on v3 key-value store for every 10-hour.
|
||||
- Compactor only supports periodic compaction.
|
||||
- Compactor records latest revisions every 5-minute, until it reaches the first compaction period (e.g. 10-hour).
|
||||
- In order to retain key-value history of last compaction period, it uses the last revision that was fetched before compaction period, from the revision records that were collected every 5-minute.
|
||||
- When `--auto-compaction-retention=10`, compactor uses revision 100 for compact revision where revision 100 is the latest revision fetched from 10 hours ago.
|
||||
- If compaction succeeds or requested revision has already been compacted, it resets period timer and starts over with new historical revision records (e.g. restart revision collect and compact for the next 10-hour period).
|
||||
- If compaction fails, it retries in 5 minutes.
|
||||
|
||||
### client v3
|
||||
|
||||
- Add `SetEndpoints` method; update endpoints at runtime.
|
||||
- Add `Sync` method; auto-update endpoints at runtime.
|
||||
- Add `Lease TimeToLive` API; fetch lease information.
|
||||
- replace Config.Logger field with global logger.
|
||||
- Get API responses are sorted in ascending order by default.
|
||||
|
||||
### etcdctl v3
|
||||
|
||||
- Add `lease timetolive` command.
|
||||
- Add `--print-value-only` flag to get command.
|
||||
- Add `--dest-prefix` flag to make-mirror command.
|
||||
- `get` command responses are sorted in ascending order by default.
|
||||
|
||||
### gRPC Proxy
|
||||
|
||||
- Experimental gRPC proxy feature.
|
||||
|
||||
### Other
|
||||
|
||||
- `recipes` now conform to sessions defined in `clientv3/concurrency`.
|
||||
- ACI has symlinks to `/usr/local/bin/etcd*`.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.7.4*](https://golang.org/doc/devel/release.html#go1.7).
|
||||
|
||||
|
||||
<hr>
|
||||
|
884
CHANGELOG-3.2.md
884
CHANGELOG-3.2.md
|
@ -1,884 +0,0 @@
|
|||
|
||||
|
||||
Previous change logs can be found at [CHANGELOG-3.1](https://github.com/etcd-io/etcd/blob/master/CHANGELOG-3.1.md).
|
||||
|
||||
|
||||
The minimum recommended etcd versions to run in **production** are 3.1.11+, 3.2.26+, and 3.3.11+.
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.27](https://github.com/etcd-io/etcd/releases/tag/v3.2.27) (2019-TBD)
|
||||
|
||||
### etcdctl
|
||||
|
||||
- [Strip out insecure endpoints from DNS SRV records when using discovery](https://github.com/etcd-io/etcd/pull/10443) with etcdctl v2
|
||||
- Add [`etcdctl endpoint health --write-out` support](https://github.com/etcd-io/etcd/pull/9540).
|
||||
- Previously, [`etcdctl endpoint health --write-out json` did not work](https://github.com/etcd-io/etcd/issues/9532).
|
||||
- The command output is changed. Previously, if endpoint is unreachable, the command output is
|
||||
"\<endpoint\> is unhealthy: failed to connect: \<error message\>". This change unified the error message, all error types
|
||||
now have the same output "\<endpoint\> is unhealthy: failed to commit proposal: \<error message\>".
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
- Fix bug where [db_compaction_total_duration_milliseconds metric incorrectly measured duration as 0](https://github.com/etcd-io/etcd/pull/10646).
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.26](https://github.com/etcd-io/etcd/releases/tag/v3.2.26) (2019-01-11)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.25...v3.2.26) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### gRPC Proxy
|
||||
|
||||
- Fix [memory leak in cache layer](https://github.com/etcd-io/etcd/pull/10327).
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
- Disable [CommonName authentication for gRPC-gateway](https://github.com/etcd-io/etcd/pull/10366) gRPC-gateway proxy requests to etcd server use the etcd client server TLS certificate. If that certificate contains CommonName we do not want to use that for authentication as it could lead to permission escalation.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.25](https://github.com/etcd-io/etcd/releases/tag/v3.2.25) (2018-10-10)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.24...v3.2.25) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Improve ["became inactive" warning log](https://github.com/etcd-io/etcd/pull/10024), which indicates message send to a peer failed.
|
||||
- Improve [read index wait timeout warning log](https://github.com/etcd-io/etcd/pull/10026), which indicates that local node might have slow network.
|
||||
- Add [gRPC interceptor for debugging logs](https://github.com/etcd-io/etcd/pull/9990); enable `etcd --debug` flag to see per-request debug information.
|
||||
- Add [consistency check in snapshot status](https://github.com/etcd-io/etcd/pull/10109). If consistency check on snapshot file fails, `snapshot status` returns `"snapshot file integrity check failed..."` error.
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.2.17/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Improve [`etcd_network_peer_round_trip_time_seconds`](https://github.com/etcd-io/etcd/pull/10155) Prometheus metric to track leader heartbeats.
|
||||
- Previously, it only samples the TCP connection for snapshot messages.
|
||||
- Display all registered [gRPC metrics at start](https://github.com/etcd-io/etcd/pull/10032).
|
||||
- Add [`etcd_snap_db_fsync_duration_seconds_count`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_snap_db_save_total_duration_seconds_bucket`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_server_id`](https://github.com/etcd-io/etcd/pull/9998) Prometheus metric.
|
||||
- Add [`etcd_server_health_success`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
|
||||
- Add [`etcd_server_health_failures`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
|
||||
- Add [`etcd_server_read_indexes_failed_total`](https://github.com/etcd-io/etcd/pull/10094) Prometheus metric.
|
||||
|
||||
### client v3
|
||||
|
||||
- Fix logic on [release lock key if cancelled](https://github.com/etcd-io/etcd/pull/10153) in `clientv3/concurrency` package.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.24](https://github.com/etcd-io/etcd/releases/tag/v3.2.24) (2018-07-24)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.23...v3.2.24) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Improve [Raft Read Index timeout warning messages](https://github.com/etcd-io/etcd/pull/9897).
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.2.27/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add [`etcd_server_go_version`](https://github.com/etcd-io/etcd/pull/9957) Prometheus metric.
|
||||
- Add [`etcd_server_heartbeat_send_failures_total`](https://github.com/etcd-io/etcd/pull/9942) Prometheus metric.
|
||||
- Add [`etcd_server_slow_apply_total`](https://github.com/etcd-io/etcd/pull/9942) Prometheus metric.
|
||||
- Add [`etcd_disk_backend_defrag_duration_seconds`](https://github.com/etcd-io/etcd/pull/9942) Prometheus metric.
|
||||
- Add [`etcd_mvcc_hash_duration_seconds`](https://github.com/etcd-io/etcd/pull/9942) Prometheus metric.
|
||||
- Add [`etcd_server_slow_read_indexes_total`](https://github.com/etcd-io/etcd/pull/9897) Prometheus metric.
|
||||
- Add [`etcd_server_quota_backend_bytes`](https://github.com/etcd-io/etcd/pull/9820) Prometheus metric.
|
||||
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
|
||||
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
|
||||
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
|
||||
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
|
||||
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
|
||||
- Add [`etcd_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819) Prometheus metric.
|
||||
- In addition to [`etcd_debugging_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819).
|
||||
- Add [`etcd_mvcc_db_total_size_in_use_in_bytes`](https://github.com/etcd-io/etcd/pull/9256) Prometheus metric.
|
||||
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_server_quota_backend_bytes`.
|
||||
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
|
||||
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
|
||||
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
|
||||
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
|
||||
|
||||
### gRPC Proxy
|
||||
|
||||
- Add [flags for specifying TLS for connecting to proxy](https://github.com/etcd-io/etcd/pull/9894):
|
||||
- Add `grpc-proxy start --cert-file`, `grpc-proxy start --key-file` and `grpc-proxy start --trusted-ca-file` flags.
|
||||
- Add [`grpc-proxy start --metrics-addr` flag for specifying a separate metrics listen address](https://github.com/etcd-io/etcd/pull/9894).
|
||||
|
||||
### client v3
|
||||
|
||||
- Fix [lease keepalive interval updates when response queue is full](https://github.com/etcd-io/etcd/pull/9952).
|
||||
- If `<-chan *clientv3LeaseKeepAliveResponse` from `clientv3.Lease.KeepAlive` was never consumed or channel is full, client was [sending keepalive request every 500ms](https://github.com/etcd-io/etcd/issues/9911) instead of expected rate of every "TTL / 3" duration.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.23](https://github.com/etcd-io/etcd/releases/tag/v3.2.23) (2018-06-15)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.22...v3.2.23) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Improve [slow request apply warning log](https://github.com/etcd-io/etcd/pull/9288).
|
||||
- e.g. `read-only range request "key:\"/a\" range_end:\"/b\" " with result "range_response_count:3 size:96" took too long (97.966µs) to execute`.
|
||||
- Redact [request value field](https://github.com/etcd-io/etcd/pull/9822).
|
||||
- Provide [response size](https://github.com/etcd-io/etcd/pull/9826).
|
||||
- Add [backoff on watch retries on transient errors](https://github.com/etcd-io/etcd/pull/9840).
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.2.27/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add [`etcd_server_version`](https://github.com/etcd-io/etcd/pull/8960) Prometheus metric.
|
||||
- To replace [Kubernetes `etcd-version-monitor`](https://github.com/etcd-io/etcd/issues/8948).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.22](https://github.com/etcd-io/etcd/releases/tag/v3.2.22) (2018-06-06)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.21...v3.2.22) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
- Support TLS cipher suite whitelisting.
|
||||
- To block [weak cipher suites](https://github.com/etcd-io/etcd/issues/8320).
|
||||
- TLS handshake fails when client hello is requested with invalid cipher suites.
|
||||
- Add [`etcd --cipher-suites`](https://github.com/etcd-io/etcd/pull/9801) flag.
|
||||
- If empty, Go auto-populates the list.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.21](https://github.com/etcd-io/etcd/releases/tag/v3.2.21) (2018-05-31)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.20...v3.2.21) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [auth storage panic when simple token provider is disabled](https://github.com/etcd-io/etcd/pull/8695).
|
||||
- Fix [`mvcc` server panic from restore operation](https://github.com/etcd-io/etcd/pull/9775).
|
||||
- Let's assume that a watcher had been requested with a future revision X and sent to node A that became network-partitioned thereafter. Meanwhile, cluster makes progress. Then when the partition gets removed, the leader sends a snapshot to node A. Previously if the snapshot's latest revision is still lower than the watch revision X, **etcd server panicked** during snapshot restore operation.
|
||||
- Now, this server-side panic has been fixed.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.20](https://github.com/etcd-io/etcd/releases/tag/v3.2.20) (2018-05-09)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.19...v3.2.20) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Purge old [`*.snap.db` snapshot files](https://github.com/etcd-io/etcd/pull/7967).
|
||||
- Previously, etcd did not respect `--max-snapshots` flag to purge old `*.snap.db` files.
|
||||
- Now, etcd purges old `*.snap.db` files to keep maximum `--max-snapshots` number of files on disk.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.19](https://github.com/etcd-io/etcd/releases/tag/v3.2.19) (2018-04-24)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.18...v3.2.19) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.2.27/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Fix [`etcd_debugging_server_lease_expired_total`](https://github.com/etcd-io/etcd/pull/9557) Prometheus metric.
|
||||
- Fix [race conditions in v2 server stat collecting](https://github.com/etcd-io/etcd/pull/9562).
|
||||
- Add [`etcd_server_is_leader`](https://github.com/etcd-io/etcd/pull/9587) Prometheus metric.
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
- Fix [TLS reload](https://github.com/etcd-io/etcd/pull/9570) when [certificate SAN field only includes IP addresses but no domain names](https://github.com/etcd-io/etcd/issues/9541).
|
||||
- In Go, server calls `(*tls.Config).GetCertificate` for TLS reload if and only if server's `(*tls.Config).Certificates` field is not empty, or `(*tls.ClientHelloInfo).ServerName` is not empty with a valid SNI from the client. Previously, etcd always populates `(*tls.Config).Certificates` on the initial client TLS handshake, as non-empty. Thus, client was always expected to supply a matching SNI in order to pass the TLS verification and to trigger `(*tls.Config).GetCertificate` to reload TLS assets.
|
||||
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/etcd-io/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
|
||||
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
|
||||
|
||||
### etcd server
|
||||
|
||||
- Add [`etcd --initial-election-tick-advance`](https://github.com/etcd-io/etcd/pull/9591) flag to configure initial election tick fast-forward.
|
||||
- By default, `etcd --initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
|
||||
- This benefits the case of larger election ticks. For instance, cross datacenter deployment may require longer election timeout of 10-second. If true, local node does not need wait up to 10-second. Instead, forwards its election ticks to 8-second, and have only 2-second left before leader election.
|
||||
- Major assumptions are that: cluster has no active leader thus advancing ticks enables faster leader election. Or cluster already has an established leader, and rejoining follower is likely to receive heartbeats from the leader after tick advance and before election timeout.
|
||||
- However, when network from leader to rejoining follower is congested, and the follower does not receive leader heartbeat within left election ticks, disruptive election has to happen thus affecting cluster availabilities.
|
||||
- Now, this can be disabled by setting `--initial-election-tick-advance=false`.
|
||||
- Disabling this would slow down initial bootstrap process for cross datacenter deployments. Make tradeoffs by configuring `--initial-election-tick-advance` at the cost of slow initial bootstrap.
|
||||
- If single-node, it advances ticks regardless.
|
||||
- Address [disruptive rejoining follower node](https://github.com/etcd-io/etcd/issues/9333).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.18](https://github.com/etcd-io/etcd/releases/tag/v3.2.18) (2018-03-29)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.17...v3.2.18) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Adjust [election timeout on server restart](https://github.com/etcd-io/etcd/pull/9415) to reduce [disruptive rejoining servers](https://github.com/etcd-io/etcd/issues/9333).
|
||||
- Previously, etcd fast-forwards election ticks on server start, with only one tick left for leader election. This is to speed up start phase, without having to wait until all election ticks elapse. Advancing election ticks is useful for cross datacenter deployments with larger election timeouts. However, it was affecting cluster availability if the last tick elapses before leader contacts the restarted node.
|
||||
- Now, when etcd restarts, it adjusts election ticks with more than one tick left, thus more time for leader to prevent disruptive restart.
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.2.27/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add missing [`etcd_network_peer_sent_failures_total` count](https://github.com/etcd-io/etcd/pull/9437).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.17](https://github.com/etcd-io/etcd/releases/tag/v3.2.17) (2018-03-08)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.16...v3.2.17) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [server panic on invalid Election Proclaim/Resign HTTP(S) requests](https://github.com/etcd-io/etcd/pull/9379).
|
||||
- Previously, wrong-formatted HTTP requests to Election API could trigger panic in etcd server.
|
||||
- e.g. `curl -L http://localhost:2379/v3/election/proclaim -X POST -d '{"value":""}'`, `curl -L http://localhost:2379/v3/election/resign -X POST -d '{"value":""}'`.
|
||||
- Prevent [overflow by large `TTL` values for `Lease` `Grant`](https://github.com/etcd-io/etcd/pull/9399).
|
||||
- `TTL` parameter to `Grant` request is unit of second.
|
||||
- Leases with too large `TTL` values exceeding `math.MaxInt64` [expire in unexpected ways](https://github.com/etcd-io/etcd/issues/9374).
|
||||
- Server now returns `rpctypes.ErrLeaseTTLTooLarge` to client, when the requested `TTL` is larger than *9,000,000,000 seconds* (which is >285 years).
|
||||
- Again, etcd `Lease` is meant for short-periodic keepalives or sessions, in the range of seconds or minutes. Not for hours or days!
|
||||
- Enable etcd server [`raft.Config.CheckQuorum` when starting with `ForceNewCluster`](https://github.com/etcd-io/etcd/pull/9347).
|
||||
|
||||
### Proxy v2
|
||||
|
||||
- Fix [v2 proxy leaky HTTP requests](https://github.com/etcd-io/etcd/pull/9336).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.16](https://github.com/etcd-io/etcd/releases/tag/v3.2.16) (2018-02-12)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.15...v3.2.16) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [`mvcc` "unsynced" watcher restore operation](https://github.com/etcd-io/etcd/pull/9297).
|
||||
- "unsynced" watcher is watcher that needs to be in sync with events that have happened.
|
||||
- That is, "unsynced" watcher is the slow watcher that was requested on old revision.
|
||||
- "unsynced" watcher restore operation was not correctly populating its underlying watcher group.
|
||||
- Which possibly causes [missing events from "unsynced" watchers](https://github.com/etcd-io/etcd/issues/9086).
|
||||
- A node gets network partitioned with a watcher on a future revision, and falls behind receiving a leader snapshot after partition gets removed. When applying this snapshot, etcd watch storage moves current synced watchers to unsynced since sync watchers might have become stale during network partition. And reset synced watcher group to restart watcher routines. Previously, there was a bug when moving from synced watcher group to unsynced, thus client would miss events when the watcher was requested to the network-partitioned node.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.15](https://github.com/etcd-io/etcd/releases/tag/v3.2.15) (2018-01-22)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.14...v3.2.15) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Prevent [server panic from member update/add](https://github.com/etcd-io/etcd/pull/9174) with [wrong scheme URLs](https://github.com/etcd-io/etcd/issues/9173).
|
||||
- Log [user context cancel errors on stream APIs in debug level with TLS](https://github.com/etcd-io/etcd/pull/9178).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.14](https://github.com/etcd-io/etcd/releases/tag/v3.2.14) (2018-01-11)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.13...v3.2.14) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Log [user context cancel errors on stream APIs in debug level](https://github.com/etcd-io/etcd/pull/9105).
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [`mvcc/backend.defragdb` nil-pointer dereference on create bucket failure](https://github.com/etcd-io/etcd/pull/9119).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.13](https://github.com/etcd-io/etcd/releases/tag/v3.2.13) (2018-01-02)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.12...v3.2.13) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Remove [verbose error messages on stream cancel and gRPC info-level logs](https://github.com/etcd-io/etcd/pull/9080) in server-side.
|
||||
- Fix [gRPC server panic on `GracefulStop` TLS-enabled server](https://github.com/etcd-io/etcd/pull/8987).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.12](https://github.com/etcd-io/etcd/releases/tag/v3.2.12) (2017-12-20)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.11...v3.2.12) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Dependency
|
||||
|
||||
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases/tag) from [**`v1.7.4`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.4) to [**`v1.7.5`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.5).
|
||||
- Upgrade [`github.com/grpc-ecosystem/grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway/releases) from [**`v1.3`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.3) to [**`v1.3.0`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.3.0).
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [error message of `Revision` compactor](https://github.com/etcd-io/etcd/pull/8999) in server-side.
|
||||
|
||||
### client v3
|
||||
|
||||
- Add [`MaxCallSendMsgSize` and `MaxCallRecvMsgSize`](https://github.com/etcd-io/etcd/pull/9047) fields to [`clientv3.Config`](https://godoc.org/github.com/etcd-io/etcd/clientv3#Config).
|
||||
- Fix [exceeded response size limit error in client-side](https://github.com/etcd-io/etcd/issues/9043).
|
||||
- Address [kubernetes#51099](https://github.com/kubernetes/kubernetes/issues/51099).
|
||||
- In previous versions(v3.2.10, v3.2.11), client response size was limited to only 4 MiB.
|
||||
- `MaxCallSendMsgSize` default value is 2 MiB, if not configured.
|
||||
- `MaxCallRecvMsgSize` default value is `math.MaxInt32`, if not configured.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.11](https://github.com/etcd-io/etcd/releases/tag/v3.2.11) (2017-12-05)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.10...v3.2.11) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Dependency
|
||||
|
||||
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases/tag) from [**`v1.7.3`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.3) to [**`v1.7.4`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.4).
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
See [security doc](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/security.md) for more details.
|
||||
|
||||
- Log [more details on TLS handshake failures](https://github.com/etcd-io/etcd/pull/8952/files).
|
||||
|
||||
### client v3
|
||||
|
||||
- Fix racey grpc-go's server handler transport `WriteStatus` call to prevent [TLS-enabled etcd server crash](https://github.com/etcd-io/etcd/issues/8904).
|
||||
- Add [gRPC RPC failure warnings](https://github.com/etcd-io/etcd/pull/8939) to help debug such issues in the future.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Remove `--listen-metrics-urls` flag in monitoring document (non-released in `v3.2.x`, planned for `v3.3.x`).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.10](https://github.com/etcd-io/etcd/releases/tag/v3.2.10) (2017-11-16)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.9...v3.2.10) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Dependency
|
||||
|
||||
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases/tag) from [**`v1.2.1`**](https://github.com/grpc/grpc-go/releases/tag/v1.2.1) to [**`v1.7.3`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.3).
|
||||
- Upgrade [`github.com/grpc-ecosystem/grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway/releases) from [**`v1.2.0`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.2.0) to [**`v1.3`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.3).
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
See [security doc](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/security.md) for more details.
|
||||
|
||||
- Revert [discovery SRV auth `ServerName` with `*.{ROOT_DOMAIN}`](https://github.com/etcd-io/etcd/pull/8651) to support non-wildcard subject alternative names in the certs (see [issue #8445](https://github.com/etcd-io/etcd/issues/8445) for more contexts).
|
||||
- For instance, `etcd --discovery-srv=etcd.local` will only authenticate peers/clients when the provided certs have root domain `etcd.local` (**not `*.etcd.local`**) as an entry in Subject Alternative Name (SAN) field.
|
||||
|
||||
### etcd server
|
||||
|
||||
- Replace backend key-value database `boltdb/bolt` with [`coreos/bbolt`](https://github.com/coreos/bbolt/releases) to address [backend database size issue](https://github.com/etcd-io/etcd/issues/8009).
|
||||
|
||||
### client v3
|
||||
|
||||
- Rewrite balancer to handle [network partitions](https://github.com/etcd-io/etcd/issues/8711).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.5*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.9](https://github.com/etcd-io/etcd/releases/tag/v3.2.9) (2017-10-06)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.8...v3.2.9) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
See [security doc](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/security.md) for more details.
|
||||
|
||||
- Update `golang.org/x/crypto/bcrypt` (see [golang/crypto@6c586e1](https://github.com/golang/crypto/commit/6c586e17d90a7d08bbbc4069984180dce3b04117)).
|
||||
- Fix discovery SRV bootstrapping to [authenticate `ServerName` with `*.{ROOT_DOMAIN}`](https://github.com/etcd-io/etcd/pull/8651), in order to support sub-domain wildcard matching (see [issue #8445](https://github.com/etcd-io/etcd/issues/8445) for more contexts).
|
||||
- For instance, `etcd --discovery-srv=etcd.local` will only authenticate peers/clients when the provided certs have root domain `*.etcd.local` as an entry in Subject Alternative Name (SAN) field.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.4*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.8](https://github.com/etcd-io/etcd/releases/tag/v3.2.8) (2017-09-29)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.7...v3.2.8) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### client v2
|
||||
|
||||
- Fix v2 client failover to next endpoint on mutable operation.
|
||||
|
||||
### gRPC Proxy
|
||||
|
||||
- Handle [`KeysOnly` flag](https://github.com/etcd-io/etcd/pull/8552).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.7](https://github.com/etcd-io/etcd/releases/tag/v3.2.7) (2017-09-01)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.6...v3.2.7) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
- Fix [server-side auth so concurrent auth operations do not return old revision error](https://github.com/etcd-io/etcd/pull/8306).
|
||||
|
||||
### client v3
|
||||
|
||||
- Fix [`concurrency/stm` Put with serializable snapshot](https://github.com/etcd-io/etcd/pull/8439).
|
||||
- Use store revision from first fetch to resolve write conflicts instead of modified revision.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.6](https://github.com/etcd-io/etcd/releases/tag/v3.2.6) (2017-08-21)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.5...v3.2.6) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix watch restore from snapshot.
|
||||
- Fix multiple URLs for `--listen-peer-urls` flag.
|
||||
- Add `--enable-pprof` flag to etcd configuration file format.
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.2.27/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Fix `etcd_debugging_mvcc_keys_total` inconsistency.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.5](https://github.com/etcd-io/etcd/releases/tag/v3.2.5) (2017-08-04)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.4...v3.2.5) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### etcdctl v3
|
||||
|
||||
- Return non-zero exit code on unhealthy `endpoint health`.
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
See [security doc](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/security.md) for more details.
|
||||
|
||||
- [Server supports reverse-lookup on wildcard DNS `SAN`](https://github.com/etcd-io/etcd/pull/8281). For instance, if peer cert contains only DNS names (no IP addresses) in Subject Alternative Name (SAN) field, server first reverse-lookups the remote IP address to get a list of names mapping to that address (e.g. `nslookup IPADDR`). Then accepts the connection if those names have a matching name with peer cert's DNS names (either by exact or wildcard match). If none is matched, server forward-lookups each DNS entry in peer cert (e.g. look up `example.default.svc` when the entry is `*.example.default.svc`), and accepts connection only when the host's resolved addresses have the matching IP address with the peer's remote IP address. For example, peer B's CSR (with `cfssl`) SAN field is `["*.example.default.svc", "*.example.default.svc.cluster.local"]` when peer B's remote IP address is `10.138.0.2`. When peer B tries to join the cluster, peer A reverse-lookup the IP `10.138.0.2` to get the list of host names. And either exact or wildcard match the host names with peer B's cert DNS names in Subject Alternative Name (SAN) field. If none of reverse/forward lookups worked, it returns an error `"tls: "10.138.0.2" does not match any of DNSNames ["*.example.default.svc","*.example.default.svc.cluster.local"]`. See [issue#8268](https://github.com/etcd-io/etcd/issues/8268) for more detail.
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.2.27/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Fix unreachable `/metrics` endpoint when `--enable-v2=false`.
|
||||
|
||||
### gRPC Proxy
|
||||
|
||||
- Handle [`PrevKv` flag](https://github.com/etcd-io/etcd/pull/8366).
|
||||
|
||||
### Other
|
||||
|
||||
- Add container registry `gcr.io/etcd-development/etcd`.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.4](https://github.com/etcd-io/etcd/releases/tag/v3.2.4) (2017-07-19)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.3...v3.2.4) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Do not block on active client stream when stopping server
|
||||
|
||||
### gRPC proxy
|
||||
|
||||
- Fix gRPC proxy Snapshot RPC error handling
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.3](https://github.com/etcd-io/etcd/releases/tag/v3.2.3) (2017-07-14)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.2...v3.2.3) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### client v3
|
||||
|
||||
- Let clients establish unlimited streams
|
||||
|
||||
### Other
|
||||
|
||||
- Tag docker images with minor versions
|
||||
- e.g. `docker pull quay.io/coreos/etcd:v3.2` to fetch latest v3.2 versions
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.2](https://github.com/etcd-io/etcd/releases/tag/v3.2.2) (2017-07-07)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.1...v3.2.2) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Rate-limit lease revoke on expiration.
|
||||
- Extend leases on promote to avoid queueing effect on lease expiration.
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
See [security doc](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/security.md) for more details.
|
||||
|
||||
- [Server accepts connections if IP matches, without checking DNS entries](https://github.com/etcd-io/etcd/pull/8223). For instance, if peer cert contains IP addresses and DNS names in Subject Alternative Name (SAN) field, and the remote IP address matches one of those IP addresses, server just accepts connection without further checking the DNS names. For example, peer B's CSR (with `cfssl`) SAN field is `["invalid.domain", "10.138.0.2"]` when peer B's remote IP address is `10.138.0.2` and `invalid.domain` is a invalid host. When peer B tries to join the cluster, peer A successfully authenticates B, since Subject Alternative Name (SAN) field has a valid matching IP address. See [issue#8206](https://github.com/etcd-io/etcd/issues/8206) for more detail.
|
||||
|
||||
### etcd server
|
||||
|
||||
- Accept connection with matched IP SAN but no DNS match.
|
||||
- Don't check DNS entries in certs if there's a matching IP.
|
||||
|
||||
### gRPC gateway
|
||||
|
||||
- Use user-provided listen address to connect to gRPC gateway.
|
||||
- `net.Listener` rewrites IPv4 0.0.0.0 to IPv6 [::], breaking IPv6 disabled hosts.
|
||||
- Only v3.2.0, v3.2.1 are affected.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.1](https://github.com/etcd-io/etcd/releases/tag/v3.2.1) (2017-06-23)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.0...v3.2.1) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix backend database in-memory index corruption issue on restore (only 3.2.0 is affected).
|
||||
|
||||
### gRPC gateway
|
||||
|
||||
- Fix Txn marshaling.
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.2.27/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Fix backend database size debugging metrics.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.2.0](https://github.com/etcd-io/etcd/releases/tag/v3.2.0) (2017-06-09)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.1.0...v3.2.0) and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.2 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Improve backend read concurrency.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Increased [`--snapshot-count` default value from 10,000 to 100,000](https://github.com/etcd-io/etcd/pull/7160).
|
||||
- Higher snapshot count means it holds Raft entries in memory for longer before discarding old entries.
|
||||
- It is a trade-off between less frequent snapshotting and [higher memory usage](https://github.com/kubernetes/kubernetes/issues/60589#issuecomment-371977156).
|
||||
- User lower `--snapshot-count` value for lower memory usage.
|
||||
- User higher `--snapshot-count` value for better availabilities of slow followers (less frequent snapshots from leader).
|
||||
- `clientv3.Lease.TimeToLive` returns `LeaseTimeToLiveResponse.TTL == -1` on lease not found.
|
||||
- `clientv3.NewFromConfigFile` is moved to `clientv3/yaml.NewConfig`.
|
||||
- `embed.Etcd.Peers` field is now `[]*peerListener`.
|
||||
- Rejects domains names for `--listen-peer-urls` and `--listen-client-urls` (3.1 only prints out warnings), since [domain name is invalid for network interface binding](https://github.com/etcd-io/etcd/issues/6336).
|
||||
|
||||
### Dependency
|
||||
|
||||
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) from [**`v1.0.4`**](https://github.com/grpc/grpc-go/releases/tag/v1.0.4) to [**`v1.2.1`**](https://github.com/grpc/grpc-go/releases/tag/v1.2.1).
|
||||
- Upgrade [`github.com/grpc-ecosystem/grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway/releases) to [**`v1.2.0`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.2.0).
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.2.27/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add [`etcd_disk_backend_snapshot_duration_seconds`](https://github.com/etcd-io/etcd/pull/7892)
|
||||
- Add `etcd_debugging_server_lease_expired_total` metrics.
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
See [security doc](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/security.md) for more details.
|
||||
|
||||
- [TLS certificates get reloaded on every client connection](https://github.com/etcd-io/etcd/pull/7829). This is useful when replacing expiry certs without stopping etcd servers; it can be done by overwriting old certs with new ones. Refreshing certs for every connection should not have too much overhead, but can be improved in the future, with caching layer. Example tests can be found [here](https://github.com/etcd-io/etcd/blob/b041ce5d514a4b4aaeefbffb008f0c7570a18986/integration/v3_grpc_test.go#L1601-L1757).
|
||||
- [Server denies incoming peer certs with wrong IP `SAN`](https://github.com/etcd-io/etcd/pull/7687). For instance, if peer cert contains any IP addresses in Subject Alternative Name (SAN) field, server authenticates a peer only when the remote IP address matches one of those IP addresses. This is to prevent unauthorized endpoints from joining the cluster. For example, peer B's CSR (with `cfssl`) SAN field is `["*.example.default.svc", "*.example.default.svc.cluster.local", "10.138.0.27"]` when peer B's actual IP address is `10.138.0.2`, not `10.138.0.27`. When peer B tries to join the cluster, peer A will reject B with the error `x509: certificate is valid for 10.138.0.27, not 10.138.0.2`, because B's remote IP address does not match the one in Subject Alternative Name (SAN) field.
|
||||
- [Server resolves TLS `DNSNames` when checking `SAN`](https://github.com/etcd-io/etcd/pull/7767). For instance, if peer cert contains only DNS names (no IP addresses) in Subject Alternative Name (SAN) field, server authenticates a peer only when forward-lookups (`dig b.com`) on those DNS names have matching IP with the remote IP address. For example, peer B's CSR (with `cfssl`) SAN field is `["b.com"]` when peer B's remote IP address is `10.138.0.2`. When peer B tries to join the cluster, peer A looks up the incoming host `b.com` to get the list of IP addresses (e.g. `dig b.com`). And rejects B if the list does not contain the IP `10.138.0.2`, with the error `tls: 10.138.0.2 does not match any of DNSNames ["b.com"]`.
|
||||
- Auth support JWT token.
|
||||
|
||||
### etcd server
|
||||
|
||||
- RPCs
|
||||
- Add Election, Lock service.
|
||||
- Native client `etcdserver/api/v3client`
|
||||
- client "embedded" in the server.
|
||||
- Logging, monitoring
|
||||
- Server warns large snapshot operations.
|
||||
- Add `etcd --enable-v2` flag to enable v2 API server.
|
||||
- `etcd --enable-v2=true` by default.
|
||||
- Add `etcd --auth-token` flag.
|
||||
- v3.2 compactor runs [every hour](https://github.com/etcd-io/etcd/pull/7875).
|
||||
- Compactor only supports periodic compaction.
|
||||
- Compactor continues to record latest revisions every 5-minute.
|
||||
- For every hour, it uses the last revision that was fetched before compaction period, from the revision records that were collected every 5-minute.
|
||||
- That is, for every hour, compactor discards historical data created before compaction period.
|
||||
- The retention window of compaction period moves to next hour.
|
||||
- For instance, when hourly writes are 100 and `--auto-compaction-retention=10`, v3.1 compacts revision 1000, 2000, and 3000 for every 10-hour, while v3.2 compacts revision 1000, 1100, and 1200 for every 1-hour.
|
||||
- If compaction succeeds or requested revision has already been compacted, it resets period timer and removes used compacted revision from historical revision records (e.g. start next revision collect and compaction from previously collected revisions).
|
||||
- If compaction fails, it retries in 5 minutes.
|
||||
- Allow snapshot over 512MB.
|
||||
|
||||
### client v3
|
||||
|
||||
- STM prefetching.
|
||||
- Add namespace feature.
|
||||
- Add `ErrOldCluster` with server version checking.
|
||||
- Translate `WithPrefix()` into `WithFromKey()` for empty key.
|
||||
|
||||
### etcdctl v3
|
||||
|
||||
- Add `check perf` command.
|
||||
- Add `etcdctl --from-key` flag to role grant-permission command.
|
||||
- `lock` command takes an optional command to execute.
|
||||
|
||||
### gRPC Proxy
|
||||
|
||||
- Proxy endpoint discovery.
|
||||
- Namespaces.
|
||||
- Coalesce lease requests.
|
||||
|
||||
### etcd gateway
|
||||
|
||||
- Support [DNS SRV priority](https://github.com/etcd-io/etcd/pull/7882) for [smart proxy routing](https://github.com/etcd-io/etcd/issues/4378).
|
||||
|
||||
### Other
|
||||
|
||||
- v3 client
|
||||
- concurrency package's elections updated to match RPC interfaces.
|
||||
- let client dial endpoints not in the balancer.
|
||||
- Release
|
||||
- Annotate acbuild with supports-systemd-notify.
|
||||
- Add `nsswitch.conf` to Docker container image.
|
||||
- Add ppc64le, arm64(experimental) builds.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.8.3*](https://golang.org/doc/devel/release.html#go1.8).
|
||||
|
||||
|
||||
<hr>
|
||||
|
734
CHANGELOG-3.3.md
734
CHANGELOG-3.3.md
|
@ -1,734 +0,0 @@
|
|||
|
||||
|
||||
Previous change logs can be found at [CHANGELOG-3.2](https://github.com/etcd-io/etcd/blob/master/CHANGELOG-3.2.md).
|
||||
|
||||
|
||||
The minimum recommended etcd versions to run in **production** are 3.1.11+, 3.2.26+, and 3.3.11+.
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.14](https://github.com/etcd-io/etcd/releases/tag/v3.3.14) (2019-TBD)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.13...v3.3.14) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [race condition in `rafthttp` transport pause/resume](https://github.com/etcd-io/etcd/pull/10826).
|
||||
|
||||
### client v3
|
||||
|
||||
- Fix [gRPC panic "send on closed channel](https://github.com/etcd-io/etcd/issues/9956) by upgrading [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) from [**`v1.7.5`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.5) to [**`v1.23.0`**](https://github.com/grpc/grpc-go/releases/tag/v1.23.0).
|
||||
- Rewrite [client balancer](https://github.com/etcd-io/etcd/pull/9860) with [new gRPC balancer interface](https://github.com/etcd-io/etcd/issues/9106).
|
||||
- Upgrade [gRPC to v1.23.0](https://github.com/etcd-io/etcd/pull/10911).
|
||||
- Improve [client balancer failover against secure endpoints](https://github.com/etcd-io/etcd/pull/10911).
|
||||
- Fix ["kube-apiserver 1.13.x refuses to work when first etcd-server is not available" (kubernetes#72102)](https://github.com/kubernetes/kubernetes/issues/72102).
|
||||
|
||||
### etcdctl v3
|
||||
|
||||
- Add [`etcdctl endpoint health --write-out` support](https://github.com/etcd-io/etcd/pull/9540).
|
||||
- Previously, [`etcdctl endpoint health --write-out json` did not work](https://github.com/etcd-io/etcd/issues/9532).
|
||||
- The command output is changed. Previously, if endpoint is unreachable, the command output is
|
||||
"\<endpoint\> is unhealthy: failed to connect: \<error message\>". This change unified the error message, all error types
|
||||
now have the same output "\<endpoint\> is unhealthy: failed to commit proposal: \<error message\>".
|
||||
- Add [missing newline in `etcdctl endpoint health`](https://github.com/etcd-io/etcd/pull/10793).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.13](https://github.com/etcd-io/etcd/releases/tag/v3.3.13) (2019-05-02)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.12...v3.3.13) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Improve [heartbeat send failure logging](https://github.com/etcd-io/etcd/pull/10663).
|
||||
- Add [`Verify` function to perform corruption check on WAL contents](https://github.com/etcd-io/etcd/pull/10603).
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.readthedocs.io/en/latest/operate.html#v3-3) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Fix bug where [db_compaction_total_duration_milliseconds metric incorrectly measured duration as 0](https://github.com/etcd-io/etcd/pull/10646).
|
||||
|
||||
### client v3
|
||||
|
||||
- Fix [`(*Client).Endpoints()` method race condition](https://github.com/etcd-io/etcd/pull/10595).
|
||||
|
||||
### Package `wal`
|
||||
|
||||
- Add [`Verify` function to perform corruption check on WAL contents](https://github.com/etcd-io/etcd/pull/10603).
|
||||
|
||||
### Dependency
|
||||
|
||||
- Migrate [`github.com/ugorji/go/codec`](https://github.com/ugorji/go/releases) to [**`github.com/json-iterator/go`**](https://github.com/json-iterator/go) (See [#10667](https://github.com/etcd-io/etcd/pull/10667) for more).
|
||||
- Migrate [`github.com/ghodss/yaml`](https://github.com/ghodss/yaml/releases) to [**`sigs.k8s.io/yaml`**](https://github.com/kubernetes-sigs/yaml) (See [#10718](https://github.com/etcd-io/etcd/pull/10718) for more).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.10.8*](https://golang.org/doc/devel/release.html#go1.10).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.12](https://github.com/etcd-io/etcd/releases/tag/v3.3.12) (2019-02-07)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.11...v3.3.12) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### etcdctl
|
||||
|
||||
- [Strip out insecure endpoints from DNS SRV records when using discovery](https://github.com/etcd-io/etcd/pull/10443) with etcdctl v2
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.10.8*](https://golang.org/doc/devel/release.html#go1.10).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.11](https://github.com/etcd-io/etcd/releases/tag/v3.3.11) (2019-01-11)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.10...v3.3.11) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### gRPC Proxy
|
||||
|
||||
- Fix [memory leak in cache layer](https://github.com/etcd-io/etcd/pull/10327).
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
- Disable [CommonName authentication for gRPC-gateway](https://github.com/etcd-io/etcd/pull/10366) gRPC-gateway proxy requests to etcd server use the etcd client server TLS certificate. If that certificate contains CommonName we do not want to use that for authentication as it could lead to permission escalation.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.10.7*](https://golang.org/doc/devel/release.html#go1.10).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.10](https://github.com/etcd-io/etcd/releases/tag/v3.3.10) (2018-10-10)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.9...v3.3.10) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Improve ["became inactive" warning log](https://github.com/etcd-io/etcd/pull/10024), which indicates message send to a peer failed.
|
||||
- Improve [read index wait timeout warning log](https://github.com/etcd-io/etcd/pull/10026), which indicates that local node might have slow network.
|
||||
- Add [gRPC interceptor for debugging logs](https://github.com/etcd-io/etcd/pull/9990); enable `etcd --debug` flag to see per-request debug information.
|
||||
- Add [consistency check in snapshot status](https://github.com/etcd-io/etcd/pull/10109). If consistency check on snapshot file fails, `snapshot status` returns `"snapshot file integrity check failed..."` error.
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.3.12/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Improve [`etcd_network_peer_round_trip_time_seconds`](https://github.com/etcd-io/etcd/pull/10155) Prometheus metric to track leader heartbeats.
|
||||
- Previously, it only samples the TCP connection for snapshot messages.
|
||||
- Add [`etcd_snap_db_fsync_duration_seconds_count`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_snap_db_save_total_duration_seconds_bucket`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_server_id`](https://github.com/etcd-io/etcd/pull/9998) Prometheus metric.
|
||||
- Add [`etcd_server_health_success`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
|
||||
- Add [`etcd_server_health_failures`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
|
||||
- Add [`etcd_server_read_indexes_failed_total`](https://github.com/etcd-io/etcd/pull/10094) Prometheus metric.
|
||||
|
||||
### client v3
|
||||
|
||||
- Fix logic on [release lock key if cancelled](https://github.com/etcd-io/etcd/pull/10153) in `clientv3/concurrency` package.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.10.4*](https://golang.org/doc/devel/release.html#go1.10).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.9](https://github.com/etcd-io/etcd/releases/tag/v3.3.9) (2018-07-24)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.8...v3.3.9) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Improve [Raft Read Index timeout warning messages](https://github.com/etcd-io/etcd/pull/9897).
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
- Compile with [*Go 1.10.3*](https://golang.org/doc/devel/release.html#go1.10) to support [crypto/x509 "Name Constraints"](https://github.com/etcd-io/etcd/issues/9912).
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.3.12/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add [`etcd_server_go_version`](https://github.com/etcd-io/etcd/pull/9957) Prometheus metric.
|
||||
- Add [`etcd_server_heartbeat_send_failures_total`](https://github.com/etcd-io/etcd/pull/9940) Prometheus metric.
|
||||
- Add [`etcd_server_slow_apply_total`](https://github.com/etcd-io/etcd/pull/9940) Prometheus metric.
|
||||
- Add [`etcd_disk_backend_defrag_duration_seconds`](https://github.com/etcd-io/etcd/pull/9940) Prometheus metric.
|
||||
- Add [`etcd_mvcc_hash_duration_seconds`](https://github.com/etcd-io/etcd/pull/9940) Prometheus metric.
|
||||
- Add [`etcd_mvcc_hash_rev_duration_seconds`](https://github.com/etcd-io/etcd/pull/9940) Prometheus metric.
|
||||
- Add [`etcd_server_slow_read_indexes_total`](https://github.com/etcd-io/etcd/pull/9897) Prometheus metric.
|
||||
- Add [`etcd_server_quota_backend_bytes`](https://github.com/etcd-io/etcd/pull/9820) Prometheus metric.
|
||||
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
|
||||
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
|
||||
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
|
||||
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
|
||||
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
|
||||
- Add [`etcd_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819) Prometheus metric.
|
||||
- In addition to [`etcd_debugging_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819).
|
||||
- Add [`etcd_mvcc_db_total_size_in_use_in_bytes`](https://github.com/etcd-io/etcd/pull/9256) Prometheus metric.
|
||||
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
|
||||
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
|
||||
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
|
||||
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
|
||||
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
|
||||
|
||||
### client v3
|
||||
|
||||
- Fix [lease keepalive interval updates when response queue is full](https://github.com/etcd-io/etcd/pull/9952).
|
||||
- If `<-chan *clientv3LeaseKeepAliveResponse` from `clientv3.Lease.KeepAlive` was never consumed or channel is full, client was [sending keepalive request every 500ms](https://github.com/etcd-io/etcd/issues/9911) instead of expected rate of every "TTL / 3" duration.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.10.3*](https://golang.org/doc/devel/release.html#go1.10).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.8](https://github.com/etcd-io/etcd/releases/tag/v3.3.8) (2018-06-15)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.7...v3.3.8) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Improve [slow request apply warning log](https://github.com/etcd-io/etcd/pull/9288).
|
||||
- e.g. `read-only range request "key:\"/a\" range_end:\"/b\" " with result "range_response_count:3 size:96" took too long (97.966µs) to execute`.
|
||||
- Redact [request value field](https://github.com/etcd-io/etcd/pull/9822).
|
||||
- Provide [response size](https://github.com/etcd-io/etcd/pull/9826).
|
||||
- Add [backoff on watch retries on transient errors](https://github.com/etcd-io/etcd/pull/9840).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.9.7*](https://golang.org/doc/devel/release.html#go1.9).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.7](https://github.com/etcd-io/etcd/releases/tag/v3.3.7) (2018-06-06)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.6...v3.3.7) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
- Support TLS cipher suite whitelisting.
|
||||
- To block [weak cipher suites](https://github.com/etcd-io/etcd/issues/8320).
|
||||
- TLS handshake fails when client hello is requested with invalid cipher suites.
|
||||
- Add [`etcd --cipher-suites`](https://github.com/etcd-io/etcd/pull/9801) flag.
|
||||
- If empty, Go auto-populates the list.
|
||||
|
||||
### etcdctl v3
|
||||
|
||||
- Fix [`etcdctl move-leader` command for TLS-enabled endpoints](https://github.com/etcd-io/etcd/pull/9807).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.9.6*](https://golang.org/doc/devel/release.html#go1.9).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.6](https://github.com/etcd-io/etcd/releases/tag/v3.3.6) (2018-05-31)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.5...v3.3.6) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Allow [empty auth token](https://github.com/etcd-io/etcd/pull/9369).
|
||||
- Previously, when auth token is an empty string, it returns [`failed to initialize the etcd server: auth: invalid auth options` error](https://github.com/etcd-io/etcd/issues/9349).
|
||||
- Fix [auth storage panic on server lease revoke routine with JWT token](https://github.com/etcd-io/etcd/issues/9695).
|
||||
- Fix [`mvcc` server panic from restore operation](https://github.com/etcd-io/etcd/pull/9775).
|
||||
- Let's assume that a watcher had been requested with a future revision X and sent to node A that became network-partitioned thereafter. Meanwhile, cluster makes progress. Then when the partition gets removed, the leader sends a snapshot to node A. Previously if the snapshot's latest revision is still lower than the watch revision X, **etcd server panicked** during snapshot restore operation.
|
||||
- Now, this server-side panic has been fixed.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.9.6*](https://golang.org/doc/devel/release.html#go1.9).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.5](https://github.com/etcd-io/etcd/releases/tag/v3.3.5) (2018-05-09)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.4...v3.3.5) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### etcdctl v3
|
||||
|
||||
- Fix [`etcdctl watch [key] [range_end] -- [exec-command…]`](https://github.com/etcd-io/etcd/pull/9688) parsing.
|
||||
- Previously, `ETCDCTL_API=3 ./bin/etcdctl watch foo -- echo watch event received` panicked.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.9.6*](https://golang.org/doc/devel/release.html#go1.9).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.4](https://github.com/etcd-io/etcd/releases/tag/v3.3.4) (2018-04-24)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.3...v3.3.4) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.3.12/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add [`etcd_server_is_leader`](https://github.com/etcd-io/etcd/pull/9587) Prometheus metric.
|
||||
- Fix [`etcd_debugging_server_lease_expired_total`](https://github.com/etcd-io/etcd/pull/9557) Prometheus metric.
|
||||
- Fix [race conditions in v2 server stat collecting](https://github.com/etcd-io/etcd/pull/9562).
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
- Fix [TLS reload](https://github.com/etcd-io/etcd/pull/9570) when [certificate SAN field only includes IP addresses but no domain names](https://github.com/etcd-io/etcd/issues/9541).
|
||||
- In Go, server calls `(*tls.Config).GetCertificate` for TLS reload if and only if server's `(*tls.Config).Certificates` field is not empty, or `(*tls.ClientHelloInfo).ServerName` is not empty with a valid SNI from the client. Previously, etcd always populates `(*tls.Config).Certificates` on the initial client TLS handshake, as non-empty. Thus, client was always expected to supply a matching SNI in order to pass the TLS verification and to trigger `(*tls.Config).GetCertificate` to reload TLS assets.
|
||||
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/etcd-io/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
|
||||
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
|
||||
|
||||
### etcd server
|
||||
|
||||
- Add [`etcd --initial-election-tick-advance`](https://github.com/etcd-io/etcd/pull/9591) flag to configure initial election tick fast-forward.
|
||||
- By default, `etcd --initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
|
||||
- This benefits the case of larger election ticks. For instance, cross datacenter deployment may require longer election timeout of 10-second. If true, local node does not need wait up to 10-second. Instead, forwards its election ticks to 8-second, and have only 2-second left before leader election.
|
||||
- Major assumptions are that: cluster has no active leader thus advancing ticks enables faster leader election. Or cluster already has an established leader, and rejoining follower is likely to receive heartbeats from the leader after tick advance and before election timeout.
|
||||
- However, when network from leader to rejoining follower is congested, and the follower does not receive leader heartbeat within left election ticks, disruptive election has to happen thus affecting cluster availabilities.
|
||||
- Now, this can be disabled by setting `--initial-election-tick-advance=false`.
|
||||
- Disabling this would slow down initial bootstrap process for cross datacenter deployments. Make tradeoffs by configuring `etcd --initial-election-tick-advance` at the cost of slow initial bootstrap.
|
||||
- If single-node, it advances ticks regardless.
|
||||
- Address [disruptive rejoining follower node](https://github.com/etcd-io/etcd/issues/9333).
|
||||
|
||||
### Package `embed`
|
||||
|
||||
- Add [`embed.Config.InitialElectionTickAdvance`](https://github.com/etcd-io/etcd/pull/9591) to enable/disable initial election tick fast-forward.
|
||||
- `embed.NewConfig()` would return `*embed.Config` with `InitialElectionTickAdvance` as true by default.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.9.5*](https://golang.org/doc/devel/release.html#go1.9).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.3](https://github.com/etcd-io/etcd/releases/tag/v3.3.3) (2018-03-29)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.2...v3.3.3) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Adjust [election timeout on server restart](https://github.com/etcd-io/etcd/pull/9415) to reduce [disruptive rejoining servers](https://github.com/etcd-io/etcd/issues/9333).
|
||||
- Previously, etcd fast-forwards election ticks on server start, with only one tick left for leader election. This is to speed up start phase, without having to wait until all election ticks elapse. Advancing election ticks is useful for cross datacenter deployments with larger election timeouts. However, it was affecting cluster availability if the last tick elapses before leader contacts the restarted node.
|
||||
- Now, when etcd restarts, it adjusts election ticks with more than one tick left, thus more time for leader to prevent disruptive restart.
|
||||
- Adjust [periodic compaction retention window](https://github.com/etcd-io/etcd/pull/9485).
|
||||
- e.g. `etcd --auto-compaction-mode=revision --auto-compaction-retention=1000` automatically `Compact` on `"latest revision" - 1000` every 5-minute (when latest revision is 30000, compact on revision 29000).
|
||||
- e.g. Previously, `etcd --auto-compaction-mode=periodic --auto-compaction-retention=72h` automatically `Compact` with 72-hour retention windown for every 7.2-hour. **Now, `Compact` happens, for every 1-hour but still with 72-hour retention window.**
|
||||
- e.g. Previously, `etcd --auto-compaction-mode=periodic --auto-compaction-retention=30m` automatically `Compact` with 30-minute retention windown for every 3-minute. **Now, `Compact` happens, for every 30-minute but still with 30-minute retention window.**
|
||||
- Periodic compactor keeps recording latest revisions for every compaction period when given period is less than 1-hour, or for every 1-hour when given compaction period is greater than 1-hour (e.g. 1-hour when `etcd --auto-compaction-mode=periodic --auto-compaction-retention=24h`).
|
||||
- For every compaction period or 1-hour, compactor uses the last revision that was fetched before compaction period, to discard historical data.
|
||||
- The retention window of compaction period moves for every given compaction period or hour.
|
||||
- For instance, when hourly writes are 100 and `etcd --auto-compaction-mode=periodic --auto-compaction-retention=24h`, `v3.2.x`, `v3.3.0`, `v3.3.1`, and `v3.3.2` compact revision 2400, 2640, and 2880 for every 2.4-hour, while `v3.3.3` *or later* compacts revision 2400, 2500, 2600 for every 1-hour.
|
||||
- Futhermore, when `etcd --auto-compaction-mode=periodic --auto-compaction-retention=30m` and writes per minute are about 1000, `v3.3.0`, `v3.3.1`, and `v3.3.2` compact revision 30000, 33000, and 36000, for every 3-minute, while `v3.3.3` *or later* compacts revision 30000, 60000, and 90000, for every 30-minute.
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.3.12/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add missing [`etcd_network_peer_sent_failures_total` count](https://github.com/etcd-io/etcd/pull/9437).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.9.5*](https://golang.org/doc/devel/release.html#go1.9).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.2](https://github.com/etcd-io/etcd/releases/tag/v3.3.2) (2018-03-08)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.1...v3.3.2) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [server panic on invalid Election Proclaim/Resign HTTP(S) requests](https://github.com/etcd-io/etcd/pull/9379).
|
||||
- Previously, wrong-formatted HTTP requests to Election API could trigger panic in etcd server.
|
||||
- e.g. `curl -L http://localhost:2379/v3/election/proclaim -X POST -d '{"value":""}'`, `curl -L http://localhost:2379/v3/election/resign -X POST -d '{"value":""}'`.
|
||||
- Fix [revision-based compaction retention parsing](https://github.com/etcd-io/etcd/pull/9339).
|
||||
- Previously, `etcd --auto-compaction-mode revision --auto-compaction-retention 1` was [translated to revision retention 3600000000000](https://github.com/etcd-io/etcd/issues/9337).
|
||||
- Now, `etcd --auto-compaction-mode revision --auto-compaction-retention 1` is correctly parsed as revision retention 1.
|
||||
- Prevent [overflow by large `TTL` values for `Lease` `Grant`](https://github.com/etcd-io/etcd/pull/9399).
|
||||
- `TTL` parameter to `Grant` request is unit of second.
|
||||
- Leases with too large `TTL` values exceeding `math.MaxInt64` [expire in unexpected ways](https://github.com/etcd-io/etcd/issues/9374).
|
||||
- Server now returns `rpctypes.ErrLeaseTTLTooLarge` to client, when the requested `TTL` is larger than *9,000,000,000 seconds* (which is >285 years).
|
||||
- Again, etcd `Lease` is meant for short-periodic keepalives or sessions, in the range of seconds or minutes. Not for hours or days!
|
||||
- Enable etcd server [`raft.Config.CheckQuorum` when starting with `ForceNewCluster`](https://github.com/etcd-io/etcd/pull/9347).
|
||||
|
||||
### Proxy v2
|
||||
|
||||
- Fix [v2 proxy leaky HTTP requests](https://github.com/etcd-io/etcd/pull/9336).
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.9.4*](https://golang.org/doc/devel/release.html#go1.9).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.1](https://github.com/etcd-io/etcd/releases/tag/v3.3.1) (2018-02-12)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0...v3.3.1) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
### Improved
|
||||
|
||||
- Add [warnings on requests taking too long](https://github.com/etcd-io/etcd/pull/9288).
|
||||
- e.g. `etcdserver: read-only range request "key:\"\\000\" range_end:\"\\000\" " took too long [3.389041388s] to execute`
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [`mvcc` "unsynced" watcher restore operation](https://github.com/etcd-io/etcd/pull/9281).
|
||||
- "unsynced" watcher is watcher that needs to be in sync with events that have happened.
|
||||
- That is, "unsynced" watcher is the slow watcher that was requested on old revision.
|
||||
- "unsynced" watcher restore operation was not correctly populating its underlying watcher group.
|
||||
- Which possibly causes [missing events from "unsynced" watchers](https://github.com/etcd-io/etcd/issues/9086).
|
||||
- A node gets network partitioned with a watcher on a future revision, and falls behind receiving a leader snapshot after partition gets removed. When applying this snapshot, etcd watch storage moves current synced watchers to unsynced since sync watchers might have become stale during network partition. And reset synced watcher group to restart watcher routines. Previously, there was a bug when moving from synced watcher group to unsynced, thus client would miss events when the watcher was requested to the network-partitioned node.
|
||||
|
||||
### Go
|
||||
|
||||
- Compile with [*Go 1.9.4*](https://golang.org/doc/devel/release.html#go1.9).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## [v3.3.0](https://github.com/etcd-io/etcd/releases/tag/v3.3.0) (2018-02-01)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.2.0...v3.3.0) and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.3 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_3.md).**
|
||||
|
||||
- [v3.3.0-rc.4](https://github.com/etcd-io/etcd/releases/tag/v3.3.0-rc.4) (2018-01-22), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0-rc.3...v3.3.0-rc.4).
|
||||
- [v3.3.0-rc.3](https://github.com/etcd-io/etcd/releases/tag/v3.3.0-rc.3) (2018-01-17), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0-rc.2...v3.3.0-rc.3).
|
||||
- [v3.3.0-rc.2](https://github.com/etcd-io/etcd/releases/tag/v3.3.0-rc.2) (2018-01-11), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0-rc.1...v3.3.0-rc.2).
|
||||
- [v3.3.0-rc.1](https://github.com/etcd-io/etcd/releases/tag/v3.3.0-rc.1) (2018-01-02), see [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0-rc.0...v3.3.0-rc.1).
|
||||
- [v3.3.0-rc.0](https://github.com/etcd-io/etcd/releases/tag/v3.3.0-rc.0) (2017-12-20), see [code changes](https://github.com/etcd-io/etcd/compare/v3.2.0...v3.3.0-rc.0).
|
||||
|
||||
### Improved
|
||||
|
||||
- Use [`coreos/bbolt`](https://github.com/coreos/bbolt/releases) to replace [`boltdb/bolt`](https://github.com/boltdb/bolt#project-status).
|
||||
- Fix [etcd database size grows until `mvcc: database space exceeded`](https://github.com/etcd-io/etcd/issues/8009).
|
||||
- [Support database size larger than 8GiB](https://github.com/etcd-io/etcd/pull/7525) (8GiB is now a suggested maximum size for normal environments)
|
||||
- [Reduce memory allocation](https://github.com/etcd-io/etcd/pull/8428) on [Range operations](https://github.com/etcd-io/etcd/pull/8475).
|
||||
- [Rate limit](https://github.com/etcd-io/etcd/pull/8099) and [randomize](https://github.com/etcd-io/etcd/pull/8101) lease revoke on restart or leader elections.
|
||||
- Prevent [spikes in Raft proposal rate](https://github.com/etcd-io/etcd/issues/8096).
|
||||
- Support `clientv3` balancer failover under [network faults/partitions](https://github.com/etcd-io/etcd/issues/8711).
|
||||
- Better warning on [mismatched `etcd --initial-cluster`](https://github.com/etcd-io/etcd/pull/8083) flag.
|
||||
- etcd compares `etcd --initial-advertise-peer-urls` against corresponding `etcd --initial-cluster` URLs with forward-lookup.
|
||||
- If resolved IP addresses of `etcd --initial-advertise-peer-urls` and `etcd --initial-cluster` do not match (e.g. [due to DNS error](https://github.com/etcd-io/etcd/pull/9210)), etcd will exit with errors.
|
||||
- v3.2 error: `etcd --initial-cluster must include s1=https://s1.test:2380 given --initial-advertise-peer-urls=https://s1.test:2380`.
|
||||
- v3.3 error: `failed to resolve https://s1.test:2380 to match --initial-cluster=s1=https://s1.test:2380 (failed to resolve "https://s1.test:2380" (error ...))`.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Require [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) [**`v1.7.4`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.4) or [**`v1.7.5`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.5).
|
||||
- Deprecate [`metadata.Incoming/OutgoingContext`](https://github.com/etcd-io/etcd/pull/7896).
|
||||
- Deprecate `grpclog.Logger`, upgrade to [`grpclog.LoggerV2`](https://github.com/etcd-io/etcd/pull/8533).
|
||||
- Deprecate [`grpc.ErrClientConnTimeout`](https://github.com/etcd-io/etcd/pull/8505) errors in `clientv3`.
|
||||
- Use [`MaxRecvMsgSize` and `MaxSendMsgSize`](https://github.com/etcd-io/etcd/pull/8437) to limit message size, in etcd server.
|
||||
- Translate [gRPC status error in v3 client `Snapshot` API](https://github.com/etcd-io/etcd/pull/9038).
|
||||
- v3 `etcdctl` [`lease timetolive LEASE_ID`](https://github.com/etcd-io/etcd/issues/9028) on expired lease now prints [`"lease LEASE_ID already expired"`](https://github.com/etcd-io/etcd/pull/9047).
|
||||
- <=3.2 prints `"lease LEASE_ID granted with TTL(0s), remaining(-1s)"`.
|
||||
- Replace [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) endpoint `/v3alpha` with [`/v3beta`](https://github.com/etcd-io/etcd/pull/8880).
|
||||
- To deprecate [`/v3alpha`](https://github.com/etcd-io/etcd/issues/8125) in v3.4.
|
||||
- In v3.3, `curl -L http://localhost:2379/v3alpha/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` still works as a fallback to `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'`, but `curl -L http://localhost:2379/v3alpha/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` won't work in v3.4. Use `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
|
||||
- Change `etcd --auto-compaction-retention` flag to [accept string values](https://github.com/etcd-io/etcd/pull/8563) with [finer granularity](https://github.com/etcd-io/etcd/issues/8503).
|
||||
- Now that `etcd --auto-compaction-retention` accepts string values, etcd configuration YAML file `auto-compaction-retention` field must be changed to `string` type.
|
||||
- Previously, `--config-file etcd.config.yaml` can have `auto-compaction-retention: 24` field, now must be `auto-compaction-retention: "24"` or `auto-compaction-retention: "24h"`.
|
||||
- If configured as `etcd --auto-compaction-mode periodic --auto-compaction-retention "24h"`, the time duration value for `etcd --auto-compaction-retention` flag must be valid for [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) function in Go.
|
||||
|
||||
### Dependency
|
||||
|
||||
- Upgrade [`boltdb/bolt`](https://github.com/boltdb/bolt#project-status) from [**`v1.3.0`**](https://github.com/boltdb/bolt/releases/tag/v1.3.0) to [`coreos/bbolt`](https://github.com/coreos/bbolt/releases) [**`v1.3.1-coreos.6`**](https://github.com/coreos/bbolt/releases/tag/v1.3.1-coreos.6).
|
||||
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) from [**`v1.2.1`**](https://github.com/grpc/grpc-go/releases/tag/v1.2.1) to [**`v1.7.5`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.5).
|
||||
- Upgrade [`github.com/ugorji/go/codec`](https://github.com/ugorji/go) to [**`v1.1`**](https://github.com/ugorji/go/releases/tag/v1.1), and [regenerate v2 `client`](https://github.com/etcd-io/etcd/pull/8721).
|
||||
- Upgrade [`github.com/ugorji/go/codec`](https://github.com/ugorji/go) to [**`ugorji/go@54210f4e0`**](https://github.com/ugorji/go/commit/54210f4e076c57f351166f0ed60e67d3fca57a36), and [regenerate v2 `client`](https://github.com/etcd-io/etcd/pull/8574).
|
||||
- Upgrade [`github.com/grpc-ecosystem/grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway/releases) from [**`v1.2.2`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.2.2) to [**`v1.3.0`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.3.0).
|
||||
- Upgrade [`golang.org/x/crypto/bcrypt`](https://github.com/golang/crypto) to [**`golang/crypto@6c586e17d`**](https://github.com/golang/crypto/commit/6c586e17d90a7d08bbbc4069984180dce3b04117).
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
See [List of metrics](https://etcd.io/docs/v3.3.12/metrics/) for all metrics per release.
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add [`etcd --listen-metrics-urls`](https://github.com/etcd-io/etcd/pull/8242) flag for additional `/metrics` and `/health` endpoints.
|
||||
- Useful for [bypassing critical APIs when monitoring etcd](https://github.com/etcd-io/etcd/issues/8060).
|
||||
- Add [`etcd_server_version`](https://github.com/etcd-io/etcd/pull/8960) Prometheus metric.
|
||||
- To replace [Kubernetes `etcd-version-monitor`](https://github.com/etcd-io/etcd/issues/8948).
|
||||
- Add [`etcd_debugging_mvcc_db_compaction_keys_total`](https://github.com/etcd-io/etcd/pull/8280) Prometheus metric.
|
||||
- Add [`etcd_debugging_server_lease_expired_total`](https://github.com/etcd-io/etcd/pull/8064) Prometheus metric.
|
||||
- To improve [lease revoke monitoring](https://github.com/etcd-io/etcd/issues/8050).
|
||||
- Document [Prometheus 2.0 rules](https://github.com/etcd-io/etcd/pull/8879).
|
||||
- Initialize gRPC server [metrics with zero values](https://github.com/etcd-io/etcd/pull/8878).
|
||||
- Fix [range/put/delete operation metrics](https://github.com/etcd-io/etcd/pull/8054) with transaction.
|
||||
- `etcd_debugging_mvcc_range_total`
|
||||
- `etcd_debugging_mvcc_put_total`
|
||||
- `etcd_debugging_mvcc_delete_total`
|
||||
- `etcd_debugging_mvcc_txn_total`
|
||||
- Fix [`etcd_debugging_mvcc_keys_total`](https://github.com/etcd-io/etcd/pull/8390) on restore.
|
||||
- Fix [`etcd_debugging_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/8120) on restore.
|
||||
- Also change to [`prometheus.NewGaugeFunc`](https://github.com/etcd-io/etcd/pull/8150).
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
See [security doc](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/security.md) for more details.
|
||||
|
||||
- Add [CRL based connection rejection](https://github.com/etcd-io/etcd/pull/8124) to manage [revoked certs](https://github.com/etcd-io/etcd/issues/4034).
|
||||
- Document [TLS authentication changes](https://github.com/etcd-io/etcd/pull/8895).
|
||||
- [Server accepts connections if IP matches, without checking DNS entries](https://github.com/etcd-io/etcd/pull/8223). For instance, if peer cert contains IP addresses and DNS names in Subject Alternative Name (SAN) field, and the remote IP address matches one of those IP addresses, server just accepts connection without further checking the DNS names.
|
||||
- [Server supports reverse-lookup on wildcard DNS `SAN`](https://github.com/etcd-io/etcd/pull/8281). For instance, if peer cert contains only DNS names (no IP addresses) in Subject Alternative Name (SAN) field, server first reverse-lookups the remote IP address to get a list of names mapping to that address (e.g. `nslookup IPADDR`). Then accepts the connection if those names have a matching name with peer cert's DNS names (either by exact or wildcard match). If none is matched, server forward-lookups each DNS entry in peer cert (e.g. look up `example.default.svc` when the entry is `*.example.default.svc`), and accepts connection only when the host's resolved addresses have the matching IP address with the peer's remote IP address.
|
||||
- Add [`etcd --peer-cert-allowed-cn`](https://github.com/etcd-io/etcd/pull/8616) flag.
|
||||
- To support [CommonName(CN) based auth](https://github.com/etcd-io/etcd/issues/8262) for inter peer connection.
|
||||
- [Swap priority](https://github.com/etcd-io/etcd/pull/8594) of cert CommonName(CN) and username + password.
|
||||
- To address ["username and password specified in the request should take priority over CN in the cert"](https://github.com/etcd-io/etcd/issues/8584).
|
||||
- Protect [lease revoke with auth](https://github.com/etcd-io/etcd/pull/8031).
|
||||
- Provide user's role on [auth permission error](https://github.com/etcd-io/etcd/pull/8164).
|
||||
- Fix [auth store panic with disabled token](https://github.com/etcd-io/etcd/pull/8695).
|
||||
|
||||
### etcd server
|
||||
|
||||
- Add [`etcd --experimental-initial-corrupt-check`](https://github.com/etcd-io/etcd/pull/8554) flag to [check cluster database hashes before serving client/peer traffic](https://github.com/etcd-io/etcd/issues/8313).
|
||||
- `etcd --experimental-initial-corrupt-check=false` by default.
|
||||
- v3.4 will enable `--initial-corrupt-check=true` by default.
|
||||
- Add [`etcd --experimental-corrupt-check-time`](https://github.com/etcd-io/etcd/pull/8420) flag to [raise corrupt alarm monitoring](https://github.com/etcd-io/etcd/issues/7125).
|
||||
- `etcd --experimental-corrupt-check-time=0s` disabled by default.
|
||||
- Add [`etcd --experimental-enable-v2v3`](https://github.com/etcd-io/etcd/pull/8407) flag to [emulate v2 API with v3](https://github.com/etcd-io/etcd/issues/6925).
|
||||
- `etcd --experimental-enable-v2v3=false` by default.
|
||||
- Add [`etcd --max-txn-ops`](https://github.com/etcd-io/etcd/pull/7976) flag to [configure maximum number operations in transaction](https://github.com/etcd-io/etcd/issues/7826).
|
||||
- Add [`etcd --max-request-bytes`](https://github.com/etcd-io/etcd/pull/7968) flag to [configure maximum client request size](https://github.com/etcd-io/etcd/issues/7923).
|
||||
- If not configured, it defaults to 1.5 MiB.
|
||||
- Add [`etcd --client-crl-file`, `--peer-crl-file`](https://github.com/etcd-io/etcd/pull/8124) flags for [Certificate revocation list](https://github.com/etcd-io/etcd/issues/4034).
|
||||
- Add [`etcd --peer-cert-allowed-cn`](https://github.com/etcd-io/etcd/pull/8616) flag to support [CN-based auth for inter-peer connection](https://github.com/etcd-io/etcd/issues/8262).
|
||||
- Add [`etcd --listen-metrics-urls`](https://github.com/etcd-io/etcd/pull/8242) flag for additional `/metrics` and `/health` endpoints.
|
||||
- Support [additional (non) TLS `/metrics` endpoints for a TLS-enabled cluster](https://github.com/etcd-io/etcd/pull/8282).
|
||||
- e.g. `etcd --listen-metrics-urls=https://localhost:2378,http://localhost:9379` to serve `/metrics` and `/health` on secure port 2378 and insecure port 9379.
|
||||
- Useful for [bypassing critical APIs when monitoring etcd](https://github.com/etcd-io/etcd/issues/8060).
|
||||
- Add [`etcd --auto-compaction-mode`](https://github.com/etcd-io/etcd/pull/8123) flag to [support revision-based compaction](https://github.com/etcd-io/etcd/issues/8098).
|
||||
- Change `etcd --auto-compaction-retention` flag to [accept string values](https://github.com/etcd-io/etcd/pull/8563) with [finer granularity](https://github.com/etcd-io/etcd/issues/8503).
|
||||
- Now that `etcd --auto-compaction-retention` accepts string values, etcd configuration YAML file `auto-compaction-retention` field must be changed to `string` type.
|
||||
- Previously, `etcd --config-file etcd.config.yaml` can have `auto-compaction-retention: 24` field, now must be `auto-compaction-retention: "24"` or `auto-compaction-retention: "24h"`.
|
||||
- If configured as `--auto-compaction-mode periodic --auto-compaction-retention "24h"`, the time duration value for `etcd --auto-compaction-retention` flag must be valid for [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) function in Go.
|
||||
- e.g. `etcd --auto-compaction-mode=revision --auto-compaction-retention=1000` automatically `Compact` on `"latest revision" - 1000` every 5-minute (when latest revision is 30000, compact on revision 29000).
|
||||
- e.g. `etcd --auto-compaction-mode=periodic --auto-compaction-retention=72h` automatically `Compact` with 72-hour retention windown, for every 7.2-hour.
|
||||
- e.g. `etcd --auto-compaction-mode=periodic --auto-compaction-retention=30m` automatically `Compact` with 30-minute retention windown, for every 3-minute.
|
||||
- Periodic compactor continues to record latest revisions for every 1/10 of given compaction period (e.g. 1-hour when `etcd --auto-compaction-mode=periodic --auto-compaction-retention=10h`).
|
||||
- For every 1/10 of given compaction period, compactor uses the last revision that was fetched before compaction period, to discard historical data.
|
||||
- The retention window of compaction period moves for every 1/10 of given compaction period.
|
||||
- For instance, when hourly writes are 100 and `--auto-compaction-retention=10`, v3.1 compacts revision 1000, 2000, and 3000 for every 10-hour, while v3.2.x, v3.3.0, v3.3.1, and v3.3.2 compact revision 1000, 1100, and 1200 for every 1-hour. Futhermore, when writes per minute are 1000, v3.3.0, v3.3.1, and v3.3.2 with `--auto-compaction-mode=periodic --auto-compaction-retention=30m` compact revision 30000, 33000, and 36000, for every 3-minute with more finer granularity.
|
||||
- Whether compaction succeeds or not, this process repeats for every 1/10 of given compaction period. If compaction succeeds, it just removes compacted revision from historical revision records.
|
||||
- Add [`etcd --grpc-keepalive-min-time`, `etcd --grpc-keepalive-interval`, `etcd --grpc-keepalive-timeout`](https://github.com/etcd-io/etcd/pull/8535) flags to configure server-side keepalive policies.
|
||||
- Serve [`/health` endpoint as unhealthy](https://github.com/etcd-io/etcd/pull/8272) when [alarm (e.g. `NOSPACE`) is raised or there's no leader](https://github.com/etcd-io/etcd/issues/8207).
|
||||
- Define [`etcdhttp.Health`](https://godoc.org/github.com/coreos/etcd/etcdserver/api/etcdhttp#Health) struct with JSON encoder.
|
||||
- Note that `"health"` field is [`string` type, not `bool`](https://github.com/etcd-io/etcd/pull/9143).
|
||||
- e.g. `{"health":"false"}`, `{"health":"true"}`
|
||||
- [Remove `"errors"` field](https://github.com/etcd-io/etcd/pull/9162) since `v3.3.0-rc.3` (did exist only in `v3.3.0-rc.0`, `v3.3.0-rc.1`, `v3.3.0-rc.2`).
|
||||
- Move [logging setup to embed package](https://github.com/etcd-io/etcd/pull/8810)
|
||||
- Disable gRPC server info-level logs by default (can be enabled with `etcd --debug` flag).
|
||||
- Use [monotonic time in Go 1.9](https://github.com/etcd-io/etcd/pull/8507) for `lease` package.
|
||||
- Warn on [empty hosts in advertise URLs](https://github.com/etcd-io/etcd/pull/8384).
|
||||
- Address [advertise client URLs accepts empty hosts](https://github.com/etcd-io/etcd/issues/8379).
|
||||
- etcd v3.4 will exit on this error.
|
||||
- e.g. `etcd --advertise-client-urls=http://:2379`.
|
||||
- Warn on [shadowed environment variables](https://github.com/etcd-io/etcd/pull/8385).
|
||||
- Address [error on shadowed environment variables](https://github.com/etcd-io/etcd/issues/8380).
|
||||
- etcd v3.4 will exit on this error.
|
||||
|
||||
### API
|
||||
|
||||
- Support [ranges in transaction comparisons](https://github.com/etcd-io/etcd/pull/8025) for [disconnected linearized reads](https://github.com/etcd-io/etcd/issues/7924).
|
||||
- Add [nested transactions](https://github.com/etcd-io/etcd/pull/8102) to extend [proxy use cases](https://github.com/etcd-io/etcd/issues/7857).
|
||||
- Add [lease comparison target in transaction](https://github.com/etcd-io/etcd/pull/8324).
|
||||
- Add [lease list](https://github.com/etcd-io/etcd/pull/8358).
|
||||
- Add [hash by revision](https://github.com/etcd-io/etcd/pull/8263) for [better corruption checking against boltdb](https://github.com/etcd-io/etcd/issues/8016).
|
||||
|
||||
### client v3
|
||||
|
||||
- Add [health balancer](https://github.com/etcd-io/etcd/pull/8545) to fix [watch API hangs](https://github.com/etcd-io/etcd/issues/7247), improve [endpoint switch under network faults](https://github.com/etcd-io/etcd/issues/7941).
|
||||
- [Refactor balancer](https://github.com/etcd-io/etcd/pull/8840) and add [client-side keepalive pings](https://github.com/etcd-io/etcd/pull/8199) to handle [network partitions](https://github.com/etcd-io/etcd/issues/8711).
|
||||
- Add [`MaxCallSendMsgSize` and `MaxCallRecvMsgSize`](https://github.com/etcd-io/etcd/pull/9047) fields to [`clientv3.Config`](https://godoc.org/github.com/coreos/etcd/clientv3#Config).
|
||||
- Fix [exceeded response size limit error in client-side](https://github.com/etcd-io/etcd/issues/9043).
|
||||
- Address [kubernetes#51099](https://github.com/kubernetes/kubernetes/issues/51099).
|
||||
- In previous versions(v3.2.10, v3.2.11), client response size was limited to only 4 MiB.
|
||||
- `MaxCallSendMsgSize` default value is 2 MiB, if not configured.
|
||||
- `MaxCallRecvMsgSize` default value is `math.MaxInt32`, if not configured.
|
||||
- Accept [`Compare_LEASE`](https://github.com/etcd-io/etcd/pull/8324) in [`clientv3.Compare`](https://godoc.org/github.com/coreos/etcd/clientv3#Compare).
|
||||
- Add [`LeaseValue` helper](https://github.com/etcd-io/etcd/pull/8488) to `Cmp` `LeaseID` values in `Txn`.
|
||||
- Add [`MoveLeader`](https://github.com/etcd-io/etcd/pull/8153) to `Maintenance`.
|
||||
- Add [`HashKV`](https://github.com/etcd-io/etcd/pull/8351) to `Maintenance`.
|
||||
- Add [`Leases`](https://github.com/etcd-io/etcd/pull/8358) to `Lease`.
|
||||
- Add [`clientv3/ordering`](https://github.com/etcd-io/etcd/pull/8092) for enforce [ordering in serialized requests](https://github.com/etcd-io/etcd/issues/7623).
|
||||
- Fix ["put at-most-once" violation](https://github.com/etcd-io/etcd/pull/8335).
|
||||
- Fix [`WatchResponse.Canceled`](https://github.com/etcd-io/etcd/pull/8283) on [compacted watch request](https://github.com/etcd-io/etcd/issues/8231).
|
||||
- Fix [`concurrency/stm` `Put` with serializable snapshot](https://github.com/etcd-io/etcd/pull/8439).
|
||||
- Use store revision from first fetch to resolve write conflicts instead of modified revision.
|
||||
|
||||
### etcdctl v3
|
||||
|
||||
- Add [`etcdctl --discovery-srv`](https://github.com/etcd-io/etcd/pull/8462) flag.
|
||||
- Add [`etcdctl --keepalive-time`, `--keepalive-timeout`](https://github.com/etcd-io/etcd/pull/8663) flags.
|
||||
- Add [`etcdctl lease list`](https://github.com/etcd-io/etcd/pull/8358) command.
|
||||
- Add [`etcdctl lease keep-alive --once`](https://github.com/etcd-io/etcd/pull/8775) flag.
|
||||
- Make [`lease timetolive LEASE_ID`](https://github.com/etcd-io/etcd/issues/9028) on expired lease print [`lease LEASE_ID already expired`](https://github.com/etcd-io/etcd/pull/9047).
|
||||
- <=3.2 prints `lease LEASE_ID granted with TTL(0s), remaining(-1s)`.
|
||||
- Add [`etcdctl snapshot restore --wal-dir`](https://github.com/etcd-io/etcd/pull/9124) flag.
|
||||
- Add [`etcdctl defrag --data-dir`](https://github.com/etcd-io/etcd/pull/8367) flag.
|
||||
- Add [`etcdctl move-leader`](https://github.com/etcd-io/etcd/pull/8153) command.
|
||||
- Add [`etcdctl endpoint hashkv`](https://github.com/etcd-io/etcd/pull/8351) command.
|
||||
- Add [`etcdctl endpoint --cluster`](https://github.com/etcd-io/etcd/pull/8143) flag, equivalent to [v2 `etcdctl cluster-health`](https://github.com/etcd-io/etcd/issues/8117).
|
||||
- Make `etcdctl endpoint health` command terminate with [non-zero exit code on unhealthy status](https://github.com/etcd-io/etcd/pull/8342).
|
||||
- Add [`etcdctl lock --ttl`](https://github.com/etcd-io/etcd/pull/8370) flag.
|
||||
- Support [`etcdctl watch [key] [range_end] -- [exec-command…]`](https://github.com/etcd-io/etcd/pull/8919), equivalent to [v2 `etcdctl exec-watch`](https://github.com/etcd-io/etcd/issues/8814).
|
||||
- Make `etcdctl watch -- [exec-command]` set environmental variables [`ETCD_WATCH_REVISION`, `ETCD_WATCH_EVENT_TYPE`, `ETCD_WATCH_KEY`, `ETCD_WATCH_VALUE`](https://github.com/etcd-io/etcd/pull/9142) for each event.
|
||||
- Support [`etcdctl watch` with environmental variables `ETCDCTL_WATCH_KEY` and `ETCDCTL_WATCH_RANGE_END`](https://github.com/etcd-io/etcd/pull/9142).
|
||||
- Enable [`clientv3.WithRequireLeader(context.Context)` for `watch`](https://github.com/etcd-io/etcd/pull/8672) command.
|
||||
- Print [`"del"` instead of `"delete"`](https://github.com/etcd-io/etcd/pull/8297) in `txn` interactive mode.
|
||||
- Print [`ETCD_INITIAL_ADVERTISE_PEER_URLS` in `member add`](https://github.com/etcd-io/etcd/pull/8332).
|
||||
|
||||
### etcdctl v3
|
||||
|
||||
- Handle [empty key permission](https://github.com/etcd-io/etcd/pull/8514) in `etcdctl`.
|
||||
|
||||
### etcdctl v2
|
||||
|
||||
- Add [`etcdctl backup --with-v3`](https://github.com/etcd-io/etcd/pull/8479) flag.
|
||||
|
||||
### gRPC Proxy
|
||||
|
||||
- Add [`grpc-proxy start --experimental-leasing-prefix`](https://github.com/etcd-io/etcd/pull/8341) flag.
|
||||
- For disconnected linearized reads.
|
||||
- Based on [V system leasing](https://github.com/etcd-io/etcd/issues/6065).
|
||||
- See ["Disconnected consistent reads with etcd" blog post](https://coreos.com/blog/coreos-labs-disconnected-consistent-reads-with-etcd).
|
||||
- Add [`grpc-proxy start --experimental-serializable-ordering`](https://github.com/etcd-io/etcd/pull/8315) flag.
|
||||
- To ensure serializable reads have monotonically increasing store revisions across endpoints.
|
||||
- Add [`grpc-proxy start --metrics-addr`](https://github.com/etcd-io/etcd/pull/8242) flag for an additional `/metrics` endpoint.
|
||||
- Set `--metrics-addr=http://[HOST]:9379` to serve `/metrics` in insecure port 9379.
|
||||
- Serve [`/health` endpoint in grpc-proxy](https://github.com/etcd-io/etcd/pull/8322).
|
||||
- Add [`grpc-proxy start --debug`](https://github.com/etcd-io/etcd/pull/8994) flag.
|
||||
- Add [`grpc-proxy start --max-send-bytes`](https://github.com/etcd-io/etcd/pull/9250) flag to [configure maximum client request size](https://github.com/etcd-io/etcd/issues/7923).
|
||||
- Add [`grpc-proxy start --max-recv-bytes`](https://github.com/etcd-io/etcd/pull/9250) flag to [configure maximum client request size](https://github.com/etcd-io/etcd/issues/7923).
|
||||
- Fix [Snapshot API error handling](https://github.com/etcd-io/etcd/commit/dbd16d52fbf81e5fd806d21ff5e9148d5bf203ab).
|
||||
- Fix [KV API `PrevKv` flag handling](https://github.com/etcd-io/etcd/pull/8366).
|
||||
- Fix [KV API `KeysOnly` flag handling](https://github.com/etcd-io/etcd/pull/8552).
|
||||
|
||||
### gRPC gateway
|
||||
|
||||
- Replace [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) endpoint `/v3alpha` with [`/v3beta`](https://github.com/etcd-io/etcd/pull/8880).
|
||||
- To deprecate [`/v3alpha`](https://github.com/etcd-io/etcd/issues/8125) in v3.4.
|
||||
- In v3.3, `curl -L http://localhost:2379/v3alpha/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` still works as a fallback to `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'`, but `curl -L http://localhost:2379/v3alpha/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` won't work in v3.4. Use `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
|
||||
- Support ["authorization" token](https://github.com/etcd-io/etcd/pull/7999).
|
||||
- Support [websocket for bi-directional streams](https://github.com/etcd-io/etcd/pull/8257).
|
||||
- Fix [`Watch` API with gRPC gateway](https://github.com/etcd-io/etcd/issues/8237).
|
||||
- Upgrade gRPC gateway to [v1.3.0](https://github.com/etcd-io/etcd/issues/8838).
|
||||
|
||||
### etcd server
|
||||
|
||||
- Fix [backend database in-memory index corruption](https://github.com/etcd-io/etcd/pull/8127) issue on restore (only 3.2.0 is affected).
|
||||
- Fix [watch restore from snapshot](https://github.com/etcd-io/etcd/pull/8427).
|
||||
- Fix [`mvcc/backend.defragdb` nil-pointer dereference on create bucket failure](https://github.com/etcd-io/etcd/pull/9119).
|
||||
- Fix [server crash](https://github.com/etcd-io/etcd/pull/8010) on [invalid transaction request from gRPC gateway](https://github.com/etcd-io/etcd/issues/7889).
|
||||
- Prevent [server panic from member update/add](https://github.com/etcd-io/etcd/pull/9174) with [wrong scheme URLs](https://github.com/etcd-io/etcd/issues/9173).
|
||||
- Make [peer dial timeout longer](https://github.com/etcd-io/etcd/pull/8599).
|
||||
- See [coreos/etcd-operator#1300](https://github.com/etcd-io/etcd-operator/issues/1300) for more detail.
|
||||
- Make server [wait up to request time-out](https://github.com/etcd-io/etcd/pull/8267) with [pending RPCs](https://github.com/etcd-io/etcd/issues/8224).
|
||||
- Fix [`grpc.Server` panic on `GracefulStop`](https://github.com/etcd-io/etcd/pull/8987) with [TLS-enabled server](https://github.com/etcd-io/etcd/issues/8916).
|
||||
- Fix ["multiple peer URLs cannot start" issue](https://github.com/etcd-io/etcd/issues/8383).
|
||||
- Fix server-side auth so [concurrent auth operations do not return old revision error](https://github.com/etcd-io/etcd/pull/8442).
|
||||
- Handle [WAL renaming failure on Windows](https://github.com/etcd-io/etcd/pull/8286).
|
||||
- Upgrade [`coreos/go-systemd`](https://github.com/coreos/go-systemd/releases) to `v15` (see https://github.com/coreos/go-systemd/releases/tag/v15).
|
||||
- [Put back `/v2/machines`](https://github.com/etcd-io/etcd/pull/8062) endpoint for python-etcd wrapper.
|
||||
|
||||
### client v2
|
||||
|
||||
- [Fail-over v2 client](https://github.com/etcd-io/etcd/pull/8519) to next endpoint on [oneshot failure](https://github.com/etcd-io/etcd/issues/8515).
|
||||
|
||||
### Package `raft`
|
||||
|
||||
- Add [non-voting member](https://github.com/etcd-io/etcd/pull/8751).
|
||||
- To implement [Raft thesis 4.2.1 Catching up new servers](https://github.com/etcd-io/etcd/issues/8568).
|
||||
- `Learner` node does not vote or promote itself.
|
||||
|
||||
### Other
|
||||
|
||||
- Support previous two minor versions (see our [new release policy](https://github.com/etcd-io/etcd/pull/8805)).
|
||||
- `v3.3.x` is the last release cycle that supports `ACI`.
|
||||
- [AppC was officially suspended](https://github.com/appc/spec#-disclaimer-), as of late 2016.
|
||||
- [`acbuild`](https://github.com/containers/build#this-project-is-currently-unmaintained) is not maintained anymore.
|
||||
- `*.aci` files won't be available from etcd v3.4 release.
|
||||
- Add container registry [`gcr.io/etcd-development/etcd`](https://gcr.io/etcd-development/etcd).
|
||||
- [quay.io/coreos/etcd](https://quay.io/coreos/etcd) is still supported as secondary.
|
||||
|
||||
### Go
|
||||
|
||||
- Require [*Go 1.9+*](https://github.com/etcd-io/etcd/issues/6174).
|
||||
- Compile with [*Go 1.9.3*](https://golang.org/doc/devel/release.html#go1.9).
|
||||
- Deprecate [`golang.org/x/net/context`](https://github.com/etcd-io/etcd/pull/8511).
|
||||
|
||||
|
||||
<hr>
|
||||
|
557
CHANGELOG-3.4.md
557
CHANGELOG-3.4.md
|
@ -1,557 +0,0 @@
|
|||
|
||||
|
||||
Previous change logs can be found at [CHANGELOG-3.3](https://github.com/etcd-io/etcd/blob/master/CHANGELOG-3.3.md).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## v3.4.0 (2019-08-30)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.3.0...v3.4.0) and [v3.4 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_4.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.4 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_4.md).**
|
||||
|
||||
### Documentation
|
||||
|
||||
- etcd now has a new website! Please visit https://etcd.io.
|
||||
|
||||
### Improved
|
||||
|
||||
- Add Raft learner: [etcd#10725](https://github.com/etcd-io/etcd/pull/10725), [etcd#10727](https://github.com/etcd-io/etcd/pull/10727), [etcd#10730](https://github.com/etcd-io/etcd/pull/10730).
|
||||
- User guide: [runtime-configuration document](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/runtime-configuration.md#add-a-new-member-as-learner).
|
||||
- API change: [API reference document](https://github.com/etcd-io/etcd/blob/master/Documentation/dev-guide/api_reference_v3.md).
|
||||
- More details on implementation: [learner design document](https://github.com/etcd-io/etcd/blob/master/Documentation/learning/design-learner.md) and [implementation task list](https://github.com/etcd-io/etcd/issues/10537).
|
||||
- Rewrite [client balancer](https://github.com/etcd-io/etcd/pull/9860) with [new gRPC balancer interface](https://github.com/etcd-io/etcd/issues/9106).
|
||||
- Upgrade [gRPC to v1.23.0](https://github.com/etcd-io/etcd/pull/10911).
|
||||
- Improve [client balancer failover against secure endpoints](https://github.com/etcd-io/etcd/pull/10911).
|
||||
- Fix ["kube-apiserver 1.13.x refuses to work when first etcd-server is not available" (kubernetes#72102)](https://github.com/kubernetes/kubernetes/issues/72102).
|
||||
- Fix [gRPC panic "send on closed channel](https://github.com/etcd-io/etcd/issues/9956).
|
||||
- Add [backoff on watch retries on transient errors](https://github.com/etcd-io/etcd/pull/9840).
|
||||
- Add [jitter to watch progress notify](https://github.com/etcd-io/etcd/pull/9278) to prevent [spikes in `etcd_network_client_grpc_sent_bytes_total`](https://github.com/etcd-io/etcd/issues/9246).
|
||||
- Improve [read index wait timeout warning log](https://github.com/etcd-io/etcd/pull/10026), which indicates that local node might have slow network.
|
||||
- Improve [slow request apply warning log](https://github.com/etcd-io/etcd/pull/9288).
|
||||
- e.g. `read-only range request "key:\"/a\" range_end:\"/b\" " with result "range_response_count:3 size:96" took too long (97.966µs) to execute`.
|
||||
- Redact [request value field](https://github.com/etcd-io/etcd/pull/9822).
|
||||
- Provide [response size](https://github.com/etcd-io/etcd/pull/9826).
|
||||
- Improve ["became inactive" warning log](https://github.com/etcd-io/etcd/pull/10024), which indicates message send to a peer failed.
|
||||
- Improve [TLS setup error logging](https://github.com/etcd-io/etcd/pull/9518) to help debug [TLS-enabled cluster configuring issues](https://github.com/etcd-io/etcd/issues/9400).
|
||||
- Improve [long-running concurrent read transactions under light write workloads](https://github.com/etcd-io/etcd/pull/9296).
|
||||
- Previously, periodic commit on pending writes blocks incoming read transactions, even if there is no pending write.
|
||||
- Now, periodic commit operation does not block concurrent read transactions, thus improves long-running read transaction performance.
|
||||
- Rebased [etcd#10523](https://github.com/etcd-io/etcd/pull/10523).
|
||||
- Improve [Raft Read Index timeout warning messages](https://github.com/etcd-io/etcd/pull/9897).
|
||||
- Adjust [election timeout on server restart](https://github.com/etcd-io/etcd/pull/9415) to reduce [disruptive rejoining servers](https://github.com/etcd-io/etcd/issues/9333).
|
||||
- Previously, etcd fast-forwards election ticks on server start, with only one tick left for leader election. This is to speed up start phase, without having to wait until all election ticks elapse. Advancing election ticks is useful for cross datacenter deployments with larger election timeouts. However, it was affecting cluster availability if the last tick elapses before leader contacts the restarted node.
|
||||
- Now, when etcd restarts, it adjusts election ticks with more than one tick left, thus more time for leader to prevent disruptive restart.
|
||||
- Add [Raft Pre-Vote feature](https://github.com/etcd-io/etcd/pull/9352) to reduce [disruptive rejoining servers](https://github.com/etcd-io/etcd/issues/9333).
|
||||
- For instance, a flaky(or rejoining) member may drop in and out, and start campaign. This member will end up with a higher term, and ignore all incoming messages with lower term. In this case, a new leader eventually need to get elected, thus disruptive to cluster availability. Raft implements Pre-Vote phase to prevent this kind of disruptions. If enabled, Raft runs an additional phase of election to check if pre-candidate can get enough votes to win an election.
|
||||
- Adjust [periodic compaction retention window](https://github.com/etcd-io/etcd/pull/9485).
|
||||
- e.g. `etcd --auto-compaction-mode=revision --auto-compaction-retention=1000` automatically `Compact` on `"latest revision" - 1000` every 5-minute (when latest revision is 30000, compact on revision 29000).
|
||||
- e.g. Previously, `etcd --auto-compaction-mode=periodic --auto-compaction-retention=24h` automatically `Compact` with 24-hour retention windown for every 2.4-hour. Now, `Compact` happens for every 1-hour.
|
||||
- e.g. Previously, `etcd --auto-compaction-mode=periodic --auto-compaction-retention=30m` automatically `Compact` with 30-minute retention windown for every 3-minute. Now, `Compact` happens for every 30-minute.
|
||||
- Periodic compactor keeps recording latest revisions for every compaction period when given period is less than 1-hour, or for every 1-hour when given compaction period is greater than 1-hour (e.g. 1-hour when `etcd --auto-compaction-mode=periodic --auto-compaction-retention=24h`).
|
||||
- For every compaction period or 1-hour, compactor uses the last revision that was fetched before compaction period, to discard historical data.
|
||||
- The retention window of compaction period moves for every given compaction period or hour.
|
||||
- For instance, when hourly writes are 100 and `etcd --auto-compaction-mode=periodic --auto-compaction-retention=24h`, `v3.2.x`, `v3.3.0`, `v3.3.1`, and `v3.3.2` compact revision 2400, 2640, and 2880 for every 2.4-hour, while `v3.3.3` *or later* compacts revision 2400, 2500, 2600 for every 1-hour.
|
||||
- Futhermore, when `etcd --auto-compaction-mode=periodic --auto-compaction-retention=30m` and writes per minute are about 1000, `v3.3.0`, `v3.3.1`, and `v3.3.2` compact revision 30000, 33000, and 36000, for every 3-minute, while `v3.3.3` *or later* compacts revision 30000, 60000, and 90000, for every 30-minute.
|
||||
- Improve [lease expire/revoke operation performance](https://github.com/etcd-io/etcd/pull/9418), address [lease scalability issue](https://github.com/etcd-io/etcd/issues/9496).
|
||||
- Make [Lease `Lookup` non-blocking with concurrent `Grant`/`Revoke`](https://github.com/etcd-io/etcd/pull/9229).
|
||||
- Make etcd server return `raft.ErrProposalDropped` on internal Raft proposal drop in [v3 applier](https://github.com/etcd-io/etcd/pull/9549) and [v2 applier](https://github.com/etcd-io/etcd/pull/9558).
|
||||
- e.g. a node is removed from cluster, or [`raftpb.MsgProp` arrives at current leader while there is an ongoing leadership transfer](https://github.com/etcd-io/etcd/issues/8975).
|
||||
- Add [`snapshot`](https://github.com/etcd-io/etcd/pull/9118) package for easier snapshot workflow (see [`godoc.org/github.com/etcd/clientv3/snapshot`](https://godoc.org/github.com/etcd-io/etcd/clientv3/snapshot) for more).
|
||||
- Improve [functional tester](https://github.com/etcd-io/etcd/tree/master/functional) coverage: [proxy layer to run network fault tests in CI](https://github.com/etcd-io/etcd/pull/9081), [TLS is enabled both for server and client](https://github.com/etcd-io/etcd/pull/9534), [liveness mode](https://github.com/etcd-io/etcd/issues/9230), [shuffle test sequence](https://github.com/etcd-io/etcd/issues/9381), [membership reconfiguration failure cases](https://github.com/etcd-io/etcd/pull/9564), [disastrous quorum loss and snapshot recover from a seed member](https://github.com/etcd-io/etcd/pull/9565), [embedded etcd](https://github.com/etcd-io/etcd/pull/9572).
|
||||
- Improve [index compaction blocking](https://github.com/etcd-io/etcd/pull/9511) by using a copy on write clone to avoid holding the lock for the traversal of the entire index.
|
||||
- Update [JWT methods](https://github.com/etcd-io/etcd/pull/9883) to allow for use of any supported signature method/algorithm.
|
||||
- Add [Lease checkpointing](https://github.com/etcd-io/etcd/pull/9924) to persist remaining TTLs to the consensus log periodically so that long lived leases progress toward expiry in the presence of leader elections and server restarts.
|
||||
- Enabled by experimental flag "--experimental-enable-lease-checkpoint".
|
||||
- Add [gRPC interceptor for debugging logs](https://github.com/etcd-io/etcd/pull/9990); enable `etcd --debug` flag to see per-request debug information.
|
||||
- Add [consistency check in snapshot status](https://github.com/etcd-io/etcd/pull/10109). If consistency check on snapshot file fails, `snapshot status` returns `"snapshot file integrity check failed..."` error.
|
||||
- Add [`Verify` function to perform corruption check on WAL contents](https://github.com/etcd-io/etcd/pull/10603).
|
||||
- Improve [heartbeat send failure logging](https://github.com/etcd-io/etcd/pull/10663).
|
||||
- Support [users with no password](https://github.com/etcd-io/etcd/pull/9817) for reducing security risk introduced by leaked password. The users can only be authenticated with CommonName based auth.
|
||||
- Add flag `--experimental-peer-skip-client-san-verification` to [skip verification of peer client address](https://github.com/etcd-io/etcd/pull/10524)
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Require [*Go 1.12+*](https://github.com/etcd-io/etcd/pull/10045).
|
||||
- Use [Go module](https://github.com/etcd-io/etcd/pull/10063) for dependency management.
|
||||
- Move [`"github.com/coreos/etcd"`](https://github.com/etcd-io/etcd/issues/9965) to [`"github.com/etcd-io/etcd"`](https://github.com/etcd-io/etcd/issues/9965).
|
||||
- Change import path to `"go.etcd.io/etcd"`.
|
||||
- e.g. `import "go.etcd.io/etcd/raft"`.
|
||||
- Make [`ETCDCTL_API=3 etcdctl` default](https://github.com/etcd-io/etcd/issues/9600).
|
||||
- Now, `etcdctl set foo bar` must be `ETCDCTL_API=2 etcdctl set foo bar`.
|
||||
- Now, `ETCDCTL_API=3 etcdctl put foo bar` could be just `etcdctl put foo bar`.
|
||||
- **Remove `etcd --ca-file` flag**, instead [use `etcd --trusted-ca-file`](https://github.com/etcd-io/etcd/pull/9470) (`etcd --ca-file` flag has been marked deprecated since v2.1).
|
||||
- **Remove `etcd --peer-ca-file` flag**, instead [use `etcd --peer-trusted-ca-file`](https://github.com/etcd-io/etcd/pull/9470) (`etcd --peer-ca-file` flag has been marked deprecated since v2.1).
|
||||
- **Remove `pkg/transport.TLSInfo.CAFile` field**, instead [use `pkg/transport.TLSInfo.TrustedCAFile`](https://github.com/etcd-io/etcd/pull/9470) (`CAFile` field has been marked deprecated since v2.1).
|
||||
- Deprecated `latest` [release container](https://console.cloud.google.com/gcr/images/etcd-development/GLOBAL/etcd) tag.
|
||||
- **`docker pull gcr.io/etcd-development/etcd:latest` would not be up-to-date**.
|
||||
- Deprecated [minor](https://semver.org/) version [release container](https://console.cloud.google.com/gcr/images/etcd-development/GLOBAL/etcd) tags.
|
||||
- `docker pull gcr.io/etcd-development/etcd:v3.3` would still work.
|
||||
- **`docker pull gcr.io/etcd-development/etcd:v3.4` would not work**.
|
||||
- Use **`docker pull gcr.io/etcd-development/etcd:v3.4.x`** instead, with the exact patch version.
|
||||
- Drop [ACIs from official release](https://github.com/etcd-io/etcd/pull/9059).
|
||||
- [AppC was officially suspended](https://github.com/appc/spec#-disclaimer-), as of late 2016.
|
||||
- [`acbuild`](https://github.com/containers/build#this-project-is-currently-unmaintained) is not maintained anymore.
|
||||
- `*.aci` files are not available from `v3.4` release.
|
||||
- Exit on [empty hosts in advertise URLs](https://github.com/etcd-io/etcd/pull/8786).
|
||||
- Address [advertise client URLs accepts empty hosts](https://github.com/etcd-io/etcd/issues/8379).
|
||||
- e.g. exit with error on `--advertise-client-urls=http://:2379`.
|
||||
- e.g. exit with error on `--initial-advertise-peer-urls=http://:2380`.
|
||||
- Exit on [shadowed environment variables](https://github.com/etcd-io/etcd/pull/9382).
|
||||
- Address [error on shadowed environment variables](https://github.com/etcd-io/etcd/issues/8380).
|
||||
- e.g. exit with error on `ETCD_NAME=abc etcd --name=def`.
|
||||
- e.g. exit with error on `ETCD_INITIAL_CLUSTER_TOKEN=abc etcd --initial-cluster-token=def`.
|
||||
- e.g. exit with error on `ETCDCTL_ENDPOINTS=abc.com ETCDCTL_API=3 etcdctl endpoint health --endpoints=def.com`.
|
||||
- Change [`etcdserverpb.AuthRoleRevokePermissionRequest/key,range_end` fields type from `string` to `bytes`](https://github.com/etcd-io/etcd/pull/9433).
|
||||
- Deprecating `etcd_debugging_mvcc_db_total_size_in_bytes` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819) instead.
|
||||
- Deprecating `etcd_debugging_mvcc_put_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_put_total`](https://github.com/etcd-io/etcd/pull/10962) instead.
|
||||
- Deprecating `etcd_debugging_mvcc_delete_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_delete_total`](https://github.com/etcd-io/etcd/pull/10962) instead.
|
||||
- Deprecating `etcd_debugging_mvcc_range_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_range_total`](https://github.com/etcd-io/etcd/pull/10968) instead.
|
||||
- Deprecating `etcd_debugging_mvcc_txn_total`Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_txn_total`](https://github.com/etcd-io/etcd/pull/10968) instead.
|
||||
- Rename `etcdserver.ServerConfig.SnapCount` field to `etcdserver.ServerConfig.SnapshotCount`, to be consistent with the flag name `etcd --snapshot-count`.
|
||||
- Rename `embed.Config.SnapCount` field to [`embed.Config.SnapshotCount`](https://github.com/etcd-io/etcd/pull/9745), to be consistent with the flag name `etcd --snapshot-count`.
|
||||
- Change [`embed.Config.CorsInfo` in `*cors.CORSInfo` type to `embed.Config.CORS` in `map[string]struct{}` type](https://github.com/etcd-io/etcd/pull/9490).
|
||||
- Remove [`embed.Config.SetupLogging`](https://github.com/etcd-io/etcd/pull/9572).
|
||||
- Now logger is set up automatically based on [`embed.Config.Logger`, `embed.Config.LogOutputs`, `embed.Config.Debug` fields](https://github.com/etcd-io/etcd/pull/9572).
|
||||
- Rename [`etcd --log-output` to `etcd --log-outputs`](https://github.com/etcd-io/etcd/pull/9624) to support multiple log outputs.
|
||||
- **`etcd --log-output`** will be deprecated in v3.5.
|
||||
- Rename [**`embed.Config.LogOutput`** to **`embed.Config.LogOutputs`**](https://github.com/etcd-io/etcd/pull/9624) to support multiple log outputs.
|
||||
- Change [**`embed.Config.LogOutputs`** type from `string` to `[]string`](https://github.com/etcd-io/etcd/pull/9579) to support multiple log outputs.
|
||||
- Now that `etcd --log-outputs` accepts multiple writers, etcd configuration YAML file `log-outputs` field must be changed to `[]string` type.
|
||||
- Previously, `etcd --config-file etcd.config.yaml` can have `log-outputs: default` field, now must be `log-outputs: [default]`.
|
||||
- Deprecating [`etcd --debug`](https://github.com/etcd-io/etcd/pull/10947) flag. Use `etcd --log-level=debug` flag instead.
|
||||
- v3.5 will deprecate `etcd --debug` flag in favor of `etcd --log-level=debug`.
|
||||
- Change v3 `etcdctl snapshot` exit codes with [`snapshot` package](https://github.com/etcd-io/etcd/pull/9118/commits/df689f4280e1cce4b9d61300be13ca604d41670a).
|
||||
- Exit on error with exit code 1 (no more exit code 5 or 6 on `snapshot save/restore` commands).
|
||||
- Migrate dependency management tool from `glide` to [`golang/dep`](https://github.com/etcd-io/etcd/pull/9155).
|
||||
- <= 3.3 puts `vendor` directory under `cmd/vendor` directory to [prevent conflicting transitive dependencies](https://github.com/etcd-io/etcd/issues/4913).
|
||||
- 3.4 moves `cmd/vendor` directory to `vendor` at repository root.
|
||||
- Remove recursive symlinks in `cmd` directory.
|
||||
- Now `go get/install/build` on `etcd` packages (e.g. `clientv3`, `tools/benchmark`) enforce builds with etcd `vendor` directory.
|
||||
- Replace [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) endpoint `/v3beta` with [`/v3`](https://github.com/etcd-io/etcd/pull/9298).
|
||||
- Deprecated [`/v3alpha`](https://github.com/etcd-io/etcd/pull/9298).
|
||||
- To deprecate [`/v3beta`](https://github.com/etcd-io/etcd/issues/9189) in v3.5.
|
||||
- In v3.4, `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` still works as a fallback to `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'`, but `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` won't work in v3.5. Use `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
|
||||
- Change [`wal` package function signatures](https://github.com/etcd-io/etcd/pull/9572) to support [structured logger and logging to file](https://github.com/etcd-io/etcd/issues/9438) in server-side.
|
||||
- Previously, `Open(dirpath string, snap walpb.Snapshot) (*WAL, error)`, now `Open(lg *zap.Logger, dirpath string, snap walpb.Snapshot) (*WAL, error)`.
|
||||
- Previously, `OpenForRead(dirpath string, snap walpb.Snapshot) (*WAL, error)`, now `OpenForRead(lg *zap.Logger, dirpath string, snap walpb.Snapshot) (*WAL, error)`.
|
||||
- Previously, `Repair(dirpath string) bool`, now `Repair(lg *zap.Logger, dirpath string) bool`.
|
||||
- Previously, `Create(dirpath string, metadata []byte) (*WAL, error)`, now `Create(lg *zap.Logger, dirpath string, metadata []byte) (*WAL, error)`.
|
||||
- Remove [`pkg/cors` package](https://github.com/etcd-io/etcd/pull/9490).
|
||||
- Move internal packages to `etcdserver`.
|
||||
- `"github.com/coreos/etcd/alarm"` to `"go.etcd.io/etcd/etcdserver/api/v3alarm"`.
|
||||
- `"github.com/coreos/etcd/compactor"` to `"go.etcd.io/etcd/etcdserver/api/v3compactor"`.
|
||||
- `"github.com/coreos/etcd/discovery"` to `"go.etcd.io/etcd/etcdserver/api/v2discovery"`.
|
||||
- `"github.com/coreos/etcd/etcdserver/auth"` to `"go.etcd.io/etcd/etcdserver/api/v2auth"`.
|
||||
- `"github.com/coreos/etcd/etcdserver/membership"` to `"go.etcd.io/etcd/etcdserver/api/membership"`.
|
||||
- `"github.com/coreos/etcd/etcdserver/stats"` to `"go.etcd.io/etcd/etcdserver/api/v2stats"`.
|
||||
- `"github.com/coreos/etcd/error"` to `"go.etcd.io/etcd/etcdserver/api/v2error"`.
|
||||
- `"github.com/coreos/etcd/rafthttp"` to `"go.etcd.io/etcd/etcdserver/api/rafthttp"`.
|
||||
- `"github.com/coreos/etcd/snap"` to `"go.etcd.io/etcd/etcdserver/api/snap"`.
|
||||
- `"github.com/coreos/etcd/store"` to `"go.etcd.io/etcd/etcdserver/api/v2store"`.
|
||||
- Change [snapshot file permissions](https://github.com/etcd-io/etcd/pull/9977): On Linux, the snapshot file changes from readable by all (mode 0644) to readable by the user only (mode 0600).
|
||||
- Change [`pkg/adt.IntervalTree` from `struct` to `interface`](https://github.com/etcd-io/etcd/pull/10959).
|
||||
- See [`pkg/adt` README](https://github.com/etcd-io/etcd/tree/master/pkg/adt) and [`pkg/adt` godoc](https://godoc.org/go.etcd.io/etcd/pkg/adt).
|
||||
- Release branch `/version` defines version `3.4.x-pre`, instead of `3.4.y+git`.
|
||||
- Use `3.4.5-pre`, instead of `3.4.4+git`.
|
||||
|
||||
### Dependency
|
||||
|
||||
- Upgrade [`github.com/coreos/bbolt`](https://github.com/etcd-io/bbolt/releases) from [**`v1.3.1-coreos.6`**](https://github.com/etcd-io/bbolt/releases/tag/v1.3.1-coreos.6) to [`go.etcd.io/bbolt`](https://github.com/etcd-io/bbolt/releases) [**`v1.3.3`**](https://github.com/etcd-io/bbolt/releases/tag/v1.3.3).
|
||||
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) from [**`v1.7.5`**](https://github.com/grpc/grpc-go/releases/tag/v1.7.5) to [**`v1.23.0`**](https://github.com/grpc/grpc-go/releases/tag/v1.23.0).
|
||||
- Migrate [`github.com/ugorji/go/codec`](https://github.com/ugorji/go/releases) to [**`github.com/json-iterator/go`**](https://github.com/json-iterator/go), to [regenerate v2 `client`](https://github.com/etcd-io/etcd/pull/9494) (See [#10667](https://github.com/etcd-io/etcd/pull/10667) for more).
|
||||
- Migrate [`github.com/ghodss/yaml`](https://github.com/ghodss/yaml/releases) to [**`sigs.k8s.io/yaml`**](https://github.com/kubernetes-sigs/yaml) (See [#10687](https://github.com/etcd-io/etcd/pull/10687) for more).
|
||||
- Upgrade [`golang.org/x/crypto`](https://github.com/golang/crypto) from [**`crypto@9419663f5`**](https://github.com/golang/crypto/commit/9419663f5a44be8b34ca85f08abc5fe1be11f8a3) to [**`crypto@0709b304e793`**](https://github.com/golang/crypto/commit/0709b304e793a5edb4a2c0145f281ecdc20838a4).
|
||||
- Upgrade [`golang.org/x/net`](https://github.com/golang/net) from [**`net@66aacef3d`**](https://github.com/golang/net/commit/66aacef3dd8a676686c7ae3716979581e8b03c47) to [**`net@adae6a3d119a`**](https://github.com/golang/net/commit/adae6a3d119ae4890b46832a2e88a95adc62b8e7).
|
||||
- Upgrade [`golang.org/x/sys`](https://github.com/golang/sys) from [**`sys@ebfc5b463`**](https://github.com/golang/sys/commit/ebfc5b4631820b793c9010c87fd8fef0f39eb082) to [**`sys@56ede360e`**](https://github.com/golang/sys/commit/56ede360ec1c541828fb88741b3f1049406d28f5).
|
||||
- Upgrade [`golang.org/x/text`](https://github.com/golang/text) from [**`text@b19bf474d`**](https://github.com/golang/text/commit/b19bf474d317b857955b12035d2c5acb57ce8b01) to [**`v0.3.0`**](https://github.com/golang/text/releases/tag/v0.3.0).
|
||||
- Upgrade [`golang.org/x/time`](https://github.com/golang/time) from [**`time@c06e80d93`**](https://github.com/golang/time/commit/c06e80d9300e4443158a03817b8a8cb37d230320) to [**`time@fbb02b229`**](https://github.com/golang/time/commit/fbb02b2291d28baffd63558aa44b4b56f178d650).
|
||||
- Upgrade [`github.com/golang/protobuf`](https://github.com/golang/protobuf/releases) from [**`golang/protobuf@1e59b77b5`**](https://github.com/golang/protobuf/commit/1e59b77b52bf8e4b449a57e6f79f21226d571845) to [**`v1.3.2`**](https://github.com/golang/protobuf/releases/tag/v1.3.2).
|
||||
- Upgrade [`gopkg.in/yaml.v2`](https://github.com/go-yaml/yaml/releases) from [**`yaml@cd8b52f82`**](https://github.com/go-yaml/yaml/commit/cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b) to [**`yaml@5420a8b67`**](https://github.com/go-yaml/yaml/commit/5420a8b6744d3b0345ab293f6fcba19c978f1183).
|
||||
- Upgrade [`github.com/dgrijalva/jwt-go`](https://github.com/dgrijalva/jwt-go/releases) from [**`v3.0.0`**](https://github.com/dgrijalva/jwt-go/releases/tag/v3.0.0) to [**`v3.2.0`**](https://github.com/dgrijalva/jwt-go/releases/tag/v3.2.0).
|
||||
- Upgrade [`github.com/soheilhy/cmux`](https://github.com/soheilhy/cmux/releases) from [**`v0.1.3`**](https://github.com/soheilhy/cmux/releases/tag/v0.1.3) to [**`v0.1.4`**](https://github.com/soheilhy/cmux/releases/tag/v0.1.4).
|
||||
- Upgrade [`github.com/google/btree`](https://github.com/google/btree/releases) from [**`google/btree@925471ac9`**](https://github.com/google/btree/commit/925471ac9e2131377a91e1595defec898166fe49) to [**`v1.0.0`**](https://github.com/google/btree/releases/tag/v1.0.0).
|
||||
- Upgrade [`github.com/spf13/cobra`](https://github.com/spf13/cobra/releases) from [**`spf13/cobra@1c44ec8d3`**](https://github.com/spf13/cobra/commit/1c44ec8d3f1552cac48999f9306da23c4d8a288b) to [**`v0.0.3`**](https://github.com/spf13/cobra/releases/tag/v0.0.3).
|
||||
- Upgrade [`github.com/spf13/pflag`](https://github.com/spf13/pflag/releases) from [**`v1.0.0`**](https://github.com/spf13/pflag/releases/tag/v1.0.0) to [**`spf13/pflag@1ce0cc6db`**](https://github.com/spf13/pflag/commit/1ce0cc6db4029d97571db82f85092fccedb572ce).
|
||||
- Upgrade [`github.com/coreos/go-systemd`](https://github.com/coreos/go-systemd/releases) from [**`v15`**](https://github.com/coreos/go-systemd/releases/tag/v15) to [**`v17`**](https://github.com/coreos/go-systemd/releases/tag/v17).
|
||||
- Upgrade [`github.com/prometheus/client_golang`](https://github.com/prometheus/client_golang/releases) from [**``prometheus/client_golang@5cec1d042``**](https://github.com/prometheus/client_golang/commit/5cec1d0429b02e4323e042eb04dafdb079ddf568) to [**`v1.0.0`**](https://github.com/prometheus/client_golang/releases/tag/v1.0.0).
|
||||
- Upgrade [`github.com/grpc-ecosystem/go-grpc-prometheus`](https://github.com/grpc-ecosystem/go-grpc-prometheus/releases) from [**``grpc-ecosystem/go-grpc-prometheus@0dafe0d49``**](https://github.com/grpc-ecosystem/go-grpc-prometheus/commit/0dafe0d496ea71181bf2dd039e7e3f44b6bd11a7) to [**`v1.2.0`**](https://github.com/grpc-ecosystem/go-grpc-prometheus/releases/tag/v1.2.0).
|
||||
- Upgrade [`github.com/grpc-ecosystem/grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway/releases) from [**`v1.3.1`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.3.1) to [**`v1.4.1`**](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.4.1).
|
||||
- Migrate [`github.com/kr/pty`](https://github.com/kr/pty/releases) to [**`github.com/creack/pty`**](https://github.com/creack/pty/releases/tag/v1.1.7), as the later has replaced the original module.
|
||||
- Upgrade [`github.com/gogo/protobuf`](https://github.com/gogo/protobuf/releases) from [**`v1.0.0`**](https://github.com/gogo/protobuf/releases/tag/v1.0.0) to [**`v1.2.1`**](https://github.com/gogo/protobuf/releases/tag/v1.2.1).
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Add [`etcd_snap_db_fsync_duration_seconds_count`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_snap_db_save_total_duration_seconds_bucket`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_send_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_success`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_failures`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_snapshot_receive_total_duration_seconds`](https://github.com/etcd-io/etcd/pull/9997) Prometheus metric.
|
||||
- Add [`etcd_network_active_peers`](https://github.com/etcd-io/etcd/pull/9762) Prometheus metric.
|
||||
- Let's say `"7339c4e5e833c029"` server `/metrics` returns `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="729934363faa4a24"} 1` and `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="b548c2511513015"} 1`. This indicates that the local node `"7339c4e5e833c029"` currently has two active remote peers `"729934363faa4a24"` and `"b548c2511513015"` in a 3-node cluster. If the node `"b548c2511513015"` is down, the local node `"7339c4e5e833c029"` will show `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="729934363faa4a24"} 1` and `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="b548c2511513015"} 0`.
|
||||
- Add [`etcd_network_disconnected_peers_total`](https://github.com/etcd-io/etcd/pull/9762) Prometheus metric.
|
||||
- If a remote peer `"b548c2511513015"` is down, the local node `"7339c4e5e833c029"` server `/metrics` would return `etcd_network_disconnected_peers_total{Local="7339c4e5e833c029",Remote="b548c2511513015"} 1`, while active peer metrics will show `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="729934363faa4a24"} 1` and `etcd_network_active_peers{Local="7339c4e5e833c029",Remote="b548c2511513015"} 0`.
|
||||
- Add [`etcd_network_server_stream_failures_total`](https://github.com/etcd-io/etcd/pull/9760) Prometheus metric.
|
||||
- e.g. `etcd_network_server_stream_failures_total{API="lease-keepalive",Type="receive"} 1`
|
||||
- e.g. `etcd_network_server_stream_failures_total{API="watch",Type="receive"} 1`
|
||||
- Improve [`etcd_network_peer_round_trip_time_seconds`](https://github.com/etcd-io/etcd/pull/10155) Prometheus metric to track leader heartbeats.
|
||||
- Previously, it only samples the TCP connection for snapshot messages.
|
||||
- Increase [`etcd_network_peer_round_trip_time_seconds`](https://github.com/etcd-io/etcd/pull/9762) Prometheus metric histogram upper-bound.
|
||||
- Previously, highest bucket only collects requests taking 0.8192 seconds or more.
|
||||
- Now, highest buckets collect 0.8192 seconds, 1.6384 seconds, and 3.2768 seconds or more.
|
||||
- Add [`etcd_server_is_leader`](https://github.com/etcd-io/etcd/pull/9587) Prometheus metric.
|
||||
- Add [`etcd_server_id`](https://github.com/etcd-io/etcd/pull/9998) Prometheus metric.
|
||||
- Add [`etcd_cluster_version`](https://github.com/etcd-io/etcd/pull/10257) Prometheus metric.
|
||||
- Add [`etcd_server_version`](https://github.com/etcd-io/etcd/pull/8960) Prometheus metric.
|
||||
- To replace [Kubernetes `etcd-version-monitor`](https://github.com/etcd-io/etcd/issues/8948).
|
||||
- Add [`etcd_server_go_version`](https://github.com/etcd-io/etcd/pull/9957) Prometheus metric.
|
||||
- Add [`etcd_server_health_success`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
|
||||
- Add [`etcd_server_health_failures`](https://github.com/etcd-io/etcd/pull/10156) Prometheus metric.
|
||||
- Add [`etcd_server_read_indexes_failed_total`](https://github.com/etcd-io/etcd/pull/10094) Prometheus metric.
|
||||
- Add [`etcd_server_heartbeat_send_failures_total`](https://github.com/etcd-io/etcd/pull/9761) Prometheus metric.
|
||||
- Add [`etcd_server_slow_apply_total`](https://github.com/etcd-io/etcd/pull/9761) Prometheus metric.
|
||||
- Add [`etcd_server_slow_read_indexes_total`](https://github.com/etcd-io/etcd/pull/9897) Prometheus metric.
|
||||
- Add [`etcd_server_quota_backend_bytes`](https://github.com/etcd-io/etcd/pull/9820) Prometheus metric.
|
||||
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
|
||||
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
|
||||
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
|
||||
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
|
||||
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
|
||||
- Add [`etcd_mvcc_db_total_size_in_use_in_bytes`](https://github.com/etcd-io/etcd/pull/9256) Prometheus metric.
|
||||
- Use it with `etcd_mvcc_db_total_size_in_bytes` and `etcd_mvcc_db_total_size_in_use_in_bytes`.
|
||||
- `etcd_server_quota_backend_bytes 2.147483648e+09` means current quota size is 2 GB.
|
||||
- `etcd_mvcc_db_total_size_in_bytes 20480` means current physically allocated DB size is 20 KB.
|
||||
- `etcd_mvcc_db_total_size_in_use_in_bytes 16384` means future DB size if defragment operation is complete.
|
||||
- `etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes` is the number of bytes that can be saved on disk with defragment operation.
|
||||
- Add [`etcd_snap_fsync_duration_seconds`](https://github.com/etcd-io/etcd/pull/9762) Prometheus metric.
|
||||
- Add [`etcd_disk_backend_defrag_duration_seconds`](https://github.com/etcd-io/etcd/pull/9761) Prometheus metric.
|
||||
- Add [`etcd_mvcc_hash_duration_seconds`](https://github.com/etcd-io/etcd/pull/9761) Prometheus metric.
|
||||
- Add [`etcd_mvcc_hash_rev_duration_seconds`](https://github.com/etcd-io/etcd/pull/9761) Prometheus metric.
|
||||
- Add [`etcd_debugging_disk_backend_commit_rebalance_duration_seconds`](https://github.com/etcd-io/etcd/pull/9834) Prometheus metric.
|
||||
- Add [`etcd_debugging_disk_backend_commit_spill_duration_seconds`](https://github.com/etcd-io/etcd/pull/9834) Prometheus metric.
|
||||
- Add [`etcd_debugging_disk_backend_commit_write_duration_seconds`](https://github.com/etcd-io/etcd/pull/9834) Prometheus metric.
|
||||
- Add [`etcd_debugging_lease_granted_total`](https://github.com/etcd-io/etcd/pull/9778) Prometheus metric.
|
||||
- Add [`etcd_debugging_lease_revoked_total`](https://github.com/etcd-io/etcd/pull/9778) Prometheus metric.
|
||||
- Add [`etcd_debugging_lease_renewed_total`](https://github.com/etcd-io/etcd/pull/9778) Prometheus metric.
|
||||
- Add [`etcd_debugging_lease_ttl_total`](https://github.com/etcd-io/etcd/pull/9778) Prometheus metric.
|
||||
- Add [`etcd_server_is_learner`](https://github.com/etcd-io/etcd/pull/10731) Prometheus metric.
|
||||
- Add [`etcd_server_learner_promote_failures`](https://github.com/etcd-io/etcd/pull/10731) Prometheus metric.
|
||||
- Add [`etcd_server_learner_promote_successes`](https://github.com/etcd-io/etcd/pull/10731) Prometheus metric.
|
||||
- Increase [`etcd_debugging_mvcc_index_compaction_pause_duration_milliseconds`](https://github.com/etcd-io/etcd/pull/9762) Prometheus metric histogram upper-bound.
|
||||
- Previously, highest bucket only collects requests taking 1.024 seconds or more.
|
||||
- Now, highest buckets collect 1.024 seconds, 2.048 seconds, and 4.096 seconds or more.
|
||||
- Fix missing [`etcd_network_peer_sent_failures_total`](https://github.com/etcd-io/etcd/pull/9437) Prometheus metric count.
|
||||
- Fix [`etcd_debugging_server_lease_expired_total`](https://github.com/etcd-io/etcd/pull/9557) Prometheus metric.
|
||||
- Fix [race conditions in v2 server stat collecting](https://github.com/etcd-io/etcd/pull/9562).
|
||||
- Change [gRPC proxy to expose etcd server endpoint /metrics](https://github.com/etcd-io/etcd/pull/10618).
|
||||
- The metrics that were exposed via the proxy were not etcd server members but instead the proxy itself.
|
||||
- Fix bug where [db_compaction_total_duration_milliseconds metric incorrectly measured duration as 0](https://github.com/etcd-io/etcd/pull/10646).
|
||||
- Deprecating `etcd_debugging_mvcc_db_total_size_in_bytes` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_db_total_size_in_bytes`](https://github.com/etcd-io/etcd/pull/9819) instead.
|
||||
- Deprecating `etcd_debugging_mvcc_put_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_put_total`](https://github.com/etcd-io/etcd/pull/10962) instead.
|
||||
- Deprecating `etcd_debugging_mvcc_delete_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_delete_total`](https://github.com/etcd-io/etcd/pull/10962) instead.
|
||||
- Deprecating `etcd_debugging_mvcc_range_total` Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_range_total`](https://github.com/etcd-io/etcd/pull/10968) instead.
|
||||
- Deprecating `etcd_debugging_mvcc_txn_total`Prometheus metric (to be removed in v3.5). Use [`etcd_mvcc_txn_total`](https://github.com/etcd-io/etcd/pull/10968) instead.
|
||||
|
||||
### Security, Authentication
|
||||
|
||||
See [security doc](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/security.md) for more details.
|
||||
|
||||
- Support TLS cipher suite whitelisting.
|
||||
- To block [weak cipher suites](https://github.com/etcd-io/etcd/issues/8320).
|
||||
- TLS handshake fails when client hello is requested with invalid cipher suites.
|
||||
- Add [`etcd --cipher-suites`](https://github.com/etcd-io/etcd/pull/9801) flag.
|
||||
- If empty, Go auto-populates the list.
|
||||
- Add [`etcd --host-whitelist`](https://github.com/etcd-io/etcd/pull/9372) flag, [`etcdserver.Config.HostWhitelist`](https://github.com/etcd-io/etcd/pull/9372), and [`embed.Config.HostWhitelist`](https://github.com/etcd-io/etcd/pull/9372), to prevent ["DNS Rebinding"](https://en.wikipedia.org/wiki/DNS_rebinding) attack.
|
||||
- Any website can simply create an authorized DNS name, and direct DNS to `"localhost"` (or any other address). Then, all HTTP endpoints of etcd server listening on `"localhost"` becomes accessible, thus vulnerable to [DNS rebinding attacks (CVE-2018-5702)](https://bugs.chromium.org/p/project-zero/issues/detail?id=1447#c2).
|
||||
- Client origin enforce policy works as follow:
|
||||
- If client connection is secure via HTTPS, allow any hostnames..
|
||||
- If client connection is not secure and `"HostWhitelist"` is not empty, only allow HTTP requests whose Host field is listed in whitelist.
|
||||
- By default, `"HostWhitelist"` is `"*"`, which means insecure server allows all client HTTP requests.
|
||||
- Note that the client origin policy is enforced whether authentication is enabled or not, for tighter controls.
|
||||
- When specifying hostnames, loopback addresses are not added automatically. To allow loopback interfaces, add them to whitelist manually (e.g. `"localhost"`, `"127.0.0.1"`, etc.).
|
||||
- e.g. `etcd --host-whitelist example.com`, then the server will reject all HTTP requests whose Host field is not `example.com` (also rejects requests to `"localhost"`).
|
||||
- Support [`etcd --cors`](https://github.com/etcd-io/etcd/pull/9490) in v3 HTTP requests (gRPC gateway).
|
||||
- Support [`ttl` field for `etcd` Authentication JWT token](https://github.com/etcd-io/etcd/pull/8302).
|
||||
- e.g. `etcd --auth-token jwt,pub-key=<pub key path>,priv-key=<priv key path>,sign-method=<sign method>,ttl=5m`.
|
||||
- Allow empty token provider in [`etcdserver.ServerConfig.AuthToken`](https://github.com/etcd-io/etcd/pull/9369).
|
||||
- Fix [TLS reload](https://github.com/etcd-io/etcd/pull/9570) when [certificate SAN field only includes IP addresses but no domain names](https://github.com/etcd-io/etcd/issues/9541).
|
||||
- In Go, server calls `(*tls.Config).GetCertificate` for TLS reload if and only if server's `(*tls.Config).Certificates` field is not empty, or `(*tls.ClientHelloInfo).ServerName` is not empty with a valid SNI from the client. Previously, etcd always populates `(*tls.Config).Certificates` on the initial client TLS handshake, as non-empty. Thus, client was always expected to supply a matching SNI in order to pass the TLS verification and to trigger `(*tls.Config).GetCertificate` to reload TLS assets.
|
||||
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/etcd-io/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
|
||||
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
|
||||
|
||||
### etcd server
|
||||
|
||||
- Add [`rpctypes.ErrLeaderChanged`](https://github.com/etcd-io/etcd/pull/10094).
|
||||
- Now linearizable requests with read index would fail fast when there is a leadership change, instead of waiting until context timeout.
|
||||
- Add [`etcd --initial-election-tick-advance`](https://github.com/etcd-io/etcd/pull/9591) flag to configure initial election tick fast-forward.
|
||||
- By default, `etcd --initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
|
||||
- This benefits the case of larger election ticks. For instance, cross datacenter deployment may require longer election timeout of 10-second. If true, local node does not need wait up to 10-second. Instead, forwards its election ticks to 8-second, and have only 2-second left before leader election.
|
||||
- Major assumptions are that: cluster has no active leader thus advancing ticks enables faster leader election. Or cluster already has an established leader, and rejoining follower is likely to receive heartbeats from the leader after tick advance and before election timeout.
|
||||
- However, when network from leader to rejoining follower is congested, and the follower does not receive leader heartbeat within left election ticks, disruptive election has to happen thus affecting cluster availabilities.
|
||||
- Now, this can be disabled by setting `etcd --initial-election-tick-advance=false`.
|
||||
- Disabling this would slow down initial bootstrap process for cross datacenter deployments. Make tradeoffs by configuring `etcd --initial-election-tick-advance` at the cost of slow initial bootstrap.
|
||||
- If single-node, it advances ticks regardless.
|
||||
- Address [disruptive rejoining follower node](https://github.com/etcd-io/etcd/issues/9333).
|
||||
- Add [`etcd --pre-vote`](https://github.com/etcd-io/etcd/pull/9352) flag to enable to run an additional Raft election phase.
|
||||
- For instance, a flaky(or rejoining) member may drop in and out, and start campaign. This member will end up with a higher term, and ignore all incoming messages with lower term. In this case, a new leader eventually need to get elected, thus disruptive to cluster availability. Raft implements Pre-Vote phase to prevent this kind of disruptions. If enabled, Raft runs an additional phase of election to check if pre-candidate can get enough votes to win an election.
|
||||
- `etcd --pre-vote=false` by default.
|
||||
- v3.5 will enable `etcd --pre-vote=true` by default.
|
||||
- Add [`etcd --discovery-srv-name`](https://github.com/etcd-io/etcd/pull/8690) flag to support custom DNS SRV name with discovery.
|
||||
- If not given, etcd queries `_etcd-server-ssl._tcp.[YOUR_HOST]` and `_etcd-server._tcp.[YOUR_HOST]`.
|
||||
- If `etcd --discovery-srv-name="foo"`, then query `_etcd-server-ssl-foo._tcp.[YOUR_HOST]` and `_etcd-server-foo._tcp.[YOUR_HOST]`.
|
||||
- Useful for operating multiple etcd clusters under the same domain.
|
||||
- Support TLS cipher suite whitelisting.
|
||||
- To block [weak cipher suites](https://github.com/etcd-io/etcd/issues/8320).
|
||||
- TLS handshake fails when client hello is requested with invalid cipher suites.
|
||||
- Add [`etcd --cipher-suites`](https://github.com/etcd-io/etcd/pull/9801) flag.
|
||||
- If empty, Go auto-populates the list.
|
||||
- Support [`etcd --cors`](https://github.com/etcd-io/etcd/pull/9490) in v3 HTTP requests (gRPC gateway).
|
||||
- Rename [`etcd --log-output` to `etcd --log-outputs`](https://github.com/etcd-io/etcd/pull/9624) to support multiple log outputs.
|
||||
- **`etcd --log-output` will be deprecated in v3.5**.
|
||||
- Add [`etcd --logger`](https://github.com/etcd-io/etcd/pull/9572) flag to support [structured logger and multiple log outputs](https://github.com/etcd-io/etcd/issues/9438) in server-side.
|
||||
- **`etcd --logger=capnslog` will be deprecated in v3.5**.
|
||||
- Main motivation is to promote automated etcd monitoring, rather than looking back server logs when it starts breaking. Future development will make etcd log as few as possible, and make etcd easier to monitor with metrics and alerts.
|
||||
- `etcd --logger=capnslog --log-outputs=default` is the default setting and same as previous etcd server logging format.
|
||||
- `etcd --logger=zap --log-outputs=default` is not supported when `etcd --logger=zap`.
|
||||
- Use `etcd --logger=zap --log-outputs=stderr` instead.
|
||||
- Or, use `etcd --logger=zap --log-outputs=systemd/journal` to send logs to the local systemd journal.
|
||||
- Previously, if etcd parent process ID (PPID) is 1 (e.g. run with systemd), `etcd --logger=capnslog --log-outputs=default` redirects server logs to local systemd journal. And if write to journald fails, it writes to `os.Stderr` as a fallback.
|
||||
- However, even with PPID 1, it can fail to dial systemd journal (e.g. run embedded etcd with Docker container). Then, [every single log write will fail](https://github.com/etcd-io/etcd/pull/9729) and fall back to `os.Stderr`, which is inefficient.
|
||||
- To avoid this problem, systemd journal logging must be configured manually.
|
||||
- `etcd --logger=zap --log-outputs=stderr` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to `os.Stderr`. Use this to override journald log redirects.
|
||||
- `etcd --logger=zap --log-outputs=stdout` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to `os.Stdout` Use this to override journald log redirects.
|
||||
- `etcd --logger=zap --log-outputs=a.log` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to the specified file `a.log`.
|
||||
- `etcd --logger=zap --log-outputs=a.log,b.log,c.log,stdout` [writes server logs to multiple files `a.log`, `b.log` and `c.log` at the same time](https://github.com/etcd-io/etcd/pull/9579) and outputs to `os.Stderr`, in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig).
|
||||
- `etcd --logger=zap --log-outputs=/dev/null` will discard all server logs.
|
||||
- Add [`etcd --log-level`](https://github.com/etcd-io/etcd/pull/10947) flag to support log level.
|
||||
- v3.5 will deprecate `etcd --debug` flag in favor of `etcd --log-level=debug`.
|
||||
- Add [`etcd --backend-batch-limit`](https://github.com/etcd-io/etcd/pull/10283) flag.
|
||||
- Add [`etcd --backend-batch-interval`](https://github.com/etcd-io/etcd/pull/10283) flag.
|
||||
- Fix [`mvcc` "unsynced" watcher restore operation](https://github.com/etcd-io/etcd/pull/9281).
|
||||
- "unsynced" watcher is watcher that needs to be in sync with events that have happened.
|
||||
- That is, "unsynced" watcher is the slow watcher that was requested on old revision.
|
||||
- "unsynced" watcher restore operation was not correctly populating its underlying watcher group.
|
||||
- Which possibly causes [missing events from "unsynced" watchers](https://github.com/etcd-io/etcd/issues/9086).
|
||||
- A node gets network partitioned with a watcher on a future revision, and falls behind receiving a leader snapshot after partition gets removed. When applying this snapshot, etcd watch storage moves current synced watchers to unsynced since sync watchers might have become stale during network partition. And reset synced watcher group to restart watcher routines. Previously, there was a bug when moving from synced watcher group to unsynced, thus client would miss events when the watcher was requested to the network-partitioned node.
|
||||
- Fix [`mvcc` server panic from restore operation](https://github.com/etcd-io/etcd/pull/9775).
|
||||
- Let's assume that a watcher had been requested with a future revision X and sent to node A that became network-partitioned thereafter. Meanwhile, cluster makes progress. Then when the partition gets removed, the leader sends a snapshot to node A. Previously if the snapshot's latest revision is still lower than the watch revision X, **etcd server panicked** during snapshot restore operation.
|
||||
- Now, this server-side panic has been fixed.
|
||||
- Fix [server panic on invalid Election Proclaim/Resign HTTP(S) requests](https://github.com/etcd-io/etcd/pull/9379).
|
||||
- Previously, wrong-formatted HTTP requests to Election API could trigger panic in etcd server.
|
||||
- e.g. `curl -L http://localhost:2379/v3/election/proclaim -X POST -d '{"value":""}'`, `curl -L http://localhost:2379/v3/election/resign -X POST -d '{"value":""}'`.
|
||||
- Fix [revision-based compaction retention parsing](https://github.com/etcd-io/etcd/pull/9339).
|
||||
- Previously, `etcd --auto-compaction-mode revision --auto-compaction-retention 1` was [translated to revision retention 3600000000000](https://github.com/etcd-io/etcd/issues/9337).
|
||||
- Now, `etcd --auto-compaction-mode revision --auto-compaction-retention 1` is correctly parsed as revision retention 1.
|
||||
- Prevent [overflow by large `TTL` values for `Lease` `Grant`](https://github.com/etcd-io/etcd/pull/9399).
|
||||
- `TTL` parameter to `Grant` request is unit of second.
|
||||
- Leases with too large `TTL` values exceeding `math.MaxInt64` [expire in unexpected ways](https://github.com/etcd-io/etcd/issues/9374).
|
||||
- Server now returns `rpctypes.ErrLeaseTTLTooLarge` to client, when the requested `TTL` is larger than *9,000,000,000 seconds* (which is >285 years).
|
||||
- Again, etcd `Lease` is meant for short-periodic keepalives or sessions, in the range of seconds or minutes. Not for hours or days!
|
||||
- Fix [expired lease revoke](https://github.com/etcd-io/etcd/pull/10693).
|
||||
- Fix ["the key is not deleted when the bound lease expires"](https://github.com/etcd-io/etcd/issues/10686).
|
||||
- Enable etcd server [`raft.Config.CheckQuorum` when starting with `ForceNewCluster`](https://github.com/etcd-io/etcd/pull/9347).
|
||||
- Allow [non-WAL files in `etcd --wal-dir` directory](https://github.com/etcd-io/etcd/pull/9743).
|
||||
- Previously, existing files such as [`lost+found`](https://github.com/etcd-io/etcd/issues/7287) in WAL directory prevent etcd server boot.
|
||||
- Now, WAL directory that contains only `lost+found` or a file that's not suffixed with `.wal` is considered non-initialized.
|
||||
- Fix [`ETCD_CONFIG_FILE` env variable parsing in `etcd`](https://github.com/etcd-io/etcd/pull/10762).
|
||||
- Fix [race condition in `rafthttp` transport pause/resume](https://github.com/etcd-io/etcd/pull/10826).
|
||||
- Fix [server crash from creating an empty role](https://github.com/etcd-io/etcd/pull/10907).
|
||||
- Previously, creating a role with an empty name crashed etcd server with an error code `Unavailable`.
|
||||
- Now, creating a role with an empty name is not allowed with an error code `InvalidArgument`.
|
||||
|
||||
### API
|
||||
|
||||
- Add `isLearner` field to `etcdserverpb.Member`, `etcdserverpb.MemberAddRequest` and `etcdserverpb.StatusResponse` as part of [raft learner implementation](https://github.com/etcd-io/etcd/pull/10725).
|
||||
- Add `MemberPromote` rpc to `etcdserverpb.Cluster` interface and the corresponding `MemberPromoteRequest` and `MemberPromoteResponse` as part of [raft learner implementation](https://github.com/etcd-io/etcd/pull/10725).
|
||||
- Add [`snapshot`](https://github.com/etcd-io/etcd/pull/9118) package for snapshot restore/save operations (see [`godoc.org/github.com/etcd/clientv3/snapshot`](https://godoc.org/github.com/coreos/etcd/clientv3/snapshot) for more).
|
||||
- Add [`watch_id` field to `etcdserverpb.WatchCreateRequest`](https://github.com/etcd-io/etcd/pull/9065) to allow user-provided watch ID to `mvcc`.
|
||||
- Corresponding `watch_id` is returned via `etcdserverpb.WatchResponse`, if any.
|
||||
- Add [`fragment` field to `etcdserverpb.WatchCreateRequest`](https://github.com/etcd-io/etcd/pull/9291) to request etcd server to [split watch events](https://github.com/etcd-io/etcd/issues/9294) when the total size of events exceeds `etcd --max-request-bytes` flag value plus gRPC-overhead 512 bytes.
|
||||
- The default server-side request bytes limit is `embed.DefaultMaxRequestBytes` which is 1.5 MiB plus gRPC-overhead 512 bytes.
|
||||
- If watch response events exceed this server-side request limit and watch request is created with `fragment` field `true`, the server will split watch events into a set of chunks, each of which is a subset of watch events below server-side request limit.
|
||||
- Useful when client-side has limited bandwidths.
|
||||
- For example, watch response contains 10 events, where each event is 1 MiB. And server `etcd --max-request-bytes` flag value is 1 MiB. Then, server will send 10 separate fragmented events to the client.
|
||||
- For example, watch response contains 5 events, where each event is 2 MiB. And server `etcd --max-request-bytes` flag value is 1 MiB and `clientv3.Config.MaxCallRecvMsgSize` is 1 MiB. Then, server will try to send 5 separate fragmented events to the client, and the client will error with `"code = ResourceExhausted desc = grpc: received message larger than max (...)"`.
|
||||
- Client must implement fragmented watch event merge (which `clientv3` does in etcd v3.4).
|
||||
- Add [`raftAppliedIndex` field to `etcdserverpb.StatusResponse`](https://github.com/etcd-io/etcd/pull/9176) for current Raft applied index.
|
||||
- Add [`errors` field to `etcdserverpb.StatusResponse`](https://github.com/etcd-io/etcd/pull/9206) for server-side error.
|
||||
- e.g. `"etcdserver: no leader", "NOSPACE", "CORRUPT"`
|
||||
- Add [`dbSizeInUse` field to `etcdserverpb.StatusResponse`](https://github.com/etcd-io/etcd/pull/9256) for actual DB size after compaction.
|
||||
- Add [`WatchRequest.WatchProgressRequest`](https://github.com/etcd-io/etcd/pull/9869).
|
||||
- To manually trigger broadcasting watch progress event (empty watch response with latest header) to all associated watch streams.
|
||||
- Think of it as `WithProgressNotify` that can be triggered manually.
|
||||
|
||||
Note: **v3.5 will deprecate `etcd --log-package-levels` flag for `capnslog`**; `etcd --logger=zap --log-outputs=stderr` will the default. **v3.5 will deprecate `[CLIENT-URL]/config/local/log` endpoint.**
|
||||
|
||||
### Package `embed`
|
||||
|
||||
- Add [`embed.Config.CipherSuites`](https://github.com/etcd-io/etcd/pull/9801) to specify a list of supported cipher suites for TLS handshake between client/server and peers.
|
||||
- If empty, Go auto-populates the list.
|
||||
- Both `embed.Config.ClientTLSInfo.CipherSuites` and `embed.Config.CipherSuites` cannot be non-empty at the same time.
|
||||
- If not empty, specify either `embed.Config.ClientTLSInfo.CipherSuites` or `embed.Config.CipherSuites`.
|
||||
- Add [`embed.Config.InitialElectionTickAdvance`](https://github.com/etcd-io/etcd/pull/9591) to enable/disable initial election tick fast-forward.
|
||||
- `embed.NewConfig()` would return `*embed.Config` with `InitialElectionTickAdvance` as true by default.
|
||||
- Define [`embed.CompactorModePeriodic`](https://godoc.org/github.com/etcd-io/etcd/embed#pkg-variables) for `compactor.ModePeriodic`.
|
||||
- Define [`embed.CompactorModeRevision`](https://godoc.org/github.com/etcd-io/etcd/embed#pkg-variables) for `compactor.ModeRevision`.
|
||||
- Change [`embed.Config.CorsInfo` in `*cors.CORSInfo` type to `embed.Config.CORS` in `map[string]struct{}` type](https://github.com/etcd-io/etcd/pull/9490).
|
||||
- Remove [`embed.Config.SetupLogging`](https://github.com/etcd-io/etcd/pull/9572).
|
||||
- Now logger is set up automatically based on [`embed.Config.Logger`, `embed.Config.LogOutputs`, `embed.Config.Debug` fields](https://github.com/etcd-io/etcd/pull/9572).
|
||||
- Add [`embed.Config.Logger`](https://github.com/etcd-io/etcd/pull/9518) to support [structured logger `zap`](https://github.com/uber-go/zap) in server-side.
|
||||
- Add [`embed.Config.LogLevel`](https://github.com/etcd-io/etcd/pull/10947).
|
||||
- Rename `embed.Config.SnapCount` field to [`embed.Config.SnapshotCount`](https://github.com/etcd-io/etcd/pull/9745), to be consistent with the flag name `etcd --snapshot-count`.
|
||||
- Rename [**`embed.Config.LogOutput`** to **`embed.Config.LogOutputs`**](https://github.com/etcd-io/etcd/pull/9624) to support multiple log outputs.
|
||||
- Change [**`embed.Config.LogOutputs`** type from `string` to `[]string`](https://github.com/etcd-io/etcd/pull/9579) to support multiple log outputs.
|
||||
- Add [`embed.Config.BackendBatchLimit`](https://github.com/etcd-io/etcd/pull/10283) field.
|
||||
- Add [`embed.Config.BackendBatchInterval`](https://github.com/etcd-io/etcd/pull/10283) field.
|
||||
|
||||
### Package `integration`
|
||||
|
||||
- Add [`CLUSTER_DEBUG` to enable test cluster logging](https://github.com/etcd-io/etcd/pull/9678).
|
||||
- Deprecated `capnslog` in integration tests.
|
||||
|
||||
### client v3
|
||||
|
||||
- Add [`MemberAddAsLearner`](https://github.com/etcd-io/etcd/pull/10725) to `Clientv3.Cluster` interface. This API is used to add a learner member to etcd cluster.
|
||||
- Add [`MemberPromote`](https://github.com/etcd-io/etcd/pull/10727) to `Clientv3.Cluster` interface. This API is used to promote a learner member in etcd cluster.
|
||||
- Client may receive [`rpctypes.ErrLeaderChanged`](https://github.com/etcd-io/etcd/pull/10094) from server.
|
||||
- Now linearizable requests with read index would fail fast when there is a leadership change, instead of waiting until context timeout.
|
||||
- Add [`WithFragment` `OpOption`](https://github.com/etcd-io/etcd/pull/9291) to support [watch events fragmentation](https://github.com/etcd-io/etcd/issues/9294) when the total size of events exceeds `etcd --max-request-bytes` flag value plus gRPC-overhead 512 bytes.
|
||||
- Watch fragmentation is disabled by default.
|
||||
- The default server-side request bytes limit is `embed.DefaultMaxRequestBytes` which is 1.5 MiB plus gRPC-overhead 512 bytes.
|
||||
- If watch response events exceed this server-side request limit and watch request is created with `fragment` field `true`, the server will split watch events into a set of chunks, each of which is a subset of watch events below server-side request limit.
|
||||
- Useful when client-side has limited bandwidths.
|
||||
- For example, watch response contains 10 events, where each event is 1 MiB. And server `etcd --max-request-bytes` flag value is 1 MiB. Then, server will send 10 separate fragmented events to the client.
|
||||
- For example, watch response contains 5 events, where each event is 2 MiB. And server `etcd --max-request-bytes` flag value is 1 MiB and `clientv3.Config.MaxCallRecvMsgSize` is 1 MiB. Then, server will try to send 5 separate fragmented events to the client, and the client will error with `"code = ResourceExhausted desc = grpc: received message larger than max (...)"`.
|
||||
- Add [`Watcher.RequestProgress` method](https://github.com/etcd-io/etcd/pull/9869).
|
||||
- To manually trigger broadcasting watch progress event (empty watch response with latest header) to all associated watch streams.
|
||||
- Think of it as `WithProgressNotify` that can be triggered manually.
|
||||
- Fix [lease keepalive interval updates when response queue is full](https://github.com/etcd-io/etcd/pull/9952).
|
||||
- If `<-chan *clientv3LeaseKeepAliveResponse` from `clientv3.Lease.KeepAlive` was never consumed or channel is full, client was [sending keepalive request every 500ms](https://github.com/etcd-io/etcd/issues/9911) instead of expected rate of every "TTL / 3" duration.
|
||||
- Change [snapshot file permissions](https://github.com/etcd-io/etcd/pull/9977): On Linux, the snapshot file changes from readable by all (mode 0644) to readable by the user only (mode 0600).
|
||||
- Client may choose to send keepalive pings to server using [`PermitWithoutStream`](https://github.com/etcd-io/etcd/pull/10146).
|
||||
- By setting `PermitWithoutStream` to true, client can send keepalive pings to server without any active streams(RPCs). In other words, it allows sending keepalive pings with unary or simple RPC calls.
|
||||
- `PermitWithoutStream` is set to false by default.
|
||||
- Fix logic on [release lock key if cancelled](https://github.com/etcd-io/etcd/pull/10153) in `clientv3/concurrency` package.
|
||||
- Fix [`(*Client).Endpoints()` method race condition](https://github.com/etcd-io/etcd/pull/10595).
|
||||
|
||||
### etcdctl v3
|
||||
|
||||
- Make [`ETCDCTL_API=3 etcdctl` default](https://github.com/etcd-io/etcd/issues/9600).
|
||||
- Now, `etcdctl set foo bar` must be `ETCDCTL_API=2 etcdctl set foo bar`.
|
||||
- Now, `ETCDCTL_API=3 etcdctl put foo bar` could be just `etcdctl put foo bar`.
|
||||
- Add [`etcdctl member add --learner` and `etcdctl member promote`](https://github.com/etcd-io/etcd/pull/10725) to add and promote raft learner member in etcd cluster.
|
||||
- Add [`etcdctl --password`](https://github.com/etcd-io/etcd/pull/9730) flag.
|
||||
- To support [`:` character in user name](https://github.com/etcd-io/etcd/issues/9691).
|
||||
- e.g. `etcdctl --user user --password password get foo`
|
||||
- Add [`etcdctl user add --new-user-password`](https://github.com/etcd-io/etcd/pull/9730) flag.
|
||||
- Add [`etcdctl check datascale`](https://github.com/etcd-io/etcd/pull/9185) command.
|
||||
- Add [`etcdctl check datascale --auto-compact, --auto-defrag`](https://github.com/etcd-io/etcd/pull/9351) flags.
|
||||
- Add [`etcdctl check perf --auto-compact, --auto-defrag`](https://github.com/etcd-io/etcd/pull/9330) flags.
|
||||
- Add [`etcdctl defrag --cluster`](https://github.com/etcd-io/etcd/pull/9390) flag.
|
||||
- Add ["raft applied index" field to `endpoint status`](https://github.com/etcd-io/etcd/pull/9176).
|
||||
- Add ["errors" field to `endpoint status`](https://github.com/etcd-io/etcd/pull/9206).
|
||||
- Add [`etcdctl endpoint health --write-out` support](https://github.com/etcd-io/etcd/pull/9540).
|
||||
- Previously, [`etcdctl endpoint health --write-out json` did not work](https://github.com/etcd-io/etcd/issues/9532).
|
||||
- Add [missing newline in `etcdctl endpoint health`](https://github.com/etcd-io/etcd/pull/10793).
|
||||
- Fix [`etcdctl watch [key] [range_end] -- [exec-command…]`](https://github.com/etcd-io/etcd/pull/9688) parsing.
|
||||
- Previously, `ETCDCTL_API=3 etcdctl watch foo -- echo watch event received` panicked.
|
||||
- Fix [`etcdctl move-leader` command for TLS-enabled endpoints](https://github.com/etcd-io/etcd/pull/9807).
|
||||
- Add [`progress` command to `etcdctl watch --interactive`](https://github.com/etcd-io/etcd/pull/9869).
|
||||
- To manually trigger broadcasting watch progress event (empty watch response with latest header) to all associated watch streams.
|
||||
- Think of it as `WithProgressNotify` that can be triggered manually.
|
||||
- Add [timeout](https://github.com/etcd-io/etcd/pull/10301) to `etcdctl snapshot
|
||||
save`.
|
||||
- User can specify timeout of `etcdctl snapshot save` command using flag `--command-timeout`.
|
||||
- Fix etcdctl to [strip out insecure endpoints from DNS SRV records when using discovery](https://github.com/etcd-io/etcd/pull/10443)
|
||||
|
||||
### gRPC proxy
|
||||
|
||||
- Fix [etcd server panic from restore operation](https://github.com/etcd-io/etcd/pull/9775).
|
||||
- Let's assume that a watcher had been requested with a future revision X and sent to node A that became network-partitioned thereafter. Meanwhile, cluster makes progress. Then when the partition gets removed, the leader sends a snapshot to node A. Previously if the snapshot's latest revision is still lower than the watch revision X, **etcd server panicked** during snapshot restore operation.
|
||||
- Especially, gRPC proxy was affected, since it detects a leader loss with a key `"proxy-namespace__lostleader"` and a watch revision `"int64(math.MaxInt64 - 2)"`.
|
||||
- Now, this server-side panic has been fixed.
|
||||
- Fix [memory leak in cache layer](https://github.com/etcd-io/etcd/pull/10327).
|
||||
- Change [gRPC proxy to expose etcd server endpoint /metrics](https://github.com/etcd-io/etcd/pull/10618).
|
||||
- The metrics that were exposed via the proxy were not etcd server members but instead the proxy itself.
|
||||
|
||||
### gRPC gateway
|
||||
|
||||
- Replace [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) endpoint `/v3beta` with [`/v3`](https://github.com/etcd-io/etcd/pull/9298).
|
||||
- Deprecated [`/v3alpha`](https://github.com/etcd-io/etcd/pull/9298).
|
||||
- To deprecate [`/v3beta`](https://github.com/etcd-io/etcd/issues/9189) in v3.5.
|
||||
- In v3.4, `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` still works as a fallback to `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'`, but `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` won't work in v3.5. Use `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
|
||||
- Add API endpoints [`/{v3beta,v3}/lease/leases, /{v3beta,v3}/lease/revoke, /{v3beta,v3}/lease/timetolive`](https://github.com/etcd-io/etcd/pull/9450).
|
||||
- To deprecate [`/{v3beta,v3}/kv/lease/leases, /{v3beta,v3}/kv/lease/revoke, /{v3beta,v3}/kv/lease/timetolive`](https://github.com/etcd-io/etcd/issues/9430) in v3.5.
|
||||
- Support [`etcd --cors`](https://github.com/etcd-io/etcd/pull/9490) in v3 HTTP requests (gRPC gateway).
|
||||
|
||||
### Package `raft`
|
||||
|
||||
- Fix [deadlock during PreVote migration process](https://github.com/etcd-io/etcd/pull/8525).
|
||||
- Add [`raft.ErrProposalDropped`](https://github.com/etcd-io/etcd/pull/9067).
|
||||
- Now [`(r *raft) Step` returns `raft.ErrProposalDropped`](https://github.com/etcd-io/etcd/pull/9137) if a proposal has been ignored.
|
||||
- e.g. a node is removed from cluster, or [`raftpb.MsgProp` arrives at current leader while there is an ongoing leadership transfer](https://github.com/etcd-io/etcd/issues/8975).
|
||||
- Improve [Raft `becomeLeader` and `stepLeader`](https://github.com/etcd-io/etcd/pull/9073) by keeping track of latest `pb.EntryConfChange` index.
|
||||
- Previously record `pendingConf` boolean field scanning the entire tail of the log, which can delay hearbeat send.
|
||||
- Fix [missing learner nodes on `(n *node) ApplyConfChange`](https://github.com/etcd-io/etcd/pull/9116).
|
||||
- Add [`raft.Config.MaxUncommittedEntriesSize`](https://github.com/etcd-io/etcd/pull/10167) to limit the total size of the uncommitted entries in bytes.
|
||||
- Once exceeded, raft returns `raft.ErrProposalDropped` error.
|
||||
- Prevent [unbounded Raft log growth](https://github.com/cockroachdb/cockroach/issues/27772).
|
||||
- There was a bug in [PR#10167](https://github.com/etcd-io/etcd/pull/10167) but fixed via [PR#10199](https://github.com/etcd-io/etcd/pull/10199).
|
||||
- Add [`raft.Ready.CommittedEntries` pagination using `raft.Config.MaxSizePerMsg`](https://github.com/etcd-io/etcd/pull/9982).
|
||||
- This prevents out-of-memory errors if the raft log has become very large and commits all at once.
|
||||
- Fix [correctness bug in CommittedEntries pagination](https://github.com/etcd-io/etcd/pull/10063).
|
||||
- Optimize [message send flow control](https://github.com/etcd-io/etcd/pull/9985).
|
||||
- Leader now sends more append entries if it has more non-empty entries to send after updating flow control information.
|
||||
- Now, Raft allows multiple in-flight append messages.
|
||||
- Optimize [memory allocation when boxing slice in `maybeCommit`](https://github.com/etcd-io/etcd/pull/10679).
|
||||
- By boxing a heap-allocated slice header instead of the slice header on the stack, we can avoid an allocation when passing through the sort.Interface interface.
|
||||
- Avoid [memory allocation in Raft entry `String` method](https://github.com/etcd-io/etcd/pull/10680).
|
||||
- Avoid [multiple memory allocations when merging stable and unstable log](https://github.com/etcd-io/etcd/pull/10684).
|
||||
- Extract [progress tracking into own component](https://github.com/etcd-io/etcd/pull/10683).
|
||||
- Add [package `raft/tracker`](https://github.com/etcd-io/etcd/pull/10807).
|
||||
- Optimize [string representation of `Progress`](https://github.com/etcd-io/etcd/pull/10882).
|
||||
- Make [relationship between `node` and `RawNode` explicit](https://github.com/etcd-io/etcd/pull/10803).
|
||||
- Prevent [learners from becoming leader](https://github.com/etcd-io/etcd/pull/10822).
|
||||
- Add [package `raft/quorum` to reason about committed indexes as well as vote outcomes for both majority and joint quorums](https://github.com/etcd-io/etcd/pull/10779).
|
||||
- Bundle [Voters and Learner into `raft/tracker.Config` struct](https://github.com/etcd-io/etcd/pull/10865).
|
||||
- Use [membership sets in progress tracking](https://github.com/etcd-io/etcd/pull/10779).
|
||||
- Implement [joint quorum computation](https://github.com/etcd-io/etcd/pull/10779).
|
||||
- Refactor [`raft/node.go` to centralize configuration change application](https://github.com/etcd-io/etcd/pull/10865).
|
||||
- Allow [voter to become learner through snapshot](https://github.com/etcd-io/etcd/pull/10864).
|
||||
- Add [package `raft/confchange` to internally support joint consensus](https://github.com/etcd-io/etcd/pull/10779).
|
||||
- Use [`RawNode` for node's event loop](https://github.com/etcd-io/etcd/pull/10892).
|
||||
- Add [`RawNode.Bootstrap` method](https://github.com/etcd-io/etcd/pull/10892).
|
||||
- Add [`raftpb.ConfChangeV2` to use joint quorums](https://github.com/etcd-io/etcd/pull/10914).
|
||||
- `raftpb.ConfChange` continues to work as today: it allows carrying out a single configuration change. A `pb.ConfChange` proposal gets added to the Raft log as such and is thus also observed by the app during Ready handling, and fed back to ApplyConfChange.
|
||||
- `raftpb.ConfChangeV2` allows joint configuration changes but will continue to carry out configuration changes in "one phase" (i.e. without ever entering a joint config) when this is possible.
|
||||
- `raftpb.ConfChangeV2` messages initiate configuration changes. They support both the simple "one at a time" membership change protocol and full Joint Consensus allowing for arbitrary changes in membership.
|
||||
- Change [`raftpb.ConfState.Nodes` to `raftpb.ConfState.Voters`](https://github.com/etcd-io/etcd/pull/10914).
|
||||
|
||||
### Package `wal`
|
||||
|
||||
- Add [`Verify` function to perform corruption check on WAL contents](https://github.com/etcd-io/etcd/pull/10603).
|
||||
- Fix [`wal` directory cleanup on creation failures](https://github.com/etcd-io/etcd/pull/10689).
|
||||
|
||||
### Tooling
|
||||
|
||||
- Add [`etcd-dump-logs --entry-type`](https://github.com/etcd-io/etcd/pull/9628) flag to support WAL log filtering by entry type.
|
||||
- Add [`etcd-dump-logs --stream-decoder`](https://github.com/etcd-io/etcd/pull/9790) flag to support custom decoder.
|
||||
|
||||
### Go
|
||||
|
||||
- Require [*Go 1.12+*](https://github.com/etcd-io/etcd/pull/10045).
|
||||
- Compile with [*Go 1.12*](https://golang.org/doc/devel/release.html#go1.12).
|
||||
|
||||
### Dockerfile
|
||||
|
||||
- [Rebase etcd image from Alpine to Debian](https://github.com/etcd-io/etcd/pull/10805) to improve security and maintenance effort for etcd release.
|
||||
|
||||
<hr>
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
|
||||
|
||||
Previous change logs can be found at [CHANGELOG-3.4](https://github.com/etcd-io/etcd/blob/master/CHANGELOG-3.4.md).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## v3.5.0 (TBD)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.4.0...v3.5.0) and [v3.5 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_5.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v3.5 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_5.md).**
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) only supports [`/v3`](TODO) endpoint.
|
||||
- Deprecated [`/v3beta`](https://github.com/etcd-io/etcd/pull/9298).
|
||||
- `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` does work in v3.5. Use `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
|
||||
- **`etcd --experimental-enable-v2v3` flag has been deprecated.** Use **`etcd --enable-v2v3`** instead.
|
||||
- Change [`etcd --experimental-enable-v2v3`](TODO) flag to `etcd --enable-v2v3`; v2 storage emulation is now stable.
|
||||
- **`etcd --debug` flag has been deprecated.** Use **`etcd --log-level=debug`** instead.
|
||||
- Remove [`embed.Config.Debug`](https://github.com/etcd-io/etcd/pull/10947).
|
||||
- **`etcd --log-output` flag has been deprecated.** Use **`etcd --log-outputs`** instead.
|
||||
- **`etcd --logger=zap --log-outputs=stderr`** is now the default.
|
||||
- **`etcd --logger=capnslog` flag value has been deprecated.**
|
||||
- **`etcd --logger=zap --log-outputs=default` flag value is not supported.**.
|
||||
- Use `etcd --logger=zap --log-outputs=stderr`.
|
||||
- Or, use `etcd --logger=zap --log-outputs=systemd/journal` to send logs to the local systemd journal.
|
||||
- Previously, if etcd parent process ID (PPID) is 1 (e.g. run with systemd), `etcd --logger=capnslog --log-outputs=default` redirects server logs to local systemd journal. And if write to journald fails, it writes to `os.Stderr` as a fallback.
|
||||
- However, even with PPID 1, it can fail to dial systemd journal (e.g. run embedded etcd with Docker container). Then, [every single log write will fail](https://github.com/etcd-io/etcd/pull/9729) and fall back to `os.Stderr`, which is inefficient.
|
||||
- To avoid this problem, systemd journal logging must be configured manually.
|
||||
- **`etcd --log-outputs=stderr`** is now the default.
|
||||
- **`etcd --log-package-levels` flag for `capnslog` has been deprecated.** Now, **`etcd --logger=zap --log-outputs=stderr`** is the default.
|
||||
- **`[CLIENT-URL]/config/local/log` endpoint has been deprecated, as is `etcd --log-package-levels` flag.**
|
||||
- `curl http://127.0.0.1:2379/config/local/log -XPUT -d '{"Level":"DEBUG"}'` won't work.
|
||||
- Please use `etcd --logger=zap --log-outputs=stderr` instead.
|
||||
- Deprecated `etcd_debugging_mvcc_db_total_size_in_bytes` Prometheus metric. Use `etcd_mvcc_db_total_size_in_bytes` instead.
|
||||
- Deprecated `etcd_debugging_mvcc_put_total` Prometheus metric. Use `etcd_mvcc_put_total` instead.
|
||||
- Deprecated `etcd_debugging_mvcc_delete_total` Prometheus metric. Use `etcd_mvcc_delete_total` instead.
|
||||
- Deprecated `etcd_debugging_mvcc_txn_total` Prometheus metric. Use `etcd_mvcc_txn_total` instead.
|
||||
- Deprecated `etcd_debugging_mvcc_range_total` Prometheus metric. Use `etcd_mvcc_range_total` instead.
|
||||
- Master branch `/version` outputs `3.5.0-pre`, instead of `3.4.0+git`.
|
||||
|
||||
### Metrics, Monitoring
|
||||
|
||||
Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
|
||||
- Deprecated `etcd_debugging_mvcc_db_total_size_in_bytes` Prometheus metric. Use `etcd_mvcc_db_total_size_in_bytes` instead.
|
||||
- Deprecated `etcd_debugging_mvcc_put_total` Prometheus metric. Use `etcd_mvcc_put_total` instead.
|
||||
- Deprecated `etcd_debugging_mvcc_delete_total` Prometheus metric. Use `etcd_mvcc_delete_total` instead.
|
||||
- Deprecated `etcd_debugging_mvcc_txn_total` Prometheus metric. Use `etcd_mvcc_txn_total` instead.
|
||||
- Deprecated `etcd_debugging_mvcc_range_total` Prometheus metric. Use `etcd_mvcc_range_total` instead.
|
||||
|
||||
### etcd server
|
||||
|
||||
- [`etcd --enable-v2v3`](TODO) flag is now stable.
|
||||
- `etcd --experimental-enable-v2v3` has been deprecated.
|
||||
- Added [more v2v3 integration tests](https://github.com/etcd-io/etcd/pull/9634).
|
||||
- `etcd --enable-v2=true --enable-v2v3=''` by default, to enable v2 API server that is backed by **v2 store**.
|
||||
- `etcd --enable-v2=true --enable-v2v3=/aaa` to enable v2 API server that is backed by **v3 storage**.
|
||||
- `etcd --enable-v2=false --enable-v2v3=''` to disable v2 API server.
|
||||
- `etcd --enable-v2=false --enable-v2v3=/aaa` to disable v2 API server. TODO: error?
|
||||
- Automatically [create parent directory if it does not exist](https://github.com/etcd-io/etcd/pull/9626) (fix [issue#9609](https://github.com/etcd-io/etcd/issues/9609)).
|
||||
- v4.0 will configure `etcd --enable-v2=true --enable-v2v3=/aaa` to enable v2 API server that is backed by **v3 storage**.
|
||||
|
||||
### Package `embed`
|
||||
|
||||
- Remove [`embed.Config.Debug`](https://github.com/etcd-io/etcd/pull/10947).
|
||||
- Use `embed.Config.LogLevel` instead.
|
||||
|
||||
### gRPC gateway
|
||||
|
||||
- [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) only supports [`/v3`](TODO) endpoint.
|
||||
- Deprecated [`/v3beta`](https://github.com/etcd-io/etcd/pull/9298).
|
||||
- `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` does work in v3.5. Use `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
|
||||
|
||||
|
||||
<hr>
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
|
||||
|
||||
Previous change logs can be found at [CHANGELOG-3.x](https://github.com/etcd-io/etcd/blob/master/CHANGELOG-3.x.md).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
## v4.0.0 (TBD)
|
||||
|
||||
See [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0...v4.0.0) and [v4.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_4_0.md) for any breaking changes.
|
||||
|
||||
**Again, before running upgrades from any previous release, please make sure to read change logs below and [v4.0 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_4_0.md).**
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- [Secure etcd by default](https://github.com/etcd-io/etcd/issues/9475)?
|
||||
- Change `/health` endpoint output.
|
||||
- Previously, `{"health":"true"}`.
|
||||
- Now, `{"health":true}`.
|
||||
- Breaks [Kubernetes `kubectl get componentstatuses` command](https://github.com/kubernetes/kubernetes/issues/58240).
|
||||
- Deprecate [`etcd --proxy*`](TODO) flags; **no more v2 proxy**.
|
||||
- Deprecate [v2 storage backend](https://github.com/etcd-io/etcd/issues/9232); **no more v2 store**.
|
||||
- v2 API is still supported via [v2 emulation](TODO).
|
||||
- Deprecate [`etcdctl backup`](TODO) command.
|
||||
- `clientv3.Client.KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error)` is now [`clientv4.Client.KeepAlive(ctx context.Context, id LeaseID) <-chan *LeaseKeepAliveResponse`](TODO).
|
||||
- Similar to `Watch`, [`KeepAlive` does not return errors](https://github.com/etcd-io/etcd/issues/7488).
|
||||
- If there's an unknown server error, kill all open channels and create a new stream on the next `KeepAlive` call.
|
||||
- Rename `github.com/coreos/client` to `github.com/coreos/clientv2`.
|
||||
- [`etcd --experimental-initial-corrupt-check`](TODO) has been deprecated.
|
||||
- Use [`etcd --initial-corrupt-check`](TODO) instead.
|
||||
- [`etcd --experimental-corrupt-check-time`](TODO) has been deprecated.
|
||||
- Use [`etcd --corrupt-check-time`](TODO) instead.
|
||||
|
||||
### etcd server
|
||||
|
||||
- [`etcd --initial-corrupt-check`](TODO) flag is now stable (`etcd --experimental-initial-corrupt-check` has been deprecated).
|
||||
- `etcd --initial-corrupt-check=true` by default, to check cluster database hashes before serving client/peer traffic.
|
||||
- [`etcd --corrupt-check-time`](TODO) flag is now stable (`etcd --experimental-corrupt-check-time` has been deprecated).
|
||||
- `etcd --corrupt-check-time=12h` by default, to check cluster database hashes for every 12-hour.
|
||||
|
||||
### Go
|
||||
|
||||
- Require [*Go 2*](https://blog.golang.org/go2draft)?
|
||||
|
||||
|
||||
<hr>
|
||||
|
|
@ -1,15 +1,10 @@
|
|||
FROM k8s.gcr.io/debian-base:v1.0.0
|
||||
FROM --platform=linux/amd64 gcr.io/distroless/static-debian11
|
||||
|
||||
ADD etcd /usr/local/bin/
|
||||
ADD etcdctl /usr/local/bin/
|
||||
RUN mkdir -p /var/etcd/
|
||||
RUN mkdir -p /var/lib/etcd/
|
||||
|
||||
# Alpine Linux doesn't use pam, which means that there is no /etc/nsswitch.conf,
|
||||
# but Golang relies on /etc/nsswitch.conf to check the order of DNS resolving
|
||||
# (see https://github.com/golang/go/commit/9dee7771f561cf6aee081c0af6658cc81fac3918)
|
||||
# To fix this we just create /etc/nsswitch.conf and add the following line:
|
||||
RUN echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf
|
||||
WORKDIR /var/etcd/
|
||||
WORKDIR /var/lib/etcd/
|
||||
|
||||
EXPOSE 2379 2380
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
FROM k8s.gcr.io/debian-base-arm64:v1.0.0
|
||||
FROM --platform=linux/arm64 gcr.io/distroless/static-debian11
|
||||
|
||||
ADD etcd /usr/local/bin/
|
||||
ADD etcdctl /usr/local/bin/
|
||||
ADD var/etcd /var/etcd
|
||||
ADD var/lib/etcd /var/lib/etcd
|
||||
|
||||
WORKDIR /var/etcd/
|
||||
WORKDIR /var/lib/etcd/
|
||||
|
||||
EXPOSE 2379 2380
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
FROM k8s.gcr.io/debian-base-ppc64le:v1.0.0
|
||||
FROM --platform=linux/ppc64le gcr.io/distroless/static-debian11
|
||||
|
||||
ADD etcd /usr/local/bin/
|
||||
ADD etcdctl /usr/local/bin/
|
||||
ADD var/etcd /var/etcd
|
||||
ADD var/lib/etcd /var/lib/etcd
|
||||
|
||||
WORKDIR /var/etcd/
|
||||
WORKDIR /var/lib/etcd/
|
||||
|
||||
EXPOSE 2379 2380
|
||||
|
||||
|
|
|
@ -271,7 +271,10 @@ etcdctl --endpoints=$ENDPOINTS endpoint health
|
|||
|
||||
<img src="https://storage.googleapis.com/etcd/demo/11_etcdctl_snapshot_2016051001.gif" alt="11_etcdctl_snapshot_2016051001"/>
|
||||
|
||||
Snapshot can only be requested from one etcd node, so `--endpoints` flag should contain only one endpoint.
|
||||
|
||||
```
|
||||
ENDPOINTS=$HOST_1:2379
|
||||
etcdctl --endpoints=$ENDPOINTS snapshot save my.db
|
||||
|
||||
Snapshot saved at my.db
|
||||
|
|
|
@ -63,7 +63,6 @@ All releases version numbers follow the format of [semantic versioning 2.0.0](ht
|
|||
|
||||
## Build release binaries and images
|
||||
|
||||
- Ensure `acbuild` is available.
|
||||
- Ensure `docker` is available.
|
||||
|
||||
Run release script in root directory:
|
||||
|
@ -129,7 +128,7 @@ for TARGET_ARCH in "amd64" "arm64" "ppc64le"; do
|
|||
TAG=quay.io/coreos/etcd GOARCH=${TARGET_ARCH} \
|
||||
BINARYDIR=release/etcd-${VERSION}-linux-${TARGET_ARCH} \
|
||||
BUILDDIR=release \
|
||||
./scripts/build-docker ${VERSION}
|
||||
./scripts/build-docker.sh ${VERSION}
|
||||
done
|
||||
```
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ etcdctl provides a `snapshot` command to create backups. See [backup][backup] fo
|
|||
|
||||
When replacing an etcd node, it's important to remove the member first and then add its replacement.
|
||||
|
||||
etcd employs distributed consensus based on a quorum model; (n+1)/2 members, a majority, must agree on a proposal before it can be committed to the cluster. These proposals include key-value updates and membership changes. This model totally avoids any possibility of split brain inconsistency. The downside is permanent quorum loss is catastrophic.
|
||||
etcd employs distributed consensus based on a quorum model; (n/2)+1 members, a majority, must agree on a proposal before it can be committed to the cluster. These proposals include key-value updates and membership changes. This model totally avoids any possibility of split brain inconsistency. The downside is permanent quorum loss is catastrophic.
|
||||
|
||||
How this applies to membership: If a 3-member cluster has 1 downed member, it can still make forward progress because the quorum is 2 and 2 members are still live. However, adding a new member to a 3-member cluster will increase the quorum to 3 because 3 votes are required for a majority of 4 members. Since the quorum increased, this extra member buys nothing in terms of fault tolerance; the cluster is still one node failure away from being unrecoverable.
|
||||
|
||||
|
|
|
@ -13,26 +13,34 @@ Background
|
|||
|
||||
Membership reconfiguration has been one of the biggest operational challenges. Let’s review common challenges.
|
||||
|
||||
### 1. New Cluster member overloads Leader
|
||||
A newly joined etcd member starts with no data, thus demanding more updates from leader until it catches up with leader’s logs. Then leader’s network is more likely to be overloaded, blocking or dropping leader heartbeats to followers. In such case, a follower may election-timeout to start a new leader election. That is, a cluster with a new member is more vulnerable to leader election. Both leader election and the subsequent update propagation to the new member are prone to causing periods of cluster unavailability (see *Figure 1*).
|
||||
|
||||
![server-learner-figure-01](img/server-learner-figure-01.png)
|
||||
|
||||
### 2. Network Partitions scenarios
|
||||
What if network partition happens? It depends on leader partition. If the leader still maintains the active quorum, the cluster would continue to operate (see *Figure 2*).
|
||||
|
||||
![server-learner-figure-02](img/server-learner-figure-02.png)
|
||||
|
||||
#### 2.1 Leader isolation
|
||||
What if the leader becomes isolated from the rest of the cluster? Leader monitors progress of each follower. When leader loses connectivity from the quorum, it reverts back to follower which will affect the cluster availability (see *Figure 3*).
|
||||
|
||||
![server-learner-figure-03](img/server-learner-figure-03.png)
|
||||
|
||||
When a new node is added to 3 node cluster, the cluster size becomes 4 and the quorum size becomes 3. What if a new node had joined the cluster, and then network partition happens? It depends on which partition the new member gets located after partition. If the new node happens to be located in the same partition as leader’s, the leader still maintains the active quorum of 3. No leadership election happens, and no cluster availability gets affected (see *Figure 4*).
|
||||
When a new node is added to 3 node cluster, the cluster size becomes 4 and the quorum size becomes 3. What if a new node had joined the cluster, and then network partition happens? It depends on which partition the new member gets located after partition.
|
||||
|
||||
#### 2.2 Cluster Split 3+1
|
||||
If the new node happens to be located in the same partition as leader’s, the leader still maintains the active quorum of 3. No leadership election happens, and no cluster availability gets affected (see *Figure 4*).
|
||||
|
||||
![server-learner-figure-04](img/server-learner-figure-04.png)
|
||||
|
||||
#### 2.3 Cluster Split 2+2
|
||||
If the cluster is 2-and-2 partitioned, then neither of partition maintains the quorum of 3. In this case, leadership election happens (see *Figure 5*).
|
||||
|
||||
![server-learner-figure-05](img/server-learner-figure-05.png)
|
||||
|
||||
#### 2.4 Quorum Lost
|
||||
What if network partition happens first, and then a new member gets added? A partitioned 3-node cluster already has one disconnected follower. When a new member is added, the quorum changes from 2 to 3. Now, this cluster has only 2 active nodes out 4, thus losing quorum and starting a new leadership election (see *Figure 6*).
|
||||
|
||||
![server-learner-figure-06](img/server-learner-figure-06.png)
|
||||
|
@ -43,6 +51,7 @@ Adding a new member to a 1-node cluster changes the quorum size to 2, immediatel
|
|||
|
||||
![server-learner-figure-07](img/server-learner-figure-07.png)
|
||||
|
||||
### 3. Cluster Misconfigurations
|
||||
An even worse case is when an added member is misconfigured. Membership reconfiguration is a two-step process: “etcdctl member add” and starting an etcd server process with the given peer URL. That is, “member add” command is applied regardless of URL, even when the URL value is invalid. If the first step is applied with invalid URLs, the second step cannot even start the new etcd. Once the cluster loses quorum, there is no way to revert the membership change (see *Figure 8*).
|
||||
|
||||
![server-learner-figure-08](img/server-learner-figure-08.png)
|
||||
|
|
|
@ -174,3 +174,5 @@ As of version v3.2 if an etcd server is launched with the option `--client-cert-
|
|||
As of version v3.3 if an etcd server is launched with the option `--peer-cert-allowed-cn` or `--peer-cert-allowed-hostname` filtering of inter-peer connections is enabled. Nodes can only join the etcd cluster if their TLS certificate identity match the allowed one.
|
||||
See [etcd security page](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/security.md) for more details.
|
||||
|
||||
## Notes on password strength
|
||||
`etcdctl` command line interface and etcd API don't check a strength (length, coexistence of numbers and alphabets, etc) of the password during creating a new user or updating password of an existing user. An administrator needs to care about a requirement of password strength by themselves.
|
||||
|
|
|
@ -446,6 +446,11 @@ Follow the instructions when using these flags.
|
|||
+ default: 0s
|
||||
+ env variable: ETCD_EXPERIMENTAL_CORRUPT_CHECK_TIME
|
||||
|
||||
### --experimental-compaction-batch-limit
|
||||
+ Sets the maximum revisions deleted in each compaction batch.
|
||||
+ default: 1000
|
||||
+ env variable: ETCD_EXPERIMENTAL_COMPACTION_BATCH_LIMIT
|
||||
|
||||
[build-cluster]: clustering.md#static
|
||||
[reconfig]: runtime-configuration.md
|
||||
[discovery]: clustering.md#discovery
|
||||
|
|
|
@ -4,7 +4,7 @@ title: etcd gateway
|
|||
|
||||
## What is etcd gateway
|
||||
|
||||
etcd gateway is a simple TCP proxy that forwards network data to the etcd cluster. The gateway is stateless and transparent; it neither inspects client requests nor interferes with cluster responses.
|
||||
etcd gateway is a simple TCP proxy that forwards network data to the etcd cluster. The gateway is stateless and transparent; it neither inspects client requests nor interferes with cluster responses. It does not terminate TLS connections, do TLS handshakes on behalf of its clients, or verify if the connection is secured.
|
||||
|
||||
The gateway supports multiple etcd server endpoints and works on a simple round-robin policy. It only routes to available endpoints and hides failures from its clients. Other retry policies, such as weighted round-robin, may be supported in the future.
|
||||
|
||||
|
@ -74,7 +74,7 @@ $ etcd gateway start --discovery-srv=example.com
|
|||
|
||||
* Comma-separated list of etcd server targets for forwarding client connections.
|
||||
* Default: `127.0.0.1:2379`
|
||||
* Invalid example: `https://127.0.0.1:2379` (gateway does not terminate TLS)
|
||||
* Invalid example: `https://127.0.0.1:2379` (gateway does not terminate TLS). Note that the gateway does not verify the HTTP schema or inspect the requests, it only forwards requests to the given endpoints.
|
||||
|
||||
#### --discovery-srv
|
||||
|
||||
|
@ -103,5 +103,5 @@ $ etcd gateway start --discovery-srv=example.com
|
|||
|
||||
#### --trusted-ca-file
|
||||
|
||||
* Path to the client TLS CA file for the etcd cluster. Used to authenticate endpoints.
|
||||
* Path to the client TLS CA file for the etcd cluster to verify the endpoints returned from SRV discovery. Note that it is ONLY used for authenticating the discovered endpoints rather than creating connections for data transferring. The gateway never terminates TLS connections or create TLS connections on behalf of its clients.
|
||||
* Default: (not set)
|
||||
|
|
|
@ -126,7 +126,7 @@ If adding multiple members the best practice is to configure a single member at
|
|||
#### Add a new member as learner
|
||||
|
||||
Starting from v3.4, etcd supports adding a new member as learner / non-voting member.
|
||||
The motivation and design can be found in [design doc](https://etcd.readthedocs.io/en/latest/server-learner.html).
|
||||
The motivation and design can be found in [design doc][design-learner].
|
||||
In order to make the process of adding a new member safer,
|
||||
and to reduce cluster downtime when the new member is added, it is recommended that the new member is added to cluster
|
||||
as a learner until it catches up. This can be described as a three step process:
|
||||
|
@ -243,3 +243,4 @@ It is enabled by default.
|
|||
[remove member]: #remove-a-member
|
||||
[runtime-reconf]: runtime-reconf-design.md
|
||||
[error cases when promoting a member]: #error-cases-when-promoting-a-learner-member
|
||||
[design-learner]: ../learning/design-learner.md
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
title: Transport security model
|
||||
---
|
||||
|
||||
etcd supports automatic TLS as well as authentication through client certificates for both clients to server as well as peer (server to server / cluster) communication.
|
||||
etcd supports automatic TLS as well as authentication through client certificates for both clients to server as well as peer (server to server / cluster) communication. **Note that etcd doesn't enable [RBAC based authentication][auth] or the authentication feature in the transport layer by default to reduce friction for users getting started with the database. Further, changing this default would be a breaking change for the project which was established since 2013. An etcd cluster which doesn't enable security features can expose its data to any clients.**
|
||||
|
||||
To get up and running, first have a CA certificate and a signed key pair for one member. It is recommended to create and sign a new key pair for every member in a cluster.
|
||||
|
||||
|
@ -426,8 +426,17 @@ Make sure to sign the certificates with a Subject Name the member's public IP ad
|
|||
|
||||
The certificate needs to be signed for the member's FQDN in its Subject Name, use Subject Alternative Names (short IP SANs) to add the IP address. The `etcd-ca` tool provides `--domain=` option for its `new-cert` command, and openssl can make [it][alt-name] too.
|
||||
|
||||
### Does etcd encrypt data stored on disk drives?
|
||||
No. etcd doesn't encrypt key/value data stored on disk drives. If a user need to encrypt data stored on etcd, there are some options:
|
||||
* Let client applications encrypt and decrypt the data
|
||||
* Use a feature of underlying storage systems for encrypting stored data like [dm-crypt]
|
||||
|
||||
### I’m seeing a log warning that "directory X exist without recommended permission -rwx------"
|
||||
When etcd create certain new directories it sets file permission to 700 to prevent unprivileged access as possible. However, if user has already created a directory with own preference, etcd uses the existing directory and logs a warning message if the permission is different than 700.
|
||||
|
||||
[cfssl]: https://github.com/cloudflare/cfssl
|
||||
[tls-setup]: ../../hack/tls-setup
|
||||
[tls-guide]: https://github.com/coreos/docs/blob/master/os/generate-self-signed-certificates.md
|
||||
[alt-name]: http://wiki.cacert.org/FAQ/subjectAltName
|
||||
[auth]: authentication.md
|
||||
[dm-crypt]: https://en.wikipedia.org/wiki/Dm-crypt
|
||||
|
|
|
@ -369,6 +369,52 @@ After
|
|||
docker pull gcr.io/etcd-development/etcd:v3.3.0
|
||||
```
|
||||
|
||||
### Upgrades to >= v3.3.14
|
||||
|
||||
[v3.3.14](https://github.com/etcd-io/etcd/releases/tag/v3.3.14) had to include some features from 3.4, while trying to minimize the difference between client balancer implementation. This release fixes ["kube-apiserver 1.13.x refuses to work when first etcd-server is not available" (kubernetes#72102)](https://github.com/kubernetes/kubernetes/issues/72102).
|
||||
|
||||
`grpc.ErrClientConnClosing` has been [deprecated in gRPC >= 1.10](https://github.com/grpc/grpc-go/pull/1854).
|
||||
|
||||
```diff
|
||||
import (
|
||||
+ "go.etcd.io/etcd/clientv3"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
+ "google.golang.org/grpc/codes"
|
||||
+ "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
_, err := kvc.Get(ctx, "a")
|
||||
-if err == grpc.ErrClientConnClosing {
|
||||
+if clientv3.IsConnCanceled(err) {
|
||||
|
||||
// or
|
||||
+s, ok := status.FromError(err)
|
||||
+if ok {
|
||||
+ if s.Code() == codes.Canceled
|
||||
```
|
||||
|
||||
[The new client balancer](https://github.com/etcd-io/etcd/blob/master/Documentation/learning/design-client.md) uses an asynchronous resolver to pass endpoints to the gRPC dial function. As a result, [v3.3.14](https://github.com/etcd-io/etcd/releases/tag/v3.3.14) or later requires `grpc.WithBlock` dial option to wait until the underlying connection is up.
|
||||
|
||||
```diff
|
||||
import (
|
||||
"time"
|
||||
"go.etcd.io/etcd/clientv3"
|
||||
+ "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
+// "grpc.WithBlock()" to block until the underlying connection is up
|
||||
ccfg := clientv3.Config{
|
||||
Endpoints: []string{"localhost:2379"},
|
||||
DialTimeout: time.Second,
|
||||
+ DialOptions: []grpc.DialOption{grpc.WithBlock()},
|
||||
DialKeepAliveTime: time.Second,
|
||||
DialKeepAliveTimeout: 500 * time.Millisecond,
|
||||
}
|
||||
```
|
||||
|
||||
Please see [CHANGELOG](https://github.com/etcd-io/etcd/blob/master/CHANGELOG-3.3.md) for a full list of changes.
|
||||
|
||||
### Server upgrade checklists
|
||||
|
||||
#### Upgrade requirements
|
||||
|
|
|
@ -35,6 +35,21 @@ OK
|
|||
+etcdctl put foo bar
|
||||
```
|
||||
|
||||
#### Make `etcd --enable-v2=false` default
|
||||
|
||||
[`etcd --enable-v2=false`](https://github.com/etcd-io/etcd/pull/10935) is now the default.
|
||||
|
||||
This means, unless `etcd --enable-v2=true` is specified, etcd v3.4 server would not serve v2 API requests.
|
||||
|
||||
If v2 API were used, make sure to enable v2 API in v3.4:
|
||||
|
||||
```diff
|
||||
-etcd
|
||||
+etcd --enable-v2=true
|
||||
```
|
||||
|
||||
Other HTTP APIs will still work (e.g. `[CLIENT-URL]/metrics`, `[CLIENT-URL]/health`, v3 gRPC gateway).
|
||||
|
||||
#### Deprecated `etcd --ca-file` and `etcd --peer-ca-file` flags
|
||||
|
||||
`--ca-file` and `--peer-ca-file` flags are deprecated; they have been deprecated since v2.1.
|
||||
|
@ -49,6 +64,50 @@ OK
|
|||
+etcd --peer-trusted-ca-file ca-peer.crt
|
||||
```
|
||||
|
||||
#### Deprecated `grpc.ErrClientConnClosing` error
|
||||
|
||||
`grpc.ErrClientConnClosing` has been [deprecated in gRPC >= 1.10](https://github.com/grpc/grpc-go/pull/1854).
|
||||
|
||||
```diff
|
||||
import (
|
||||
+ "go.etcd.io/etcd/clientv3"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
+ "google.golang.org/grpc/codes"
|
||||
+ "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
_, err := kvc.Get(ctx, "a")
|
||||
-if err == grpc.ErrClientConnClosing {
|
||||
+if clientv3.IsConnCanceled(err) {
|
||||
|
||||
// or
|
||||
+s, ok := status.FromError(err)
|
||||
+if ok {
|
||||
+ if s.Code() == codes.Canceled
|
||||
```
|
||||
|
||||
#### Require `grpc.WithBlock` for client dial
|
||||
|
||||
[The new client balancer](https://github.com/etcd-io/etcd/blob/master/Documentation/learning/design-client.md) uses an asynchronous resolver to pass endpoints to the gRPC dial function. As a result, v3.4 client requires `grpc.WithBlock` dial option to wait until the underlying connection is up.
|
||||
|
||||
```diff
|
||||
import (
|
||||
"time"
|
||||
"go.etcd.io/etcd/clientv3"
|
||||
+ "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
+// "grpc.WithBlock()" to block until the underlying connection is up
|
||||
ccfg := clientv3.Config{
|
||||
Endpoints: []string{"localhost:2379"},
|
||||
DialTimeout: time.Second,
|
||||
+ DialOptions: []grpc.DialOption{grpc.WithBlock()},
|
||||
DialKeepAliveTime: time.Second,
|
||||
DialKeepAliveTimeout: 500 * time.Millisecond,
|
||||
}
|
||||
```
|
||||
|
||||
#### Deprecating `etcd_debugging_mvcc_db_total_size_in_bytes` Prometheus metrics
|
||||
|
||||
v3.4 promotes `etcd_debugging_mvcc_db_total_size_in_bytes` Prometheus metrics to `etcd_mvcc_db_total_size_in_bytes`, in order to encourage etcd storage monitoring.
|
||||
|
|
|
@ -53,7 +53,6 @@ All releases version numbers follow the format of [semantic versioning 2.0.0](ht
|
|||
|
||||
## Build Release Binaries and Images
|
||||
|
||||
- Ensure `acbuild` is available.
|
||||
- Ensure `docker` is available.
|
||||
|
||||
Run release script in root directory:
|
||||
|
@ -74,8 +73,6 @@ The following commands are used for public release sign:
|
|||
cd release
|
||||
# personal GPG is okay for now
|
||||
for i in etcd-*{.zip,.tar.gz}; do gpg --sign ${i}; done
|
||||
# use `CoreOS ACI Builder <release@coreos.com>` secret key
|
||||
for aci in etcd-${VERSION}.*.aci; do gpg -u 88182190 -a --output ${aci}.asc --detach-sig ${aci}; done
|
||||
```
|
||||
|
||||
## Publish Release Page in GitHub
|
||||
|
|
10
Makefile
10
Makefile
|
@ -51,7 +51,7 @@ docker-remove:
|
|||
|
||||
|
||||
|
||||
GO_VERSION ?= 1.12.7
|
||||
GO_VERSION ?= 1.19.9
|
||||
ETCD_VERSION ?= $(shell git rev-parse --short HEAD || echo "GitNotFound")
|
||||
|
||||
TEST_SUFFIX = $(shell date +%s | base64 | head -c 15)
|
||||
|
@ -65,11 +65,11 @@ endif
|
|||
|
||||
|
||||
# Example:
|
||||
# GO_VERSION=1.10.7 make build-docker-test
|
||||
# GO_VERSION=1.12.17 make build-docker-test
|
||||
# make build-docker-test
|
||||
#
|
||||
# gcloud docker -- login -u _json_key -p "$(cat /etc/gcp-key-etcd-development.json)" https://gcr.io
|
||||
# GO_VERSION=1.10.7 make push-docker-test
|
||||
# GO_VERSION=1.12.17 make push-docker-test
|
||||
# make push-docker-test
|
||||
#
|
||||
# gsutil -m acl ch -u allUsers:R -r gs://artifacts.etcd-development.appspot.com
|
||||
|
@ -104,7 +104,7 @@ compile-with-docker-test:
|
|||
--rm \
|
||||
--mount type=bind,source=`pwd`,destination=/go/src/go.etcd.io/etcd \
|
||||
gcr.io/etcd-development/etcd-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "GO_BUILD_FLAGS=-v ./build && ./bin/etcd --version"
|
||||
/bin/bash -c "GO_BUILD_FLAGS=-v GOOS=linux GOARCH=amd64 ./build && ./bin/etcd --version"
|
||||
|
||||
compile-setup-gopath-with-docker-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
|
@ -112,7 +112,7 @@ compile-setup-gopath-with-docker-test:
|
|||
--rm \
|
||||
--mount type=bind,source=`pwd`,destination=/etcd \
|
||||
gcr.io/etcd-development/etcd-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && ETCD_SETUP_GOPATH=1 GO_BUILD_FLAGS=-v ./build && ./bin/etcd --version && rm -rf ./gopath"
|
||||
/bin/bash -c "cd /etcd && ETCD_SETUP_GOPATH=1 GO_BUILD_FLAGS=-v GOOS=linux GOARCH=amd64 ./build && ./bin/etcd --version && rm -rf ./gopath"
|
||||
|
||||
|
||||
|
||||
|
|
21
ROADMAP.md
21
ROADMAP.md
|
@ -1,21 +0,0 @@
|
|||
# etcd roadmap
|
||||
|
||||
**work in progress**
|
||||
|
||||
This document defines a high level roadmap for etcd development.
|
||||
|
||||
The dates below should not be considered authoritative, but rather indicative of the projected timeline of the project. The [milestones defined in GitHub](https://github.com/etcd-io/etcd/milestones) represent the most up-to-date and issue-for-issue plans.
|
||||
|
||||
etcd 3.3 is our current stable branch. The roadmap below outlines new features that will be added to etcd, and while subject to change, define what future stable will look like.
|
||||
|
||||
### etcd 3.4 (2019)
|
||||
|
||||
- Stabilization of 3.3 experimental features
|
||||
- Support/document downgrade
|
||||
- Snapshot restore as Go library
|
||||
- Improved client balancer with new gRPC balancer interface
|
||||
- Improve single-client put performance
|
||||
- Improve large response handling
|
||||
- Improve test coverage
|
||||
- Decrease test runtime
|
||||
- Migrate to Go module for dependency management
|
|
@ -2,16 +2,18 @@
|
|||
// source: auth.proto
|
||||
|
||||
/*
|
||||
Package authpb is a generated protocol buffer package.
|
||||
Package authpb is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
auth.proto
|
||||
It is generated from these files:
|
||||
|
||||
It has these top-level messages:
|
||||
UserAddOptions
|
||||
User
|
||||
Permission
|
||||
Role
|
||||
auth.proto
|
||||
|
||||
It has these top-level messages:
|
||||
|
||||
UserAddOptions
|
||||
User
|
||||
Permission
|
||||
Role
|
||||
*/
|
||||
package authpb
|
||||
|
||||
|
|
25
auth/jwt.go
25
auth/jwt.go
|
@ -21,7 +21,7 @@ import (
|
|||
"errors"
|
||||
"time"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -42,7 +42,7 @@ func (t *tokenJWT) info(ctx context.Context, token string, rev uint64) (*AuthInf
|
|||
// rev isn't used in JWT, it is only used in simple token
|
||||
var (
|
||||
username string
|
||||
revision uint64
|
||||
revision float64
|
||||
)
|
||||
|
||||
parsed, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
|
||||
|
@ -82,10 +82,19 @@ func (t *tokenJWT) info(ctx context.Context, token string, rev uint64) (*AuthInf
|
|||
return nil, false
|
||||
}
|
||||
|
||||
username = claims["username"].(string)
|
||||
revision = uint64(claims["revision"].(float64))
|
||||
username, ok = claims["username"].(string)
|
||||
if !ok {
|
||||
t.lg.Warn("failed to obtain user claims from jwt token")
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &AuthInfo{Username: username, Revision: revision}, true
|
||||
revision, ok = claims["revision"].(float64)
|
||||
if !ok {
|
||||
t.lg.Warn("failed to obtain revision claims from jwt token")
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &AuthInfo{Username: username, Revision: uint64(revision)}, true
|
||||
}
|
||||
|
||||
func (t *tokenJWT) assign(ctx context.Context, username string, revision uint64) (string, error) {
|
||||
|
@ -105,7 +114,7 @@ func (t *tokenJWT) assign(ctx context.Context, username string, revision uint64)
|
|||
token, err := tk.SignedString(t.key)
|
||||
if err != nil {
|
||||
if t.lg != nil {
|
||||
t.lg.Warn(
|
||||
t.lg.Debug(
|
||||
"failed to sign a JWT token",
|
||||
zap.String("user-name", username),
|
||||
zap.Uint64("revision", revision),
|
||||
|
@ -118,7 +127,7 @@ func (t *tokenJWT) assign(ctx context.Context, username string, revision uint64)
|
|||
}
|
||||
|
||||
if t.lg != nil {
|
||||
t.lg.Info(
|
||||
t.lg.Debug(
|
||||
"created/assigned a new JWT token",
|
||||
zap.String("user-name", username),
|
||||
zap.Uint64("revision", revision),
|
||||
|
@ -136,7 +145,7 @@ func newTokenProviderJWT(lg *zap.Logger, optMap map[string]string) (*tokenJWT, e
|
|||
err = opts.ParseWithDefaults(optMap)
|
||||
if err != nil {
|
||||
if lg != nil {
|
||||
lg.Warn("problem loading JWT options", zap.Error(err))
|
||||
lg.Error("problem loading JWT options", zap.Error(err))
|
||||
} else {
|
||||
plog.Errorf("problem loading JWT options: %s", err)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,10 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -202,3 +205,75 @@ func TestJWTBad(t *testing.T) {
|
|||
func testJWTOpts() string {
|
||||
return fmt.Sprintf("%s,pub-key=%s,priv-key=%s,sign-method=RS256", tokenTypeJWT, jwtRSAPubKey, jwtRSAPrivKey)
|
||||
}
|
||||
|
||||
func TestJWTTokenWithMissingFields(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
username string // An empty string means not present
|
||||
revision uint64 // 0 means not present
|
||||
expectValid bool
|
||||
}{
|
||||
{
|
||||
name: "valid token",
|
||||
username: "hello",
|
||||
revision: 100,
|
||||
expectValid: true,
|
||||
},
|
||||
{
|
||||
name: "no username",
|
||||
username: "",
|
||||
revision: 100,
|
||||
expectValid: false,
|
||||
},
|
||||
{
|
||||
name: "no revision",
|
||||
username: "hello",
|
||||
revision: 0,
|
||||
expectValid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
optsMap := map[string]string{
|
||||
"priv-key": jwtRSAPrivKey,
|
||||
"sign-method": "RS256",
|
||||
"ttl": "1h",
|
||||
}
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// prepare claims
|
||||
claims := jwt.MapClaims{
|
||||
"exp": time.Now().Add(time.Hour).Unix(),
|
||||
}
|
||||
if tc.username != "" {
|
||||
claims["username"] = tc.username
|
||||
}
|
||||
if tc.revision != 0 {
|
||||
claims["revision"] = tc.revision
|
||||
}
|
||||
|
||||
// generate a JWT token with the given claims
|
||||
var opts jwtOptions
|
||||
err := opts.ParseWithDefaults(optsMap)
|
||||
require.NoError(t, err)
|
||||
key, err := opts.Key()
|
||||
require.NoError(t, err)
|
||||
|
||||
tk := jwt.NewWithClaims(opts.SignMethod, claims)
|
||||
token, err := tk.SignedString(key)
|
||||
require.NoError(t, err)
|
||||
|
||||
// verify the token
|
||||
jwtProvider, err := newTokenProviderJWT(zap.NewNop(), optsMap)
|
||||
require.NoError(t, err)
|
||||
ai, ok := jwtProvider.info(context.TODO(), token, 123)
|
||||
|
||||
require.Equal(t, tc.expectValid, ok)
|
||||
if ok {
|
||||
require.Equal(t, tc.username, ai.Username)
|
||||
require.Equal(t, tc.revision, ai.Revision)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
currentAuthRevision = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
|
||||
Namespace: "etcd_debugging",
|
||||
Subsystem: "auth",
|
||||
Name: "revision",
|
||||
Help: "The current revision of auth store.",
|
||||
},
|
||||
func() float64 {
|
||||
reportCurrentAuthRevMu.RLock()
|
||||
defer reportCurrentAuthRevMu.RUnlock()
|
||||
return reportCurrentAuthRev()
|
||||
},
|
||||
)
|
||||
// overridden by auth store initialization
|
||||
reportCurrentAuthRevMu sync.RWMutex
|
||||
reportCurrentAuthRev = func() float64 { return 0 }
|
||||
)
|
||||
|
||||
func init() {
|
||||
prometheus.MustRegister(currentAuthRevision)
|
||||
}
|
|
@ -21,7 +21,7 @@ import (
|
|||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -76,8 +76,10 @@ func checkKeyInterval(
|
|||
cachedPerms *unifiedRangePermissions,
|
||||
key, rangeEnd []byte,
|
||||
permtyp authpb.Permission_Type) bool {
|
||||
if len(rangeEnd) == 1 && rangeEnd[0] == 0 {
|
||||
if isOpenEnded(rangeEnd) {
|
||||
rangeEnd = nil
|
||||
// nil rangeEnd will be converetd to []byte{}, the largest element of BytesAffineComparable,
|
||||
// in NewBytesAffineInterval().
|
||||
}
|
||||
|
||||
ivl := adt.NewBytesAffineInterval(key, rangeEnd)
|
||||
|
@ -113,41 +115,99 @@ func checkKeyPoint(lg *zap.Logger, cachedPerms *unifiedRangePermissions, key []b
|
|||
return false
|
||||
}
|
||||
|
||||
func (as *authStore) isRangeOpPermitted(tx backend.BatchTx, userName string, key, rangeEnd []byte, permtyp authpb.Permission_Type) bool {
|
||||
// assumption: tx is Lock()ed
|
||||
_, ok := as.rangePermCache[userName]
|
||||
func (as *authStore) isRangeOpPermitted(userName string, key, rangeEnd []byte, permtyp authpb.Permission_Type) bool {
|
||||
as.rangePermCacheMu.RLock()
|
||||
defer as.rangePermCacheMu.RUnlock()
|
||||
|
||||
rangePerm, ok := as.rangePermCache[userName]
|
||||
if !ok {
|
||||
perms := getMergedPerms(as.lg, tx, userName)
|
||||
if perms == nil {
|
||||
if as.lg != nil {
|
||||
as.lg.Warn(
|
||||
"failed to create a merged permission",
|
||||
zap.String("user-name", userName),
|
||||
)
|
||||
} else {
|
||||
plog.Errorf("failed to create a unified permission of user %s", userName)
|
||||
}
|
||||
return false
|
||||
}
|
||||
as.rangePermCache[userName] = perms
|
||||
as.lg.Error(
|
||||
"user doesn't exist",
|
||||
zap.String("user-name", userName),
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
if len(rangeEnd) == 0 {
|
||||
return checkKeyPoint(as.lg, as.rangePermCache[userName], key, permtyp)
|
||||
return checkKeyPoint(as.lg, rangePerm, key, permtyp)
|
||||
}
|
||||
|
||||
return checkKeyInterval(as.lg, as.rangePermCache[userName], key, rangeEnd, permtyp)
|
||||
return checkKeyInterval(as.lg, rangePerm, key, rangeEnd, permtyp)
|
||||
}
|
||||
|
||||
func (as *authStore) clearCachedPerm() {
|
||||
func (as *authStore) refreshRangePermCache(tx backend.BatchTx) {
|
||||
// Note that every authentication configuration update calls this method and it invalidates the entire
|
||||
// rangePermCache and reconstruct it based on information of users and roles stored in the backend.
|
||||
// This can be a costly operation.
|
||||
as.rangePermCacheMu.Lock()
|
||||
defer as.rangePermCacheMu.Unlock()
|
||||
|
||||
as.rangePermCache = make(map[string]*unifiedRangePermissions)
|
||||
}
|
||||
|
||||
func (as *authStore) invalidateCachedPerm(userName string) {
|
||||
delete(as.rangePermCache, userName)
|
||||
users := getAllUsers(as.lg, tx)
|
||||
for _, user := range users {
|
||||
userName := string(user.Name)
|
||||
perms := getMergedPerms(as.lg, tx, userName)
|
||||
if perms == nil {
|
||||
as.lg.Error(
|
||||
"failed to create a merged permission",
|
||||
zap.String("user-name", userName),
|
||||
)
|
||||
continue
|
||||
}
|
||||
as.rangePermCache[userName] = perms
|
||||
}
|
||||
}
|
||||
|
||||
type unifiedRangePermissions struct {
|
||||
readPerms adt.IntervalTree
|
||||
writePerms adt.IntervalTree
|
||||
}
|
||||
|
||||
// Constraints related to key range
|
||||
// Assumptions:
|
||||
// a1. key must be non-nil
|
||||
// a2. []byte{} (in the case of string, "") is not a valid key of etcd
|
||||
// For representing an open-ended range, BytesAffineComparable uses []byte{} as the largest element.
|
||||
// a3. []byte{0x00} is the minimum valid etcd key
|
||||
//
|
||||
// Based on the above assumptions, key and rangeEnd must follow below rules:
|
||||
// b1. for representing a single key point, rangeEnd should be nil or zero length byte array (in the case of string, "")
|
||||
// Rule a2 guarantees that (X, []byte{}) for any X is not a valid range. So such ranges can be used for representing
|
||||
// a single key permission.
|
||||
//
|
||||
// b2. key range with upper limit, like (X, Y), larger or equal to X and smaller than Y
|
||||
//
|
||||
// b3. key range with open-ended, like (X, <open ended>), is represented like (X, []byte{0x00})
|
||||
// Because of rule a3, if we have (X, []byte{0x00}), such a range represents an empty range and makes no sense to have
|
||||
// such a permission. So we use []byte{0x00} for representing an open-ended permission.
|
||||
// Note that rangeEnd with []byte{0x00} will be converted into []byte{} before inserted into the interval tree
|
||||
// (rule a2 ensures that this is the largest element).
|
||||
// Special range like key = []byte{0x00} and rangeEnd = []byte{0x00} is treated as a range which matches with all keys.
|
||||
//
|
||||
// Treating a range whose rangeEnd with []byte{0x00} as an open-ended comes from the rules of Range() and Watch() API.
|
||||
|
||||
func isOpenEnded(rangeEnd []byte) bool { // check rule b3
|
||||
return len(rangeEnd) == 1 && rangeEnd[0] == 0
|
||||
}
|
||||
|
||||
func isValidPermissionRange(key, rangeEnd []byte) bool {
|
||||
if len(key) == 0 {
|
||||
return false
|
||||
}
|
||||
if rangeEnd == nil || len(rangeEnd) == 0 { // ensure rule b1
|
||||
return true
|
||||
}
|
||||
|
||||
begin := adt.BytesAffineComparable(key)
|
||||
end := adt.BytesAffineComparable(rangeEnd)
|
||||
if begin.Compare(end) == -1 { // rule b2
|
||||
return true
|
||||
}
|
||||
|
||||
if isOpenEnded(rangeEnd) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -45,6 +45,26 @@ func TestRangePermission(t *testing.T) {
|
|||
[]byte("a"), []byte("f"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte("a"), []byte("d")), adt.NewBytesAffineInterval([]byte("a"), []byte("b")), adt.NewBytesAffineInterval([]byte("c"), []byte("f"))},
|
||||
[]byte("a"), []byte{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte("a"), []byte{})},
|
||||
[]byte("a"), []byte{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte{0x00}, []byte{})},
|
||||
[]byte("a"), []byte{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte{0x00}, []byte{})},
|
||||
[]byte{0x00}, []byte{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
|
@ -86,6 +106,16 @@ func TestKeyPermission(t *testing.T) {
|
|||
[]byte("f"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte("a"), []byte("d")), adt.NewBytesAffineInterval([]byte("a"), []byte("b")), adt.NewBytesAffineInterval([]byte("c"), []byte{})},
|
||||
[]byte("f"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte("a"), []byte("d")), adt.NewBytesAffineInterval([]byte("a"), []byte("b")), adt.NewBytesAffineInterval([]byte{0x00}, []byte{})},
|
||||
[]byte("f"),
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
|
@ -100,3 +130,88 @@ func TestKeyPermission(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRangeCheck(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key []byte
|
||||
rangeEnd []byte
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "valid single key",
|
||||
key: []byte("a"),
|
||||
rangeEnd: []byte(""),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid single key",
|
||||
key: []byte("a"),
|
||||
rangeEnd: nil,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid key range, key < rangeEnd",
|
||||
key: []byte("a"),
|
||||
rangeEnd: []byte("b"),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "invalid empty key range, key == rangeEnd",
|
||||
key: []byte("a"),
|
||||
rangeEnd: []byte("a"),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid empty key range, key > rangeEnd",
|
||||
key: []byte("b"),
|
||||
rangeEnd: []byte("a"),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid key, key must not be \"\"",
|
||||
key: []byte(""),
|
||||
rangeEnd: []byte("a"),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid key range, key must not be \"\"",
|
||||
key: []byte(""),
|
||||
rangeEnd: []byte(""),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid key range, key must not be \"\"",
|
||||
key: []byte(""),
|
||||
rangeEnd: []byte("\x00"),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "valid single key (not useful in practice)",
|
||||
key: []byte("\x00"),
|
||||
rangeEnd: []byte(""),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid key range, larger or equals to \"a\"",
|
||||
key: []byte("a"),
|
||||
rangeEnd: []byte("\x00"),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid key range, which includes all keys",
|
||||
key: []byte("\x00"),
|
||||
rangeEnd: []byte("\x00"),
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := isValidPermissionRange(tt.key, tt.rangeEnd)
|
||||
if result != tt.want {
|
||||
t.Errorf("#%d: result=%t, want=%t", i, result, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ const (
|
|||
|
||||
// var for testing purposes
|
||||
var (
|
||||
simpleTokenTTL = 5 * time.Minute
|
||||
simpleTokenTTLDefault = 300 * time.Second
|
||||
simpleTokenTTLResolution = 1 * time.Second
|
||||
)
|
||||
|
||||
|
@ -47,6 +47,7 @@ type simpleTokenTTLKeeper struct {
|
|||
stopc chan struct{}
|
||||
deleteTokenFunc func(string)
|
||||
mu *sync.Mutex
|
||||
simpleTokenTTL time.Duration
|
||||
}
|
||||
|
||||
func (tm *simpleTokenTTLKeeper) stop() {
|
||||
|
@ -58,12 +59,12 @@ func (tm *simpleTokenTTLKeeper) stop() {
|
|||
}
|
||||
|
||||
func (tm *simpleTokenTTLKeeper) addSimpleToken(token string) {
|
||||
tm.tokens[token] = time.Now().Add(simpleTokenTTL)
|
||||
tm.tokens[token] = time.Now().Add(tm.simpleTokenTTL)
|
||||
}
|
||||
|
||||
func (tm *simpleTokenTTLKeeper) resetSimpleToken(token string) {
|
||||
if _, ok := tm.tokens[token]; ok {
|
||||
tm.tokens[token] = time.Now().Add(simpleTokenTTL)
|
||||
tm.tokens[token] = time.Now().Add(tm.simpleTokenTTL)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,6 +102,7 @@ type tokenSimple struct {
|
|||
simpleTokenKeeper *simpleTokenTTLKeeper
|
||||
simpleTokensMu sync.Mutex
|
||||
simpleTokens map[string]string // token -> username
|
||||
simpleTokenTTL time.Duration
|
||||
}
|
||||
|
||||
func (t *tokenSimple) genTokenPrefix() (string, error) {
|
||||
|
@ -157,6 +159,15 @@ func (t *tokenSimple) invalidateUser(username string) {
|
|||
}
|
||||
|
||||
func (t *tokenSimple) enable() {
|
||||
t.simpleTokensMu.Lock()
|
||||
defer t.simpleTokensMu.Unlock()
|
||||
if t.simpleTokenKeeper != nil { // already enabled
|
||||
return
|
||||
}
|
||||
if t.simpleTokenTTL <= 0 {
|
||||
t.simpleTokenTTL = simpleTokenTTLDefault
|
||||
}
|
||||
|
||||
delf := func(tk string) {
|
||||
if username, ok := t.simpleTokens[tk]; ok {
|
||||
if t.lg != nil {
|
||||
|
@ -177,6 +188,7 @@ func (t *tokenSimple) enable() {
|
|||
stopc: make(chan struct{}),
|
||||
deleteTokenFunc: delf,
|
||||
mu: &t.simpleTokensMu,
|
||||
simpleTokenTTL: t.simpleTokenTTL,
|
||||
}
|
||||
go t.simpleTokenKeeper.run()
|
||||
}
|
||||
|
@ -234,10 +246,14 @@ func (t *tokenSimple) isValidSimpleToken(ctx context.Context, token string) bool
|
|||
return false
|
||||
}
|
||||
|
||||
func newTokenProviderSimple(lg *zap.Logger, indexWaiter func(uint64) <-chan struct{}) *tokenSimple {
|
||||
func newTokenProviderSimple(lg *zap.Logger, indexWaiter func(uint64) <-chan struct{}, TokenTTL time.Duration) *tokenSimple {
|
||||
if lg == nil {
|
||||
lg = zap.NewNop()
|
||||
}
|
||||
return &tokenSimple{
|
||||
lg: lg,
|
||||
simpleTokens: make(map[string]string),
|
||||
indexWaiter: indexWaiter,
|
||||
lg: lg,
|
||||
simpleTokens: make(map[string]string),
|
||||
indexWaiter: indexWaiter,
|
||||
simpleTokenTTL: TokenTTL,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ import (
|
|||
// TestSimpleTokenDisabled ensures that TokenProviderSimple behaves correctly when
|
||||
// disabled.
|
||||
func TestSimpleTokenDisabled(t *testing.T) {
|
||||
initialState := newTokenProviderSimple(zap.NewExample(), dummyIndexWaiter)
|
||||
initialState := newTokenProviderSimple(zap.NewExample(), dummyIndexWaiter, simpleTokenTTLDefault)
|
||||
|
||||
explicitlyDisabled := newTokenProviderSimple(zap.NewExample(), dummyIndexWaiter)
|
||||
explicitlyDisabled := newTokenProviderSimple(zap.NewExample(), dummyIndexWaiter, simpleTokenTTLDefault)
|
||||
explicitlyDisabled.enable()
|
||||
explicitlyDisabled.disable()
|
||||
|
||||
|
@ -48,7 +48,7 @@ func TestSimpleTokenDisabled(t *testing.T) {
|
|||
// TestSimpleTokenAssign ensures that TokenProviderSimple can correctly assign a
|
||||
// token, look it up with info, and invalidate it by user.
|
||||
func TestSimpleTokenAssign(t *testing.T) {
|
||||
tp := newTokenProviderSimple(zap.NewExample(), dummyIndexWaiter)
|
||||
tp := newTokenProviderSimple(zap.NewExample(), dummyIndexWaiter, simpleTokenTTLDefault)
|
||||
tp.enable()
|
||||
ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
|
||||
token, err := tp.assign(ctx, "user1", 0)
|
||||
|
|
167
auth/store.go
167
auth/store.go
|
@ -23,6 +23,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go.etcd.io/etcd/auth/authpb"
|
||||
"go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
|
@ -58,7 +59,9 @@ var (
|
|||
ErrRoleAlreadyExist = errors.New("auth: role already exists")
|
||||
ErrRoleNotFound = errors.New("auth: role not found")
|
||||
ErrRoleEmpty = errors.New("auth: role name is empty")
|
||||
ErrPermissionNotGiven = errors.New("auth: permission not given")
|
||||
ErrAuthFailed = errors.New("auth: authentication failed, invalid user ID or password")
|
||||
ErrNoPasswordUser = errors.New("auth: authentication failed, password was given for no password user")
|
||||
ErrPermissionDenied = errors.New("auth: permission denied")
|
||||
ErrRoleNotGranted = errors.New("auth: role is not granted to the user")
|
||||
ErrPermissionNotGranted = errors.New("auth: permission is not granted to the role")
|
||||
|
@ -94,6 +97,9 @@ type AuthenticateParamIndex struct{}
|
|||
// AuthenticateParamSimpleTokenPrefix is used for a key of context in the parameters of Authenticate()
|
||||
type AuthenticateParamSimpleTokenPrefix struct{}
|
||||
|
||||
// saveConsistentIndexFunc is used to sync consistentIndex to backend, now reusing store.saveIndex
|
||||
type saveConsistentIndexFunc func(tx backend.BatchTx)
|
||||
|
||||
// AuthStore defines auth storage interface.
|
||||
type AuthStore interface {
|
||||
// AuthEnable turns on the authentication feature
|
||||
|
@ -186,6 +192,9 @@ type AuthStore interface {
|
|||
|
||||
// HasRole checks that user has role
|
||||
HasRole(user, role string) bool
|
||||
|
||||
// SetConsistentIndexSyncer sets consistentIndex syncer
|
||||
SetConsistentIndexSyncer(syncer saveConsistentIndexFunc)
|
||||
}
|
||||
|
||||
type TokenProvider interface {
|
||||
|
@ -207,12 +216,23 @@ type authStore struct {
|
|||
enabled bool
|
||||
enabledMu sync.RWMutex
|
||||
|
||||
rangePermCache map[string]*unifiedRangePermissions // username -> unifiedRangePermissions
|
||||
// rangePermCache needs to be protected by rangePermCacheMu
|
||||
// rangePermCacheMu needs to be write locked only in initialization phase or configuration changes
|
||||
// Hot paths like Range(), needs to acquire read lock for improving performance
|
||||
//
|
||||
// Note that BatchTx and ReadTx cannot be a mutex for rangePermCache because they are independent resources
|
||||
// see also: https://github.com/etcd-io/etcd/pull/13920#discussion_r849114855
|
||||
rangePermCache map[string]*unifiedRangePermissions // username -> unifiedRangePermissions
|
||||
rangePermCacheMu sync.RWMutex
|
||||
|
||||
tokenProvider TokenProvider
|
||||
bcryptCost int // the algorithm cost / strength for hashing auth passwords
|
||||
tokenProvider TokenProvider
|
||||
syncConsistentIndex saveConsistentIndexFunc
|
||||
bcryptCost int // the algorithm cost / strength for hashing auth passwords
|
||||
}
|
||||
|
||||
func (as *authStore) SetConsistentIndexSyncer(syncer saveConsistentIndexFunc) {
|
||||
as.syncConsistentIndex = syncer
|
||||
}
|
||||
func (as *authStore) AuthEnable() error {
|
||||
as.enabledMu.Lock()
|
||||
defer as.enabledMu.Unlock()
|
||||
|
@ -246,7 +266,7 @@ func (as *authStore) AuthEnable() error {
|
|||
as.enabled = true
|
||||
as.tokenProvider.enable()
|
||||
|
||||
as.rangePermCache = make(map[string]*unifiedRangePermissions)
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
as.setRevision(getRevision(tx))
|
||||
|
||||
|
@ -269,6 +289,7 @@ func (as *authStore) AuthDisable() {
|
|||
tx.Lock()
|
||||
tx.UnsafePut(authBucketName, enableFlagKey, authDisabled)
|
||||
as.commitRevision(tx)
|
||||
as.saveConsistentIndex(tx)
|
||||
tx.Unlock()
|
||||
b.ForceCommit()
|
||||
|
||||
|
@ -306,7 +327,7 @@ func (as *authStore) Authenticate(ctx context.Context, username, password string
|
|||
return nil, ErrAuthFailed
|
||||
}
|
||||
|
||||
if user.Options.NoPassword {
|
||||
if user.Options != nil && user.Options.NoPassword {
|
||||
return nil, ErrAuthFailed
|
||||
}
|
||||
|
||||
|
@ -335,17 +356,27 @@ func (as *authStore) CheckPassword(username, password string) (uint64, error) {
|
|||
return 0, ErrAuthNotEnabled
|
||||
}
|
||||
|
||||
tx := as.be.BatchTx()
|
||||
tx.Lock()
|
||||
defer tx.Unlock()
|
||||
var user *authpb.User
|
||||
// CompareHashAndPassword is very expensive, so we use closures
|
||||
// to avoid putting it in the critical section of the tx lock.
|
||||
revision, err := func() (uint64, error) {
|
||||
tx := as.be.BatchTx()
|
||||
tx.Lock()
|
||||
defer tx.Unlock()
|
||||
|
||||
user := getUser(as.lg, tx, username)
|
||||
if user == nil {
|
||||
return 0, ErrAuthFailed
|
||||
}
|
||||
user = getUser(as.lg, tx, username)
|
||||
if user == nil {
|
||||
return 0, ErrAuthFailed
|
||||
}
|
||||
|
||||
if user.Options.NoPassword {
|
||||
return 0, ErrAuthFailed
|
||||
if user.Options != nil && user.Options.NoPassword {
|
||||
return 0, ErrNoPasswordUser
|
||||
}
|
||||
|
||||
return getRevision(tx), nil
|
||||
}()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if bcrypt.CompareHashAndPassword(user.Password, []byte(password)) != nil {
|
||||
|
@ -356,7 +387,7 @@ func (as *authStore) CheckPassword(username, password string) (uint64, error) {
|
|||
}
|
||||
return 0, ErrAuthFailed
|
||||
}
|
||||
return getRevision(tx), nil
|
||||
return revision, nil
|
||||
}
|
||||
|
||||
func (as *authStore) Recover(be backend.Backend) {
|
||||
|
@ -372,11 +403,15 @@ func (as *authStore) Recover(be backend.Backend) {
|
|||
}
|
||||
|
||||
as.setRevision(getRevision(tx))
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
tx.Unlock()
|
||||
|
||||
as.enabledMu.Lock()
|
||||
as.enabled = enabled
|
||||
if enabled {
|
||||
as.tokenProvider.enable()
|
||||
}
|
||||
as.enabledMu.Unlock()
|
||||
}
|
||||
|
||||
|
@ -388,7 +423,8 @@ func (as *authStore) UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse,
|
|||
var hashed []byte
|
||||
var err error
|
||||
|
||||
if !r.Options.NoPassword {
|
||||
noPassword := r.Options != nil && r.Options.NoPassword
|
||||
if !noPassword {
|
||||
hashed, err = bcrypt.GenerateFromPassword([]byte(r.Password), as.bcryptCost)
|
||||
if err != nil {
|
||||
if as.lg != nil {
|
||||
|
@ -429,6 +465,8 @@ func (as *authStore) UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse,
|
|||
putUser(as.lg, tx, newUser)
|
||||
|
||||
as.commitRevision(tx)
|
||||
as.saveConsistentIndex(tx)
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
if as.lg != nil {
|
||||
as.lg.Info("added a user", zap.String("user-name", r.Name))
|
||||
|
@ -460,8 +498,9 @@ func (as *authStore) UserDelete(r *pb.AuthUserDeleteRequest) (*pb.AuthUserDelete
|
|||
delUser(tx, r.Name)
|
||||
|
||||
as.commitRevision(tx)
|
||||
as.saveConsistentIndex(tx)
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
as.invalidateCachedPerm(r.Name)
|
||||
as.tokenProvider.invalidateUser(r.Name)
|
||||
|
||||
if as.lg != nil {
|
||||
|
@ -512,8 +551,9 @@ func (as *authStore) UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*p
|
|||
putUser(as.lg, tx, updatedUser)
|
||||
|
||||
as.commitRevision(tx)
|
||||
as.saveConsistentIndex(tx)
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
as.invalidateCachedPerm(r.Name)
|
||||
as.tokenProvider.invalidateUser(r.Name)
|
||||
|
||||
if as.lg != nil {
|
||||
|
@ -565,9 +605,9 @@ func (as *authStore) UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUser
|
|||
|
||||
putUser(as.lg, tx, user)
|
||||
|
||||
as.invalidateCachedPerm(r.User)
|
||||
|
||||
as.commitRevision(tx)
|
||||
as.saveConsistentIndex(tx)
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
if as.lg != nil {
|
||||
as.lg.Info(
|
||||
|
@ -651,9 +691,9 @@ func (as *authStore) UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUs
|
|||
|
||||
putUser(as.lg, tx, updatedUser)
|
||||
|
||||
as.invalidateCachedPerm(r.Name)
|
||||
|
||||
as.commitRevision(tx)
|
||||
as.saveConsistentIndex(tx)
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
if as.lg != nil {
|
||||
as.lg.Info(
|
||||
|
@ -723,11 +763,9 @@ func (as *authStore) RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest)
|
|||
|
||||
putRole(as.lg, tx, updatedRole)
|
||||
|
||||
// TODO(mitake): currently single role update invalidates every cache
|
||||
// It should be optimized.
|
||||
as.clearCachedPerm()
|
||||
|
||||
as.commitRevision(tx)
|
||||
as.saveConsistentIndex(tx)
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
if as.lg != nil {
|
||||
as.lg.Info(
|
||||
|
@ -783,10 +821,11 @@ func (as *authStore) RoleDelete(r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDelete
|
|||
|
||||
putUser(as.lg, tx, updatedUser)
|
||||
|
||||
as.invalidateCachedPerm(string(user.Name))
|
||||
}
|
||||
|
||||
as.commitRevision(tx)
|
||||
as.saveConsistentIndex(tx)
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
if as.lg != nil {
|
||||
as.lg.Info("deleted a role", zap.String("role-name", r.Role))
|
||||
|
@ -817,6 +856,7 @@ func (as *authStore) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse,
|
|||
putRole(as.lg, tx, newRole)
|
||||
|
||||
as.commitRevision(tx)
|
||||
as.saveConsistentIndex(tx)
|
||||
|
||||
if as.lg != nil {
|
||||
as.lg.Info("created a role", zap.String("role-name", r.Name))
|
||||
|
@ -845,6 +885,13 @@ func (perms permSlice) Swap(i, j int) {
|
|||
}
|
||||
|
||||
func (as *authStore) RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error) {
|
||||
if r.Perm == nil {
|
||||
return nil, ErrPermissionNotGiven
|
||||
}
|
||||
if !isValidPermissionRange(r.Perm.Key, r.Perm.RangeEnd) {
|
||||
return nil, ErrInvalidAuthMgmt
|
||||
}
|
||||
|
||||
tx := as.be.BatchTx()
|
||||
tx.Lock()
|
||||
defer tx.Unlock()
|
||||
|
@ -875,11 +922,9 @@ func (as *authStore) RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (
|
|||
|
||||
putRole(as.lg, tx, role)
|
||||
|
||||
// TODO(mitake): currently single role update invalidates every cache
|
||||
// It should be optimized.
|
||||
as.clearCachedPerm()
|
||||
|
||||
as.commitRevision(tx)
|
||||
as.saveConsistentIndex(tx)
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
if as.lg != nil {
|
||||
as.lg.Info(
|
||||
|
@ -903,8 +948,21 @@ func (as *authStore) isOpPermitted(userName string, revision uint64, key, rangeE
|
|||
if revision == 0 {
|
||||
return ErrUserEmpty
|
||||
}
|
||||
|
||||
if revision < as.Revision() {
|
||||
rev := as.Revision()
|
||||
if revision < rev {
|
||||
if as.lg != nil {
|
||||
as.lg.Warn("request auth revision is less than current node auth revision",
|
||||
zap.Uint64("current node auth revision", rev),
|
||||
zap.Uint64("request auth revision", revision),
|
||||
zap.ByteString("request key", key),
|
||||
zap.Error(ErrAuthOldRevision))
|
||||
} else {
|
||||
plog.Warningf("request auth revision is less than current node auth revision,"+
|
||||
"current node auth revision is %d,"+
|
||||
"request auth revision is %d,"+
|
||||
"request key is %s, "+
|
||||
"err is %v", rev, revision, key, ErrAuthOldRevision)
|
||||
}
|
||||
return ErrAuthOldRevision
|
||||
}
|
||||
|
||||
|
@ -927,7 +985,7 @@ func (as *authStore) isOpPermitted(userName string, revision uint64, key, rangeE
|
|||
return nil
|
||||
}
|
||||
|
||||
if as.isRangeOpPermitted(tx, userName, key, rangeEnd, permTyp) {
|
||||
if as.isRangeOpPermitted(userName, key, rangeEnd, permTyp) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -950,7 +1008,7 @@ func (as *authStore) IsAdminPermitted(authInfo *AuthInfo) error {
|
|||
if !as.IsAuthEnabled() {
|
||||
return nil
|
||||
}
|
||||
if authInfo == nil {
|
||||
if authInfo == nil || authInfo.Username == "" {
|
||||
return ErrUserEmpty
|
||||
}
|
||||
|
||||
|
@ -993,7 +1051,15 @@ func getUser(lg *zap.Logger, tx backend.BatchTx, username string) *authpb.User {
|
|||
}
|
||||
|
||||
func getAllUsers(lg *zap.Logger, tx backend.BatchTx) []*authpb.User {
|
||||
_, vs := tx.UnsafeRange(authUsersBucketName, []byte{0}, []byte{0xff}, -1)
|
||||
var vs [][]byte
|
||||
err := tx.UnsafeForEach(authUsersBucketName, func(k []byte, v []byte) error {
|
||||
vs = append(vs, v)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
lg.Panic("failed to get users",
|
||||
zap.Error(err))
|
||||
}
|
||||
if len(vs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
@ -1144,6 +1210,10 @@ func NewAuthStore(lg *zap.Logger, be backend.Backend, tp TokenProvider, bcryptCo
|
|||
as.commitRevision(tx)
|
||||
}
|
||||
|
||||
as.setupMetricsReporter()
|
||||
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
tx.Unlock()
|
||||
be.ForceCommit()
|
||||
|
||||
|
@ -1305,7 +1375,8 @@ func decomposeOpts(lg *zap.Logger, optstr string) (string, map[string]string, er
|
|||
func NewTokenProvider(
|
||||
lg *zap.Logger,
|
||||
tokenOpts string,
|
||||
indexWaiter func(uint64) <-chan struct{}) (TokenProvider, error) {
|
||||
indexWaiter func(uint64) <-chan struct{},
|
||||
TokenTTL time.Duration) (TokenProvider, error) {
|
||||
tokenType, typeSpecificOpts, err := decomposeOpts(lg, tokenOpts)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidAuthOpts
|
||||
|
@ -1318,7 +1389,7 @@ func NewTokenProvider(
|
|||
} else {
|
||||
plog.Warningf("simple token is not cryptographically signed")
|
||||
}
|
||||
return newTokenProviderSimple(lg, indexWaiter), nil
|
||||
return newTokenProviderSimple(lg, indexWaiter, TokenTTL), nil
|
||||
|
||||
case tokenTypeJWT:
|
||||
return newTokenProviderJWT(lg, typeSpecificOpts)
|
||||
|
@ -1418,3 +1489,23 @@ func (as *authStore) HasRole(user, role string) bool {
|
|||
func (as *authStore) BcryptCost() int {
|
||||
return as.bcryptCost
|
||||
}
|
||||
|
||||
func (as *authStore) saveConsistentIndex(tx backend.BatchTx) {
|
||||
if as.syncConsistentIndex != nil {
|
||||
as.syncConsistentIndex(tx)
|
||||
} else {
|
||||
if as.lg != nil {
|
||||
as.lg.Error("failed to save consistentIndex,syncConsistentIndex is nil")
|
||||
} else {
|
||||
plog.Error("failed to save consistentIndex,syncConsistentIndex is nil")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (as *authStore) setupMetricsReporter() {
|
||||
reportCurrentAuthRevMu.Lock()
|
||||
reportCurrentAuthRev = func() float64 {
|
||||
return float64(as.Revision())
|
||||
}
|
||||
reportCurrentAuthRevMu.Unlock()
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ package auth
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
|
@ -28,6 +29,7 @@ import (
|
|||
"go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
pb "go.etcd.io/etcd/etcdserver/etcdserverpb"
|
||||
"go.etcd.io/etcd/mvcc/backend"
|
||||
"go.etcd.io/etcd/pkg/adt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
@ -48,7 +50,7 @@ func TestNewAuthStoreRevision(t *testing.T) {
|
|||
b, tPath := backend.NewDefaultTmpBackend()
|
||||
defer os.Remove(tPath)
|
||||
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -78,7 +80,7 @@ func TestNewAuthStoreBcryptCost(t *testing.T) {
|
|||
b, tPath := backend.NewDefaultTmpBackend()
|
||||
defer os.Remove(tPath)
|
||||
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -98,7 +100,7 @@ func TestNewAuthStoreBcryptCost(t *testing.T) {
|
|||
func setupAuthStore(t *testing.T) (store *authStore, teardownfunc func(t *testing.T)) {
|
||||
b, tPath := backend.NewDefaultTmpBackend()
|
||||
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -151,7 +153,8 @@ func TestUserAdd(t *testing.T) {
|
|||
as, tearDown := setupAuthStore(t)
|
||||
defer tearDown(t)
|
||||
|
||||
ua := &pb.AuthUserAddRequest{Name: "foo", Options: &authpb.UserAddOptions{NoPassword: false}}
|
||||
const userName = "foo"
|
||||
ua := &pb.AuthUserAddRequest{Name: userName, Options: &authpb.UserAddOptions{NoPassword: false}}
|
||||
_, err := as.UserAdd(ua) // add an existing user
|
||||
if err == nil {
|
||||
t.Fatalf("expected %v, got %v", ErrUserAlreadyExist, err)
|
||||
|
@ -165,6 +168,11 @@ func TestUserAdd(t *testing.T) {
|
|||
if err != ErrUserEmpty {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, ok := as.rangePermCache[userName]; !ok {
|
||||
t.Fatalf("user %s should be added but it doesn't exist in rangePermCache", userName)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecover(t *testing.T) {
|
||||
|
@ -179,6 +187,30 @@ func TestRecover(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRecoverWithEmptyRangePermCache(t *testing.T) {
|
||||
as, tearDown := setupAuthStore(t)
|
||||
defer as.Close()
|
||||
defer tearDown(t)
|
||||
|
||||
as.enabled = false
|
||||
as.rangePermCache = map[string]*unifiedRangePermissions{}
|
||||
as.Recover(as.be)
|
||||
|
||||
if !as.IsAuthEnabled() {
|
||||
t.Fatalf("expected auth enabled got disabled")
|
||||
}
|
||||
|
||||
if len(as.rangePermCache) != 2 {
|
||||
t.Fatalf("rangePermCache should have permission information for 2 users (\"root\" and \"foo\"), but has %d information", len(as.rangePermCache))
|
||||
}
|
||||
if _, ok := as.rangePermCache["root"]; !ok {
|
||||
t.Fatal("user \"root\" should be created by setupAuthStore() but doesn't exist in rangePermCache")
|
||||
}
|
||||
if _, ok := as.rangePermCache["foo"]; !ok {
|
||||
t.Fatal("user \"foo\" should be created by setupAuthStore() but doesn't exist in rangePermCache")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckPassword(t *testing.T) {
|
||||
as, tearDown := setupAuthStore(t)
|
||||
defer tearDown(t)
|
||||
|
@ -213,7 +245,8 @@ func TestUserDelete(t *testing.T) {
|
|||
defer tearDown(t)
|
||||
|
||||
// delete an existing user
|
||||
ud := &pb.AuthUserDeleteRequest{Name: "foo"}
|
||||
const userName = "foo"
|
||||
ud := &pb.AuthUserDeleteRequest{Name: userName}
|
||||
_, err := as.UserDelete(ud)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -227,6 +260,47 @@ func TestUserDelete(t *testing.T) {
|
|||
if err != ErrUserNotFound {
|
||||
t.Fatalf("expected %v, got %v", ErrUserNotFound, err)
|
||||
}
|
||||
|
||||
if _, ok := as.rangePermCache[userName]; ok {
|
||||
t.Fatalf("user %s should be deleted but it exists in rangePermCache", userName)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserDeleteAndPermCache(t *testing.T) {
|
||||
as, tearDown := setupAuthStore(t)
|
||||
defer tearDown(t)
|
||||
|
||||
// delete an existing user
|
||||
const deletedUserName = "foo"
|
||||
ud := &pb.AuthUserDeleteRequest{Name: deletedUserName}
|
||||
_, err := as.UserDelete(ud)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// delete a non-existing user
|
||||
_, err = as.UserDelete(ud)
|
||||
if err != ErrUserNotFound {
|
||||
t.Fatalf("expected %v, got %v", ErrUserNotFound, err)
|
||||
}
|
||||
|
||||
if _, ok := as.rangePermCache[deletedUserName]; ok {
|
||||
t.Fatalf("user %s should be deleted but it exists in rangePermCache", deletedUserName)
|
||||
}
|
||||
|
||||
// add a new user
|
||||
const newUser = "bar"
|
||||
ua := &pb.AuthUserAddRequest{Name: newUser, Options: &authpb.UserAddOptions{NoPassword: false}}
|
||||
_, err = as.UserAdd(ua)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, ok := as.rangePermCache[newUser]; !ok {
|
||||
t.Fatalf("user %s should exist but it doesn't exist in rangePermCache", deletedUserName)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserChangePassword(t *testing.T) {
|
||||
|
@ -445,6 +519,162 @@ func TestRoleGrantPermission(t *testing.T) {
|
|||
if !reflect.DeepEqual(perm, r.Perm[0]) {
|
||||
t.Errorf("expected %v, got %v", perm, r.Perm[0])
|
||||
}
|
||||
|
||||
// trying to grant nil permissions returns an error (and doesn't change the actual permissions!)
|
||||
_, err = as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
|
||||
Name: "role-test-1",
|
||||
})
|
||||
|
||||
if err != ErrPermissionNotGiven {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
r, err = as.RoleGet(&pb.AuthRoleGetRequest{Role: "role-test-1"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(perm, r.Perm[0]) {
|
||||
t.Errorf("expected %v, got %v", perm, r.Perm[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleGrantInvalidPermission(t *testing.T) {
|
||||
as, tearDown := setupAuthStore(t)
|
||||
defer tearDown(t)
|
||||
|
||||
_, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test-1"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
perm *authpb.Permission
|
||||
want error
|
||||
}{
|
||||
{
|
||||
name: "valid range",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("Keys"),
|
||||
RangeEnd: []byte("RangeEnd"),
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid range: nil key",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: nil,
|
||||
RangeEnd: []byte("RangeEnd"),
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "valid range: single key",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("Keys"),
|
||||
RangeEnd: nil,
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid range: single key",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("Keys"),
|
||||
RangeEnd: []byte{},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid range: empty (Key == RangeEnd)",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("a"),
|
||||
RangeEnd: []byte("a"),
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "invalid range: empty (Key > RangeEnd)",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("b"),
|
||||
RangeEnd: []byte("a"),
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "invalid range: length of key is 0",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte(""),
|
||||
RangeEnd: []byte("a"),
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "invalid range: length of key is 0",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte(""),
|
||||
RangeEnd: []byte(""),
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "invalid range: length of key is 0",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte(""),
|
||||
RangeEnd: []byte{0x00},
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "valid range: single key permission for []byte{0x00}",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte{0x00},
|
||||
RangeEnd: []byte(""),
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid range: \"a\" or larger keys",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("a"),
|
||||
RangeEnd: []byte{0x00},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid range: the entire keys",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte{0x00},
|
||||
RangeEnd: []byte{0x00},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err = as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
|
||||
Name: "role-test-1",
|
||||
Perm: tt.perm,
|
||||
})
|
||||
|
||||
if !errors.Is(err, tt.want) {
|
||||
t.Errorf("#%d: result=%t, want=%t", i, err, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleRevokePermission(t *testing.T) {
|
||||
|
@ -503,17 +733,44 @@ func TestUserRevokePermission(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test"})
|
||||
const userName = "foo"
|
||||
_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: userName, Role: "role-test"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "role-test-1"})
|
||||
_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: userName, Role: "role-test-1"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
u, err := as.UserGet(&pb.AuthUserGetRequest{Name: "foo"})
|
||||
perm := &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("WriteKeyBegin"),
|
||||
RangeEnd: []byte("WriteKeyEnd"),
|
||||
}
|
||||
_, err = as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
|
||||
Name: "role-test-1",
|
||||
Perm: perm,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, ok := as.rangePermCache[userName]; !ok {
|
||||
t.Fatalf("User %s should have its entry in rangePermCache", userName)
|
||||
}
|
||||
unifiedPerm := as.rangePermCache[userName]
|
||||
pt1 := adt.NewBytesAffinePoint([]byte("WriteKeyBegin"))
|
||||
if !unifiedPerm.writePerms.Contains(pt1) {
|
||||
t.Fatal("rangePermCache should contain WriteKeyBegin")
|
||||
}
|
||||
pt2 := adt.NewBytesAffinePoint([]byte("OutOfRange"))
|
||||
if unifiedPerm.writePerms.Contains(pt2) {
|
||||
t.Fatal("rangePermCache should not contain OutOfRange")
|
||||
}
|
||||
|
||||
u, err := as.UserGet(&pb.AuthUserGetRequest{Name: userName})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -523,12 +780,12 @@ func TestUserRevokePermission(t *testing.T) {
|
|||
t.Fatalf("expected %v, got %v", expected, u.Roles)
|
||||
}
|
||||
|
||||
_, err = as.UserRevokeRole(&pb.AuthUserRevokeRoleRequest{Name: "foo", Role: "role-test-1"})
|
||||
_, err = as.UserRevokeRole(&pb.AuthUserRevokeRoleRequest{Name: userName, Role: "role-test-1"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
u, err = as.UserGet(&pb.AuthUserGetRequest{Name: "foo"})
|
||||
u, err = as.UserGet(&pb.AuthUserGetRequest{Name: userName})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -626,7 +883,7 @@ func TestAuthInfoFromCtxRace(t *testing.T) {
|
|||
b, tPath := backend.NewDefaultTmpBackend()
|
||||
defer os.Remove(tPath)
|
||||
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -658,6 +915,12 @@ func TestIsAdminPermitted(t *testing.T) {
|
|||
t.Errorf("expected %v, got %v", ErrUserNotFound, err)
|
||||
}
|
||||
|
||||
// empty user
|
||||
err = as.IsAdminPermitted(&AuthInfo{Username: "", Revision: 1})
|
||||
if err != ErrUserEmpty {
|
||||
t.Errorf("expected %v, got %v", ErrUserEmpty, err)
|
||||
}
|
||||
|
||||
// non-admin user
|
||||
err = as.IsAdminPermitted(&AuthInfo{Username: "foo", Revision: 1})
|
||||
if err != ErrPermissionDenied {
|
||||
|
@ -692,7 +955,7 @@ func TestRecoverFromSnapshot(t *testing.T) {
|
|||
|
||||
as.Close()
|
||||
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -725,13 +988,13 @@ func contains(array []string, str string) bool {
|
|||
|
||||
func TestHammerSimpleAuthenticate(t *testing.T) {
|
||||
// set TTL values low to try to trigger races
|
||||
oldTTL, oldTTLRes := simpleTokenTTL, simpleTokenTTLResolution
|
||||
oldTTL, oldTTLRes := simpleTokenTTLDefault, simpleTokenTTLResolution
|
||||
defer func() {
|
||||
simpleTokenTTL = oldTTL
|
||||
simpleTokenTTLDefault = oldTTL
|
||||
simpleTokenTTLResolution = oldTTLRes
|
||||
}()
|
||||
simpleTokenTTL = 10 * time.Millisecond
|
||||
simpleTokenTTLResolution = simpleTokenTTL
|
||||
simpleTokenTTLDefault = 10 * time.Millisecond
|
||||
simpleTokenTTLResolution = simpleTokenTTLDefault
|
||||
users := make(map[string]struct{})
|
||||
|
||||
as, tearDown := setupAuthStore(t)
|
||||
|
@ -774,7 +1037,7 @@ func TestRolesOrder(t *testing.T) {
|
|||
b, tPath := backend.NewDefaultTmpBackend()
|
||||
defer os.Remove(tPath)
|
||||
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter)
|
||||
tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -829,7 +1092,7 @@ func testAuthInfoFromCtxWithRoot(t *testing.T, opts string) {
|
|||
b, tPath := backend.NewDefaultTmpBackend()
|
||||
defer os.Remove(tPath)
|
||||
|
||||
tp, err := NewTokenProvider(zap.NewExample(), opts, dummyIndexWaiter)
|
||||
tp, err := NewTokenProvider(zap.NewExample(), opts, dummyIndexWaiter, simpleTokenTTLDefault)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -44,15 +44,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/dgrijalva/jwt-go",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT License",
|
||||
"confidence": 0.9891304347826086
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/dustin/go-humanize",
|
||||
"licenses": [
|
||||
|
@ -71,6 +62,15 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/golang-jwt/jwt",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT License",
|
||||
"confidence": 0.9891304347826086
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/golang/groupcache/lru",
|
||||
"licenses": [
|
||||
|
@ -378,7 +378,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"project": "golang.org/x/sys/unix",
|
||||
"project": "golang.org/x/sys",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "BSD 3-clause \"New\" or \"Revised\" License",
|
||||
|
|
22
build
22
build
|
@ -1,22 +1,24 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# set some environment variables
|
||||
ORG_PATH="go.etcd.io"
|
||||
REPO_PATH="${ORG_PATH}/etcd"
|
||||
|
||||
GIT_SHA=$(git rev-parse --short HEAD || echo "GitNotFound")
|
||||
if [[ -n "$FAILPOINTS" ]]; then
|
||||
if [[ -n "${FAILPOINTS:-}" ]]; then
|
||||
GIT_SHA="$GIT_SHA"-FAILPOINTS
|
||||
fi
|
||||
|
||||
# Set GO_LDFLAGS="-s" for building without symbols for debugging.
|
||||
GO_LDFLAGS="$GO_LDFLAGS -X ${REPO_PATH}/version.GitSHA=${GIT_SHA}"
|
||||
GO_LDFLAGS="${GO_LDFLAGS:-} -X ${REPO_PATH}/version.GitSHA=${GIT_SHA}"
|
||||
|
||||
# enable/disable failpoints
|
||||
toggle_failpoints() {
|
||||
mode="$1"
|
||||
if command -v gofail >/dev/null 2>&1; then
|
||||
gofail "$mode" etcdserver/ mvcc/backend/
|
||||
gofail "$mode" etcdserver/ mvcc/backend/ wal/
|
||||
elif [[ "$mode" != "disable" ]]; then
|
||||
echo "FAILPOINTS set but gofail not found"
|
||||
exit 1
|
||||
|
@ -30,7 +32,7 @@ etcd_setup_gopath() {
|
|||
cd "$CDIR" || return
|
||||
etcdGOPATH="${CDIR}/gopath"
|
||||
# preserve old gopath to support building with unvendored tooling deps (e.g., gofail)
|
||||
if [[ -n "$GOPATH" ]]; then
|
||||
if [[ -n "${GOPATH:-}" ]]; then
|
||||
GOPATH=":$GOPATH"
|
||||
fi
|
||||
rm -rf "${etcdGOPATH:?}/"
|
||||
|
@ -42,23 +44,23 @@ etcd_setup_gopath() {
|
|||
|
||||
toggle_failpoints_default() {
|
||||
mode="disable"
|
||||
if [[ -n "$FAILPOINTS" ]]; then mode="enable"; fi
|
||||
if [[ -n "${FAILPOINTS:-}" ]]; then mode="enable"; fi
|
||||
toggle_failpoints "$mode"
|
||||
}
|
||||
|
||||
etcd_build() {
|
||||
out="bin"
|
||||
if [[ -n "${BINDIR}" ]]; then out="${BINDIR}"; fi
|
||||
if [[ -n "${BINDIR:-}" ]]; then out="${BINDIR}"; fi
|
||||
toggle_failpoints_default
|
||||
|
||||
# Static compilation is useful when etcd is run in a container. $GO_BUILD_FLAGS is OK
|
||||
# shellcheck disable=SC2086
|
||||
CGO_ENABLED=0 go build $GO_BUILD_FLAGS \
|
||||
CGO_ENABLED=0 go build ${GO_BUILD_FLAGS:-} \
|
||||
-installsuffix cgo \
|
||||
-ldflags "$GO_LDFLAGS" \
|
||||
-o "${out}/etcd" ${REPO_PATH} || return
|
||||
# shellcheck disable=SC2086
|
||||
CGO_ENABLED=0 go build $GO_BUILD_FLAGS \
|
||||
CGO_ENABLED=0 go build ${GO_BUILD_FLAGS:-} \
|
||||
-installsuffix cgo \
|
||||
-ldflags "$GO_LDFLAGS" \
|
||||
-o "${out}/etcdctl" ${REPO_PATH}/etcdctl || return
|
||||
|
@ -66,7 +68,7 @@ etcd_build() {
|
|||
|
||||
tools_build() {
|
||||
out="bin"
|
||||
if [[ -n "${BINDIR}" ]]; then out="${BINDIR}"; fi
|
||||
if [[ -n "${BINDIR:-}" ]]; then out="${BINDIR}"; fi
|
||||
tools_path="tools/benchmark
|
||||
tools/etcd-dump-db
|
||||
tools/etcd-dump-logs
|
||||
|
@ -88,7 +90,7 @@ tools_build() {
|
|||
|
||||
toggle_failpoints_default
|
||||
|
||||
if [[ "${ETCD_SETUP_GOPATH}" == "1" ]]; then
|
||||
if [[ "${ETCD_SETUP_GOPATH:-}" == "1" ]]; then
|
||||
etcd_setup_gopath
|
||||
fi
|
||||
|
||||
|
|
|
@ -68,6 +68,5 @@ Use a custom context to set timeouts on your operations:
|
|||
// handle error
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
package client
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.etcd.io/etcd/clientv3/balancer/picker"
|
||||
"go.etcd.io/etcd/clientv3/balancer/resolver/endpoint"
|
||||
|
@ -92,24 +91,25 @@ func TestRoundRobinBalancedResolvableNoFailover(t *testing.T) {
|
|||
return picked, err
|
||||
}
|
||||
|
||||
prev, switches := "", 0
|
||||
_, picked, err := warmupConnections(reqFunc, tc.serverCount, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure %v", err)
|
||||
}
|
||||
|
||||
// verify that we round robin
|
||||
prev, switches := picked, 0
|
||||
for i := 0; i < tc.reqN; i++ {
|
||||
picked, err := reqFunc(context.Background())
|
||||
picked, err = reqFunc(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("#%d: unexpected failure %v", i, err)
|
||||
}
|
||||
if prev == "" {
|
||||
prev = picked
|
||||
continue
|
||||
}
|
||||
if prev != picked {
|
||||
switches++
|
||||
}
|
||||
prev = picked
|
||||
}
|
||||
if tc.serverCount > 1 && switches < tc.reqN-3 { // -3 for initial resolutions
|
||||
// TODO: FIX ME
|
||||
t.Skipf("expected balanced loads for %d requests, got switches %d", tc.reqN, switches)
|
||||
if tc.serverCount > 1 && switches != tc.reqN {
|
||||
t.Fatalf("expected balanced loads for %d requests, got switches %d", tc.reqN, switches)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -160,26 +160,21 @@ func TestRoundRobinBalancedResolvableFailoverFromServerFail(t *testing.T) {
|
|||
}
|
||||
|
||||
// stop first server, loads should be redistributed
|
||||
// stopped server should never be picked
|
||||
ms.StopAt(0)
|
||||
available := make(map[string]struct{})
|
||||
for i := 1; i < serverCount; i++ {
|
||||
available[eps[i]] = struct{}{}
|
||||
// stopped server will be transitioned into TRANSIENT_FAILURE state
|
||||
// but it doesn't happen instantaneously and it can still be picked for a short period of time
|
||||
// we ignore "transport is closing" in such case
|
||||
available, picked, err := warmupConnections(reqFunc, serverCount-1, "transport is closing")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure %v", err)
|
||||
}
|
||||
|
||||
reqN := 10
|
||||
prev, switches := "", 0
|
||||
prev, switches := picked, 0
|
||||
for i := 0; i < reqN; i++ {
|
||||
picked, err := reqFunc(context.Background())
|
||||
if err != nil && strings.Contains(err.Error(), "transport is closing") {
|
||||
continue
|
||||
}
|
||||
if prev == "" { // first failover
|
||||
if eps[0] == picked {
|
||||
t.Fatalf("expected failover from %q, picked %q", eps[0], picked)
|
||||
}
|
||||
prev = picked
|
||||
continue
|
||||
picked, err = reqFunc(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("#%d: unexpected failure %v", i, err)
|
||||
}
|
||||
if _, ok := available[picked]; !ok {
|
||||
t.Fatalf("picked unavailable address %q (available %v)", picked, available)
|
||||
|
@ -189,18 +184,18 @@ func TestRoundRobinBalancedResolvableFailoverFromServerFail(t *testing.T) {
|
|||
}
|
||||
prev = picked
|
||||
}
|
||||
if switches < reqN-3 { // -3 for initial resolutions + failover
|
||||
// TODO: FIX ME!
|
||||
t.Skipf("expected balanced loads for %d requests, got switches %d", reqN, switches)
|
||||
if switches != reqN {
|
||||
t.Fatalf("expected balanced loads for %d requests, got switches %d", reqN, switches)
|
||||
}
|
||||
|
||||
// now failed server comes back
|
||||
ms.StartAt(0)
|
||||
available, picked, err = warmupConnections(reqFunc, serverCount, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure %v", err)
|
||||
}
|
||||
|
||||
// enough time for reconnecting to recovered server
|
||||
time.Sleep(time.Second)
|
||||
|
||||
prev, switches = "", 0
|
||||
prev, switches = picked, 0
|
||||
recoveredAddr, recovered := eps[0], 0
|
||||
available[recoveredAddr] = struct{}{}
|
||||
|
||||
|
@ -209,10 +204,6 @@ func TestRoundRobinBalancedResolvableFailoverFromServerFail(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("#%d: unexpected failure %v", i, err)
|
||||
}
|
||||
if prev == "" {
|
||||
prev = picked
|
||||
continue
|
||||
}
|
||||
if _, ok := available[picked]; !ok {
|
||||
t.Fatalf("#%d: picked unavailable address %q (available %v)", i, picked, available)
|
||||
}
|
||||
|
@ -224,10 +215,10 @@ func TestRoundRobinBalancedResolvableFailoverFromServerFail(t *testing.T) {
|
|||
}
|
||||
prev = picked
|
||||
}
|
||||
if switches < reqN-3 { // -3 for initial resolutions
|
||||
if switches != 2*reqN {
|
||||
t.Fatalf("expected balanced loads for %d requests, got switches %d", reqN, switches)
|
||||
}
|
||||
if recovered < reqN/serverCount {
|
||||
if recovered != 2*reqN/serverCount {
|
||||
t.Fatalf("recovered server %q got only %d requests", recoveredAddr, recovered)
|
||||
}
|
||||
}
|
||||
|
@ -242,11 +233,10 @@ func TestRoundRobinBalancedResolvableFailoverFromRequestFail(t *testing.T) {
|
|||
}
|
||||
defer ms.Stop()
|
||||
var eps []string
|
||||
available := make(map[string]struct{})
|
||||
for _, svr := range ms.Servers {
|
||||
eps = append(eps, svr.ResolverAddress().Addr)
|
||||
available[svr.Address] = struct{}{}
|
||||
}
|
||||
|
||||
rsv, err := endpoint.NewResolverGroup("requestfail")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -277,6 +267,11 @@ func TestRoundRobinBalancedResolvableFailoverFromRequestFail(t *testing.T) {
|
|||
return picked, err
|
||||
}
|
||||
|
||||
available, picked, err := warmupConnections(reqFunc, serverCount, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure %v", err)
|
||||
}
|
||||
|
||||
reqN := 20
|
||||
prev, switches := "", 0
|
||||
for i := 0; i < reqN; i++ {
|
||||
|
@ -285,17 +280,13 @@ func TestRoundRobinBalancedResolvableFailoverFromRequestFail(t *testing.T) {
|
|||
if i%2 == 0 {
|
||||
cancel()
|
||||
}
|
||||
picked, err := reqFunc(ctx)
|
||||
picked, err = reqFunc(ctx)
|
||||
if i%2 == 0 {
|
||||
if s, ok := status.FromError(err); ok && s.Code() != codes.Canceled || picked != "" {
|
||||
if s, ok := status.FromError(err); ok && s.Code() != codes.Canceled {
|
||||
t.Fatalf("#%d: expected %v, got %v", i, context.Canceled, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if prev == "" && picked != "" {
|
||||
prev = picked
|
||||
continue
|
||||
}
|
||||
if _, ok := available[picked]; !ok {
|
||||
t.Fatalf("#%d: picked unavailable address %q (available %v)", i, picked, available)
|
||||
}
|
||||
|
@ -304,7 +295,29 @@ func TestRoundRobinBalancedResolvableFailoverFromRequestFail(t *testing.T) {
|
|||
}
|
||||
prev = picked
|
||||
}
|
||||
if switches < reqN/2-3 { // -3 for initial resolutions + failover
|
||||
if switches != reqN/2 {
|
||||
t.Fatalf("expected balanced loads for %d requests, got switches %d", reqN, switches)
|
||||
}
|
||||
}
|
||||
|
||||
type reqFuncT = func(ctx context.Context) (picked string, err error)
|
||||
|
||||
func warmupConnections(reqFunc reqFuncT, serverCount int, ignoreErr string) (map[string]struct{}, string, error) {
|
||||
var picked string
|
||||
var err error
|
||||
available := make(map[string]struct{})
|
||||
// cycle through all peers to indirectly verify that balancer subconn list is fully loaded
|
||||
// otherwise we can't reliably count switches between 'picked' peers in the test assert phase
|
||||
for len(available) < serverCount {
|
||||
picked, err = reqFunc(context.Background())
|
||||
if err != nil {
|
||||
if ignoreErr != "" && strings.Contains(err.Error(), ignoreErr) {
|
||||
// skip ignored errors
|
||||
continue
|
||||
}
|
||||
return available, picked, err
|
||||
}
|
||||
available[picked] = struct{}{}
|
||||
}
|
||||
return available, picked, err
|
||||
}
|
||||
|
|
|
@ -57,9 +57,9 @@ func (rc *recorder) GetCurrentState() (state connectivity.State) {
|
|||
// RecordTransition records state change happening in subConn and based on that
|
||||
// it evaluates what aggregated state should be.
|
||||
//
|
||||
// - If at least one SubConn in Ready, the aggregated state is Ready;
|
||||
// - Else if at least one SubConn in Connecting, the aggregated state is Connecting;
|
||||
// - Else the aggregated state is TransientFailure.
|
||||
// - If at least one SubConn in Ready, the aggregated state is Ready;
|
||||
// - Else if at least one SubConn in Connecting, the aggregated state is Connecting;
|
||||
// - Else the aggregated state is TransientFailure.
|
||||
//
|
||||
// Idle and Shutdown are not considered.
|
||||
//
|
||||
|
|
|
@ -34,6 +34,6 @@ func (ep *errPicker) String() string {
|
|||
return ep.p.String()
|
||||
}
|
||||
|
||||
func (ep *errPicker) Pick(context.Context, balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
||||
func (ep *errPicker) Pick(context.Context, balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
||||
return nil, nil, ep.err
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ type rrBalanced struct {
|
|||
func (rb *rrBalanced) String() string { return rb.p.String() }
|
||||
|
||||
// Pick is called for every client request.
|
||||
func (rb *rrBalanced) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
||||
func (rb *rrBalanced) Pick(ctx context.Context, opts balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
||||
rb.mu.RLock()
|
||||
n := len(rb.scs)
|
||||
rb.mu.RUnlock()
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
package endpoint
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -109,7 +111,7 @@ func (e *ResolverGroup) Close() {
|
|||
}
|
||||
|
||||
// Build creates or reuses an etcd resolver for the etcd cluster name identified by the authority part of the target.
|
||||
func (b *builder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
|
||||
func (b *builder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
|
||||
if len(target.Authority) < 1 {
|
||||
return nil, fmt.Errorf("'etcd' target scheme requires non-empty authority identifying etcd cluster being routed to")
|
||||
}
|
||||
|
@ -177,7 +179,7 @@ func epsToAddrs(eps ...string) (addrs []resolver.Address) {
|
|||
return addrs
|
||||
}
|
||||
|
||||
func (*Resolver) ResolveNow(o resolver.ResolveNowOption) {}
|
||||
func (*Resolver) ResolveNow(o resolver.ResolveNowOptions) {}
|
||||
|
||||
func (r *Resolver) Close() {
|
||||
es, err := bldr.getResolverGroup(r.endpointID)
|
||||
|
@ -228,13 +230,18 @@ func ParseTarget(target string) (string, string, error) {
|
|||
return parts[0], parts[1], nil
|
||||
}
|
||||
|
||||
// ParseHostPort splits a "<host>:<port>" string into the host and port parts.
|
||||
// The port part is optional.
|
||||
func ParseHostPort(hostPort string) (host string, port string) {
|
||||
parts := strings.SplitN(hostPort, ":", 2)
|
||||
host = parts[0]
|
||||
if len(parts) > 1 {
|
||||
port = parts[1]
|
||||
// Dialer dials a endpoint using net.Dialer.
|
||||
// Context cancelation and timeout are supported.
|
||||
func Dialer(ctx context.Context, dialEp string) (net.Conn, error) {
|
||||
proto, host, _ := ParseEndpoint(dialEp)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
return host, port
|
||||
dialer := &net.Dialer{}
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
dialer.Deadline = deadline
|
||||
}
|
||||
return dialer.DialContext(ctx, proto, host)
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ import (
|
|||
"google.golang.org/grpc/codes"
|
||||
grpccredentials "google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
|
@ -129,8 +128,12 @@ func NewFromURLs(urls []string) (*Client, error) {
|
|||
// Close shuts down the client's etcd connections.
|
||||
func (c *Client) Close() error {
|
||||
c.cancel()
|
||||
c.Watcher.Close()
|
||||
c.Lease.Close()
|
||||
if c.Watcher != nil {
|
||||
c.Watcher.Close()
|
||||
}
|
||||
if c.Lease != nil {
|
||||
c.Lease.Close()
|
||||
}
|
||||
if c.resolverGroup != nil {
|
||||
c.resolverGroup.Close()
|
||||
}
|
||||
|
@ -171,7 +174,9 @@ func (c *Client) Sync(ctx context.Context) error {
|
|||
}
|
||||
var eps []string
|
||||
for _, m := range mresp.Members {
|
||||
eps = append(eps, m.ClientURLs...)
|
||||
if len(m.Name) != 0 && !m.IsLearner {
|
||||
eps = append(eps, m.ClientURLs...)
|
||||
}
|
||||
}
|
||||
c.SetEndpoints(eps...)
|
||||
return nil
|
||||
|
@ -226,24 +231,17 @@ func (c *Client) dialSetupOpts(creds grpccredentials.TransportCredentials, dopts
|
|||
}
|
||||
opts = append(opts, dopts...)
|
||||
|
||||
// Provide a net dialer that supports cancelation and timeout.
|
||||
f := func(dialEp string, t time.Duration) (net.Conn, error) {
|
||||
proto, host, _ := endpoint.ParseEndpoint(dialEp)
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return nil, c.ctx.Err()
|
||||
default:
|
||||
}
|
||||
dialer := &net.Dialer{Timeout: t}
|
||||
return dialer.DialContext(c.ctx, proto, host)
|
||||
}
|
||||
opts = append(opts, grpc.WithDialer(f))
|
||||
|
||||
dialer := endpoint.Dialer
|
||||
if creds != nil {
|
||||
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||
// gRPC load balancer workaround. See credentials.transportCredential for details.
|
||||
if credsDialer, ok := creds.(TransportCredentialsWithDialer); ok {
|
||||
dialer = credsDialer.Dialer
|
||||
}
|
||||
} else {
|
||||
opts = append(opts, grpc.WithInsecure())
|
||||
}
|
||||
opts = append(opts, grpc.WithContextDialer(dialer))
|
||||
|
||||
// Interceptor retry and backoff.
|
||||
// TODO: Replace all of clientv3/retry.go with interceptor based retry, or with
|
||||
|
@ -262,7 +260,10 @@ func (c *Client) dialSetupOpts(creds grpccredentials.TransportCredentials, dopts
|
|||
|
||||
// Dial connects to a single endpoint using the client's config.
|
||||
func (c *Client) Dial(ep string) (*grpc.ClientConn, error) {
|
||||
creds := c.directDialCreds(ep)
|
||||
creds, err := c.directDialCreds(ep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Use the grpc passthrough resolver to directly dial a single endpoint.
|
||||
// This resolver passes through the 'unix' and 'unixs' endpoints schemes used
|
||||
// by etcd without modification, allowing us to directly dial endpoints and
|
||||
|
@ -274,8 +275,8 @@ func (c *Client) getToken(ctx context.Context) error {
|
|||
var err error // return last error in a case of fail
|
||||
var auth *authenticator
|
||||
|
||||
for i := 0; i < len(c.cfg.Endpoints); i++ {
|
||||
ep := c.cfg.Endpoints[i]
|
||||
eps := c.Endpoints()
|
||||
for _, ep := range eps {
|
||||
// use dial options without dopts to avoid reusing the client balancer
|
||||
var dOpts []grpc.DialOption
|
||||
_, host, _ := endpoint.ParseEndpoint(ep)
|
||||
|
@ -365,8 +366,8 @@ func (c *Client) dial(target string, creds grpccredentials.TransportCredentials,
|
|||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Client) directDialCreds(ep string) grpccredentials.TransportCredentials {
|
||||
_, hostPort, scheme := endpoint.ParseEndpoint(ep)
|
||||
func (c *Client) directDialCreds(ep string) (grpccredentials.TransportCredentials, error) {
|
||||
_, host, scheme := endpoint.ParseEndpoint(ep)
|
||||
creds := c.creds
|
||||
if len(scheme) != 0 {
|
||||
creds = c.processCreds(scheme)
|
||||
|
@ -375,12 +376,17 @@ func (c *Client) directDialCreds(ep string) grpccredentials.TransportCredentials
|
|||
// Set the server name must to the endpoint hostname without port since grpc
|
||||
// otherwise attempts to check if x509 cert is valid for the full endpoint
|
||||
// including the scheme and port, which fails.
|
||||
host, _ := endpoint.ParseHostPort(hostPort)
|
||||
clone.OverrideServerName(host)
|
||||
overrideServerName, _, err := net.SplitHostPort(host)
|
||||
if err != nil {
|
||||
// Either the host didn't have a port or the host could not be parsed. Either way, continue with the
|
||||
// original host string.
|
||||
overrideServerName = host
|
||||
}
|
||||
clone.OverrideServerName(overrideServerName)
|
||||
creds = clone
|
||||
}
|
||||
}
|
||||
return creds
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
func (c *Client) dialWithBalancerCreds(ep string) grpccredentials.TransportCredentials {
|
||||
|
@ -392,13 +398,6 @@ func (c *Client) dialWithBalancerCreds(ep string) grpccredentials.TransportCrede
|
|||
return creds
|
||||
}
|
||||
|
||||
// WithRequireLeader requires client requests to only succeed
|
||||
// when the cluster has a leader.
|
||||
func WithRequireLeader(ctx context.Context) context.Context {
|
||||
md := metadata.Pairs(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
|
||||
return metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
|
||||
func newClient(cfg *Config) (*Client, error) {
|
||||
if cfg == nil {
|
||||
cfg = &Config{}
|
||||
|
@ -519,13 +518,17 @@ func (c *Client) roundRobinQuorumBackoff(waitBetween time.Duration, jitterFracti
|
|||
|
||||
func (c *Client) checkVersion() (err error) {
|
||||
var wg sync.WaitGroup
|
||||
errc := make(chan error, len(c.cfg.Endpoints))
|
||||
|
||||
eps := c.Endpoints()
|
||||
errc := make(chan error, len(eps))
|
||||
ctx, cancel := context.WithCancel(c.ctx)
|
||||
if c.cfg.DialTimeout > 0 {
|
||||
ctx, cancel = context.WithTimeout(ctx, c.cfg.DialTimeout)
|
||||
cancel()
|
||||
ctx, cancel = context.WithTimeout(c.ctx, c.cfg.DialTimeout)
|
||||
}
|
||||
wg.Add(len(c.cfg.Endpoints))
|
||||
for _, ep := range c.cfg.Endpoints {
|
||||
|
||||
wg.Add(len(eps))
|
||||
for _, ep := range eps {
|
||||
// if cluster is current, any endpoint gives a recent version
|
||||
go func(e string) {
|
||||
defer wg.Done()
|
||||
|
@ -537,8 +540,15 @@ func (c *Client) checkVersion() (err error) {
|
|||
vs := strings.Split(resp.Version, ".")
|
||||
maj, min := 0, 0
|
||||
if len(vs) >= 2 {
|
||||
maj, _ = strconv.Atoi(vs[0])
|
||||
min, rerr = strconv.Atoi(vs[1])
|
||||
var serr error
|
||||
if maj, serr = strconv.Atoi(vs[0]); serr != nil {
|
||||
errc <- serr
|
||||
return
|
||||
}
|
||||
if min, serr = strconv.Atoi(vs[1]); serr != nil {
|
||||
errc <- serr
|
||||
return
|
||||
}
|
||||
}
|
||||
if maj < 3 || (maj == 3 && min < 2) {
|
||||
rerr = ErrOldCluster
|
||||
|
@ -547,7 +557,7 @@ func (c *Client) checkVersion() (err error) {
|
|||
}(ep)
|
||||
}
|
||||
// wait for success
|
||||
for i := 0; i < len(c.cfg.Endpoints); i++ {
|
||||
for range eps {
|
||||
if err = <-errc; err == nil {
|
||||
break
|
||||
}
|
||||
|
@ -587,10 +597,13 @@ func isUnavailableErr(ctx context.Context, err error) bool {
|
|||
if err == nil {
|
||||
return false
|
||||
}
|
||||
ev, _ := status.FromError(err)
|
||||
// Unavailable codes mean the system will be right back.
|
||||
// (e.g., can't connect, lost leader)
|
||||
return ev.Code() == codes.Unavailable
|
||||
ev, ok := status.FromError(err)
|
||||
if ok {
|
||||
// Unavailable codes mean the system will be right back.
|
||||
// (e.g., can't connect, lost leader)
|
||||
return ev.Code() == codes.Unavailable
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func toErr(ctx context.Context, err error) error {
|
||||
|
@ -610,9 +623,6 @@ func toErr(ctx context.Context, err error) error {
|
|||
if ctx.Err() != nil {
|
||||
err = ctx.Err()
|
||||
}
|
||||
case codes.Unavailable:
|
||||
case codes.FailedPrecondition:
|
||||
err = grpc.ErrClientConnClosing
|
||||
}
|
||||
}
|
||||
return err
|
||||
|
@ -632,16 +642,25 @@ func IsConnCanceled(err error) bool {
|
|||
if err == nil {
|
||||
return false
|
||||
}
|
||||
// >= gRPC v1.10.x
|
||||
|
||||
// >= gRPC v1.23.x
|
||||
s, ok := status.FromError(err)
|
||||
if ok {
|
||||
// connection is canceled or server has already closed the connection
|
||||
return s.Code() == codes.Canceled || s.Message() == "transport is closing"
|
||||
}
|
||||
|
||||
// >= gRPC v1.10.x
|
||||
if err == context.Canceled {
|
||||
return true
|
||||
}
|
||||
|
||||
// <= gRPC v1.7.x returns 'errors.New("grpc: the client connection is closing")'
|
||||
return strings.Contains(err.Error(), "grpc: the client connection is closing")
|
||||
}
|
||||
|
||||
// TransportCredentialsWithDialer is for a gRPC load balancer workaround. See credentials.transportCredential for details.
|
||||
type TransportCredentialsWithDialer interface {
|
||||
grpccredentials.TransportCredentials
|
||||
Dialer(ctx context.Context, dialEp string) (net.Conn, error)
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"time"
|
||||
|
||||
"go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
"go.etcd.io/etcd/etcdserver/etcdserverpb"
|
||||
"go.etcd.io/etcd/pkg/testutil"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
@ -156,3 +157,61 @@ func TestIsHaltErr(t *testing.T) {
|
|||
t.Errorf("cancel on context should be Halted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloseCtxClient(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := NewCtxClient(ctx)
|
||||
err := c.Close()
|
||||
// Close returns ctx.toErr, a nil error means an open Done channel
|
||||
if err == nil {
|
||||
t.Errorf("failed to Close the client. %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncFiltersMembers(t *testing.T) {
|
||||
defer testutil.AfterTest(t)
|
||||
|
||||
c, _ := New(Config{Endpoints: []string{"http://254.0.0.1:12345"}})
|
||||
c.Cluster = &mockCluster{
|
||||
[]*etcdserverpb.Member{
|
||||
{ID: 0, Name: "", ClientURLs: []string{"http://254.0.0.1:12345"}, IsLearner: false},
|
||||
{ID: 1, Name: "isStarted", ClientURLs: []string{"http://254.0.0.2:12345"}, IsLearner: true},
|
||||
{ID: 2, Name: "isStartedAndNotLearner", ClientURLs: []string{"http://254.0.0.3:12345"}, IsLearner: false},
|
||||
},
|
||||
}
|
||||
c.Sync(context.Background())
|
||||
|
||||
endpoints := c.Endpoints()
|
||||
if len(endpoints) != 1 || endpoints[0] != "http://254.0.0.3:12345" {
|
||||
t.Error("Client.Sync uses learner and/or non-started member client URLs")
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
|
||||
type mockCluster struct {
|
||||
members []*etcdserverpb.Member
|
||||
}
|
||||
|
||||
func (mc *mockCluster) MemberList(ctx context.Context) (*MemberListResponse, error) {
|
||||
return &MemberListResponse{Members: mc.members}, nil
|
||||
}
|
||||
|
||||
func (mc *mockCluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (mc *mockCluster) MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (mc *mockCluster) MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (mc *mockCluster) MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (mc *mockCluster) MemberPromote(ctx context.Context, id uint64) (*MemberPromoteResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -65,22 +65,18 @@ func TestResumeElection(t *testing.T) {
|
|||
|
||||
respChan := make(chan *clientv3.GetResponse)
|
||||
go func() {
|
||||
defer close(respChan)
|
||||
o := e.Observe(ctx)
|
||||
respChan <- nil
|
||||
for {
|
||||
select {
|
||||
case resp, ok := <-o:
|
||||
if !ok {
|
||||
t.Fatal("Observe() channel closed prematurely")
|
||||
}
|
||||
// Ignore any observations that candidate1 was elected
|
||||
if string(resp.Kvs[0].Value) == "candidate1" {
|
||||
continue
|
||||
}
|
||||
respChan <- &resp
|
||||
return
|
||||
for resp := range o {
|
||||
// Ignore any observations that candidate1 was elected
|
||||
if string(resp.Kvs[0].Value) == "candidate1" {
|
||||
continue
|
||||
}
|
||||
respChan <- &resp
|
||||
return
|
||||
}
|
||||
t.Error("Observe() channel closed prematurely")
|
||||
}()
|
||||
|
||||
// wait until observe goroutine is running
|
||||
|
|
|
@ -68,6 +68,8 @@ type Config struct {
|
|||
RejectOldCluster bool `json:"reject-old-cluster"`
|
||||
|
||||
// DialOptions is a list of dial options for the grpc client (e.g., for interceptors).
|
||||
// For example, pass "grpc.WithBlock()" to block until the underlying connection is up.
|
||||
// Without this, Dial returns immediately and connecting the server happens in background.
|
||||
DialOptions []grpc.DialOption
|
||||
|
||||
// Context is the default client context; it can be used to cancel grpc dial out and
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"net"
|
||||
"sync"
|
||||
|
||||
"go.etcd.io/etcd/clientv3/balancer/resolver/endpoint"
|
||||
"go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
grpccredentials "google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
@ -65,38 +66,37 @@ func (b *bundle) NewWithMode(mode string) (grpccredentials.Bundle, error) {
|
|||
}
|
||||
|
||||
// transportCredential implements "grpccredentials.TransportCredentials" interface.
|
||||
// transportCredential wraps TransportCredentials to track which
|
||||
// addresses are dialed for which endpoints, and then sets the authority when checking the endpoint's cert to the
|
||||
// hostname or IP of the dialed endpoint.
|
||||
// This is a workaround of a gRPC load balancer issue. gRPC uses the dialed target's service name as the authority when
|
||||
// checking all endpoint certs, which does not work for etcd servers using their hostname or IP as the Subject Alternative Name
|
||||
// in their TLS certs.
|
||||
// To enable, include both WithTransportCredentials(creds) and WithContextDialer(creds.Dialer)
|
||||
// when dialing.
|
||||
type transportCredential struct {
|
||||
gtc grpccredentials.TransportCredentials
|
||||
mu sync.Mutex
|
||||
// addrToEndpoint maps from the connection addresses that are dialed to the hostname or IP of the
|
||||
// endpoint provided to the dialer when dialing
|
||||
addrToEndpoint map[string]string
|
||||
}
|
||||
|
||||
func newTransportCredential(cfg *tls.Config) *transportCredential {
|
||||
return &transportCredential{
|
||||
gtc: grpccredentials.NewTLS(cfg),
|
||||
gtc: grpccredentials.NewTLS(cfg),
|
||||
addrToEndpoint: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *transportCredential) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, grpccredentials.AuthInfo, error) {
|
||||
// Only overwrite when authority is an IP address!
|
||||
// Let's say, a server runs SRV records on "etcd.local" that resolves
|
||||
// to "m1.etcd.local", and its SAN field also includes "m1.etcd.local".
|
||||
// But what if SAN does not include its resolved IP address (e.g. 127.0.0.1)?
|
||||
// Then, the server should only authenticate using its DNS hostname "m1.etcd.local",
|
||||
// instead of overwriting it with its IP address.
|
||||
// And we do not overwrite "localhost" either. Only overwrite IP addresses!
|
||||
if isIP(authority) {
|
||||
target := rawConn.RemoteAddr().String()
|
||||
if authority != target {
|
||||
// When user dials with "grpc.WithDialer", "grpc.DialContext" "cc.parsedTarget"
|
||||
// update only happens once. This is problematic, because when TLS is enabled,
|
||||
// retries happen through "grpc.WithDialer" with static "cc.parsedTarget" from
|
||||
// the initial dial call.
|
||||
// If the server authenticates by IP addresses, we want to set a new endpoint as
|
||||
// a new authority. Otherwise
|
||||
// "transport: authentication handshake failed: x509: certificate is valid for 127.0.0.1, 192.168.121.180, not 192.168.223.156"
|
||||
// when the new dial target is "192.168.121.180" whose certificate host name is also "192.168.121.180"
|
||||
// but client tries to authenticate with previously set "cc.parsedTarget" field "192.168.223.156"
|
||||
authority = target
|
||||
}
|
||||
// Set the authority when checking the endpoint's cert to the hostname or IP of the dialed endpoint
|
||||
tc.mu.Lock()
|
||||
dialEp, ok := tc.addrToEndpoint[rawConn.RemoteAddr().String()]
|
||||
tc.mu.Unlock()
|
||||
if ok {
|
||||
_, host, _ := endpoint.ParseEndpoint(dialEp)
|
||||
authority = host
|
||||
}
|
||||
return tc.gtc.ClientHandshake(ctx, authority, rawConn)
|
||||
}
|
||||
|
@ -115,8 +115,15 @@ func (tc *transportCredential) Info() grpccredentials.ProtocolInfo {
|
|||
}
|
||||
|
||||
func (tc *transportCredential) Clone() grpccredentials.TransportCredentials {
|
||||
copy := map[string]string{}
|
||||
tc.mu.Lock()
|
||||
for k, v := range tc.addrToEndpoint {
|
||||
copy[k] = v
|
||||
}
|
||||
tc.mu.Unlock()
|
||||
return &transportCredential{
|
||||
gtc: tc.gtc.Clone(),
|
||||
gtc: tc.gtc.Clone(),
|
||||
addrToEndpoint: copy,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,6 +131,17 @@ func (tc *transportCredential) OverrideServerName(serverNameOverride string) err
|
|||
return tc.gtc.OverrideServerName(serverNameOverride)
|
||||
}
|
||||
|
||||
func (tc *transportCredential) Dialer(ctx context.Context, dialEp string) (net.Conn, error) {
|
||||
// Keep track of which addresses are dialed for which endpoints
|
||||
conn, err := endpoint.Dialer(ctx, dialEp)
|
||||
if conn != nil {
|
||||
tc.mu.Lock()
|
||||
tc.addrToEndpoint[conn.RemoteAddr().String()] = dialEp
|
||||
tc.mu.Unlock()
|
||||
}
|
||||
return conn, err
|
||||
}
|
||||
|
||||
// perRPCCredential implements "grpccredentials.PerRPCCredentials" interface.
|
||||
type perRPCCredential struct {
|
||||
authToken string
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2020 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
"go.etcd.io/etcd/version"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
// WithRequireLeader requires client requests to only succeed
|
||||
// when the cluster has a leader.
|
||||
func WithRequireLeader(ctx context.Context) context.Context {
|
||||
md, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok { // no outgoing metadata ctx key, create one
|
||||
md = metadata.Pairs(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
|
||||
return metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
copied := md.Copy() // avoid racey updates
|
||||
// overwrite/add 'hasleader' key/value
|
||||
metadataSet(copied, rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
|
||||
return metadata.NewOutgoingContext(ctx, copied)
|
||||
}
|
||||
|
||||
// embeds client version
|
||||
func withVersion(ctx context.Context) context.Context {
|
||||
md, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok { // no outgoing metadata ctx key, create one
|
||||
md = metadata.Pairs(rpctypes.MetadataClientAPIVersionKey, version.APIVersion)
|
||||
return metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
copied := md.Copy() // avoid racey updates
|
||||
// overwrite/add version key/value
|
||||
metadataSet(copied, rpctypes.MetadataClientAPIVersionKey, version.APIVersion)
|
||||
return metadata.NewOutgoingContext(ctx, copied)
|
||||
}
|
||||
|
||||
func metadataGet(md metadata.MD, k string) []string {
|
||||
k = strings.ToLower(k)
|
||||
return md[k]
|
||||
}
|
||||
|
||||
func metadataSet(md metadata.MD, k string, vals ...string) {
|
||||
if len(vals) == 0 {
|
||||
return
|
||||
}
|
||||
k = strings.ToLower(k)
|
||||
md[k] = vals
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2020 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
"go.etcd.io/etcd/version"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func TestMetadataWithRequireLeader(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
md, ok := metadata.FromOutgoingContext(ctx)
|
||||
if ok {
|
||||
t.Fatal("expected no outgoing metadata ctx key")
|
||||
}
|
||||
|
||||
// add a conflicting key with some other value
|
||||
md = metadata.Pairs(rpctypes.MetadataRequireLeaderKey, "invalid")
|
||||
// add a key, and expect not be overwritten
|
||||
metadataSet(md, "hello", "1", "2")
|
||||
ctx = metadata.NewOutgoingContext(ctx, md)
|
||||
|
||||
// expect overwrites but still keep other keys
|
||||
ctx = WithRequireLeader(ctx)
|
||||
md, ok = metadata.FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
t.Fatal("expected outgoing metadata ctx key")
|
||||
}
|
||||
if ss := metadataGet(md, rpctypes.MetadataRequireLeaderKey); !reflect.DeepEqual(ss, []string{rpctypes.MetadataHasLeader}) {
|
||||
t.Fatalf("unexpected metadata for %q %v", rpctypes.MetadataRequireLeaderKey, ss)
|
||||
}
|
||||
if ss := metadataGet(md, "hello"); !reflect.DeepEqual(ss, []string{"1", "2"}) {
|
||||
t.Fatalf("unexpected metadata for 'hello' %v", ss)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetadataWithClientAPIVersion(t *testing.T) {
|
||||
ctx := withVersion(WithRequireLeader(context.TODO()))
|
||||
|
||||
md, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
t.Fatal("expected outgoing metadata ctx key")
|
||||
}
|
||||
if ss := metadataGet(md, rpctypes.MetadataRequireLeaderKey); !reflect.DeepEqual(ss, []string{rpctypes.MetadataHasLeader}) {
|
||||
t.Fatalf("unexpected metadata for %q %v", rpctypes.MetadataRequireLeaderKey, ss)
|
||||
}
|
||||
if ss := metadataGet(md, rpctypes.MetadataClientAPIVersionKey); !reflect.DeepEqual(ss, []string{version.APIVersion}) {
|
||||
t.Fatalf("unexpected metadata for %q %v", rpctypes.MetadataClientAPIVersionKey, ss)
|
||||
}
|
||||
}
|
|
@ -90,7 +90,7 @@
|
|||
// // with etcd clientv3 <= v3.3
|
||||
// if err == context.Canceled {
|
||||
// // grpc balancer calls 'Get' with an inflight client.Close
|
||||
// } else if err == grpc.ErrClientConnClosing {
|
||||
// } else if err == grpc.ErrClientConnClosing { // <= gRCP v1.7.x
|
||||
// // grpc balancer calls 'Get' after client.Close.
|
||||
// }
|
||||
// // with etcd clientv3 >= v3.4
|
||||
|
@ -102,5 +102,4 @@
|
|||
// The grpc load balancer is registered statically and is shared across etcd clients.
|
||||
// To enable detailed load balancer logging, set the ETCD_CLIENT_DEBUG environment
|
||||
// variable. E.g. "ETCD_CLIENT_DEBUG=1".
|
||||
//
|
||||
package clientv3
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !cluster_proxy
|
||||
// +build !cluster_proxy
|
||||
|
||||
package integration
|
||||
|
|
|
@ -276,8 +276,7 @@ func TestMemberPromote(t *testing.T) {
|
|||
select {
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
case <-timeout:
|
||||
t.Errorf("failed all attempts to promote learner member, last error: %v", err)
|
||||
break
|
||||
t.Fatalf("failed all attempts to promote learner member, last error: %v", err)
|
||||
}
|
||||
|
||||
_, err = capi.MemberPromote(context.Background(), learnerID)
|
||||
|
|
|
@ -37,8 +37,8 @@ func TestKVPutError(t *testing.T) {
|
|||
defer testutil.AfterTest(t)
|
||||
|
||||
var (
|
||||
maxReqBytes = 1.5 * 1024 * 1024 // hard coded max in v3_server.go
|
||||
quota = int64(int(maxReqBytes) + 8*os.Getpagesize())
|
||||
maxReqBytes = 1.5 * 1024 * 1024 // hard coded max in v3_server.go
|
||||
quota = int64(int(maxReqBytes*1.2) + 8*os.Getpagesize()) // make sure we have enough overhead in backend quota. See discussion in #6486.
|
||||
)
|
||||
clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1, QuotaBackendBytes: quota, ClientMaxCallSendMsgSize: 100 * 1024 * 1024})
|
||||
defer clus.Terminate(t)
|
||||
|
@ -463,7 +463,7 @@ func TestKVGetErrConnClosed(t *testing.T) {
|
|||
defer close(donec)
|
||||
_, err := cli.Get(context.TODO(), "foo")
|
||||
if !clientv3.IsConnCanceled(err) {
|
||||
t.Errorf("expected %v or %v, got %v", context.Canceled, grpc.ErrClientConnClosing, err)
|
||||
t.Errorf("expected %v, got %v", context.Canceled, err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -490,7 +490,7 @@ func TestKVNewAfterClose(t *testing.T) {
|
|||
go func() {
|
||||
_, err := cli.Get(context.TODO(), "foo")
|
||||
if !clientv3.IsConnCanceled(err) {
|
||||
t.Errorf("expected %v or %v, got %v", context.Canceled, grpc.ErrClientConnClosing, err)
|
||||
t.Errorf("expected %v, got %v", context.Canceled, err)
|
||||
}
|
||||
close(donec)
|
||||
}()
|
||||
|
|
|
@ -27,8 +27,6 @@ import (
|
|||
"go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
"go.etcd.io/etcd/integration"
|
||||
"go.etcd.io/etcd/pkg/testutil"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func TestLeaseNotFoundError(t *testing.T) {
|
||||
|
@ -300,9 +298,8 @@ func TestLeaseGrantErrConnClosed(t *testing.T) {
|
|||
defer close(donec)
|
||||
_, err := cli.Grant(context.TODO(), 5)
|
||||
if !clientv3.IsConnCanceled(err) {
|
||||
// grpc.ErrClientConnClosing if grpc-go balancer calls 'Get' after client.Close.
|
||||
// context.Canceled if grpc-go balancer calls 'Get' with an inflight client.Close.
|
||||
t.Errorf("expected %v, %v or server unavailable, got %v", err != context.Canceled, grpc.ErrClientConnClosing, err)
|
||||
t.Errorf("expected %v, or server unavailable, got %v", context.Canceled, err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -372,7 +369,7 @@ func TestLeaseGrantNewAfterClose(t *testing.T) {
|
|||
go func() {
|
||||
_, err := cli.Grant(context.TODO(), 5)
|
||||
if !clientv3.IsConnCanceled(err) {
|
||||
t.Errorf("expected %v, %v or server unavailable, got %v", err != context.Canceled, grpc.ErrClientConnClosing, err)
|
||||
t.Errorf("expected %v or server unavailable, got %v", context.Canceled, err)
|
||||
}
|
||||
close(donec)
|
||||
}()
|
||||
|
@ -405,7 +402,7 @@ func TestLeaseRevokeNewAfterClose(t *testing.T) {
|
|||
go func() {
|
||||
_, err := cli.Revoke(context.TODO(), leaseID)
|
||||
if !clientv3.IsConnCanceled(err) {
|
||||
t.Fatalf("expected %v, %v or server unavailable, got %v", err != context.Canceled, grpc.ErrClientConnClosing, err)
|
||||
t.Fatalf("expected %v or server unavailable, got %v", context.Canceled, err)
|
||||
}
|
||||
close(donec)
|
||||
}()
|
||||
|
|
|
@ -619,16 +619,28 @@ func TestLeasingTxnOwnerGet(t *testing.T) {
|
|||
clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
||||
defer clus.Terminate(t)
|
||||
|
||||
client := clus.Client(0)
|
||||
|
||||
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
|
||||
testutil.AssertNil(t, err)
|
||||
defer closeLKV()
|
||||
|
||||
defer func() {
|
||||
// In '--tags cluster_proxy' mode the client need to be closed before
|
||||
// closeLKV(). This interrupts all outstanding watches. Closing by closeLKV()
|
||||
// is not sufficient as (unfortunately) context close does not interrupts Watches.
|
||||
// See ./clientv3/watch.go:
|
||||
// >> Currently, client contexts are overwritten with "valCtx" that never closes. <<
|
||||
clus.TakeClient(0) // avoid double Close() of the client.
|
||||
client.Close()
|
||||
closeLKV()
|
||||
}()
|
||||
|
||||
keyCount := rand.Intn(10) + 1
|
||||
var ops []clientv3.Op
|
||||
presps := make([]*clientv3.PutResponse, keyCount)
|
||||
for i := range presps {
|
||||
k := fmt.Sprintf("k-%d", i)
|
||||
presp, err := clus.Client(0).Put(context.TODO(), k, k+k)
|
||||
presp, err := client.Put(context.TODO(), k, k+k)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -149,7 +150,7 @@ func TestMaintenanceSnapshotErrorInflight(t *testing.T) {
|
|||
clus.Members[0].Stop(t)
|
||||
dpath := filepath.Join(clus.Members[0].DataDir, "member", "snap", "db")
|
||||
b := backend.NewDefaultBackend(dpath)
|
||||
s := mvcc.NewStore(zap.NewExample(), b, &lease.FakeLessor{}, nil)
|
||||
s := mvcc.NewStore(zap.NewExample(), b, &lease.FakeLessor{}, nil, mvcc.StoreConfig{CompactionBatchLimit: math.MaxInt32})
|
||||
rev := 100000
|
||||
for i := 2; i <= rev; i++ {
|
||||
s.Put([]byte(fmt.Sprintf("%10d", i)), bytes.Repeat([]byte("a"), 1024), lease.NoLease)
|
||||
|
|
|
@ -41,12 +41,13 @@ func TestV3ClientMetrics(t *testing.T) {
|
|||
var (
|
||||
addr = "localhost:27989"
|
||||
ln net.Listener
|
||||
err error
|
||||
)
|
||||
|
||||
// listen for all Prometheus metrics
|
||||
donec := make(chan struct{})
|
||||
go func() {
|
||||
var err error
|
||||
|
||||
defer close(donec)
|
||||
|
||||
srv := &http.Server{Handler: promhttp.Handler()}
|
||||
|
@ -87,7 +88,7 @@ func TestV3ClientMetrics(t *testing.T) {
|
|||
|
||||
pBefore := sumCountersForMetricAndLabels(t, url, "grpc_client_started_total", "Put", "unary")
|
||||
|
||||
_, err = cli.Put(context.Background(), "foo", "bar")
|
||||
_, err := cli.Put(context.Background(), "foo", "bar")
|
||||
if err != nil {
|
||||
t.Errorf("Error putting value in key store")
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !cluster_proxy
|
||||
// +build !cluster_proxy
|
||||
|
||||
package integration
|
||||
|
|
|
@ -65,8 +65,8 @@ func TestUserErrorAuth(t *testing.T) {
|
|||
authSetupRoot(t, authapi.Auth)
|
||||
|
||||
// unauthenticated client
|
||||
if _, err := authapi.UserAdd(context.TODO(), "foo", "bar"); err != rpctypes.ErrUserNotFound {
|
||||
t.Fatalf("expected %v, got %v", rpctypes.ErrUserNotFound, err)
|
||||
if _, err := authapi.UserAdd(context.TODO(), "foo", "bar"); err != rpctypes.ErrUserEmpty {
|
||||
t.Fatalf("expected %v, got %v", rpctypes.ErrUserEmpty, err)
|
||||
}
|
||||
|
||||
// wrong id or password
|
||||
|
@ -114,7 +114,7 @@ func authSetupRoot(t *testing.T, auth clientv3.Auth) {
|
|||
func TestGetTokenWithoutAuth(t *testing.T) {
|
||||
defer testutil.AfterTest(t)
|
||||
|
||||
clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 10})
|
||||
clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 2})
|
||||
defer clus.Terminate(t)
|
||||
|
||||
authapi := clus.RandClient()
|
||||
|
@ -130,7 +130,7 @@ func TestGetTokenWithoutAuth(t *testing.T) {
|
|||
// "Username" and "Password" must be used
|
||||
cfg := clientv3.Config{
|
||||
Endpoints: authapi.Endpoints(),
|
||||
DialTimeout: 1 * time.Second, // make sure all connection time of connect all endpoint must be more DialTimeout
|
||||
DialTimeout: 5 * time.Second,
|
||||
Username: "root",
|
||||
Password: "123",
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ func TestGetTokenWithoutAuth(t *testing.T) {
|
|||
|
||||
switch err {
|
||||
case nil:
|
||||
t.Log("passes as expected, but may be connection time less than DialTimeout")
|
||||
t.Log("passes as expected")
|
||||
case context.DeadlineExceeded:
|
||||
t.Errorf("not expected result:%v with endpoint:%s", err, authapi.Endpoints())
|
||||
case rpctypes.ErrAuthNotEnabled:
|
||||
|
@ -150,5 +150,4 @@ func TestGetTokenWithoutAuth(t *testing.T) {
|
|||
default:
|
||||
t.Errorf("other errors:%v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !cluster_proxy
|
||||
// +build !cluster_proxy
|
||||
|
||||
package integration
|
||||
|
|
|
@ -338,6 +338,9 @@ func putAndWatch(t *testing.T, wctx *watchctx, key, val string) {
|
|||
if !ok {
|
||||
t.Fatalf("unexpected watch close")
|
||||
}
|
||||
if err := v.Err(); err != nil {
|
||||
t.Fatalf("unexpected watch response error: %v", err)
|
||||
}
|
||||
if string(v.Events[0].Kv.Value) != val {
|
||||
t.Fatalf("bad value got %v, wanted %v", v.Events[0].Kv.Value, val)
|
||||
}
|
||||
|
@ -582,7 +585,34 @@ func testWatchWithProgressNotify(t *testing.T, watchOnPut bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestConfigurableWatchProgressNotifyInterval(t *testing.T) {
|
||||
progressInterval := 200 * time.Millisecond
|
||||
clus := integration.NewClusterV3(t,
|
||||
&integration.ClusterConfig{
|
||||
Size: 3,
|
||||
WatchProgressNotifyInterval: progressInterval,
|
||||
})
|
||||
defer clus.Terminate(t)
|
||||
|
||||
opts := []clientv3.OpOption{clientv3.WithProgressNotify()}
|
||||
rch := clus.RandClient().Watch(context.Background(), "foo", opts...)
|
||||
|
||||
timeout := 1 * time.Second // we expect to receive watch progress notify in 2 * progressInterval,
|
||||
// but for CPU-starved situation it may take longer. So we use 1 second here for timeout.
|
||||
select {
|
||||
case resp := <-rch: // waiting for a watch progress notify response
|
||||
if !resp.IsProgressNotify() {
|
||||
t.Fatalf("expected resp.IsProgressNotify() == true")
|
||||
}
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("timed out waiting for watch progress notify response in %v", timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatchRequestProgress(t *testing.T) {
|
||||
if integration.ThroughProxy {
|
||||
t.Skip("grpc-proxy does not support WatchProgress yet")
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
watchers []string
|
||||
|
@ -986,12 +1016,12 @@ func TestWatchCancelOnServer(t *testing.T) {
|
|||
// TestWatchOverlapContextCancel stresses the watcher stream teardown path by
|
||||
// creating/canceling watchers to ensure that new watchers are not taken down
|
||||
// by a torn down watch stream. The sort of race that's being detected:
|
||||
// 1. create w1 using a cancelable ctx with %v as "ctx"
|
||||
// 2. cancel ctx
|
||||
// 3. watcher client begins tearing down watcher grpc stream since no more watchers
|
||||
// 3. start creating watcher w2 using a new "ctx" (not canceled), attaches to old grpc stream
|
||||
// 4. watcher client finishes tearing down stream on "ctx"
|
||||
// 5. w2 comes back canceled
|
||||
// 1. create w1 using a cancelable ctx with %v as "ctx"
|
||||
// 2. cancel ctx
|
||||
// 3. watcher client begins tearing down watcher grpc stream since no more watchers
|
||||
// 3. start creating watcher w2 using a new "ctx" (not canceled), attaches to old grpc stream
|
||||
// 4. watcher client finishes tearing down stream on "ctx"
|
||||
// 5. w2 comes back canceled
|
||||
func TestWatchOverlapContextCancel(t *testing.T) {
|
||||
f := func(clus *integration.ClusterV3) {}
|
||||
testWatchOverlapContextCancel(t, f)
|
||||
|
|
|
@ -19,28 +19,27 @@
|
|||
//
|
||||
// First, create a leasing KV from a clientv3.Client 'cli':
|
||||
//
|
||||
// lkv, err := leasing.NewKV(cli, "leasing-prefix")
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// lkv, err := leasing.NewKV(cli, "leasing-prefix")
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
//
|
||||
// A range request for a key "abc" tries to acquire a leasing key so it can cache the range's
|
||||
// key locally. On the server, the leasing key is stored to "leasing-prefix/abc":
|
||||
//
|
||||
// resp, err := lkv.Get(context.TODO(), "abc")
|
||||
// resp, err := lkv.Get(context.TODO(), "abc")
|
||||
//
|
||||
// Future linearized read requests using 'lkv' will be served locally for the lease's lifetime:
|
||||
//
|
||||
// resp, err = lkv.Get(context.TODO(), "abc")
|
||||
// resp, err = lkv.Get(context.TODO(), "abc")
|
||||
//
|
||||
// If another leasing client writes to a leased key, then the owner relinquishes its exclusive
|
||||
// access, permitting the writer to modify the key:
|
||||
//
|
||||
// lkv2, err := leasing.NewKV(cli, "leasing-prefix")
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// lkv2.Put(context.TODO(), "abc", "456")
|
||||
// resp, err = lkv.Get("abc")
|
||||
//
|
||||
// lkv2, err := leasing.NewKV(cli, "leasing-prefix")
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// lkv2.Put(context.TODO(), "abc", "456")
|
||||
// resp, err = lkv.Get("abc")
|
||||
package leasing
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"io"
|
||||
|
||||
pb "go.etcd.io/etcd/etcdserver/etcdserverpb"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
@ -68,6 +69,7 @@ type Maintenance interface {
|
|||
}
|
||||
|
||||
type maintenance struct {
|
||||
lg *zap.Logger
|
||||
dial func(endpoint string) (pb.MaintenanceClient, func(), error)
|
||||
remote pb.MaintenanceClient
|
||||
callOpts []grpc.CallOption
|
||||
|
@ -75,6 +77,7 @@ type maintenance struct {
|
|||
|
||||
func NewMaintenance(c *Client) Maintenance {
|
||||
api := &maintenance{
|
||||
lg: c.lg,
|
||||
dial: func(endpoint string) (pb.MaintenanceClient, func(), error) {
|
||||
conn, err := c.Dial(endpoint)
|
||||
if err != nil {
|
||||
|
@ -93,6 +96,7 @@ func NewMaintenance(c *Client) Maintenance {
|
|||
|
||||
func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient, c *Client) Maintenance {
|
||||
api := &maintenance{
|
||||
lg: c.lg,
|
||||
dial: func(string) (pb.MaintenanceClient, func(), error) {
|
||||
return remote, func() {}, nil
|
||||
},
|
||||
|
@ -193,23 +197,32 @@ func (m *maintenance) Snapshot(ctx context.Context) (io.ReadCloser, error) {
|
|||
return nil, toErr(ctx, err)
|
||||
}
|
||||
|
||||
m.lg.Info("opened snapshot stream; downloading")
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
for {
|
||||
resp, err := ss.Recv()
|
||||
if err != nil {
|
||||
switch err {
|
||||
case io.EOF:
|
||||
m.lg.Info("completed snapshot read; closing")
|
||||
default:
|
||||
m.lg.Warn("failed to receive from snapshot stream; closing", zap.Error(err))
|
||||
}
|
||||
pw.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
if resp == nil && err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// can "resp == nil && err == nil"
|
||||
// before we receive snapshot SHA digest?
|
||||
// No, server sends EOF with an empty response
|
||||
// after it sends SHA digest at the end
|
||||
|
||||
if _, werr := pw.Write(resp.Blob); werr != nil {
|
||||
pw.CloseWithError(werr)
|
||||
return
|
||||
}
|
||||
}
|
||||
pw.Close()
|
||||
}()
|
||||
return &snapshotReadCloser{ctx: ctx, ReadCloser: pr}, nil
|
||||
}
|
||||
|
|
|
@ -39,5 +39,4 @@
|
|||
// resp, _ = cli.Get(context.TODO(), "abc")
|
||||
// fmt.Printf("%s\n", resp.Kvs[0].Value)
|
||||
// // Output: 456
|
||||
//
|
||||
package namespace
|
||||
|
|
|
@ -52,5 +52,4 @@
|
|||
// r := &etcdnaming.GRPCResolver{Client: c}
|
||||
// return r.Update(c.Ctx(), service, naming.Update{Op: naming.Add, Addr: addr}, clientv3.WithLease(lid))
|
||||
// }
|
||||
//
|
||||
package naming
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package naming
|
||||
package naming_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -21,6 +21,7 @@ import (
|
|||
"testing"
|
||||
|
||||
etcd "go.etcd.io/etcd/clientv3"
|
||||
namingv3 "go.etcd.io/etcd/clientv3/naming"
|
||||
"go.etcd.io/etcd/integration"
|
||||
"go.etcd.io/etcd/pkg/testutil"
|
||||
|
||||
|
@ -33,7 +34,7 @@ func TestGRPCResolver(t *testing.T) {
|
|||
clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
||||
defer clus.Terminate(t)
|
||||
|
||||
r := GRPCResolver{
|
||||
r := namingv3.GRPCResolver{
|
||||
Client: clus.RandClient(),
|
||||
}
|
||||
|
||||
|
@ -107,7 +108,7 @@ func TestGRPCResolverMulti(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := GRPCResolver{c}
|
||||
r := namingv3.GRPCResolver{c}
|
||||
|
||||
w, err := r.Resolve("foo")
|
||||
if err != nil {
|
||||
|
|
|
@ -77,6 +77,9 @@ type Op struct {
|
|||
cmps []Cmp
|
||||
thenOps []Op
|
||||
elseOps []Op
|
||||
|
||||
isOptsWithFromKey bool
|
||||
isOptsWithPrefix bool
|
||||
}
|
||||
|
||||
// accessors / mutators
|
||||
|
@ -216,6 +219,10 @@ func (op Op) isWrite() bool {
|
|||
return op.t != tRange
|
||||
}
|
||||
|
||||
func NewOp() *Op {
|
||||
return &Op{key: []byte("")}
|
||||
}
|
||||
|
||||
// OpGet returns "get" operation based on given key and operation options.
|
||||
func OpGet(key string, opts ...OpOption) Op {
|
||||
// WithPrefix and WithFromKey are not supported together
|
||||
|
@ -387,6 +394,7 @@ func WithPrefix() OpOption {
|
|||
return
|
||||
}
|
||||
op.end = getPrefix(op.key)
|
||||
op.isOptsWithPrefix = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,6 +414,7 @@ func WithFromKey() OpOption {
|
|||
op.key = []byte{0}
|
||||
}
|
||||
op.end = []byte("\x00")
|
||||
op.isOptsWithFromKey = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -554,7 +563,21 @@ func toLeaseTimeToLiveRequest(id LeaseID, opts ...LeaseOption) *pb.LeaseTimeToLi
|
|||
}
|
||||
|
||||
// isWithPrefix returns true if WithPrefix is being called in the op
|
||||
func isWithPrefix(opts []OpOption) bool { return isOpFuncCalled("WithPrefix", opts) }
|
||||
func isWithPrefix(opts []OpOption) bool {
|
||||
ret := NewOp()
|
||||
for _, opt := range opts {
|
||||
opt(ret)
|
||||
}
|
||||
|
||||
return ret.isOptsWithPrefix
|
||||
}
|
||||
|
||||
// isWithFromKey returns true if WithFromKey is being called in the op
|
||||
func isWithFromKey(opts []OpOption) bool { return isOpFuncCalled("WithFromKey", opts) }
|
||||
func isWithFromKey(opts []OpOption) bool {
|
||||
ret := NewOp()
|
||||
for _, opt := range opts {
|
||||
opt(ret)
|
||||
}
|
||||
|
||||
return ret.isOptsWithFromKey
|
||||
}
|
||||
|
|
|
@ -38,5 +38,4 @@
|
|||
// cli.KV = ordering.NewKV(cli.KV, vf)
|
||||
//
|
||||
// Now calls using 'cli' will reject order violations with an error.
|
||||
//
|
||||
package ordering
|
||||
|
|
|
@ -16,8 +16,7 @@ package ordering
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
"sync/atomic"
|
||||
|
||||
"go.etcd.io/etcd/clientv3"
|
||||
)
|
||||
|
@ -26,26 +25,18 @@ type OrderViolationFunc func(op clientv3.Op, resp clientv3.OpResponse, prevRev i
|
|||
|
||||
var ErrNoGreaterRev = errors.New("etcdclient: no cluster members have a revision higher than the previously received revision")
|
||||
|
||||
func NewOrderViolationSwitchEndpointClosure(c clientv3.Client) OrderViolationFunc {
|
||||
var mu sync.Mutex
|
||||
violationCount := 0
|
||||
return func(op clientv3.Op, resp clientv3.OpResponse, prevRev int64) error {
|
||||
if violationCount > len(c.Endpoints()) {
|
||||
func NewOrderViolationSwitchEndpointClosure(c *clientv3.Client) OrderViolationFunc {
|
||||
violationCount := int32(0)
|
||||
return func(_ clientv3.Op, _ clientv3.OpResponse, _ int64) error {
|
||||
// Each request is assigned by round-robin load-balancer's picker to a different
|
||||
// endpoints. If we cycled them 5 times (even with some level of concurrency),
|
||||
// with high probability no endpoint points on a member with fresh data.
|
||||
// TODO: Ideally we should track members (resp.opp.Header) that returned
|
||||
// stale result and explicitly temporarily disable them in 'picker'.
|
||||
if atomic.LoadInt32(&violationCount) > int32(5*len(c.Endpoints())) {
|
||||
return ErrNoGreaterRev
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
eps := c.Endpoints()
|
||||
// force client to connect to given endpoint by limiting to a single endpoint
|
||||
c.SetEndpoints(eps[violationCount%len(eps)])
|
||||
// give enough time for operation
|
||||
time.Sleep(1 * time.Second)
|
||||
// set available endpoints back to all endpoints in to ensure
|
||||
// the client has access to all the endpoints.
|
||||
c.SetEndpoints(eps...)
|
||||
// give enough time for operation
|
||||
time.Sleep(1 * time.Second)
|
||||
violationCount++
|
||||
atomic.AddInt32(&violationCount, 1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,19 +64,19 @@ func TestEndpointSwitchResolvesViolation(t *testing.T) {
|
|||
// NewOrderViolationSwitchEndpointClosure will be able to
|
||||
// access the full list of endpoints.
|
||||
cli.SetEndpoints(eps...)
|
||||
OrderingKv := NewKV(cli.KV, NewOrderViolationSwitchEndpointClosure(*cli))
|
||||
orderingKv := NewKV(cli.KV, NewOrderViolationSwitchEndpointClosure(cli))
|
||||
// set prevRev to the second member's revision of "foo" such that
|
||||
// the revision is higher than the third member's revision of "foo"
|
||||
_, err = OrderingKv.Get(ctx, "foo")
|
||||
_, err = orderingKv.Get(ctx, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Reconfigure client to speak only to the 'partitioned' member")
|
||||
cli.SetEndpoints(clus.Members[2].GRPCAddr())
|
||||
time.Sleep(1 * time.Second) // give enough time for operation
|
||||
_, err = OrderingKv.Get(ctx, "foo", clientv3.WithSerializable())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to resolve order violation %v", err)
|
||||
_, err = orderingKv.Get(ctx, "foo", clientv3.WithSerializable())
|
||||
if err != ErrNoGreaterRev {
|
||||
t.Fatal("While speaking to partitioned leader, we should get ErrNoGreaterRev error")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ func TestUnresolvableOrderViolation(t *testing.T) {
|
|||
// access the full list of endpoints.
|
||||
cli.SetEndpoints(eps...)
|
||||
time.Sleep(1 * time.Second) // give enough time for operation
|
||||
OrderingKv := NewKV(cli.KV, NewOrderViolationSwitchEndpointClosure(*cli))
|
||||
OrderingKv := NewKV(cli.KV, NewOrderViolationSwitchEndpointClosure(cli))
|
||||
// set prevRev to the first member's revision of "foo" such that
|
||||
// the revision is higher than the fourth and fifth members' revision of "foo"
|
||||
_, err = OrderingKv.Get(ctx, "foo")
|
||||
|
|
|
@ -38,6 +38,7 @@ import (
|
|||
func (c *Client) unaryClientInterceptor(logger *zap.Logger, optFuncs ...retryOption) grpc.UnaryClientInterceptor {
|
||||
intOpts := reuseOrNewWithCallOptions(defaultOptions, optFuncs)
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
ctx = withVersion(ctx)
|
||||
grpcOpts, retryOpts := filterCallOptions(opts)
|
||||
callOpts := reuseOrNewWithCallOptions(intOpts, retryOpts)
|
||||
// short circuit for simplicity, and avoiding allocations.
|
||||
|
@ -72,8 +73,8 @@ func (c *Client) unaryClientInterceptor(logger *zap.Logger, optFuncs ...retryOpt
|
|||
// its the callCtx deadline or cancellation, in which case try again.
|
||||
continue
|
||||
}
|
||||
if callOpts.retryAuth && rpctypes.Error(lastErr) == rpctypes.ErrInvalidAuthToken {
|
||||
gterr := c.getToken(ctx)
|
||||
if c.shouldRefreshToken(lastErr, callOpts) {
|
||||
gterr := c.refreshToken(ctx)
|
||||
if gterr != nil {
|
||||
logger.Warn(
|
||||
"retrying of unary invoker failed to fetch new auth token",
|
||||
|
@ -103,6 +104,17 @@ func (c *Client) unaryClientInterceptor(logger *zap.Logger, optFuncs ...retryOpt
|
|||
func (c *Client) streamClientInterceptor(logger *zap.Logger, optFuncs ...retryOption) grpc.StreamClientInterceptor {
|
||||
intOpts := reuseOrNewWithCallOptions(defaultOptions, optFuncs)
|
||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
ctx = withVersion(ctx)
|
||||
// getToken automatically
|
||||
// TODO(cfc4n): keep this code block, remove codes about getToken in client.go after pr #12165 merged.
|
||||
if c.authTokenBundle != nil {
|
||||
// equal to c.Username != "" && c.Password != ""
|
||||
err := c.getToken(ctx)
|
||||
if err != nil && rpctypes.Error(err) != rpctypes.ErrAuthNotEnabled {
|
||||
logger.Error("clientv3/retry_interceptor: getToken failed", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
grpcOpts, retryOpts := filterCallOptions(opts)
|
||||
callOpts := reuseOrNewWithCallOptions(intOpts, retryOpts)
|
||||
// short circuit for simplicity, and avoiding allocations.
|
||||
|
@ -113,10 +125,9 @@ func (c *Client) streamClientInterceptor(logger *zap.Logger, optFuncs ...retryOp
|
|||
return nil, status.Errorf(codes.Unimplemented, "clientv3/retry_interceptor: cannot retry on ClientStreams, set Disable()")
|
||||
}
|
||||
newStreamer, err := streamer(ctx, desc, cc, method, grpcOpts...)
|
||||
logger.Warn("retry stream intercept", zap.Error(err))
|
||||
if err != nil {
|
||||
// TODO(mwitkow): Maybe dial and transport errors should be retriable?
|
||||
return nil, err
|
||||
logger.Error("streamer failed to create ClientStream", zap.Error(err))
|
||||
return nil, err // TODO(mwitkow): Maybe dial and transport errors should be retriable?
|
||||
}
|
||||
retryingStreamer := &serverStreamingRetryingStream{
|
||||
client: c,
|
||||
|
@ -131,6 +142,37 @@ func (c *Client) streamClientInterceptor(logger *zap.Logger, optFuncs ...retryOp
|
|||
}
|
||||
}
|
||||
|
||||
// shouldRefreshToken checks whether there's a need to refresh the token based on the error and callOptions,
|
||||
// and returns a boolean value.
|
||||
func (c *Client) shouldRefreshToken(err error, callOpts *options) bool {
|
||||
if rpctypes.Error(err) == rpctypes.ErrUserEmpty {
|
||||
// refresh the token when username, password is present but the server returns ErrUserEmpty
|
||||
// which is possible when the client token is cleared somehow
|
||||
return c.authTokenBundle != nil // equal to c.Username != "" && c.Password != ""
|
||||
}
|
||||
|
||||
return callOpts.retryAuth &&
|
||||
(rpctypes.Error(err) == rpctypes.ErrInvalidAuthToken || rpctypes.Error(err) == rpctypes.ErrAuthOldRevision)
|
||||
}
|
||||
|
||||
func (c *Client) refreshToken(ctx context.Context) error {
|
||||
if c.authTokenBundle == nil {
|
||||
// c.authTokenBundle will be initialized only when
|
||||
// c.Username != "" && c.Password != "".
|
||||
//
|
||||
// When users use the TLS CommonName based authentication, the
|
||||
// authTokenBundle is always nil. But it's possible for the clients
|
||||
// to get `rpctypes.ErrAuthOldRevision` response when the clients
|
||||
// concurrently modify auth data (e.g, addUser, deleteUser etc.).
|
||||
// In this case, there is no need to refresh the token; instead the
|
||||
// clients just need to retry the operations (e.g. Put, Delete etc).
|
||||
return nil
|
||||
}
|
||||
// clear auth token before refreshing it.
|
||||
c.authTokenBundle.UpdateAuthToken("")
|
||||
return c.getToken(ctx)
|
||||
}
|
||||
|
||||
// type serverStreamingRetryingStream is the implementation of grpc.ClientStream that acts as a
|
||||
// proxy to the underlying call. If any of the RecvMsg() calls fail, it will try to reestablish
|
||||
// a new ClientStream according to the retry policy.
|
||||
|
@ -185,6 +227,7 @@ func (s *serverStreamingRetryingStream) RecvMsg(m interface{}) error {
|
|||
if !attemptRetry {
|
||||
return lastErr // success or hard failure
|
||||
}
|
||||
|
||||
// We start off from attempt 1, because zeroth was already made on normal SendMsg().
|
||||
for attempt := uint(1); attempt < s.callOpts.max; attempt++ {
|
||||
if err := waitRetryBackoff(s.ctx, attempt, s.callOpts); err != nil {
|
||||
|
@ -192,12 +235,13 @@ func (s *serverStreamingRetryingStream) RecvMsg(m interface{}) error {
|
|||
}
|
||||
newStream, err := s.reestablishStreamAndResendBuffer(s.ctx)
|
||||
if err != nil {
|
||||
// TODO(mwitkow): Maybe dial and transport errors should be retriable?
|
||||
return err
|
||||
s.client.lg.Error("failed reestablishStreamAndResendBuffer", zap.Error(err))
|
||||
return err // TODO(mwitkow): Maybe dial and transport errors should be retriable?
|
||||
}
|
||||
s.setStream(newStream)
|
||||
|
||||
s.client.lg.Warn("retrying RecvMsg", zap.Error(lastErr))
|
||||
attemptRetry, lastErr = s.receiveMsgAndIndicateRetry(m)
|
||||
//fmt.Printf("Received message and indicate: %v %v\n", attemptRetry, lastErr)
|
||||
if !attemptRetry {
|
||||
return lastErr
|
||||
}
|
||||
|
@ -226,8 +270,8 @@ func (s *serverStreamingRetryingStream) receiveMsgAndIndicateRetry(m interface{}
|
|||
// its the callCtx deadline or cancellation, in which case try again.
|
||||
return true, err
|
||||
}
|
||||
if s.callOpts.retryAuth && rpctypes.Error(err) == rpctypes.ErrInvalidAuthToken {
|
||||
gterr := s.client.getToken(s.ctx)
|
||||
if s.client.shouldRefreshToken(err, s.callOpts) {
|
||||
gterr := s.client.refreshToken(s.ctx)
|
||||
if gterr != nil {
|
||||
s.client.lg.Warn("retry failed to fetch new auth token", zap.Error(gterr))
|
||||
return false, err // return the original error for simplicity
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright 2022 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Based on github.com/grpc-ecosystem/go-grpc-middleware/retry, but modified to support the more
|
||||
// fine grained error checking required by write-at-most-once retry semantics of etcd.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"go.etcd.io/etcd/clientv3/credentials"
|
||||
"go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
grpccredentials "google.golang.org/grpc/credentials"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type dummyAuthTokenBundle struct{}
|
||||
|
||||
func (d dummyAuthTokenBundle) TransportCredentials() grpccredentials.TransportCredentials {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d dummyAuthTokenBundle) PerRPCCredentials() grpccredentials.PerRPCCredentials {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d dummyAuthTokenBundle) NewWithMode(mode string) (grpccredentials.Bundle, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d dummyAuthTokenBundle) UpdateAuthToken(token string) {
|
||||
}
|
||||
|
||||
func TestClientShouldRefreshToken(t *testing.T) {
|
||||
type fields struct {
|
||||
authTokenBundle credentials.Bundle
|
||||
}
|
||||
type args struct {
|
||||
err error
|
||||
callOpts *options
|
||||
}
|
||||
|
||||
optsWithTrue := &options{
|
||||
retryAuth: true,
|
||||
}
|
||||
optsWithFalse := &options{
|
||||
retryAuth: false,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "ErrUserEmpty and non nil authTokenBundle",
|
||||
fields: fields{
|
||||
authTokenBundle: &dummyAuthTokenBundle{},
|
||||
},
|
||||
args: args{rpctypes.ErrGRPCUserEmpty, optsWithTrue},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "ErrUserEmpty and nil authTokenBundle",
|
||||
fields: fields{
|
||||
authTokenBundle: nil,
|
||||
},
|
||||
args: args{rpctypes.ErrGRPCUserEmpty, optsWithTrue},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "ErrGRPCInvalidAuthToken and retryAuth",
|
||||
fields: fields{
|
||||
authTokenBundle: nil,
|
||||
},
|
||||
args: args{rpctypes.ErrGRPCInvalidAuthToken, optsWithTrue},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "ErrGRPCInvalidAuthToken and !retryAuth",
|
||||
fields: fields{
|
||||
authTokenBundle: nil,
|
||||
},
|
||||
args: args{rpctypes.ErrGRPCInvalidAuthToken, optsWithFalse},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "ErrGRPCAuthOldRevision and retryAuth",
|
||||
fields: fields{
|
||||
authTokenBundle: nil,
|
||||
},
|
||||
args: args{rpctypes.ErrGRPCAuthOldRevision, optsWithTrue},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "ErrGRPCAuthOldRevision and !retryAuth",
|
||||
fields: fields{
|
||||
authTokenBundle: nil,
|
||||
},
|
||||
args: args{rpctypes.ErrGRPCAuthOldRevision, optsWithFalse},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Other error and retryAuth",
|
||||
fields: fields{
|
||||
authTokenBundle: nil,
|
||||
},
|
||||
args: args{rpctypes.ErrGRPCAuthFailed, optsWithTrue},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Other error and !retryAuth",
|
||||
fields: fields{
|
||||
authTokenBundle: nil,
|
||||
},
|
||||
args: args{rpctypes.ErrGRPCAuthFailed, optsWithFalse},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
authTokenBundle: tt.fields.authTokenBundle,
|
||||
}
|
||||
if got := c.shouldRefreshToken(tt.args.err, tt.args.callOpts); got != tt.want {
|
||||
t.Errorf("shouldRefreshToken() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -69,8 +69,8 @@ func TestSnapshotV3RestoreMultiMemberAdd(t *testing.T) {
|
|||
cfg.Name = "3"
|
||||
cfg.InitialClusterToken = testClusterTkn
|
||||
cfg.ClusterState = "existing"
|
||||
cfg.LCUrls, cfg.ACUrls = newCURLs, newCURLs
|
||||
cfg.LPUrls, cfg.APUrls = newPURLs, newPURLs
|
||||
cfg.ListenClientUrls, cfg.AdvertiseClientUrls = newCURLs, newCURLs
|
||||
cfg.ListenPeerUrls, cfg.AdvertisePeerUrls = newPURLs, newPURLs
|
||||
cfg.InitialCluster = ""
|
||||
for i := 0; i < clusterN; i++ {
|
||||
cfg.InitialCluster += fmt.Sprintf(",%d=%s", i, pURLs[i].String())
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"go.etcd.io/etcd/clientv3"
|
||||
"go.etcd.io/etcd/etcdserver"
|
||||
|
@ -39,6 +40,7 @@ import (
|
|||
"go.etcd.io/etcd/mvcc"
|
||||
"go.etcd.io/etcd/mvcc/backend"
|
||||
"go.etcd.io/etcd/pkg/fileutil"
|
||||
"go.etcd.io/etcd/pkg/traceutil"
|
||||
"go.etcd.io/etcd/pkg/types"
|
||||
"go.etcd.io/etcd/raft"
|
||||
"go.etcd.io/etcd/raft/raftpb"
|
||||
|
@ -87,6 +89,14 @@ type v3Manager struct {
|
|||
skipHashCheck bool
|
||||
}
|
||||
|
||||
// hasChecksum returns "true" if the file size "n"
|
||||
// has appended sha256 hash digest.
|
||||
func hasChecksum(n int64) bool {
|
||||
// 512 is chosen because it's a minimum disk sector size
|
||||
// smaller than (and multiplies to) OS page size in most systems
|
||||
return (n % 512) == sha256.Size
|
||||
}
|
||||
|
||||
// Save fetches snapshot from remote etcd server and saves data to target path.
|
||||
func (s *v3Manager) Save(ctx context.Context, cfg clientv3.Config, dbPath string) error {
|
||||
if len(cfg.Endpoints) != 1 {
|
||||
|
@ -106,10 +116,7 @@ func (s *v3Manager) Save(ctx context.Context, cfg clientv3.Config, dbPath string
|
|||
if err != nil {
|
||||
return fmt.Errorf("could not open %s (%v)", partpath, err)
|
||||
}
|
||||
s.lg.Info(
|
||||
"created temporary db file",
|
||||
zap.String("path", partpath),
|
||||
)
|
||||
s.lg.Info("created temporary db file", zap.String("path", partpath))
|
||||
|
||||
now := time.Now()
|
||||
var rd io.ReadCloser
|
||||
|
@ -117,13 +124,15 @@ func (s *v3Manager) Save(ctx context.Context, cfg clientv3.Config, dbPath string
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.lg.Info(
|
||||
"fetching snapshot",
|
||||
zap.String("endpoint", cfg.Endpoints[0]),
|
||||
)
|
||||
if _, err = io.Copy(f, rd); err != nil {
|
||||
s.lg.Info("fetching snapshot", zap.String("endpoint", cfg.Endpoints[0]))
|
||||
var size int64
|
||||
size, err = io.Copy(f, rd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !hasChecksum(size) {
|
||||
return fmt.Errorf("sha256 checksum not found [bytes: %d]", size)
|
||||
}
|
||||
if err = fileutil.Fsync(f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -133,6 +142,7 @@ func (s *v3Manager) Save(ctx context.Context, cfg clientv3.Config, dbPath string
|
|||
s.lg.Info(
|
||||
"fetched snapshot",
|
||||
zap.String("endpoint", cfg.Endpoints[0]),
|
||||
zap.String("size", humanize.Bytes(uint64(size))),
|
||||
zap.Duration("took", time.Since(now)),
|
||||
)
|
||||
|
||||
|
@ -345,7 +355,7 @@ func (s *v3Manager) saveDB() error {
|
|||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
hasHash := (off % 512) == sha256.Size
|
||||
hasHash := hasChecksum(off)
|
||||
if hasHash {
|
||||
if err := db.Truncate(off - sha256.Size); err != nil {
|
||||
return err
|
||||
|
@ -381,10 +391,10 @@ func (s *v3Manager) saveDB() error {
|
|||
be := backend.NewDefaultBackend(dbpath)
|
||||
|
||||
// a lessor never timeouts leases
|
||||
lessor := lease.NewLessor(s.lg, be, lease.LessorConfig{MinLeaseTTL: math.MaxInt64})
|
||||
lessor := lease.NewLessor(s.lg, be, nil, lease.LessorConfig{MinLeaseTTL: math.MaxInt64})
|
||||
|
||||
mvs := mvcc.NewStore(s.lg, be, lessor, (*initIndex)(&commit))
|
||||
txn := mvs.Write()
|
||||
mvs := mvcc.NewStore(s.lg, be, lessor, (*initIndex)(&commit), mvcc.StoreConfig{CompactionBatchLimit: math.MaxInt32})
|
||||
txn := mvs.Write(traceutil.TODO())
|
||||
btx := be.BatchTx()
|
||||
del := func(k, v []byte) error {
|
||||
txn.DeleteRange(k, nil)
|
||||
|
|
|
@ -51,8 +51,8 @@ func TestSnapshotV3RestoreSingle(t *testing.T) {
|
|||
cfg.Name = "s1"
|
||||
cfg.InitialClusterToken = testClusterTkn
|
||||
cfg.ClusterState = "existing"
|
||||
cfg.LCUrls, cfg.ACUrls = cURLs, cURLs
|
||||
cfg.LPUrls, cfg.APUrls = pURLs, pURLs
|
||||
cfg.ListenClientUrls, cfg.AdvertiseClientUrls = cURLs, cURLs
|
||||
cfg.ListenPeerUrls, cfg.AdvertisePeerUrls = pURLs, pURLs
|
||||
cfg.InitialCluster = fmt.Sprintf("%s=%s", cfg.Name, pURLs[0].String())
|
||||
cfg.Dir = filepath.Join(os.TempDir(), fmt.Sprint(time.Now().Nanosecond()))
|
||||
|
||||
|
@ -87,7 +87,8 @@ func TestSnapshotV3RestoreSingle(t *testing.T) {
|
|||
}
|
||||
|
||||
var cli *clientv3.Client
|
||||
cli, err = clientv3.New(clientv3.Config{Endpoints: []string{cfg.ACUrls[0].String()}})
|
||||
cli, err = clientv3.New(clientv3.Config{Endpoints: []string{cfg.AdvertiseClientUrls[0].String()}})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -203,8 +204,8 @@ func createSnapshotFile(t *testing.T, kvs []kv) string {
|
|||
cfg.Debug = false
|
||||
cfg.Name = "default"
|
||||
cfg.ClusterState = "new"
|
||||
cfg.LCUrls, cfg.ACUrls = cURLs, cURLs
|
||||
cfg.LPUrls, cfg.APUrls = pURLs, pURLs
|
||||
cfg.ListenClientUrls, cfg.AdvertiseClientUrls = cURLs, cURLs
|
||||
cfg.ListenPeerUrls, cfg.AdvertisePeerUrls = pURLs, pURLs
|
||||
cfg.InitialCluster = fmt.Sprintf("%s=%s", cfg.Name, pURLs[0].String())
|
||||
cfg.Dir = filepath.Join(os.TempDir(), fmt.Sprint(time.Now().Nanosecond()))
|
||||
srv, err := embed.StartEtcd(cfg)
|
||||
|
@ -221,7 +222,7 @@ func createSnapshotFile(t *testing.T, kvs []kv) string {
|
|||
t.Fatalf("failed to start embed.Etcd for creating snapshots")
|
||||
}
|
||||
|
||||
ccfg := clientv3.Config{Endpoints: []string{cfg.ACUrls[0].String()}}
|
||||
ccfg := clientv3.Config{Endpoints: []string{cfg.AdvertiseClientUrls[0].String()}}
|
||||
cli, err := clientv3.New(ccfg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -271,8 +272,8 @@ func restoreCluster(t *testing.T, clusterN int, dbPath string) (
|
|||
cfg.Name = fmt.Sprintf("%d", i)
|
||||
cfg.InitialClusterToken = testClusterTkn
|
||||
cfg.ClusterState = "existing"
|
||||
cfg.LCUrls, cfg.ACUrls = []url.URL{cURLs[i]}, []url.URL{cURLs[i]}
|
||||
cfg.LPUrls, cfg.APUrls = []url.URL{pURLs[i]}, []url.URL{pURLs[i]}
|
||||
cfg.ListenClientUrls, cfg.AdvertiseClientUrls = []url.URL{cURLs[i]}, []url.URL{cURLs[i]}
|
||||
cfg.ListenPeerUrls, cfg.AdvertisePeerUrls = []url.URL{pURLs[i]}, []url.URL{pURLs[i]}
|
||||
cfg.InitialCluster = ics
|
||||
cfg.Dir = filepath.Join(os.TempDir(), fmt.Sprint(time.Now().Nanosecond()+i))
|
||||
|
||||
|
|
|
@ -25,15 +25,14 @@ import (
|
|||
|
||||
// Txn is the interface that wraps mini-transactions.
|
||||
//
|
||||
// Txn(context.TODO()).If(
|
||||
// Compare(Value(k1), ">", v1),
|
||||
// Compare(Version(k1), "=", 2)
|
||||
// ).Then(
|
||||
// OpPut(k2,v2), OpPut(k3,v3)
|
||||
// ).Else(
|
||||
// OpPut(k4,v4), OpPut(k5,v5)
|
||||
// ).Commit()
|
||||
//
|
||||
// Txn(context.TODO()).If(
|
||||
// Compare(Value(k1), ">", v1),
|
||||
// Compare(Version(k1), "=", 2)
|
||||
// ).Then(
|
||||
// OpPut(k2,v2), OpPut(k3,v3)
|
||||
// ).Else(
|
||||
// OpPut(k4,v4), OpPut(k5,v5)
|
||||
// ).Commit()
|
||||
type Txn interface {
|
||||
// If takes a list of comparison. If all comparisons passed in succeed,
|
||||
// the operations passed into Then() will be executed. Or the operations
|
||||
|
|
|
@ -16,9 +16,6 @@ package clientv3
|
|||
|
||||
import (
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -32,18 +29,3 @@ func jitterUp(duration time.Duration, jitter float64) time.Duration {
|
|||
multiplier := jitter * (rand.Float64()*2 - 1)
|
||||
return time.Duration(float64(duration) * (1 + multiplier))
|
||||
}
|
||||
|
||||
// Check if the provided function is being called in the op options.
|
||||
func isOpFuncCalled(op string, opts []OpOption) bool {
|
||||
for _, opt := range opts {
|
||||
v := reflect.ValueOf(opt)
|
||||
if v.Kind() == reflect.Func {
|
||||
if opFunc := runtime.FuncForPC(v.Pointer()); opFunc != nil {
|
||||
if strings.Contains(opFunc.Name(), op) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
pb "go.etcd.io/etcd/etcdserver/etcdserverpb"
|
||||
mvccpb "go.etcd.io/etcd/mvcc/mvccpb"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
@ -36,6 +37,13 @@ const (
|
|||
EventTypePut = mvccpb.PUT
|
||||
|
||||
closeSendErrTimeout = 250 * time.Millisecond
|
||||
|
||||
// AutoWatchID is the watcher ID passed in WatchStream.Watch when no
|
||||
// user-provided ID is available. If pass, an ID will automatically be assigned.
|
||||
AutoWatchID = 0
|
||||
|
||||
// InvalidWatchID represents an invalid watch ID and prevents duplication with an existing watch.
|
||||
InvalidWatchID = -1
|
||||
)
|
||||
|
||||
type Event mvccpb.Event
|
||||
|
@ -140,6 +148,7 @@ type watcher struct {
|
|||
|
||||
// streams holds all the active grpc streams keyed by ctx value.
|
||||
streams map[string]*watchGrpcStream
|
||||
lg *zap.Logger
|
||||
}
|
||||
|
||||
// watchGrpcStream tracks all watch resources attached to a single grpc stream.
|
||||
|
@ -176,6 +185,8 @@ type watchGrpcStream struct {
|
|||
resumec chan struct{}
|
||||
// closeErr is the error that closed the watch stream
|
||||
closeErr error
|
||||
|
||||
lg *zap.Logger
|
||||
}
|
||||
|
||||
// watchStreamRequest is a union of the supported watch request operation types
|
||||
|
@ -242,6 +253,7 @@ func NewWatchFromWatchClient(wc pb.WatchClient, c *Client) Watcher {
|
|||
}
|
||||
if c != nil {
|
||||
w.callOpts = c.callOpts
|
||||
w.lg = c.lg
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
@ -273,6 +285,7 @@ func (w *watcher) newWatcherGrpcStream(inctx context.Context) *watchGrpcStream {
|
|||
errc: make(chan error, 1),
|
||||
closingc: make(chan *watcherStream),
|
||||
resumec: make(chan struct{}),
|
||||
lg: w.lg,
|
||||
}
|
||||
go wgs.run()
|
||||
return wgs
|
||||
|
@ -437,7 +450,7 @@ func (w *watcher) closeStream(wgs *watchGrpcStream) {
|
|||
|
||||
func (w *watchGrpcStream) addSubstream(resp *pb.WatchResponse, ws *watcherStream) {
|
||||
// check watch ID for backward compatibility (<= v3.3)
|
||||
if resp.WatchId == -1 || (resp.Canceled && resp.CancelReason != "") {
|
||||
if resp.WatchId == InvalidWatchID || (resp.Canceled && resp.CancelReason != "") {
|
||||
w.closeErr = v3rpc.Error(errors.New(resp.CancelReason))
|
||||
// failed; no channel
|
||||
close(ws.recvc)
|
||||
|
@ -468,7 +481,7 @@ func (w *watchGrpcStream) closeSubstream(ws *watcherStream) {
|
|||
} else if ws.outc != nil {
|
||||
close(ws.outc)
|
||||
}
|
||||
if ws.id != -1 {
|
||||
if ws.id != InvalidWatchID {
|
||||
delete(w.substreams, ws.id)
|
||||
return
|
||||
}
|
||||
|
@ -520,6 +533,7 @@ func (w *watchGrpcStream) run() {
|
|||
cancelSet := make(map[int64]struct{})
|
||||
|
||||
var cur *pb.WatchResponse
|
||||
backoff := time.Millisecond
|
||||
for {
|
||||
select {
|
||||
// Watch() requested
|
||||
|
@ -530,7 +544,7 @@ func (w *watchGrpcStream) run() {
|
|||
// TODO: pass custom watch ID?
|
||||
ws := &watcherStream{
|
||||
initReq: *wreq,
|
||||
id: -1,
|
||||
id: InvalidWatchID,
|
||||
outc: outc,
|
||||
// unbuffered so resumes won't cause repeat events
|
||||
recvc: make(chan *WatchResponse),
|
||||
|
@ -544,10 +558,18 @@ func (w *watchGrpcStream) run() {
|
|||
w.resuming = append(w.resuming, ws)
|
||||
if len(w.resuming) == 1 {
|
||||
// head of resume queue, can register a new watcher
|
||||
wc.Send(ws.initReq.toPB())
|
||||
if err := wc.Send(ws.initReq.toPB()); err != nil {
|
||||
if w.lg != nil {
|
||||
w.lg.Debug("error when sending request", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
case *progressRequest:
|
||||
wc.Send(wreq.toPB())
|
||||
if err := wc.Send(wreq.toPB()); err != nil {
|
||||
if w.lg != nil {
|
||||
w.lg.Debug("error when sending request", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// new events from the watch client
|
||||
|
@ -571,7 +593,11 @@ func (w *watchGrpcStream) run() {
|
|||
}
|
||||
|
||||
if ws := w.nextResume(); ws != nil {
|
||||
wc.Send(ws.initReq.toPB())
|
||||
if err := wc.Send(ws.initReq.toPB()); err != nil {
|
||||
if w.lg != nil {
|
||||
w.lg.Debug("error when sending request", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset for next iteration
|
||||
|
@ -616,7 +642,14 @@ func (w *watchGrpcStream) run() {
|
|||
},
|
||||
}
|
||||
req := &pb.WatchRequest{RequestUnion: cr}
|
||||
wc.Send(req)
|
||||
if w.lg != nil {
|
||||
w.lg.Debug("sending watch cancel request for failed dispatch", zap.Int64("watch-id", pbresp.WatchId))
|
||||
}
|
||||
if err := wc.Send(req); err != nil {
|
||||
if w.lg != nil {
|
||||
w.lg.Debug("failed to send watch cancel request", zap.Int64("watch-id", pbresp.WatchId), zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// watch client failed on Recv; spawn another if possible
|
||||
|
@ -625,11 +658,16 @@ func (w *watchGrpcStream) run() {
|
|||
closeErr = err
|
||||
return
|
||||
}
|
||||
backoff = w.backoffIfUnavailable(backoff, err)
|
||||
if wc, closeErr = w.newWatchClient(); closeErr != nil {
|
||||
return
|
||||
}
|
||||
if ws := w.nextResume(); ws != nil {
|
||||
wc.Send(ws.initReq.toPB())
|
||||
if err := wc.Send(ws.initReq.toPB()); err != nil {
|
||||
if w.lg != nil {
|
||||
w.lg.Debug("error when sending request", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
cancelSet = make(map[int64]struct{})
|
||||
|
||||
|
@ -637,6 +675,25 @@ func (w *watchGrpcStream) run() {
|
|||
return
|
||||
|
||||
case ws := <-w.closingc:
|
||||
if ws.id != InvalidWatchID {
|
||||
// client is closing an established watch; close it on the server proactively instead of waiting
|
||||
// to close when the next message arrives
|
||||
cancelSet[ws.id] = struct{}{}
|
||||
cr := &pb.WatchRequest_CancelRequest{
|
||||
CancelRequest: &pb.WatchCancelRequest{
|
||||
WatchId: ws.id,
|
||||
},
|
||||
}
|
||||
req := &pb.WatchRequest{RequestUnion: cr}
|
||||
if w.lg != nil {
|
||||
w.lg.Debug("sending watch cancel request for closed watcher", zap.Int64("watch-id", ws.id))
|
||||
}
|
||||
if err := wc.Send(req); err != nil {
|
||||
if w.lg != nil {
|
||||
w.lg.Debug("failed to send watch cancel request", zap.Int64("watch-id", ws.id), zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
w.closeSubstream(ws)
|
||||
delete(closing, ws)
|
||||
// no more watchers on this stream, shutdown
|
||||
|
@ -675,9 +732,9 @@ func (w *watchGrpcStream) dispatchEvent(pbresp *pb.WatchResponse) bool {
|
|||
cancelReason: pbresp.CancelReason,
|
||||
}
|
||||
|
||||
// watch IDs are zero indexed, so request notify watch responses are assigned a watch ID of -1 to
|
||||
// watch IDs are zero indexed, so request notify watch responses are assigned a watch ID of InvalidWatchID to
|
||||
// indicate they should be broadcast.
|
||||
if wr.IsProgressNotify() && pbresp.WatchId == -1 {
|
||||
if wr.IsProgressNotify() && pbresp.WatchId == InvalidWatchID {
|
||||
return w.broadcastResponse(wr)
|
||||
}
|
||||
|
||||
|
@ -798,7 +855,7 @@ func (w *watchGrpcStream) serveSubstream(ws *watcherStream, resumec chan struct{
|
|||
}
|
||||
} else {
|
||||
// current progress of watch; <= store revision
|
||||
nextRev = wr.Header.Revision
|
||||
nextRev = wr.Header.Revision + 1
|
||||
}
|
||||
|
||||
if len(wr.Events) > 0 {
|
||||
|
@ -832,7 +889,7 @@ func (w *watchGrpcStream) newWatchClient() (pb.Watch_WatchClient, error) {
|
|||
w.resumec = make(chan struct{})
|
||||
w.joinSubstreams()
|
||||
for _, ws := range w.substreams {
|
||||
ws.id = -1
|
||||
ws.id = InvalidWatchID
|
||||
w.resuming = append(w.resuming, ws)
|
||||
}
|
||||
// strip out nils, if any
|
||||
|
@ -922,6 +979,21 @@ func (w *watchGrpcStream) joinSubstreams() {
|
|||
|
||||
var maxBackoff = 100 * time.Millisecond
|
||||
|
||||
func (w *watchGrpcStream) backoffIfUnavailable(backoff time.Duration, err error) time.Duration {
|
||||
if isUnavailableErr(w.ctx, err) {
|
||||
// retry, but backoff
|
||||
if backoff < maxBackoff {
|
||||
// 25% backoff factor
|
||||
backoff = backoff + backoff/4
|
||||
if backoff > maxBackoff {
|
||||
backoff = maxBackoff
|
||||
}
|
||||
}
|
||||
time.Sleep(backoff)
|
||||
}
|
||||
return backoff
|
||||
}
|
||||
|
||||
// openWatchClient retries opening a watch client until success or halt.
|
||||
// manually retry in case "ws==nil && err==nil"
|
||||
// TODO: remove FailFast=false
|
||||
|
@ -942,17 +1014,7 @@ func (w *watchGrpcStream) openWatchClient() (ws pb.Watch_WatchClient, err error)
|
|||
if isHaltErr(w.ctx, err) {
|
||||
return nil, v3rpc.Error(err)
|
||||
}
|
||||
if isUnavailableErr(w.ctx, err) {
|
||||
// retry, but backoff
|
||||
if backoff < maxBackoff {
|
||||
// 25% backoff factor
|
||||
backoff = backoff + backoff/4
|
||||
if backoff > maxBackoff {
|
||||
backoff = maxBackoff
|
||||
}
|
||||
}
|
||||
time.Sleep(backoff)
|
||||
}
|
||||
backoff = w.backoffIfUnavailable(backoff, err)
|
||||
}
|
||||
return ws, nil
|
||||
}
|
||||
|
|
261
embed/config.go
261
embed/config.go
|
@ -18,6 +18,7 @@ import (
|
|||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -53,7 +54,9 @@ const (
|
|||
DefaultMaxSnapshots = 5
|
||||
DefaultMaxWALs = 5
|
||||
DefaultMaxTxnOps = uint(128)
|
||||
DefaultWarningApplyDuration = 100 * time.Millisecond
|
||||
DefaultMaxRequestBytes = 1.5 * 1024 * 1024
|
||||
DefaultMaxConcurrentStreams = math.MaxUint32
|
||||
DefaultGRPCKeepAliveMinTime = 5 * time.Second
|
||||
DefaultGRPCKeepAliveInterval = 2 * time.Hour
|
||||
DefaultGRPCKeepAliveTimeout = 20 * time.Second
|
||||
|
@ -176,18 +179,27 @@ type Config struct {
|
|||
MaxTxnOps uint `json:"max-txn-ops"`
|
||||
MaxRequestBytes uint `json:"max-request-bytes"`
|
||||
|
||||
LPUrls, LCUrls []url.URL
|
||||
APUrls, ACUrls []url.URL
|
||||
ClientTLSInfo transport.TLSInfo
|
||||
ClientAutoTLS bool
|
||||
PeerTLSInfo transport.TLSInfo
|
||||
PeerAutoTLS bool
|
||||
// MaxConcurrentStreams specifies the maximum number of concurrent
|
||||
// streams that each client can open at a time.
|
||||
MaxConcurrentStreams uint32 `json:"max-concurrent-streams"`
|
||||
|
||||
ListenPeerUrls, ListenClientUrls, ListenClientHttpUrls []url.URL
|
||||
AdvertisePeerUrls, AdvertiseClientUrls []url.URL
|
||||
ClientTLSInfo transport.TLSInfo
|
||||
ClientAutoTLS bool
|
||||
PeerTLSInfo transport.TLSInfo
|
||||
PeerAutoTLS bool
|
||||
|
||||
// CipherSuites is a list of supported TLS cipher suites between
|
||||
// client/server and peers. If empty, Go auto-populates the list.
|
||||
// Note that cipher suites are prioritized in the given order.
|
||||
CipherSuites []string `json:"cipher-suites"`
|
||||
|
||||
// TlsMinVersion is the minimum accepted TLS version between client/server and peers.
|
||||
TlsMinVersion string `json:"tls-min-version"`
|
||||
// TlsMaxVersion is the maximum accepted TLS version between client/server and peers.
|
||||
TlsMaxVersion string `json:"tls-max-version"`
|
||||
|
||||
ClusterState string `json:"initial-cluster-state"`
|
||||
DNSCluster string `json:"discovery-srv"`
|
||||
DNSClusterServiceName string `json:"discovery-srv-name"`
|
||||
|
@ -273,13 +285,26 @@ type Config struct {
|
|||
AuthToken string `json:"auth-token"`
|
||||
BcryptCost uint `json:"bcrypt-cost"`
|
||||
|
||||
// AuthTokenTTL specifies the TTL in seconds of the simple token
|
||||
AuthTokenTTL uint `json:"auth-token-ttl"`
|
||||
|
||||
ExperimentalInitialCorruptCheck bool `json:"experimental-initial-corrupt-check"`
|
||||
ExperimentalCorruptCheckTime time.Duration `json:"experimental-corrupt-check-time"`
|
||||
ExperimentalEnableV2V3 string `json:"experimental-enable-v2v3"`
|
||||
// ExperimentalBackendFreelistType specifies the type of freelist that boltdb backend uses (array and map are supported types).
|
||||
ExperimentalBackendFreelistType string `json:"experimental-backend-bbolt-freelist-type"`
|
||||
// ExperimentalEnableLeaseCheckpoint enables primary lessor to persist lease remainingTTL to prevent indefinite auto-renewal of long lived leases.
|
||||
// ExperimentalEnableLeaseCheckpoint enables leader to send regular checkpoints to other members to prevent reset of remaining TTL on leader change.
|
||||
ExperimentalEnableLeaseCheckpoint bool `json:"experimental-enable-lease-checkpoint"`
|
||||
// ExperimentalEnableLeaseCheckpointPersist enables persisting remainingTTL to prevent indefinite auto-renewal of long lived leases. Always enabled in v3.6. Should be used to ensure smooth upgrade from v3.5 clusters with this feature enabled.
|
||||
// Requires experimental-enable-lease-checkpoint to be enabled.
|
||||
// Deprecated in v3.6.
|
||||
// TODO: Delete in v3.7
|
||||
ExperimentalEnableLeaseCheckpointPersist bool `json:"experimental-enable-lease-checkpoint-persist"`
|
||||
ExperimentalCompactionBatchLimit int `json:"experimental-compaction-batch-limit"`
|
||||
ExperimentalWatchProgressNotifyInterval time.Duration `json:"experimental-watch-progress-notify-interval"`
|
||||
// ExperimentalWarningApplyDuration is the time duration after which a warning is generated if applying request
|
||||
// takes more time than this value.
|
||||
ExperimentalWarningApplyDuration time.Duration `json:"experimental-warning-apply-duration"`
|
||||
|
||||
// ForceNewCluster starts a new cluster even if previously started; unsafe.
|
||||
ForceNewCluster bool `json:"force-new-cluster"`
|
||||
|
@ -302,8 +327,8 @@ type Config struct {
|
|||
// It can be multiple when "Logger" is zap.
|
||||
LogOutputs []string `json:"log-outputs"`
|
||||
|
||||
// zapLoggerBuilder is used to build the zap logger.
|
||||
zapLoggerBuilder func(*Config) error
|
||||
// ZapLoggerBuilder is used to build the zap logger.
|
||||
ZapLoggerBuilder func(*Config) error
|
||||
|
||||
// logger logs server-side operations. The default is nil,
|
||||
// and "setupLogging" must be called before starting server.
|
||||
|
@ -334,6 +359,10 @@ type Config struct {
|
|||
// Only valid if "logger" option is "capnslog".
|
||||
// WARN: DO NOT USE THIS!
|
||||
LogPkgLevels string `json:"log-package-levels"`
|
||||
|
||||
// UnsafeNoFsync disables all uses of fsync.
|
||||
// Setting this is unsafe and will cause data loss.
|
||||
UnsafeNoFsync bool `json:"unsafe-no-fsync"`
|
||||
}
|
||||
|
||||
// configYAML holds the config suitable for yaml parsing
|
||||
|
@ -344,10 +373,11 @@ type configYAML struct {
|
|||
|
||||
// configJSON has file options that are translated into Config options
|
||||
type configJSON struct {
|
||||
LPUrlsJSON string `json:"listen-peer-urls"`
|
||||
LCUrlsJSON string `json:"listen-client-urls"`
|
||||
APUrlsJSON string `json:"initial-advertise-peer-urls"`
|
||||
ACUrlsJSON string `json:"advertise-client-urls"`
|
||||
ListenPeerUrls string `json:"listen-peer-urls"`
|
||||
ListenClientUrls string `json:"listen-client-urls"`
|
||||
ListenClientHttpUrls string `json:"listen-client-http-urls"`
|
||||
AdvertisePeerUrls string `json:"initial-advertise-peer-urls"`
|
||||
AdvertiseClientUrls string `json:"advertise-client-urls"`
|
||||
|
||||
CORSJSON string `json:"cors"`
|
||||
HostWhitelistJSON string `json:"host-whitelist"`
|
||||
|
@ -379,8 +409,10 @@ func NewConfig() *Config {
|
|||
SnapshotCount: etcdserver.DefaultSnapshotCount,
|
||||
SnapshotCatchUpEntries: etcdserver.DefaultSnapshotCatchUpEntries,
|
||||
|
||||
MaxTxnOps: DefaultMaxTxnOps,
|
||||
MaxRequestBytes: DefaultMaxRequestBytes,
|
||||
MaxTxnOps: DefaultMaxTxnOps,
|
||||
MaxRequestBytes: DefaultMaxRequestBytes,
|
||||
MaxConcurrentStreams: DefaultMaxConcurrentStreams,
|
||||
ExperimentalWarningApplyDuration: DefaultWarningApplyDuration,
|
||||
|
||||
GRPCKeepAliveMinTime: DefaultGRPCKeepAliveMinTime,
|
||||
GRPCKeepAliveInterval: DefaultGRPCKeepAliveInterval,
|
||||
|
@ -390,10 +422,10 @@ func NewConfig() *Config {
|
|||
ElectionMs: 1000,
|
||||
InitialElectionTickAdvance: true,
|
||||
|
||||
LPUrls: []url.URL{*lpurl},
|
||||
LCUrls: []url.URL{*lcurl},
|
||||
APUrls: []url.URL{*apurl},
|
||||
ACUrls: []url.URL{*acurl},
|
||||
ListenPeerUrls: []url.URL{*lpurl},
|
||||
ListenClientUrls: []url.URL{*lcurl},
|
||||
AdvertisePeerUrls: []url.URL{*apurl},
|
||||
AdvertiseClientUrls: []url.URL{*acurl},
|
||||
|
||||
ClusterState: ClusterStateFlagNew,
|
||||
InitialClusterToken: "etcd-cluster",
|
||||
|
@ -405,8 +437,9 @@ func NewConfig() *Config {
|
|||
CORS: map[string]struct{}{"*": {}},
|
||||
HostWhitelist: map[string]struct{}{"*": {}},
|
||||
|
||||
AuthToken: "simple",
|
||||
BcryptCost: uint(bcrypt.DefaultCost),
|
||||
AuthToken: "simple",
|
||||
BcryptCost: uint(bcrypt.DefaultCost),
|
||||
AuthTokenTTL: 300,
|
||||
|
||||
PreVote: false, // TODO: enable by default in v3.5
|
||||
|
||||
|
@ -457,40 +490,49 @@ func (cfg *configYAML) configFromFile(path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if cfg.LPUrlsJSON != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.LPUrlsJSON, ","))
|
||||
if cfg.configJSON.ListenPeerUrls != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.configJSON.ListenPeerUrls, ","))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unexpected error setting up listen-peer-urls: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.LPUrls = []url.URL(u)
|
||||
cfg.Config.ListenPeerUrls = u
|
||||
}
|
||||
|
||||
if cfg.LCUrlsJSON != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.LCUrlsJSON, ","))
|
||||
if cfg.configJSON.ListenClientUrls != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.configJSON.ListenClientUrls, ","))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unexpected error setting up listen-client-urls: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.LCUrls = []url.URL(u)
|
||||
cfg.Config.ListenClientUrls = u
|
||||
}
|
||||
|
||||
if cfg.APUrlsJSON != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.APUrlsJSON, ","))
|
||||
if cfg.configJSON.ListenClientHttpUrls != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.configJSON.ListenClientHttpUrls, ","))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unexpected error setting up listen-client-http-urls: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.Config.ListenClientHttpUrls = u
|
||||
}
|
||||
|
||||
if cfg.configJSON.AdvertisePeerUrls != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.configJSON.AdvertisePeerUrls, ","))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unexpected error setting up initial-advertise-peer-urls: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.APUrls = []url.URL(u)
|
||||
cfg.Config.AdvertisePeerUrls = u
|
||||
}
|
||||
|
||||
if cfg.ACUrlsJSON != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.ACUrlsJSON, ","))
|
||||
if cfg.configJSON.AdvertiseClientUrls != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.configJSON.AdvertiseClientUrls, ","))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unexpected error setting up advertise-peer-urls: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.ACUrls = []url.URL(u)
|
||||
cfg.Config.AdvertiseClientUrls = u
|
||||
}
|
||||
|
||||
if cfg.ListenMetricsUrlsJSON != "" {
|
||||
|
@ -539,39 +581,56 @@ func updateCipherSuites(tls *transport.TLSInfo, ss []string) error {
|
|||
return fmt.Errorf("TLSInfo.CipherSuites is already specified (given %v)", ss)
|
||||
}
|
||||
if len(ss) > 0 {
|
||||
cs := make([]uint16, len(ss))
|
||||
for i, s := range ss {
|
||||
var ok bool
|
||||
cs[i], ok = tlsutil.GetCipherSuite(s)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected TLS cipher suite %q", s)
|
||||
}
|
||||
cs, err := tlsutil.GetCipherSuites(ss)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tls.CipherSuites = cs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateMinMaxVersions(info *transport.TLSInfo, min, max string) {
|
||||
// Validate() has been called to check the user input, so it should never fail.
|
||||
var err error
|
||||
if info.MinVersion, err = tlsutil.GetTLSVersion(min); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if info.MaxVersion, err = tlsutil.GetTLSVersion(max); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate ensures that '*embed.Config' fields are properly configured.
|
||||
func (cfg *Config) Validate() error {
|
||||
if err := cfg.setupLogging(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkBindURLs(cfg.LPUrls); err != nil {
|
||||
if err := checkBindURLs(cfg.ListenPeerUrls); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkBindURLs(cfg.LCUrls); err != nil {
|
||||
if err := checkBindURLs(cfg.ListenClientUrls); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkBindURLs(cfg.ListenClientHttpUrls); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(cfg.ListenClientHttpUrls) == 0 {
|
||||
if cfg.logger != nil {
|
||||
cfg.logger.Warn("Running http and grpc server on single port. This is not recommended for production.")
|
||||
} else {
|
||||
plog.Warning("Running http and grpc server on single port. This is not recommended for production.")
|
||||
}
|
||||
}
|
||||
if err := checkBindURLs(cfg.ListenMetricsUrls); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkHostURLs(cfg.APUrls); err != nil {
|
||||
addrs := cfg.getAPURLs()
|
||||
if err := checkHostURLs(cfg.AdvertisePeerUrls); err != nil {
|
||||
addrs := cfg.getAdvertisePeerUrls()
|
||||
return fmt.Errorf(`--initial-advertise-peer-urls %q must be "host:port" (%v)`, strings.Join(addrs, ","), err)
|
||||
}
|
||||
if err := checkHostURLs(cfg.ACUrls); err != nil {
|
||||
addrs := cfg.getACURLs()
|
||||
if err := checkHostURLs(cfg.AdvertiseClientUrls); err != nil {
|
||||
addrs := cfg.getAdvertiseClientUrls()
|
||||
return fmt.Errorf(`--advertise-client-urls %q must be "host:port" (%v)`, strings.Join(addrs, ","), err)
|
||||
}
|
||||
// Check if conflicting flags are passed.
|
||||
|
@ -604,7 +663,7 @@ func (cfg *Config) Validate() error {
|
|||
}
|
||||
|
||||
// check this last since proxying in etcdmain may make this OK
|
||||
if cfg.LCUrls != nil && cfg.ACUrls == nil {
|
||||
if cfg.ListenClientUrls != nil && cfg.AdvertiseClientUrls == nil {
|
||||
return ErrUnsetAdvertiseClientURLsFlag
|
||||
}
|
||||
|
||||
|
@ -615,6 +674,33 @@ func (cfg *Config) Validate() error {
|
|||
return fmt.Errorf("unknown auto-compaction-mode %q", cfg.AutoCompactionMode)
|
||||
}
|
||||
|
||||
if !cfg.ExperimentalEnableLeaseCheckpointPersist && cfg.ExperimentalEnableLeaseCheckpoint {
|
||||
cfg.logger.Warn("Detected that checkpointing is enabled without persistence. Consider enabling experimental-enable-lease-checkpoint-persist")
|
||||
}
|
||||
|
||||
if cfg.ExperimentalEnableLeaseCheckpointPersist && !cfg.ExperimentalEnableLeaseCheckpoint {
|
||||
return fmt.Errorf("setting experimental-enable-lease-checkpoint-persist requires experimental-enable-lease-checkpoint")
|
||||
}
|
||||
|
||||
minVersion, err := tlsutil.GetTLSVersion(cfg.TlsMinVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
maxVersion, err := tlsutil.GetTLSVersion(cfg.TlsMaxVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// maxVersion == 0 means that Go selects the highest available version.
|
||||
if maxVersion != 0 && minVersion > maxVersion {
|
||||
return fmt.Errorf("min version (%s) is greater than max version (%s)", cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||
}
|
||||
|
||||
// Check if user attempted to configure ciphers for TLS1.3 only: Go does not support that currently.
|
||||
if minVersion == tls.VersionTLS13 && len(cfg.CipherSuites) > 0 {
|
||||
return fmt.Errorf("cipher suites cannot be configured when only TLS1.3 is enabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -626,7 +712,7 @@ func (cfg *Config) PeerURLsMapAndToken(which string) (urlsmap types.URLsMap, tok
|
|||
urlsmap = types.URLsMap{}
|
||||
// If using discovery, generate a temporary cluster based on
|
||||
// self's advertised peer URLs
|
||||
urlsmap[cfg.Name] = cfg.APUrls
|
||||
urlsmap[cfg.Name] = cfg.AdvertisePeerUrls
|
||||
token = cfg.Durl
|
||||
|
||||
case cfg.DNSCluster != "":
|
||||
|
@ -682,7 +768,7 @@ func (cfg *Config) GetDNSClusterNames() ([]string, error) {
|
|||
|
||||
// Use both etcd-server-ssl and etcd-server for discovery.
|
||||
// Combine the results if both are available.
|
||||
clusterStrs, cerr = srv.GetCluster("https", "etcd-server-ssl"+serviceNameSuffix, cfg.Name, cfg.DNSCluster, cfg.APUrls)
|
||||
clusterStrs, cerr = srv.GetCluster("https", "etcd-server-ssl"+serviceNameSuffix, cfg.Name, cfg.DNSCluster, cfg.AdvertisePeerUrls)
|
||||
if cerr != nil {
|
||||
clusterStrs = make([]string, 0)
|
||||
}
|
||||
|
@ -693,13 +779,13 @@ func (cfg *Config) GetDNSClusterNames() ([]string, error) {
|
|||
zap.String("service-name", "etcd-server-ssl"+serviceNameSuffix),
|
||||
zap.String("server-name", cfg.Name),
|
||||
zap.String("discovery-srv", cfg.DNSCluster),
|
||||
zap.Strings("advertise-peer-urls", cfg.getAPURLs()),
|
||||
zap.Strings("advertise-peer-urls", cfg.getAdvertisePeerUrls()),
|
||||
zap.Strings("found-cluster", clusterStrs),
|
||||
zap.Error(cerr),
|
||||
)
|
||||
}
|
||||
|
||||
defaultHTTPClusterStrs, httpCerr := srv.GetCluster("http", "etcd-server"+serviceNameSuffix, cfg.Name, cfg.DNSCluster, cfg.APUrls)
|
||||
defaultHTTPClusterStrs, httpCerr := srv.GetCluster("http", "etcd-server"+serviceNameSuffix, cfg.Name, cfg.DNSCluster, cfg.AdvertisePeerUrls)
|
||||
if httpCerr != nil {
|
||||
clusterStrs = append(clusterStrs, defaultHTTPClusterStrs...)
|
||||
}
|
||||
|
@ -710,7 +796,7 @@ func (cfg *Config) GetDNSClusterNames() ([]string, error) {
|
|||
zap.String("service-name", "etcd-server"+serviceNameSuffix),
|
||||
zap.String("server-name", cfg.Name),
|
||||
zap.String("discovery-srv", cfg.DNSCluster),
|
||||
zap.Strings("advertise-peer-urls", cfg.getAPURLs()),
|
||||
zap.Strings("advertise-peer-urls", cfg.getAdvertisePeerUrls()),
|
||||
zap.Strings("found-cluster", clusterStrs),
|
||||
zap.Error(httpCerr),
|
||||
)
|
||||
|
@ -720,15 +806,15 @@ func (cfg *Config) GetDNSClusterNames() ([]string, error) {
|
|||
}
|
||||
|
||||
func (cfg Config) InitialClusterFromName(name string) (ret string) {
|
||||
if len(cfg.APUrls) == 0 {
|
||||
if len(cfg.AdvertisePeerUrls) == 0 {
|
||||
return ""
|
||||
}
|
||||
n := name
|
||||
if name == "" {
|
||||
n = DefaultName
|
||||
}
|
||||
for i := range cfg.APUrls {
|
||||
ret = ret + "," + n + "=" + cfg.APUrls[i].String()
|
||||
for i := range cfg.AdvertisePeerUrls {
|
||||
ret = ret + "," + n + "=" + cfg.AdvertisePeerUrls[i].String()
|
||||
}
|
||||
return ret[1:]
|
||||
}
|
||||
|
@ -737,11 +823,11 @@ func (cfg Config) IsNewCluster() bool { return cfg.ClusterState == ClusterStateF
|
|||
func (cfg Config) ElectionTicks() int { return int(cfg.ElectionMs / cfg.TickMs) }
|
||||
|
||||
func (cfg Config) defaultPeerHost() bool {
|
||||
return len(cfg.APUrls) == 1 && cfg.APUrls[0].String() == DefaultInitialAdvertisePeerURLs
|
||||
return len(cfg.AdvertisePeerUrls) == 1 && cfg.AdvertisePeerUrls[0].String() == DefaultInitialAdvertisePeerURLs
|
||||
}
|
||||
|
||||
func (cfg Config) defaultClientHost() bool {
|
||||
return len(cfg.ACUrls) == 1 && cfg.ACUrls[0].String() == DefaultAdvertiseClientURLs
|
||||
return len(cfg.AdvertiseClientUrls) == 1 && cfg.AdvertiseClientUrls[0].String() == DefaultAdvertiseClientURLs
|
||||
}
|
||||
|
||||
func (cfg *Config) ClientSelfCert() (err error) {
|
||||
|
@ -756,9 +842,12 @@ func (cfg *Config) ClientSelfCert() (err error) {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
chosts := make([]string, len(cfg.LCUrls))
|
||||
for i, u := range cfg.LCUrls {
|
||||
chosts[i] = u.Host
|
||||
chosts := make([]string, 0, len(cfg.ListenClientUrls)+len(cfg.ListenClientHttpUrls))
|
||||
for _, u := range cfg.ListenClientUrls {
|
||||
chosts = append(chosts, u.Host)
|
||||
}
|
||||
for _, u := range cfg.ListenClientHttpUrls {
|
||||
chosts = append(chosts, u.Host)
|
||||
}
|
||||
cfg.ClientTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "client"), chosts)
|
||||
if err != nil {
|
||||
|
@ -779,8 +868,8 @@ func (cfg *Config) PeerSelfCert() (err error) {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
phosts := make([]string, len(cfg.LPUrls))
|
||||
for i, u := range cfg.LPUrls {
|
||||
phosts := make([]string, len(cfg.ListenPeerUrls))
|
||||
for i, u := range cfg.ListenPeerUrls {
|
||||
phosts[i] = u.Host
|
||||
}
|
||||
cfg.PeerTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "peer"), phosts)
|
||||
|
@ -808,9 +897,9 @@ func (cfg *Config) UpdateDefaultClusterFromName(defaultInitialCluster string) (s
|
|||
}
|
||||
|
||||
used := false
|
||||
pip, pport := cfg.LPUrls[0].Hostname(), cfg.LPUrls[0].Port()
|
||||
pip, pport := cfg.ListenPeerUrls[0].Hostname(), cfg.ListenPeerUrls[0].Port()
|
||||
if cfg.defaultPeerHost() && pip == "0.0.0.0" {
|
||||
cfg.APUrls[0] = url.URL{Scheme: cfg.APUrls[0].Scheme, Host: fmt.Sprintf("%s:%s", defaultHostname, pport)}
|
||||
cfg.AdvertisePeerUrls[0] = url.URL{Scheme: cfg.AdvertisePeerUrls[0].Scheme, Host: fmt.Sprintf("%s:%s", defaultHostname, pport)}
|
||||
used = true
|
||||
}
|
||||
// update 'initial-cluster' when only the name is specified (e.g. 'etcd --name=abc')
|
||||
|
@ -818,9 +907,9 @@ func (cfg *Config) UpdateDefaultClusterFromName(defaultInitialCluster string) (s
|
|||
cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name)
|
||||
}
|
||||
|
||||
cip, cport := cfg.LCUrls[0].Hostname(), cfg.LCUrls[0].Port()
|
||||
cip, cport := cfg.ListenClientUrls[0].Hostname(), cfg.ListenClientUrls[0].Port()
|
||||
if cfg.defaultClientHost() && cip == "0.0.0.0" {
|
||||
cfg.ACUrls[0] = url.URL{Scheme: cfg.ACUrls[0].Scheme, Host: fmt.Sprintf("%s:%s", defaultHostname, cport)}
|
||||
cfg.AdvertiseClientUrls[0] = url.URL{Scheme: cfg.AdvertiseClientUrls[0].Scheme, Host: fmt.Sprintf("%s:%s", defaultHostname, cport)}
|
||||
used = true
|
||||
}
|
||||
dhost := defaultHostname
|
||||
|
@ -865,34 +954,42 @@ func checkHostURLs(urls []url.URL) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Config) getAPURLs() (ss []string) {
|
||||
ss = make([]string, len(cfg.APUrls))
|
||||
for i := range cfg.APUrls {
|
||||
ss[i] = cfg.APUrls[i].String()
|
||||
func (cfg *Config) getAdvertisePeerUrls() (ss []string) {
|
||||
ss = make([]string, len(cfg.AdvertisePeerUrls))
|
||||
for i := range cfg.AdvertisePeerUrls {
|
||||
ss[i] = cfg.AdvertisePeerUrls[i].String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func (cfg *Config) getLPURLs() (ss []string) {
|
||||
ss = make([]string, len(cfg.LPUrls))
|
||||
for i := range cfg.LPUrls {
|
||||
ss[i] = cfg.LPUrls[i].String()
|
||||
func (cfg *Config) getListenPeerUrls() (ss []string) {
|
||||
ss = make([]string, len(cfg.ListenPeerUrls))
|
||||
for i := range cfg.ListenPeerUrls {
|
||||
ss[i] = cfg.ListenPeerUrls[i].String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func (cfg *Config) getACURLs() (ss []string) {
|
||||
ss = make([]string, len(cfg.ACUrls))
|
||||
for i := range cfg.ACUrls {
|
||||
ss[i] = cfg.ACUrls[i].String()
|
||||
func (cfg *Config) getAdvertiseClientUrls() (ss []string) {
|
||||
ss = make([]string, len(cfg.AdvertiseClientUrls))
|
||||
for i := range cfg.AdvertiseClientUrls {
|
||||
ss[i] = cfg.AdvertiseClientUrls[i].String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func (cfg *Config) getLCURLs() (ss []string) {
|
||||
ss = make([]string, len(cfg.LCUrls))
|
||||
for i := range cfg.LCUrls {
|
||||
ss[i] = cfg.LCUrls[i].String()
|
||||
func (cfg *Config) getListenClientUrls() (ss []string) {
|
||||
ss = make([]string, len(cfg.ListenClientUrls))
|
||||
for i := range cfg.ListenClientUrls {
|
||||
ss[i] = cfg.ListenClientUrls[i].String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func (cfg *Config) getListenClientHttpUrls() (ss []string) {
|
||||
ss = make([]string, len(cfg.ListenClientHttpUrls))
|
||||
for i := range cfg.ListenClientHttpUrls {
|
||||
ss[i] = cfg.ListenClientHttpUrls[i].String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
|
|
@ -170,7 +170,10 @@ func (cfg *Config) setupLogging() error {
|
|||
}
|
||||
|
||||
if !isJournal {
|
||||
copied := logutil.AddOutputPaths(logutil.DefaultZapLoggerConfig, outputPaths, errOutputPaths)
|
||||
copied := logutil.DefaultZapLoggerConfig
|
||||
copied.OutputPaths = outputPaths
|
||||
copied.ErrorOutputPaths = errOutputPaths
|
||||
copied = logutil.MergeOutputPaths(copied)
|
||||
copied.Level = zap.NewAtomicLevelAt(logutil.ConvertToZapLevel(cfg.LogLevel))
|
||||
if cfg.Debug || cfg.LogLevel == "debug" {
|
||||
// enable tracing even when "--debug --log-level info"
|
||||
|
@ -178,8 +181,8 @@ func (cfg *Config) setupLogging() error {
|
|||
// TODO: remove "Debug" check in v3.5
|
||||
grpc.EnableTracing = true
|
||||
}
|
||||
if cfg.zapLoggerBuilder == nil {
|
||||
cfg.zapLoggerBuilder = func(c *Config) error {
|
||||
if cfg.ZapLoggerBuilder == nil {
|
||||
cfg.ZapLoggerBuilder = func(c *Config) error {
|
||||
var err error
|
||||
c.logger, err = copied.Build()
|
||||
if err != nil {
|
||||
|
@ -193,10 +196,14 @@ func (cfg *Config) setupLogging() error {
|
|||
grpcLogOnce.Do(func() {
|
||||
// debug true, enable info, warning, error
|
||||
// debug false, only discard info
|
||||
var gl grpclog.LoggerV2
|
||||
gl, err = logutil.NewGRPCLoggerV2(copied)
|
||||
if err == nil {
|
||||
grpclog.SetLoggerV2(gl)
|
||||
if cfg.LogLevel == "debug" {
|
||||
var gl grpclog.LoggerV2
|
||||
gl, err = logutil.NewGRPCLoggerV2(copied)
|
||||
if err == nil {
|
||||
grpclog.SetLoggerV2(gl)
|
||||
}
|
||||
} else {
|
||||
grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, os.Stderr, os.Stderr))
|
||||
}
|
||||
})
|
||||
return nil
|
||||
|
@ -232,8 +239,8 @@ func (cfg *Config) setupLogging() error {
|
|||
syncer,
|
||||
lvl,
|
||||
)
|
||||
if cfg.zapLoggerBuilder == nil {
|
||||
cfg.zapLoggerBuilder = func(c *Config) error {
|
||||
if cfg.ZapLoggerBuilder == nil {
|
||||
cfg.ZapLoggerBuilder = func(c *Config) error {
|
||||
c.logger = zap.New(cr, zap.AddCaller(), zap.ErrorOutput(syncer))
|
||||
c.loggerMu.Lock()
|
||||
defer c.loggerMu.Unlock()
|
||||
|
@ -242,14 +249,18 @@ func (cfg *Config) setupLogging() error {
|
|||
c.loggerWriteSyncer = syncer
|
||||
|
||||
grpcLogOnce.Do(func() {
|
||||
grpclog.SetLoggerV2(logutil.NewGRPCLoggerV2FromZapCore(cr, syncer))
|
||||
if cfg.LogLevel == "debug" {
|
||||
grpclog.SetLoggerV2(logutil.NewGRPCLoggerV2FromZapCore(cr, syncer))
|
||||
} else {
|
||||
grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, os.Stderr, os.Stderr))
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := cfg.zapLoggerBuilder(cfg)
|
||||
err := cfg.ZapLoggerBuilder(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package embed
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package embed
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package embed
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
|
@ -22,6 +23,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.etcd.io/etcd/pkg/transport"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
|
@ -75,12 +77,12 @@ func TestConfigFileOtherFields(t *testing.T) {
|
|||
func TestUpdateDefaultClusterFromName(t *testing.T) {
|
||||
cfg := NewConfig()
|
||||
defaultInitialCluster := cfg.InitialCluster
|
||||
oldscheme := cfg.APUrls[0].Scheme
|
||||
origpeer := cfg.APUrls[0].String()
|
||||
origadvc := cfg.ACUrls[0].String()
|
||||
oldscheme := cfg.AdvertisePeerUrls[0].Scheme
|
||||
origpeer := cfg.AdvertisePeerUrls[0].String()
|
||||
origadvc := cfg.AdvertiseClientUrls[0].String()
|
||||
|
||||
cfg.Name = "abc"
|
||||
lpport := cfg.LPUrls[0].Port()
|
||||
lpport := cfg.ListenPeerUrls[0].Port()
|
||||
|
||||
// in case of 'etcd --name=abc'
|
||||
exp := fmt.Sprintf("%s=%s://localhost:%s", cfg.Name, oldscheme, lpport)
|
||||
|
@ -89,12 +91,12 @@ func TestUpdateDefaultClusterFromName(t *testing.T) {
|
|||
t.Fatalf("initial-cluster expected %q, got %q", exp, cfg.InitialCluster)
|
||||
}
|
||||
// advertise peer URL should not be affected
|
||||
if origpeer != cfg.APUrls[0].String() {
|
||||
t.Fatalf("advertise peer url expected %q, got %q", origadvc, cfg.APUrls[0].String())
|
||||
if origpeer != cfg.AdvertisePeerUrls[0].String() {
|
||||
t.Fatalf("advertise peer url expected %q, got %q", origadvc, cfg.AdvertisePeerUrls[0].String())
|
||||
}
|
||||
// advertise client URL should not be affected
|
||||
if origadvc != cfg.ACUrls[0].String() {
|
||||
t.Fatalf("advertise client url expected %q, got %q", origadvc, cfg.ACUrls[0].String())
|
||||
if origadvc != cfg.AdvertiseClientUrls[0].String() {
|
||||
t.Fatalf("advertise client url expected %q, got %q", origadvc, cfg.AdvertiseClientUrls[0].String())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,17 +109,17 @@ func TestUpdateDefaultClusterFromNameOverwrite(t *testing.T) {
|
|||
|
||||
cfg := NewConfig()
|
||||
defaultInitialCluster := cfg.InitialCluster
|
||||
oldscheme := cfg.APUrls[0].Scheme
|
||||
origadvc := cfg.ACUrls[0].String()
|
||||
oldscheme := cfg.AdvertisePeerUrls[0].Scheme
|
||||
origadvc := cfg.AdvertiseClientUrls[0].String()
|
||||
|
||||
cfg.Name = "abc"
|
||||
lpport := cfg.LPUrls[0].Port()
|
||||
cfg.LPUrls[0] = url.URL{Scheme: cfg.LPUrls[0].Scheme, Host: fmt.Sprintf("0.0.0.0:%s", lpport)}
|
||||
lpport := cfg.ListenPeerUrls[0].Port()
|
||||
cfg.ListenPeerUrls[0] = url.URL{Scheme: cfg.ListenPeerUrls[0].Scheme, Host: fmt.Sprintf("0.0.0.0:%s", lpport)}
|
||||
dhost, _ := cfg.UpdateDefaultClusterFromName(defaultInitialCluster)
|
||||
if dhost != defaultHostname {
|
||||
t.Fatalf("expected default host %q, got %q", defaultHostname, dhost)
|
||||
}
|
||||
aphost, apport := cfg.APUrls[0].Hostname(), cfg.APUrls[0].Port()
|
||||
aphost, apport := cfg.AdvertisePeerUrls[0].Hostname(), cfg.AdvertisePeerUrls[0].Port()
|
||||
if apport != lpport {
|
||||
t.Fatalf("advertise peer url got different port %s, expected %s", apport, lpport)
|
||||
}
|
||||
|
@ -130,8 +132,8 @@ func TestUpdateDefaultClusterFromNameOverwrite(t *testing.T) {
|
|||
}
|
||||
|
||||
// advertise client URL should not be affected
|
||||
if origadvc != cfg.ACUrls[0].String() {
|
||||
t.Fatalf("advertise-client-url expected %q, got %q", origadvc, cfg.ACUrls[0].String())
|
||||
if origadvc != cfg.AdvertiseClientUrls[0].String() {
|
||||
t.Fatalf("advertise-client-url expected %q, got %q", origadvc, cfg.AdvertiseClientUrls[0].String())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,9 +180,11 @@ func TestAutoCompactionModeParse(t *testing.T) {
|
|||
{"revision", "1", false, 1},
|
||||
{"revision", "1h", false, time.Hour},
|
||||
{"revision", "a", true, 0},
|
||||
{"revision", "-1", true, 0},
|
||||
// periodic
|
||||
{"periodic", "1", false, time.Hour},
|
||||
{"periodic", "a", true, 0},
|
||||
{"revision", "-1", true, 0},
|
||||
// err mode
|
||||
{"errmode", "1", false, 0},
|
||||
{"errmode", "1h", false, time.Hour},
|
||||
|
@ -200,3 +204,80 @@ func TestAutoCompactionModeParse(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSVersionMinMax(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
givenTLSMinVersion string
|
||||
givenTLSMaxVersion string
|
||||
givenCipherSuites []string
|
||||
expectError bool
|
||||
expectedMinTLSVersion uint16
|
||||
expectedMaxTLSVersion uint16
|
||||
}{
|
||||
{
|
||||
name: "Minimum TLS version is set",
|
||||
givenTLSMinVersion: "TLS1.3",
|
||||
expectedMinTLSVersion: tls.VersionTLS13,
|
||||
expectedMaxTLSVersion: 0,
|
||||
},
|
||||
{
|
||||
name: "Maximum TLS version is set",
|
||||
givenTLSMaxVersion: "TLS1.2",
|
||||
expectedMinTLSVersion: 0,
|
||||
expectedMaxTLSVersion: tls.VersionTLS12,
|
||||
},
|
||||
{
|
||||
name: "Minimum and Maximum TLS versions are set",
|
||||
givenTLSMinVersion: "TLS1.3",
|
||||
givenTLSMaxVersion: "TLS1.3",
|
||||
expectedMinTLSVersion: tls.VersionTLS13,
|
||||
expectedMaxTLSVersion: tls.VersionTLS13,
|
||||
},
|
||||
{
|
||||
name: "Minimum and Maximum TLS versions are set in reverse order",
|
||||
givenTLSMinVersion: "TLS1.3",
|
||||
givenTLSMaxVersion: "TLS1.2",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid minimum TLS version",
|
||||
givenTLSMinVersion: "invalid version",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid maximum TLS version",
|
||||
givenTLSMaxVersion: "invalid version",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Cipher suites configured for TLS 1.3",
|
||||
givenTLSMinVersion: "TLS1.3",
|
||||
givenCipherSuites: []string{"TLS_AES_128_GCM_SHA256"},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := NewConfig()
|
||||
cfg.TlsMinVersion = tt.givenTLSMinVersion
|
||||
cfg.TlsMaxVersion = tt.givenTLSMaxVersion
|
||||
cfg.CipherSuites = tt.givenCipherSuites
|
||||
|
||||
err := cfg.Validate()
|
||||
if err != nil {
|
||||
assert.True(t, tt.expectError, "Validate() returned error while expecting success: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
updateMinMaxVersions(&cfg.PeerTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||
updateMinMaxVersions(&cfg.ClientTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||
|
||||
assert.Equal(t, tt.expectedMinTLSVersion, cfg.PeerTLSInfo.MinVersion)
|
||||
assert.Equal(t, tt.expectedMaxTLSVersion, cfg.PeerTLSInfo.MaxVersion)
|
||||
assert.Equal(t, tt.expectedMinTLSVersion, cfg.ClientTLSInfo.MinVersion)
|
||||
assert.Equal(t, tt.expectedMaxTLSVersion, cfg.ClientTLSInfo.MaxVersion)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
271
embed/etcd.go
271
embed/etcd.go
|
@ -20,6 +20,7 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
defaultLog "log"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -29,6 +30,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"go.etcd.io/etcd/clientv3/credentials"
|
||||
"go.etcd.io/etcd/etcdserver"
|
||||
"go.etcd.io/etcd/etcdserver/api/etcdhttp"
|
||||
"go.etcd.io/etcd/etcdserver/api/rafthttp"
|
||||
|
@ -116,7 +118,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
|
|||
if e.cfg.logger != nil {
|
||||
e.cfg.logger.Info(
|
||||
"configuring peer listeners",
|
||||
zap.Strings("listen-peer-urls", e.cfg.getLPURLs()),
|
||||
zap.Strings("listen-peer-urls", e.cfg.getListenPeerUrls()),
|
||||
)
|
||||
}
|
||||
if e.Peers, err = configurePeerListeners(cfg); err != nil {
|
||||
|
@ -126,7 +128,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
|
|||
if e.cfg.logger != nil {
|
||||
e.cfg.logger.Info(
|
||||
"configuring client listeners",
|
||||
zap.Strings("listen-client-urls", e.cfg.getLCURLs()),
|
||||
zap.Strings("listen-client-urls", e.cfg.getListenClientUrls()),
|
||||
)
|
||||
}
|
||||
if e.sctxs, err = configureClientListeners(cfg); err != nil {
|
||||
|
@ -162,49 +164,56 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
|
|||
backendFreelistType := parseBackendFreelistType(cfg.ExperimentalBackendFreelistType)
|
||||
|
||||
srvcfg := etcdserver.ServerConfig{
|
||||
Name: cfg.Name,
|
||||
ClientURLs: cfg.ACUrls,
|
||||
PeerURLs: cfg.APUrls,
|
||||
DataDir: cfg.Dir,
|
||||
DedicatedWALDir: cfg.WalDir,
|
||||
SnapshotCount: cfg.SnapshotCount,
|
||||
SnapshotCatchUpEntries: cfg.SnapshotCatchUpEntries,
|
||||
MaxSnapFiles: cfg.MaxSnapFiles,
|
||||
MaxWALFiles: cfg.MaxWalFiles,
|
||||
InitialPeerURLsMap: urlsmap,
|
||||
InitialClusterToken: token,
|
||||
DiscoveryURL: cfg.Durl,
|
||||
DiscoveryProxy: cfg.Dproxy,
|
||||
NewCluster: cfg.IsNewCluster(),
|
||||
PeerTLSInfo: cfg.PeerTLSInfo,
|
||||
TickMs: cfg.TickMs,
|
||||
ElectionTicks: cfg.ElectionTicks(),
|
||||
InitialElectionTickAdvance: cfg.InitialElectionTickAdvance,
|
||||
AutoCompactionRetention: autoCompactionRetention,
|
||||
AutoCompactionMode: cfg.AutoCompactionMode,
|
||||
QuotaBackendBytes: cfg.QuotaBackendBytes,
|
||||
BackendBatchLimit: cfg.BackendBatchLimit,
|
||||
BackendFreelistType: backendFreelistType,
|
||||
BackendBatchInterval: cfg.BackendBatchInterval,
|
||||
MaxTxnOps: cfg.MaxTxnOps,
|
||||
MaxRequestBytes: cfg.MaxRequestBytes,
|
||||
StrictReconfigCheck: cfg.StrictReconfigCheck,
|
||||
ClientCertAuthEnabled: cfg.ClientTLSInfo.ClientCertAuth,
|
||||
AuthToken: cfg.AuthToken,
|
||||
BcryptCost: cfg.BcryptCost,
|
||||
CORS: cfg.CORS,
|
||||
HostWhitelist: cfg.HostWhitelist,
|
||||
InitialCorruptCheck: cfg.ExperimentalInitialCorruptCheck,
|
||||
CorruptCheckTime: cfg.ExperimentalCorruptCheckTime,
|
||||
PreVote: cfg.PreVote,
|
||||
Logger: cfg.logger,
|
||||
LoggerConfig: cfg.loggerConfig,
|
||||
LoggerCore: cfg.loggerCore,
|
||||
LoggerWriteSyncer: cfg.loggerWriteSyncer,
|
||||
Debug: cfg.Debug,
|
||||
ForceNewCluster: cfg.ForceNewCluster,
|
||||
EnableGRPCGateway: cfg.EnableGRPCGateway,
|
||||
EnableLeaseCheckpoint: cfg.ExperimentalEnableLeaseCheckpoint,
|
||||
Name: cfg.Name,
|
||||
ClientURLs: cfg.AdvertiseClientUrls,
|
||||
PeerURLs: cfg.AdvertisePeerUrls,
|
||||
DataDir: cfg.Dir,
|
||||
DedicatedWALDir: cfg.WalDir,
|
||||
SnapshotCount: cfg.SnapshotCount,
|
||||
SnapshotCatchUpEntries: cfg.SnapshotCatchUpEntries,
|
||||
MaxSnapFiles: cfg.MaxSnapFiles,
|
||||
MaxWALFiles: cfg.MaxWalFiles,
|
||||
InitialPeerURLsMap: urlsmap,
|
||||
InitialClusterToken: token,
|
||||
DiscoveryURL: cfg.Durl,
|
||||
DiscoveryProxy: cfg.Dproxy,
|
||||
NewCluster: cfg.IsNewCluster(),
|
||||
PeerTLSInfo: cfg.PeerTLSInfo,
|
||||
TickMs: cfg.TickMs,
|
||||
ElectionTicks: cfg.ElectionTicks(),
|
||||
InitialElectionTickAdvance: cfg.InitialElectionTickAdvance,
|
||||
AutoCompactionRetention: autoCompactionRetention,
|
||||
AutoCompactionMode: cfg.AutoCompactionMode,
|
||||
QuotaBackendBytes: cfg.QuotaBackendBytes,
|
||||
BackendBatchLimit: cfg.BackendBatchLimit,
|
||||
BackendFreelistType: backendFreelistType,
|
||||
BackendBatchInterval: cfg.BackendBatchInterval,
|
||||
MaxTxnOps: cfg.MaxTxnOps,
|
||||
MaxRequestBytes: cfg.MaxRequestBytes,
|
||||
MaxConcurrentStreams: cfg.MaxConcurrentStreams,
|
||||
StrictReconfigCheck: cfg.StrictReconfigCheck,
|
||||
ClientCertAuthEnabled: cfg.ClientTLSInfo.ClientCertAuth,
|
||||
AuthToken: cfg.AuthToken,
|
||||
BcryptCost: cfg.BcryptCost,
|
||||
TokenTTL: cfg.AuthTokenTTL,
|
||||
CORS: cfg.CORS,
|
||||
HostWhitelist: cfg.HostWhitelist,
|
||||
InitialCorruptCheck: cfg.ExperimentalInitialCorruptCheck,
|
||||
CorruptCheckTime: cfg.ExperimentalCorruptCheckTime,
|
||||
PreVote: cfg.PreVote,
|
||||
Logger: cfg.logger,
|
||||
LoggerConfig: cfg.loggerConfig,
|
||||
LoggerCore: cfg.loggerCore,
|
||||
LoggerWriteSyncer: cfg.loggerWriteSyncer,
|
||||
Debug: cfg.Debug,
|
||||
ForceNewCluster: cfg.ForceNewCluster,
|
||||
EnableGRPCGateway: cfg.EnableGRPCGateway,
|
||||
UnsafeNoFsync: cfg.UnsafeNoFsync,
|
||||
EnableLeaseCheckpoint: cfg.ExperimentalEnableLeaseCheckpoint,
|
||||
LeaseCheckpointPersist: cfg.ExperimentalEnableLeaseCheckpointPersist,
|
||||
CompactionBatchLimit: cfg.ExperimentalCompactionBatchLimit,
|
||||
WatchProgressNotifyInterval: cfg.ExperimentalWatchProgressNotifyInterval,
|
||||
WarningApplyDuration: cfg.ExperimentalWarningApplyDuration,
|
||||
}
|
||||
print(e.cfg.logger, *cfg, srvcfg, memberInitialized)
|
||||
if e.Server, err = etcdserver.NewServer(srvcfg); err != nil {
|
||||
|
@ -240,10 +249,10 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
|
|||
e.cfg.logger.Info(
|
||||
"now serving peer/client/metrics",
|
||||
zap.String("local-member-id", e.Server.ID().String()),
|
||||
zap.Strings("initial-advertise-peer-urls", e.cfg.getAPURLs()),
|
||||
zap.Strings("listen-peer-urls", e.cfg.getLPURLs()),
|
||||
zap.Strings("advertise-client-urls", e.cfg.getACURLs()),
|
||||
zap.Strings("listen-client-urls", e.cfg.getLCURLs()),
|
||||
zap.Strings("initial-advertise-peer-urls", e.cfg.getAdvertisePeerUrls()),
|
||||
zap.Strings("listen-peer-urls", e.cfg.getListenPeerUrls()),
|
||||
zap.Strings("advertise-client-urls", e.cfg.getAdvertiseClientUrls()),
|
||||
zap.Strings("listen-client-urls", e.cfg.getListenClientUrls()),
|
||||
zap.Strings("listen-metrics-urls", e.cfg.getMetricsURLs()),
|
||||
)
|
||||
}
|
||||
|
@ -315,18 +324,23 @@ func print(lg *zap.Logger, ec Config, sc etcdserver.ServerConfig, memberInitiali
|
|||
zap.String("election-timeout", fmt.Sprintf("%v", time.Duration(sc.ElectionTicks*int(sc.TickMs))*time.Millisecond)),
|
||||
zap.Bool("initial-election-tick-advance", sc.InitialElectionTickAdvance),
|
||||
zap.Uint64("snapshot-count", sc.SnapshotCount),
|
||||
zap.Uint("max-wals", sc.MaxWALFiles),
|
||||
zap.Uint("max-snapshots", sc.MaxSnapFiles),
|
||||
zap.Uint64("snapshot-catchup-entries", sc.SnapshotCatchUpEntries),
|
||||
zap.Strings("initial-advertise-peer-urls", ec.getAPURLs()),
|
||||
zap.Strings("listen-peer-urls", ec.getLPURLs()),
|
||||
zap.Strings("advertise-client-urls", ec.getACURLs()),
|
||||
zap.Strings("listen-client-urls", ec.getLCURLs()),
|
||||
zap.Strings("initial-advertise-peer-urls", ec.getAdvertisePeerUrls()),
|
||||
zap.Strings("listen-peer-urls", ec.getListenPeerUrls()),
|
||||
zap.Strings("advertise-client-urls", ec.getAdvertiseClientUrls()),
|
||||
zap.Strings("listen-client-urls", ec.getListenClientUrls()),
|
||||
zap.Strings("listen-metrics-urls", ec.getMetricsURLs()),
|
||||
zap.Strings("cors", cors),
|
||||
zap.Strings("host-whitelist", hss),
|
||||
zap.String("initial-cluster", sc.InitialPeerURLsMap.String()),
|
||||
zap.String("initial-cluster-state", ec.ClusterState),
|
||||
zap.String("initial-cluster-token", sc.InitialClusterToken),
|
||||
zap.Int64("quota-size-bytes", quota),
|
||||
zap.Int64("quota-backend-bytes", quota),
|
||||
zap.Uint("max-request-bytes", sc.MaxRequestBytes),
|
||||
zap.Uint32("max-concurrent-streams", sc.MaxConcurrentStreams),
|
||||
|
||||
zap.Bool("pre-vote", sc.PreVote),
|
||||
zap.Bool("initial-corrupt-check", sc.InitialCorruptCheck),
|
||||
zap.String("corrupt-check-time-interval", sc.CorruptCheckTime.String()),
|
||||
|
@ -351,8 +365,8 @@ func (e *Etcd) Close() {
|
|||
fields := []zap.Field{
|
||||
zap.String("name", e.cfg.Name),
|
||||
zap.String("data-dir", e.cfg.Dir),
|
||||
zap.Strings("advertise-peer-urls", e.cfg.getAPURLs()),
|
||||
zap.Strings("advertise-client-urls", e.cfg.getACURLs()),
|
||||
zap.Strings("advertise-peer-urls", e.cfg.getAdvertisePeerUrls()),
|
||||
zap.Strings("advertise-client-urls", e.cfg.getAdvertiseClientUrls()),
|
||||
}
|
||||
lg := e.GetLogger()
|
||||
if lg != nil {
|
||||
|
@ -420,7 +434,7 @@ func stopServers(ctx context.Context, ss *servers) {
|
|||
// do not grpc.Server.GracefulStop with TLS enabled etcd server
|
||||
// See https://github.com/grpc/grpc-go/issues/1384#issuecomment-317124531
|
||||
// and https://github.com/etcd-io/etcd/issues/8916
|
||||
if ss.secure {
|
||||
if ss.secure && ss.http != nil {
|
||||
shutdownNow()
|
||||
return
|
||||
}
|
||||
|
@ -459,6 +473,9 @@ func configurePeerListeners(cfg *Config) (peers []*peerListener, err error) {
|
|||
plog.Fatalf("could not get certs (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
updateMinMaxVersions(&cfg.PeerTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||
|
||||
if !cfg.PeerTLSInfo.Empty() {
|
||||
if cfg.logger != nil {
|
||||
cfg.logger.Info(
|
||||
|
@ -471,7 +488,7 @@ func configurePeerListeners(cfg *Config) (peers []*peerListener, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
peers = make([]*peerListener, len(cfg.LPUrls))
|
||||
peers = make([]*peerListener, len(cfg.ListenPeerUrls))
|
||||
defer func() {
|
||||
if err == nil {
|
||||
return
|
||||
|
@ -481,11 +498,11 @@ func configurePeerListeners(cfg *Config) (peers []*peerListener, err error) {
|
|||
if cfg.logger != nil {
|
||||
cfg.logger.Warn(
|
||||
"closing peer listener",
|
||||
zap.String("address", cfg.LPUrls[i].String()),
|
||||
zap.String("address", cfg.ListenPeerUrls[i].String()),
|
||||
zap.Error(err),
|
||||
)
|
||||
} else {
|
||||
plog.Info("stopping listening for peers on ", cfg.LPUrls[i].String())
|
||||
plog.Info("stopping listening for peers on ", cfg.ListenPeerUrls[i].String())
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
peers[i].close(ctx)
|
||||
|
@ -494,7 +511,7 @@ func configurePeerListeners(cfg *Config) (peers []*peerListener, err error) {
|
|||
}
|
||||
}()
|
||||
|
||||
for i, u := range cfg.LPUrls {
|
||||
for i, u := range cfg.ListenPeerUrls {
|
||||
if u.Scheme == "http" {
|
||||
if !cfg.PeerTLSInfo.Empty() {
|
||||
if cfg.logger != nil {
|
||||
|
@ -596,6 +613,9 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
|||
plog.Fatalf("could not get certs (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
updateMinMaxVersions(&cfg.ClientTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||
|
||||
if cfg.EnablePprof {
|
||||
if cfg.logger != nil {
|
||||
cfg.logger.Info("pprof is enabled", zap.String("path", debugutil.HTTPPrefixPProf))
|
||||
|
@ -605,8 +625,7 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
|||
}
|
||||
|
||||
sctxs = make(map[string]*serveCtx)
|
||||
for _, u := range cfg.LCUrls {
|
||||
sctx := newServeCtx(cfg.logger)
|
||||
for _, u := range append(cfg.ListenClientUrls, cfg.ListenClientHttpUrls...) {
|
||||
if u.Scheme == "http" || u.Scheme == "unix" {
|
||||
if !cfg.ClientTLSInfo.Empty() {
|
||||
if cfg.logger != nil {
|
||||
|
@ -626,29 +645,45 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
|||
if (u.Scheme == "https" || u.Scheme == "unixs") && cfg.ClientTLSInfo.Empty() {
|
||||
return nil, fmt.Errorf("TLS key/cert (--cert-file, --key-file) must be provided for client url %s with HTTPS scheme", u.String())
|
||||
}
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
addr := u.Host
|
||||
if u.Scheme == "unix" || u.Scheme == "unixs" {
|
||||
network = "unix"
|
||||
addr = u.Host + u.Path
|
||||
for _, u := range cfg.ListenClientUrls {
|
||||
addr, secure, network := resolveUrl(u)
|
||||
sctx := sctxs[addr]
|
||||
if sctx == nil {
|
||||
sctx = newServeCtx(cfg.logger)
|
||||
sctxs[addr] = sctx
|
||||
}
|
||||
sctx.secure = sctx.secure || secure
|
||||
sctx.insecure = sctx.insecure || !secure
|
||||
sctx.scheme = u.Scheme
|
||||
sctx.addr = addr
|
||||
sctx.network = network
|
||||
}
|
||||
for _, u := range cfg.ListenClientHttpUrls {
|
||||
addr, secure, network := resolveUrl(u)
|
||||
|
||||
sctx.secure = u.Scheme == "https" || u.Scheme == "unixs"
|
||||
sctx.insecure = !sctx.secure
|
||||
if oldctx := sctxs[addr]; oldctx != nil {
|
||||
oldctx.secure = oldctx.secure || sctx.secure
|
||||
oldctx.insecure = oldctx.insecure || sctx.insecure
|
||||
continue
|
||||
sctx := sctxs[addr]
|
||||
if sctx == nil {
|
||||
sctx = newServeCtx(cfg.logger)
|
||||
sctxs[addr] = sctx
|
||||
} else if !sctx.httpOnly {
|
||||
return nil, fmt.Errorf("cannot bind both --client-listen-urls and --client-listen-http-urls on the same url %s", u.String())
|
||||
}
|
||||
sctx.secure = sctx.secure || secure
|
||||
sctx.insecure = sctx.insecure || !secure
|
||||
sctx.scheme = u.Scheme
|
||||
sctx.addr = addr
|
||||
sctx.network = network
|
||||
sctx.httpOnly = true
|
||||
}
|
||||
|
||||
if sctx.l, err = net.Listen(network, addr); err != nil {
|
||||
for _, sctx := range sctxs {
|
||||
if sctx.l, err = net.Listen(sctx.network, sctx.addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// net.Listener will rewrite ipv4 0.0.0.0 to ipv6 [::], breaking
|
||||
// hosts that disable ipv6. So, use the address given by the user.
|
||||
sctx.addr = addr
|
||||
|
||||
if fdLimit, fderr := runtimeutil.FDLimit(); fderr == nil {
|
||||
if fdLimit <= reservedInternalFDNum {
|
||||
|
@ -665,27 +700,27 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
|||
sctx.l = transport.LimitListener(sctx.l, int(fdLimit-reservedInternalFDNum))
|
||||
}
|
||||
|
||||
if network == "tcp" {
|
||||
if sctx.l, err = transport.NewKeepAliveListener(sctx.l, network, nil); err != nil {
|
||||
if sctx.network == "tcp" {
|
||||
if sctx.l, err = transport.NewKeepAliveListener(sctx.l, sctx.network, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err == nil {
|
||||
defer func(sctx *serveCtx) {
|
||||
if err == nil || sctx.l == nil {
|
||||
return
|
||||
}
|
||||
sctx.l.Close()
|
||||
if cfg.logger != nil {
|
||||
cfg.logger.Warn(
|
||||
"closing peer listener",
|
||||
zap.String("address", u.Host),
|
||||
zap.String("address", sctx.addr),
|
||||
zap.Error(err),
|
||||
)
|
||||
} else {
|
||||
plog.Info("stopping listening for client requests on ", u.Host)
|
||||
plog.Info("stopping listening for client requests on ", sctx.addr)
|
||||
}
|
||||
}()
|
||||
}(sctx)
|
||||
for k := range cfg.UserHandlers {
|
||||
sctx.userHandlers[k] = cfg.UserHandlers[k]
|
||||
}
|
||||
|
@ -696,11 +731,21 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
|||
if cfg.Debug {
|
||||
sctx.registerTrace()
|
||||
}
|
||||
sctxs[addr] = sctx
|
||||
}
|
||||
return sctxs, nil
|
||||
}
|
||||
|
||||
func resolveUrl(u url.URL) (addr string, secure bool, network string) {
|
||||
addr = u.Host
|
||||
network = "tcp"
|
||||
if u.Scheme == "unix" || u.Scheme == "unixs" {
|
||||
addr = u.Host + u.Path
|
||||
network = "unix"
|
||||
}
|
||||
secure = u.Scheme == "https" || u.Scheme == "unixs"
|
||||
return addr, secure, network
|
||||
}
|
||||
|
||||
func (e *Etcd) serveClients() (err error) {
|
||||
if !e.cfg.ClientTLSInfo.Empty() {
|
||||
if e.cfg.logger != nil {
|
||||
|
@ -744,15 +789,69 @@ func (e *Etcd) serveClients() (err error) {
|
|||
}))
|
||||
}
|
||||
|
||||
splitHttp := false
|
||||
for _, sctx := range e.sctxs {
|
||||
if sctx.httpOnly {
|
||||
splitHttp = true
|
||||
}
|
||||
}
|
||||
|
||||
// start client servers in each goroutine
|
||||
for _, sctx := range e.sctxs {
|
||||
go func(s *serveCtx) {
|
||||
e.errHandler(s.serve(e.Server, &e.cfg.ClientTLSInfo, h, e.errHandler, gopts...))
|
||||
e.errHandler(s.serve(e.Server, &e.cfg.ClientTLSInfo, h, e.errHandler, e.grpcGatewayDial(splitHttp), splitHttp, gopts...))
|
||||
}(sctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Etcd) grpcGatewayDial(splitHttp bool) (grpcDial func(ctx context.Context) (*grpc.ClientConn, error)) {
|
||||
if !e.cfg.EnableGRPCGateway {
|
||||
return nil
|
||||
}
|
||||
sctx := e.pickGrpcGatewayServeContext(splitHttp)
|
||||
addr := sctx.addr
|
||||
if network := sctx.network; network == "unix" {
|
||||
// explicitly define unix network for gRPC socket support
|
||||
addr = fmt.Sprintf("%s://%s", network, addr)
|
||||
}
|
||||
|
||||
opts := []grpc.DialOption{grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32))}
|
||||
if sctx.secure {
|
||||
tlscfg, tlsErr := e.cfg.ClientTLSInfo.ServerConfig()
|
||||
if tlsErr != nil {
|
||||
return func(ctx context.Context) (*grpc.ClientConn, error) {
|
||||
return nil, tlsErr
|
||||
}
|
||||
}
|
||||
dtls := tlscfg.Clone()
|
||||
// trust local server
|
||||
dtls.InsecureSkipVerify = true
|
||||
bundle := credentials.NewBundle(credentials.Config{TLSConfig: dtls})
|
||||
opts = append(opts, grpc.WithTransportCredentials(bundle.TransportCredentials()))
|
||||
} else {
|
||||
opts = append(opts, grpc.WithInsecure())
|
||||
}
|
||||
|
||||
return func(ctx context.Context) (*grpc.ClientConn, error) {
|
||||
conn, err := grpc.DialContext(ctx, addr, opts...)
|
||||
if err != nil {
|
||||
sctx.lg.Error("grpc gateway failed to dial", zap.String("addr", addr), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return conn, err
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Etcd) pickGrpcGatewayServeContext(splitHttp bool) *serveCtx {
|
||||
for _, sctx := range e.sctxs {
|
||||
if !splitHttp || !sctx.httpOnly {
|
||||
return sctx
|
||||
}
|
||||
}
|
||||
panic("Expect at least one context able to serve grpc")
|
||||
}
|
||||
|
||||
func (e *Etcd) serveMetrics() (err error) {
|
||||
if e.cfg.Metrics == "extensive" {
|
||||
grpc_prometheus.EnableHandlingTimeHistogram()
|
||||
|
@ -810,7 +909,7 @@ func (e *Etcd) GetLogger() *zap.Logger {
|
|||
|
||||
func parseCompactionRetention(mode, retention string) (ret time.Duration, err error) {
|
||||
h, err := strconv.Atoi(retention)
|
||||
if err == nil {
|
||||
if err == nil && h >= 0 {
|
||||
switch mode {
|
||||
case CompactorModeRevision:
|
||||
ret = time.Duration(int64(h))
|
||||
|
|
237
embed/serve.go
237
embed/serve.go
|
@ -23,7 +23,6 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"go.etcd.io/etcd/clientv3/credentials"
|
||||
"go.etcd.io/etcd/etcdserver"
|
||||
"go.etcd.io/etcd/etcdserver/api/v3client"
|
||||
"go.etcd.io/etcd/etcdserver/api/v3election"
|
||||
|
@ -42,17 +41,21 @@ import (
|
|||
"github.com/soheilhy/cmux"
|
||||
"github.com/tmc/grpc-websocket-proxy/wsproxy"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/trace"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type serveCtx struct {
|
||||
lg *zap.Logger
|
||||
l net.Listener
|
||||
lg *zap.Logger
|
||||
l net.Listener
|
||||
|
||||
scheme string
|
||||
addr string
|
||||
network string
|
||||
secure bool
|
||||
insecure bool
|
||||
httpOnly bool
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
@ -87,6 +90,8 @@ func (sctx *serveCtx) serve(
|
|||
tlsinfo *transport.TLSInfo,
|
||||
handler http.Handler,
|
||||
errHandler func(error),
|
||||
grpcDialForRestGatewayBackends func(ctx context.Context) (*grpc.ClientConn, error),
|
||||
splitHttp bool,
|
||||
gopts ...grpc.ServerOption) (err error) {
|
||||
logger := defaultLog.New(ioutil.Discard, "etcdhttp", 0)
|
||||
<-s.ReadyNotify()
|
||||
|
@ -96,48 +101,103 @@ func (sctx *serveCtx) serve(
|
|||
}
|
||||
|
||||
m := cmux.New(sctx.l)
|
||||
var server func() error
|
||||
onlyGRPC := splitHttp && !sctx.httpOnly
|
||||
onlyHttp := splitHttp && sctx.httpOnly
|
||||
grpcEnabled := !onlyHttp
|
||||
httpEnabled := !onlyGRPC
|
||||
|
||||
v3c := v3client.New(s)
|
||||
servElection := v3election.NewElectionServer(v3c)
|
||||
servLock := v3lock.NewLockServer(v3c)
|
||||
|
||||
var gs *grpc.Server
|
||||
defer func() {
|
||||
if err != nil && gs != nil {
|
||||
gs.Stop()
|
||||
// Make sure serversC is closed even if we prematurely exit the function.
|
||||
defer close(sctx.serversC)
|
||||
var gwmux *gw.ServeMux
|
||||
if s.Cfg.EnableGRPCGateway {
|
||||
// GRPC gateway connects to grpc server via connection provided by grpc dial.
|
||||
gwmux, err = sctx.registerGateway(grpcDialForRestGatewayBackends)
|
||||
if err != nil {
|
||||
sctx.lg.Error("registerGateway failed", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
}()
|
||||
}
|
||||
var traffic string
|
||||
switch {
|
||||
case onlyGRPC:
|
||||
traffic = "grpc"
|
||||
case onlyHttp:
|
||||
traffic = "http"
|
||||
default:
|
||||
traffic = "grpc+http"
|
||||
}
|
||||
|
||||
if sctx.insecure {
|
||||
gs = v3rpc.Server(s, nil, gopts...)
|
||||
v3electionpb.RegisterElectionServer(gs, servElection)
|
||||
v3lockpb.RegisterLockServer(gs, servLock)
|
||||
if sctx.serviceRegister != nil {
|
||||
sctx.serviceRegister(gs)
|
||||
}
|
||||
grpcl := m.Match(cmux.HTTP2())
|
||||
go func() { errHandler(gs.Serve(grpcl)) }()
|
||||
|
||||
var gwmux *gw.ServeMux
|
||||
if s.Cfg.EnableGRPCGateway {
|
||||
gwmux, err = sctx.registerGateway([]grpc.DialOption{grpc.WithInsecure()})
|
||||
if err != nil {
|
||||
var gs *grpc.Server
|
||||
var srv *http.Server
|
||||
if httpEnabled {
|
||||
httpmux := sctx.createMux(gwmux, handler)
|
||||
srv = &http.Server{
|
||||
Handler: createAccessController(sctx.lg, s, httpmux),
|
||||
ErrorLog: logger, // do not log user error
|
||||
}
|
||||
if err := configureHttpServer(srv, s.Cfg); err != nil {
|
||||
sctx.lg.Error("Configure http server failed", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
if grpcEnabled {
|
||||
gs = v3rpc.Server(s, nil, gopts...)
|
||||
v3electionpb.RegisterElectionServer(gs, servElection)
|
||||
v3lockpb.RegisterLockServer(gs, servLock)
|
||||
if sctx.serviceRegister != nil {
|
||||
sctx.serviceRegister(gs)
|
||||
}
|
||||
defer func(gs *grpc.Server) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
httpmux := sctx.createMux(gwmux, handler)
|
||||
if sctx.lg != nil {
|
||||
sctx.lg.Warn("stopping insecure grpc server due to error", zap.Error(err))
|
||||
} else {
|
||||
plog.Warningf("stopping insecure grpc server due to error: %s", err)
|
||||
}
|
||||
|
||||
srvhttp := &http.Server{
|
||||
Handler: createAccessController(sctx.lg, s, httpmux),
|
||||
ErrorLog: logger, // do not log user error
|
||||
gs.Stop()
|
||||
|
||||
if sctx.lg != nil {
|
||||
sctx.lg.Warn("stopped insecure grpc server due to error", zap.Error(err))
|
||||
} else {
|
||||
plog.Warningf("stopped insecure grpc server due to error: %s", err)
|
||||
}
|
||||
}(gs)
|
||||
}
|
||||
httpl := m.Match(cmux.HTTP1())
|
||||
go func() { errHandler(srvhttp.Serve(httpl)) }()
|
||||
if onlyGRPC {
|
||||
server = func() error {
|
||||
return gs.Serve(sctx.l)
|
||||
}
|
||||
} else {
|
||||
server = m.Serve
|
||||
|
||||
sctx.serversC <- &servers{grpc: gs, http: srvhttp}
|
||||
httpl := m.Match(cmux.HTTP1())
|
||||
go func(srvhttp *http.Server, tlsLis net.Listener) {
|
||||
errHandler(srvhttp.Serve(tlsLis))
|
||||
}(srv, httpl)
|
||||
|
||||
if grpcEnabled {
|
||||
grpcl := m.Match(cmux.HTTP2())
|
||||
go func(gs *grpc.Server, l net.Listener) {
|
||||
errHandler(gs.Serve(l))
|
||||
}(gs, grpcl)
|
||||
}
|
||||
}
|
||||
|
||||
sctx.serversC <- &servers{grpc: gs, http: srv}
|
||||
if sctx.lg != nil {
|
||||
sctx.lg.Info(
|
||||
"serving client traffic insecurely; this is strongly discouraged!",
|
||||
zap.String("traffic", traffic),
|
||||
zap.String("address", sctx.l.Addr().String()),
|
||||
)
|
||||
} else {
|
||||
|
@ -146,50 +206,77 @@ func (sctx *serveCtx) serve(
|
|||
}
|
||||
|
||||
if sctx.secure {
|
||||
var gs *grpc.Server
|
||||
var srv *http.Server
|
||||
|
||||
tlscfg, tlsErr := tlsinfo.ServerConfig()
|
||||
if tlsErr != nil {
|
||||
return tlsErr
|
||||
}
|
||||
gs = v3rpc.Server(s, tlscfg, gopts...)
|
||||
v3electionpb.RegisterElectionServer(gs, servElection)
|
||||
v3lockpb.RegisterLockServer(gs, servLock)
|
||||
if sctx.serviceRegister != nil {
|
||||
sctx.serviceRegister(gs)
|
||||
}
|
||||
handler = grpcHandlerFunc(gs, handler)
|
||||
|
||||
var gwmux *gw.ServeMux
|
||||
if s.Cfg.EnableGRPCGateway {
|
||||
dtls := tlscfg.Clone()
|
||||
// trust local server
|
||||
dtls.InsecureSkipVerify = true
|
||||
bundle := credentials.NewBundle(credentials.Config{TLSConfig: dtls})
|
||||
opts := []grpc.DialOption{grpc.WithTransportCredentials(bundle.TransportCredentials())}
|
||||
gwmux, err = sctx.registerGateway(opts)
|
||||
if err != nil {
|
||||
if grpcEnabled {
|
||||
gs = v3rpc.Server(s, tlscfg, gopts...)
|
||||
v3electionpb.RegisterElectionServer(gs, servElection)
|
||||
v3lockpb.RegisterLockServer(gs, servLock)
|
||||
if sctx.serviceRegister != nil {
|
||||
sctx.serviceRegister(gs)
|
||||
}
|
||||
defer func(gs *grpc.Server) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if sctx.lg != nil {
|
||||
sctx.lg.Warn("stopping secure grpc server due to error", zap.Error(err))
|
||||
} else {
|
||||
plog.Warningf("stopping secure grpc server due to error: %s", err)
|
||||
}
|
||||
|
||||
gs.Stop()
|
||||
|
||||
if sctx.lg != nil {
|
||||
sctx.lg.Warn("stopped secure grpc server due to error", zap.Error(err))
|
||||
} else {
|
||||
plog.Warningf("stopped secure grpc server due to error: %s", err)
|
||||
}
|
||||
}(gs)
|
||||
}
|
||||
if httpEnabled {
|
||||
if grpcEnabled {
|
||||
handler = grpcHandlerFunc(gs, handler)
|
||||
}
|
||||
httpmux := sctx.createMux(gwmux, handler)
|
||||
|
||||
srv = &http.Server{
|
||||
Handler: createAccessController(sctx.lg, s, httpmux),
|
||||
TLSConfig: tlscfg,
|
||||
ErrorLog: logger, // do not log user error
|
||||
}
|
||||
if err := configureHttpServer(srv, s.Cfg); err != nil {
|
||||
sctx.lg.Error("Configure https server failed", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var tlsl net.Listener
|
||||
tlsl, err = transport.NewTLSListener(m.Match(cmux.Any()), tlsinfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: add debug flag; enable logging when debug flag is set
|
||||
httpmux := sctx.createMux(gwmux, handler)
|
||||
if onlyGRPC {
|
||||
server = func() error { return gs.Serve(sctx.l) }
|
||||
} else {
|
||||
server = m.Serve
|
||||
|
||||
srv := &http.Server{
|
||||
Handler: createAccessController(sctx.lg, s, httpmux),
|
||||
TLSConfig: tlscfg,
|
||||
ErrorLog: logger, // do not log user error
|
||||
tlsl, err := transport.NewTLSListener(m.Match(cmux.Any()), tlsinfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func(srvhttp *http.Server, tlsl net.Listener) {
|
||||
errHandler(srvhttp.Serve(tlsl))
|
||||
}(srv, tlsl)
|
||||
}
|
||||
go func() { errHandler(srv.Serve(tlsl)) }()
|
||||
|
||||
sctx.serversC <- &servers{secure: true, grpc: gs, http: srv}
|
||||
if sctx.lg != nil {
|
||||
sctx.lg.Info(
|
||||
"serving client traffic insecurely",
|
||||
"serving client traffic securely",
|
||||
zap.String("traffic", traffic),
|
||||
zap.String("address", sctx.l.Addr().String()),
|
||||
)
|
||||
} else {
|
||||
|
@ -197,8 +284,16 @@ func (sctx *serveCtx) serve(
|
|||
}
|
||||
}
|
||||
|
||||
close(sctx.serversC)
|
||||
return m.Serve()
|
||||
return server()
|
||||
}
|
||||
|
||||
func configureHttpServer(srv *http.Server, cfg etcdserver.ServerConfig) error {
|
||||
// todo (ahrtr): should we support configuring other parameters in the future as well?
|
||||
return http2.ConfigureServer(srv, &http2.Server{
|
||||
MaxConcurrentStreams: cfg.MaxConcurrentStreams,
|
||||
// Override to avoid using priority scheduler which is affected by https://github.com/golang/go/issues/58804.
|
||||
NewWriteScheduler: http2.NewRandomWriteScheduler,
|
||||
})
|
||||
}
|
||||
|
||||
// grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC
|
||||
|
@ -220,16 +315,10 @@ func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Ha
|
|||
|
||||
type registerHandlerFunc func(context.Context, *gw.ServeMux, *grpc.ClientConn) error
|
||||
|
||||
func (sctx *serveCtx) registerGateway(opts []grpc.DialOption) (*gw.ServeMux, error) {
|
||||
func (sctx *serveCtx) registerGateway(dial func(ctx context.Context) (*grpc.ClientConn, error)) (*gw.ServeMux, error) {
|
||||
ctx := sctx.ctx
|
||||
|
||||
addr := sctx.addr
|
||||
if network := sctx.network; network == "unix" {
|
||||
// explicitly define unix network for gRPC socket support
|
||||
addr = fmt.Sprintf("%s://%s", network, addr)
|
||||
}
|
||||
|
||||
conn, err := grpc.DialContext(ctx, addr, opts...)
|
||||
conn, err := dial(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -268,6 +357,18 @@ func (sctx *serveCtx) registerGateway(opts []grpc.DialOption) (*gw.ServeMux, err
|
|||
return gwmux, nil
|
||||
}
|
||||
|
||||
type wsProxyZapLogger struct {
|
||||
*zap.Logger
|
||||
}
|
||||
|
||||
func (w wsProxyZapLogger) Warnln(i ...interface{}) {
|
||||
w.Warn(fmt.Sprint(i...))
|
||||
}
|
||||
|
||||
func (w wsProxyZapLogger) Debugln(i ...interface{}) {
|
||||
w.Debug(fmt.Sprint(i...))
|
||||
}
|
||||
|
||||
func (sctx *serveCtx) createMux(gwmux *gw.ServeMux, handler http.Handler) *http.ServeMux {
|
||||
httpmux := http.NewServeMux()
|
||||
for path, h := range sctx.userHandlers {
|
||||
|
@ -286,6 +387,8 @@ func (sctx *serveCtx) createMux(gwmux *gw.ServeMux, handler http.Handler) *http.
|
|||
return outgoing
|
||||
},
|
||||
),
|
||||
wsproxy.WithMaxRespBodyBufferSize(0x7fffffff),
|
||||
wsproxy.WithLogger(wsProxyZapLogger{sctx.lg}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build cov
|
||||
// +build cov
|
||||
|
||||
package ctlv2
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !cov
|
||||
// +build !cov
|
||||
|
||||
package ctlv2
|
||||
|
|
|
@ -311,6 +311,8 @@ func newCheckDatascaleCommand(cmd *cobra.Command, args []string) {
|
|||
ExitWithError(ExitError, errEndpoints)
|
||||
}
|
||||
|
||||
sec := secureCfgFromCmd(cmd)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
resp, err := clients[0].Get(ctx, checkDatascalePrefix, v3.WithPrefix(), v3.WithLimit(1))
|
||||
cancel()
|
||||
|
@ -329,7 +331,7 @@ func newCheckDatascaleCommand(cmd *cobra.Command, args []string) {
|
|||
wg.Add(len(clients))
|
||||
|
||||
// get the process_resident_memory_bytes and process_virtual_memory_bytes before the put operations
|
||||
bytesBefore := endpointMemoryMetrics(eps[0])
|
||||
bytesBefore := endpointMemoryMetrics(eps[0], sec)
|
||||
if bytesBefore == 0 {
|
||||
fmt.Println("FAIL: Could not read process_resident_memory_bytes before the put operations.")
|
||||
os.Exit(ExitError)
|
||||
|
@ -367,7 +369,7 @@ func newCheckDatascaleCommand(cmd *cobra.Command, args []string) {
|
|||
s := <-sc
|
||||
|
||||
// get the process_resident_memory_bytes after the put operations
|
||||
bytesAfter := endpointMemoryMetrics(eps[0])
|
||||
bytesAfter := endpointMemoryMetrics(eps[0], sec)
|
||||
if bytesAfter == 0 {
|
||||
fmt.Println("FAIL: Could not read process_resident_memory_bytes after the put operations.")
|
||||
os.Exit(ExitError)
|
||||
|
|
|
@ -31,6 +31,7 @@ var (
|
|||
getFromKey bool
|
||||
getRev int64
|
||||
getKeysOnly bool
|
||||
getCountOnly bool
|
||||
printValueOnly bool
|
||||
)
|
||||
|
||||
|
@ -50,6 +51,7 @@ func NewGetCommand() *cobra.Command {
|
|||
cmd.Flags().BoolVar(&getFromKey, "from-key", false, "Get keys that are greater than or equal to the given key using byte compare")
|
||||
cmd.Flags().Int64Var(&getRev, "rev", 0, "Specify the kv revision")
|
||||
cmd.Flags().BoolVar(&getKeysOnly, "keys-only", false, "Get only the keys")
|
||||
cmd.Flags().BoolVar(&getCountOnly, "count-only", false, "Get only the count")
|
||||
cmd.Flags().BoolVar(&printValueOnly, "print-value-only", false, `Only write values when using the "simple" output format`)
|
||||
return cmd
|
||||
}
|
||||
|
@ -64,6 +66,12 @@ func getCommandFunc(cmd *cobra.Command, args []string) {
|
|||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
if getCountOnly {
|
||||
if _, fields := display.(*fieldsPrinter); !fields {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("--count-only is only for `--write-out=fields`"))
|
||||
}
|
||||
}
|
||||
|
||||
if printValueOnly {
|
||||
dp, simple := (display).(*simplePrinter)
|
||||
if !simple {
|
||||
|
@ -83,6 +91,10 @@ func getGetOp(args []string) (string, []clientv3.OpOption) {
|
|||
ExitWithError(ExitBadArgs, fmt.Errorf("`--prefix` and `--from-key` cannot be set at the same time, choose one"))
|
||||
}
|
||||
|
||||
if getKeysOnly && getCountOnly {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("`--keys-only` and `--count-only` cannot be set at the same time, choose one"))
|
||||
}
|
||||
|
||||
opts := []clientv3.OpOption{}
|
||||
switch getConsistency {
|
||||
case "s":
|
||||
|
@ -159,5 +171,9 @@ func getGetOp(args []string) (string, []clientv3.OpOption) {
|
|||
opts = append(opts, clientv3.WithKeysOnly())
|
||||
}
|
||||
|
||||
if getCountOnly {
|
||||
opts = append(opts, clientv3.WithCountOnly())
|
||||
}
|
||||
|
||||
return key, opts
|
||||
}
|
||||
|
|
|
@ -156,28 +156,8 @@ func memberAddCommandFunc(cmd *cobra.Command, args []string) {
|
|||
display.MemberAdd(*resp)
|
||||
|
||||
if _, ok := (display).(*simplePrinter); ok {
|
||||
ctx, cancel = commandCtx(cmd)
|
||||
listResp, err := cli.MemberList(ctx)
|
||||
// get latest member list; if there's failover new member might have outdated list
|
||||
for {
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
if listResp.Header.MemberId == resp.Header.MemberId {
|
||||
break
|
||||
}
|
||||
// quorum get to sync cluster list
|
||||
gresp, gerr := cli.Get(ctx, "_")
|
||||
if gerr != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
resp.Header.MemberId = gresp.Header.MemberId
|
||||
listResp, err = cli.MemberList(ctx)
|
||||
}
|
||||
cancel()
|
||||
|
||||
conf := []string{}
|
||||
for _, memb := range listResp.Members {
|
||||
for _, memb := range resp.Members {
|
||||
for _, u := range memb.PeerURLs {
|
||||
n := memb.Name
|
||||
if memb.ID == newID {
|
||||
|
|
|
@ -42,7 +42,8 @@ func transferLeadershipCommandFunc(cmd *cobra.Command, args []string) {
|
|||
ExitWithError(ExitBadArgs, err)
|
||||
}
|
||||
|
||||
c := mustClientFromCmd(cmd)
|
||||
cfg := clientConfigFromCmd(cmd)
|
||||
c := cfg.mustClient()
|
||||
eps := c.Endpoints()
|
||||
c.Close()
|
||||
|
||||
|
@ -52,7 +53,6 @@ func transferLeadershipCommandFunc(cmd *cobra.Command, args []string) {
|
|||
var leaderCli *clientv3.Client
|
||||
var leaderID uint64
|
||||
for _, ep := range eps {
|
||||
cfg := clientConfigFromCmd(cmd)
|
||||
cfg.endpoints = []string{ep}
|
||||
cli := cfg.mustClient()
|
||||
resp, serr := cli.Status(ctx, ep)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue