Compare commits
1115 Commits
v3.2.21
...
v3.2.10_pl
Author | SHA1 | Date | |
---|---|---|---|
15bfc1b361 | |||
8e4d1cb707 | |||
f649132a5a | |||
f62cd1d66f | |||
e1b1ec8348 | |||
fb9e78ff3e | |||
75ababa61f | |||
08434d0665 | |||
1ce3a41e69 | |||
f6f0fb12e0 | |||
a4c407ece4 | |||
3cff8dd6f8 | |||
6a4a30f5d1 | |||
24b19ee222 | |||
23fb330df7 | |||
3766b04b38 | |||
ba163efe2e | |||
cbe8c7eda7 | |||
7a55a4084d | |||
37b3108ce5 | |||
9b772ba94c | |||
94355cb6a5 | |||
fe7b094f63 | |||
6260df7404 | |||
4a8c788dbf | |||
092b270697 | |||
79446ea677 | |||
627cffd6f8 | |||
0f9f452722 | |||
c706c6e238 | |||
5fd419ff50 | |||
02be1ace59 | |||
980942fa44 | |||
ab526e8814 | |||
ce6bb4f1c9 | |||
d01f3daf95 | |||
f0497de216 | |||
ec25a5c5b4 | |||
1bca2e969f | |||
6f077bd74c | |||
6ba39450c3 | |||
632ba72c6d | |||
eaf47ec053 | |||
eb19ab14e2 | |||
adeb1fb620 | |||
02ae7a3005 | |||
5a154e8e2b | |||
deb514989c | |||
977f33a5a6 | |||
a8fde603b1 | |||
43dfefe9e3 | |||
75110dd839 | |||
c6f2db2e92 | |||
65a606e2e8 | |||
0b03d22b5b | |||
b64c1bfce6 | |||
c669ff9765 | |||
93f12da1be | |||
123b869a0f | |||
103efd922b | |||
012b013538 | |||
64acd71c11 | |||
52f4bc9061 | |||
dfe0f8c2bc | |||
0dbcd7c1a7 | |||
6654ae4c2a | |||
700c9a50c3 | |||
8d309bf34a | |||
00b15e38df | |||
993a0cf569 | |||
527d03e0d2 | |||
973857107e | |||
143de553e6 | |||
7ccde4ac8a | |||
bb4637bffe | |||
10a863aac2 | |||
75c7e62dc7 | |||
3a0e24e6c5 | |||
05e5b3b62d | |||
ec881b0507 | |||
7ba4ae01b8 | |||
c0c19465fc | |||
24c718f7de | |||
672d4ae93f | |||
370ff6b670 | |||
5cea18baf1 | |||
21178f5119 | |||
5ed5ee51f5 | |||
efb0057513 | |||
0ce02abf59 | |||
706cf20339 | |||
47728b8caf | |||
dd35fce66c | |||
f49f5c9094 | |||
1b9f96ebc1 | |||
d83820d143 | |||
f48fe8ecda | |||
9791429524 | |||
38942a2a51 | |||
1d01aaa395 | |||
568b856be8 | |||
ba233e2f4d | |||
21ba9a89a7 | |||
0b72f651a1 | |||
80d5e1cbb7 | |||
5d98710b2e | |||
de950a40e0 | |||
e35d34ccea | |||
31912e35b3 | |||
3f93d9ae00 | |||
41d37fcc51 | |||
0048df6faf | |||
ef475b2502 | |||
adc3cea8cf | |||
cdc71ae38e | |||
b65435b86d | |||
f6ca686882 | |||
ef0e8e17d9 | |||
6127f785a4 | |||
1fa295e3ba | |||
4b1e09f2b4 | |||
f8bec0f631 | |||
ff05596ba7 | |||
70f64bb1b6 | |||
736b9f0be3 | |||
3ac54be402 | |||
67722fc3ff | |||
9e509eb77f | |||
791370bacf | |||
0ca8f420d4 | |||
ba749166d5 | |||
4e2ef67f2b | |||
0f86f0e0e6 | |||
2c13231e7b | |||
63d0ac0fe6 | |||
8d23e1c870 | |||
9731910754 | |||
a37dd0055f | |||
9ca733255a | |||
8d5c284b6c | |||
299c704295 | |||
bea930f44d | |||
2200450022 | |||
a41f3b64aa | |||
87ad10c155 | |||
ca1e6a74e0 | |||
4eb5a70126 | |||
5d169b866f | |||
d75a6a39f5 | |||
03ce2fa037 | |||
2cea13ba68 | |||
732c40531b | |||
0fcafcb828 | |||
62821158aa | |||
9d95cfb105 | |||
aaf4a70cd0 | |||
1c3567da90 | |||
a33a3b2872 | |||
e980bde82d | |||
a9996f8768 | |||
0160cd76e5 | |||
0bfc6a0d92 | |||
cb188d0b26 | |||
f46c063285 | |||
6a8d6b6ad9 | |||
af53f54042 | |||
9b26bde147 | |||
10c971db70 | |||
7d7e9b6e43 | |||
20f2914e13 | |||
8fa35216b0 | |||
995d79a0fc | |||
cea7387b73 | |||
c50cfbeaf6 | |||
3462d8ba70 | |||
6f8c476599 | |||
b6f770fc24 | |||
da0a387aac | |||
fff1fb2ed7 | |||
ff2ed93b5c | |||
f42534cb21 | |||
5032feaf22 | |||
d095a5c48b | |||
6277828f13 | |||
8d1f9c654a | |||
abc606f139 | |||
d16de1b914 | |||
109f52e3d6 | |||
fdaa04e95f | |||
2a49b04f09 | |||
0d76ede274 | |||
d5fc37072c | |||
cd4ca4065e | |||
1724cfa937 | |||
249a2c30d2 | |||
6337e4a1ec | |||
319658aef3 | |||
997469a8cf | |||
2b5733d742 | |||
fa7c8f3f83 | |||
149ee61e02 | |||
b699c7cff7 | |||
97f0b28bdb | |||
2ae10a8184 | |||
f65575073a | |||
5943229921 | |||
3899f9e3c5 | |||
59af91fc69 | |||
63ab5addfa | |||
725df70664 | |||
5eef654c3c | |||
6f0771d2f6 | |||
0c5ca488c1 | |||
06e591d526 | |||
ebc09b1149 | |||
785a5a11ed | |||
439c97d465 | |||
7ffcca5946 | |||
6c35754481 | |||
2feb8ba545 | |||
f83ac25412 | |||
81ca10f991 | |||
1b2a62d9d0 | |||
cd859cfaa3 | |||
12a6efb74b | |||
b896e985b6 | |||
40b6fcd761 | |||
54ef60d033 | |||
1fa60c9882 | |||
141170c1d4 | |||
c09a89d834 | |||
fecd26f141 | |||
b46ab2c36e | |||
ad7882590c | |||
f95f865060 | |||
87fe8c12ae | |||
29aa4ce2a1 | |||
a2c61cf04f | |||
2540859ee7 | |||
c945b7b44a | |||
1549403dd2 | |||
ad24700252 | |||
a8f9de2abf | |||
5790ffde7c | |||
39fe293649 | |||
b989e1992f | |||
d3c9643761 | |||
d392debf82 | |||
f0a78eb516 | |||
764a0f79b2 | |||
e80b2474fa | |||
0ef0abf9bf | |||
7f2b6a19d6 | |||
bc03ce9cab | |||
500c2499f4 | |||
8329963d69 | |||
e9e17e3fe5 | |||
826de3c07a | |||
8224c748c9 | |||
fbed568b6a | |||
1704443c6d | |||
e47be1f325 | |||
d44f7d5f67 | |||
ed92420950 | |||
09a38a7953 | |||
2bbd26e8e0 | |||
6571829f16 | |||
66f2a65f6b | |||
71197ab2a5 | |||
90c3f91f29 | |||
5096b4ed5d | |||
04940efcc2 | |||
a68a3dc79e | |||
abc81d03a7 | |||
b766a26059 | |||
e8e3467455 | |||
bed5f388a8 | |||
0cdf5b2d58 | |||
077b361bfc | |||
1109c6c321 | |||
dcaa0cddfc | |||
1c6fbcd3d0 | |||
d2b3e578e7 | |||
39912e7018 | |||
d9e8d4665c | |||
37eabd770e | |||
c58ba620dd | |||
db0ea5d44b | |||
cab94ac128 | |||
f79d5aaca4 | |||
5d3a5912eb | |||
d57159f79a | |||
e7e24dab64 | |||
09f02e5507 | |||
867e3da0c4 | |||
b0dc639807 | |||
70aa30f281 | |||
8b75689c05 | |||
9154b31bf3 | |||
75a51f77f3 | |||
b3ff3982b8 | |||
2c93dbf0a8 | |||
ded97c874b | |||
f5b1da6a20 | |||
db1be7ebc0 | |||
85bbd0cead | |||
23a302364c | |||
b401659fbb | |||
0e6e2f5ec5 | |||
999f329c87 | |||
1f2197b1f8 | |||
05f96e8770 | |||
58e825c636 | |||
2b09a554a2 | |||
863dfd1f0e | |||
78c57418e0 | |||
b2f5393b64 | |||
7fb5b90bed | |||
69031e3a6d | |||
e44ce19c1f | |||
6555262cae | |||
207c90c5e7 | |||
0199bdc266 | |||
182d071fd0 | |||
01e83a4334 | |||
72fbe0576d | |||
46223a2202 | |||
530d421f61 | |||
8b7fc3e28f | |||
c6e7d3ab7d | |||
b186265003 | |||
3f596db104 | |||
3a566fd3ad | |||
245d03f129 | |||
834add042e | |||
5f7ce4f7e1 | |||
1d28a7a69b | |||
70018e9207 | |||
aac652009d | |||
f361dcc639 | |||
bc5b7c0937 | |||
bcef78c665 | |||
0e48b5fa0d | |||
daa224a088 | |||
f8e63934b1 | |||
0e1993f131 | |||
253259452b | |||
733de98cfb | |||
99cda531cb | |||
2cfe0d6774 | |||
65ffb52e5f | |||
b5c31522ee | |||
044aca7f50 | |||
741d7e9dca | |||
55b728973c | |||
6b06a69aba | |||
10202a54ef | |||
4b3d4000af | |||
157c8eccf0 | |||
32e15d790f | |||
e1e236155c | |||
8f6a0ee26c | |||
398e6ba2a6 | |||
636815909d | |||
a439095697 | |||
b6b4898f6b | |||
92f5746c54 | |||
554298d429 | |||
f815d9a65b | |||
2240b6a592 | |||
090c192517 | |||
c63d6b6a25 | |||
b355232dd6 | |||
607d0762eb | |||
4830ca74e6 | |||
35e285674b | |||
230323255a | |||
6515a1dfd0 | |||
1296281b27 | |||
cbddcfd9ad | |||
fbc7acde95 | |||
527429f30a | |||
24cce732b6 | |||
36e37580f3 | |||
517c15d3e1 | |||
3a7858c439 | |||
8a4c8dc3b0 | |||
e8c18e3368 | |||
c50960e39a | |||
94b5071c30 | |||
e709f83253 | |||
aca8a0d5b9 | |||
a819e689b0 | |||
f45ba5935a | |||
8dc4833a3e | |||
5bb9f9591f | |||
94e563e111 | |||
bcbf18491f | |||
b9c4f5b22a | |||
3cad5e4da1 | |||
a4777080cb | |||
9801fd7297 | |||
085adc5b8b | |||
166e6918a6 | |||
49e5e78d0f | |||
efd7800e0f | |||
e3deb9f482 | |||
84db8fdaea | |||
6cf0fd7cb0 | |||
58b98c6a14 | |||
4afb99ffc1 | |||
7f4464415a | |||
4366f35e1e | |||
1b85dad7b0 | |||
e4c0e11702 | |||
1ae6f1614d | |||
510d884e62 | |||
6f6279075a | |||
846255b95e | |||
10b731baa8 | |||
28a22075ca | |||
4fa1dd196c | |||
9553afbb24 | |||
bb4e0473ae | |||
98e4a05068 | |||
5f36875272 | |||
69f32bac34 | |||
7761a4672e | |||
6f76d52a1a | |||
18ba4d60ec | |||
bc50a4591a | |||
0b2d8a6c96 | |||
3b3d392540 | |||
f37ff4a4e2 | |||
d6c33367c4 | |||
80aa810309 | |||
f4355a00ae | |||
76a35e71be | |||
ba89bbb47d | |||
640c0e6ff4 | |||
9a1e294ec6 | |||
ae63ac1cf7 | |||
f445b463a2 | |||
6930e471ed | |||
70c20a9e73 | |||
0e0d9e492f | |||
e49d93ccb7 | |||
6e39a39e3a | |||
eb55917ef6 | |||
89ee9d6671 | |||
24498ea167 | |||
9a726b424d | |||
9d12ba26e0 | |||
887a0585e6 | |||
f65aee0759 | |||
ff31fb4b8b | |||
a44e11414f | |||
252cab0c13 | |||
9c3474e4e0 | |||
63aa64d240 | |||
2bb893b478 | |||
2d0eec0b35 | |||
4587d56731 | |||
ec36d0040b | |||
9abe9da9db | |||
a0361ea3f9 | |||
3c1845604b | |||
05d7dc307b | |||
7c50c06fb8 | |||
7063a5e5cc | |||
77a19cd9d4 | |||
4cbe2e8cae | |||
40e969b02a | |||
b1595f2792 | |||
550765d037 | |||
8a351f9851 | |||
15c3c1be28 | |||
312c68a9c6 | |||
9a84c84ea6 | |||
9021b85692 | |||
9a0f8c5917 | |||
589a7a19ac | |||
a51135a5f0 | |||
09e30117f5 | |||
59d232adf9 | |||
e832048a1f | |||
248384a468 | |||
079d578959 | |||
b70263247d | |||
4cd99d1091 | |||
9f7375c225 | |||
b61c7489e0 | |||
1b19a5c708 | |||
4c725cee26 | |||
9d79d5fe65 | |||
be7d488982 | |||
492bbc9659 | |||
32bfd9e5ab | |||
d4b8193c55 | |||
e9cf07fa4d | |||
a0adee5209 | |||
5d669290e3 | |||
75eb05a272 | |||
cab7572b00 | |||
8091be6e97 | |||
525fbba1bd | |||
758c3c09fd | |||
1d3afd4bb5 | |||
565831c21c | |||
b847cde981 | |||
7d4a8a6935 | |||
409805e9c7 | |||
247b4ef904 | |||
cd772ea737 | |||
a671703c08 | |||
d31c442197 | |||
7cf8eb8dce | |||
896447ed99 | |||
bd53ae5680 | |||
86d15d1b1c | |||
3fefac17b2 | |||
9b92e1b2d0 | |||
60d46a3626 | |||
fec145f086 | |||
54fcdb4b5c | |||
1dea4c688e | |||
c9f677c0ea | |||
e441c57972 | |||
ef5e77e361 | |||
d76b29c4d7 | |||
52855bac49 | |||
4ec31f4f7f | |||
752c161ebf | |||
d3c8f9e856 | |||
dfed636e5a | |||
67d932154c | |||
897cadc88c | |||
8e7a0de114 | |||
b206afc4a7 | |||
1d195521c7 | |||
b9ef49142c | |||
d2ca782277 | |||
af4957ead8 | |||
5c975fdb10 | |||
603f84bb6d | |||
35c5dcefc2 | |||
6e02779c4f | |||
5c611a493b | |||
86aeaad924 | |||
1f734e0299 | |||
31381da53a | |||
35dffc7bc1 | |||
153ba92830 | |||
b8bcc891a6 | |||
6be5f9a841 | |||
65c054003f | |||
0bf404676d | |||
02c6f0d559 | |||
94e80e5f57 | |||
5c03ade973 | |||
cf0a07be52 | |||
f58c0cfb66 | |||
7e6a0a8f92 | |||
13041c15ba | |||
953c199b74 | |||
ee5bdf458b | |||
d3f5109215 | |||
8b872196d0 | |||
10b65c97dd | |||
a9e56e103c | |||
8a956459d8 | |||
bea33f65a4 | |||
47d5ae4971 | |||
3e32cd3877 | |||
126e91c449 | |||
2321835c47 | |||
dc4ab898eb | |||
6fd37dd9a3 | |||
1f228e753d | |||
7734b97b57 | |||
6464574952 | |||
35b11bf438 | |||
c1b7e78c60 | |||
15c511ea6a | |||
f4183c68cc | |||
f33d64a930 | |||
abe5c9c63e | |||
46b42e3cf0 | |||
61fd39e5d7 | |||
0c456df5c3 | |||
bfb1d9d6a6 | |||
fa32a85e69 | |||
c9c20d93ac | |||
8060b9dd83 | |||
e24de6c9ac | |||
f1509a102c | |||
deb0098d33 | |||
01f1013203 | |||
1f20d5d924 | |||
556c1a1fe0 | |||
f8141db2c7 | |||
15ef98a4ee | |||
d25ae50c02 | |||
8005f00bcf | |||
a7413bbf28 | |||
099fbde809 | |||
8df21326f9 | |||
135b7f78c9 | |||
2513e8c9ce | |||
6489084a51 | |||
6c4d990c1a | |||
fe344ef302 | |||
ccd1bb1780 | |||
32866572bf | |||
195744aea6 | |||
04413454ac | |||
9c21eefd09 | |||
754f454974 | |||
921e0dbd72 | |||
39432ac31f | |||
2c958939bb | |||
7ef41aa285 | |||
cf0eb3b7ce | |||
61ebb98e55 | |||
a9b9ef5640 | |||
c9cd3afa58 | |||
e4e61479f2 | |||
43ccc549fb | |||
5176b63fa0 | |||
9982cd0528 | |||
8c32cd96fb | |||
b39891eb45 | |||
6ca928c669 | |||
7d4b470397 | |||
4aa528c58e | |||
da7f5725e0 | |||
d3716b86ae | |||
8fe94356f4 | |||
b402ea8590 | |||
9be715bb66 | |||
468078ffcd | |||
a425e98a7e | |||
366f5381e0 | |||
6a4194c556 | |||
c3ae033f25 | |||
faa4a62410 | |||
71a706509e | |||
107c18f19f | |||
5072530a80 | |||
a3ef719598 | |||
b7b31e5770 | |||
033c0cbdd8 | |||
e77ecb593c | |||
322e6ff022 | |||
42cc64a9e5 | |||
ae748716e6 | |||
9040b3eb2b | |||
d543870966 | |||
98adbbf031 | |||
45e6b658dd | |||
9f1bfd9e4b | |||
b89ef7e295 | |||
7de417d745 | |||
fdba9e5fb1 | |||
10db0319d1 | |||
4669aaa9a2 | |||
8385c6682a | |||
585b1d7bdc | |||
1c75c383a1 | |||
3740793b42 | |||
df5a3d15ce | |||
bb86c327e2 | |||
4c2c5b0084 | |||
e0843c691b | |||
073fa562d8 | |||
cd142a0d1c | |||
6603a77561 | |||
661da1e609 | |||
b8fd5c3dba | |||
cd37ef2c1b | |||
7b8fb3cf0a | |||
341664f7b6 | |||
79660db61b | |||
52b031cfa2 | |||
ec4ca4408f | |||
71e56a44b7 | |||
d8ca2bbffb | |||
2951faf770 | |||
f216165aad | |||
98fc5e5769 | |||
ca586147bd | |||
451b062184 | |||
785deebd62 | |||
b36463efe5 | |||
8c7b639f81 | |||
4267d368df | |||
ff1c8c2191 | |||
8365233d2a | |||
f6acd0316c | |||
9fee4b77de | |||
8a589d2d73 | |||
be794d586c | |||
fca56f132a | |||
5088ae3e67 | |||
2a348fb8e9 | |||
ee1c340126 | |||
8609521ce2 | |||
766c2540ae | |||
9b6799a5b6 | |||
ff7a021c8f | |||
411ab276b0 | |||
74c8050adc | |||
78432e3bd2 | |||
16943f04e2 | |||
8b1177194e | |||
a6ae677d8f | |||
deca9879c2 | |||
478ba2c4f2 | |||
05603c4908 | |||
9581f7676c | |||
318caeee7e | |||
6fb08672d8 | |||
ebcfdd1a3d | |||
ffa54929ea | |||
d2654f8522 | |||
26bf8c0524 | |||
93826f2f78 | |||
fe33bd1879 | |||
986e98418d | |||
51d7786050 | |||
dfd3ef42cf | |||
09f67a0d5e | |||
e9a7f3551b | |||
e9d5f75323 | |||
52dd13fa35 | |||
b1aa962233 | |||
bb0e144b43 | |||
2eb9353019 | |||
954ec4d1a5 | |||
107828d777 | |||
1dcae41b20 | |||
c5447c2ec9 | |||
efbee9d8c7 | |||
1365f87d40 | |||
d5a0d4d696 | |||
5d6c6ad20e | |||
426ad25924 | |||
7c22d35dff | |||
5c6a6bdc5a | |||
a92ceeec25 | |||
488df4db34 | |||
a64d15eeed | |||
2c4e22fd43 | |||
fe1ddab714 | |||
fb717aec9b | |||
01a49a9f7e | |||
c06953ae08 | |||
46ee06a85c | |||
887df72d13 | |||
cfbf666dd4 | |||
55d445b891 | |||
bb42d2b40e | |||
608df0fc90 | |||
9dc65936b1 | |||
f78498b42a | |||
91470a8a54 | |||
61a736a068 | |||
d8481c9fda | |||
45206b6edf | |||
21232017fa | |||
82126a742e | |||
ebb7649e3d | |||
9ce7bb6a1c | |||
fbb75d24a4 | |||
3dcd2cdcb4 | |||
ed052ce9a3 | |||
34fd848a4f | |||
334554f655 | |||
d28334831d | |||
c47d4450c7 | |||
8463b377d9 | |||
9bb5ede659 | |||
511f4d5c99 | |||
89e4b62a01 | |||
5133d8e993 | |||
fe0941426d | |||
858938e32d | |||
7ff1e8f3bb | |||
755270fa6a | |||
3614c5185d | |||
28b4dce4f1 | |||
3dd7de3908 | |||
14401021ee | |||
02585157f6 | |||
026e05518e | |||
17be3b551a | |||
1b4f8d9904 | |||
fd3516f283 | |||
1e72ace38d | |||
0a2b580f73 | |||
148ed90b0d | |||
ae33c5e82a | |||
52101e6e93 | |||
da2f4bb25d | |||
39f4502cc0 | |||
07bc71b87c | |||
1010b82de2 | |||
acfde8aba0 | |||
e29db923bc | |||
97f37e42e6 | |||
91dbebfeb2 | |||
e51c34124c | |||
69e8a9241a | |||
64840adf70 | |||
9391c06004 | |||
68eb96e985 | |||
4e897529c2 | |||
4caf9fc7fa | |||
67fa8b823f | |||
e6f4563ea1 | |||
eacb46bf50 | |||
00aede4a39 | |||
ab95eb0795 | |||
e9d096ae6b | |||
b8bc005e60 | |||
63350f5ac1 | |||
2e7615281e | |||
a57405a958 | |||
2a30a754e9 | |||
a2a80cb1bf | |||
d48e59e389 | |||
4df1970188 | |||
870302afa6 | |||
89ced7c0c9 | |||
2b9bfda1d5 | |||
318e9c766f | |||
75665c0fd0 | |||
894751ef44 | |||
1408b337b6 | |||
663f8835cf | |||
b7cf080e2c | |||
cc114caddd | |||
673c6f0650 | |||
8d41820741 | |||
36c655f29b | |||
49fe77eea0 | |||
7ab442f8c5 | |||
c678dcbd91 | |||
4dd7ef0a60 | |||
625c67f5c5 | |||
db595887cf | |||
e0c33ef881 | |||
86eced670c | |||
54a75f9431 | |||
703663d1f6 | |||
204c4aa0b0 | |||
6ea5676db4 | |||
d4289588ac | |||
6e9b776fce | |||
581a83dfd9 | |||
c5532dd2a2 | |||
403ba1dfa7 | |||
3e263d5a4d | |||
b1a0ae3a3e | |||
939bbd77c0 | |||
265303c19a | |||
d82f2572a4 | |||
7fffd8b827 | |||
47a8156851 | |||
0fe8fdcb29 | |||
4d6174f770 | |||
4c43fb83df | |||
9e574afb84 | |||
861ebe6950 | |||
f7df3c80d5 | |||
e22d00a9f1 | |||
cdc7d77beb | |||
f2d8929a09 | |||
ac061671d5 | |||
c38c00f7c3 | |||
310a09691f | |||
522e75cb4f | |||
4e6c77185b | |||
9cb12deca6 | |||
6fe2249bcd | |||
23c816718a | |||
a3f8f47422 | |||
e461017ac5 | |||
b10ea20113 | |||
f465e3ea8a | |||
f400010028 | |||
f8dbcd86ec | |||
0dd4c2ac69 | |||
6ed51dc621 | |||
5c7efaa288 | |||
822473bc31 | |||
8b09309c81 | |||
1a2be432c5 | |||
7ebcfcf871 | |||
a40cdc7baa | |||
20881bde05 | |||
6e31901108 | |||
7689a2535e | |||
ca6d7bd836 | |||
1df8b90d67 | |||
8ce2c79197 | |||
117a83c1bf | |||
30029c0019 | |||
c1e3172e3a | |||
71e6fe183f | |||
ac62c6c811 | |||
8837719a8d | |||
41e26f741b | |||
798b14979c | |||
87d16af2e2 | |||
7d7d1ae6a0 | |||
322976bedc | |||
a65e3c69a6 | |||
66f553a96b | |||
8f8f550443 | |||
51a568aa81 | |||
02164874d9 | |||
45fbac5544 | |||
df2cc4bc8c | |||
e475a4ea71 | |||
8f34d0c8b6 | |||
7ff6e62c56 | |||
aeb2dc03aa | |||
fcf1abd23b | |||
fafb054624 | |||
8d7c29c732 | |||
7f149d8fb6 | |||
a825709940 | |||
1acc8090e3 | |||
e962b0c849 | |||
44a6c2121b | |||
8fa96cb303 | |||
42584f84b4 | |||
03ab4d9cc5 | |||
5fedaf2dd7 | |||
5e059fd8dc | |||
0d0c0f3959 | |||
5fe58228b4 | |||
b9a53db0c2 | |||
639687bb89 | |||
15b86d064d | |||
b6b56160cd | |||
703893f334 | |||
099952136a | |||
52afc03d68 | |||
6e74c335e2 | |||
aa0e6b26c0 | |||
44422f3898 | |||
dcf52bbfac | |||
95bc33f37f | |||
5bba05703c | |||
037e33e833 | |||
1748fe3eda | |||
f5b96991a1 | |||
1f206c027a | |||
3a37b68cda | |||
c27634c215 | |||
e5aa938fec | |||
13d9438cf9 | |||
66687da3ba | |||
0caab26310 | |||
ee0c805de2 | |||
0011b78bd5 | |||
e6d26675e6 | |||
e402606f02 | |||
750dc7f157 | |||
74e020b715 | |||
3993f37a26 | |||
e006e2dbcb | |||
a7c33d48de | |||
4445996a38 | |||
5ae04259c4 | |||
b7741c6ecf | |||
4ebeba0e18 | |||
2afd0a726f | |||
7ff5b05004 | |||
933aa09b73 | |||
3fcb8336aa | |||
b194276289 | |||
7f3127441b | |||
3a6180d490 | |||
bdddbcc414 | |||
84e6aaff66 | |||
d5b917daad | |||
ad0b3cfdab | |||
9543431aeb | |||
d6750158fb | |||
56841bbc5f | |||
798119ed6f | |||
5bb0a091fc | |||
d173b09a1b | |||
da48f1feaf | |||
ad22aaa354 | |||
3b460506d9 | |||
56db7e56f9 | |||
a8c073c51e | |||
762b2c625c | |||
74a2b2e873 | |||
2caae60004 | |||
4dff7aaa2a | |||
9ffdb3a59e | |||
300feea177 | |||
45fd8279f0 | |||
d335821c51 | |||
c6330d86f1 | |||
c2dadbd9f8 | |||
eb3622942b | |||
fa4903c83c | |||
aaa9e1735a | |||
3df9352c00 | |||
8f8f79db56 | |||
7b68318284 | |||
0c655902f2 | |||
83b2ea2f60 | |||
0352ce79b8 | |||
8d8d1d225a | |||
fe727f3106 | |||
a36d62a30c | |||
29911195de | |||
c3fcf0f339 | |||
09abea5784 | |||
87a3c87e45 | |||
fb086ef13f | |||
fd71da47d1 | |||
4c5f9e0910 | |||
e12c7f6dd4 | |||
8542f2e673 | |||
d83d7e8262 | |||
d8935903a2 | |||
9a367a39d0 | |||
7350525937 | |||
0989780a77 | |||
1711fdba32 | |||
f5a5abf8ad | |||
402fa8a827 | |||
ef63abdf7f | |||
85f433232a | |||
17ad275124 | |||
42104fd44b | |||
2332afe877 | |||
e3ff4bf095 | |||
1561eb612c | |||
8fbf7ce744 | |||
88a3bb74b3 | |||
f5fc6649fe | |||
aefd3eb4cf | |||
9c2bbc51ca | |||
3cbbb54927 | |||
ace1760628 | |||
887db5a3db | |||
9b33aa1967 | |||
591443d838 | |||
68e0e4abc1 | |||
cdb722123a | |||
1be245269e | |||
97519cf79f | |||
156612bb25 | |||
4301f49988 | |||
c578ac4a1a | |||
1cbc7cc274 | |||
f80be42a55 | |||
82153e8840 | |||
e0653043ff | |||
0c923bdf11 | |||
085bea5c5a | |||
166ae10ca3 | |||
ea8561c35c | |||
1b48d6e5df | |||
00e581754b | |||
8effbda3a7 | |||
d8210da505 | |||
1467b456ae | |||
85095760ff | |||
f03ed33c87 | |||
7acd43e8bb | |||
a20e667c5b | |||
3748e3cf28 | |||
119bca6ce7 | |||
c250e7be9e | |||
0970fe78a0 | |||
5d837e5ab3 | |||
c3879e3776 | |||
84226a722c | |||
1de75d2035 | |||
ee45c948ac | |||
e42d5174ef | |||
e66a1439db | |||
6846e49edf | |||
3e1eb1a2e7 | |||
ac4855e911 | |||
73dee0bec4 | |||
0506f49f9e | |||
343a018361 | |||
57de98f132 | |||
99366c6b42 | |||
384a84ceee | |||
dac2c10ce9 | |||
9b6c8d216f | |||
2f84f3d8d8 | |||
212a1efd47 | |||
68a72c6b6e | |||
9e7740011b | |||
b003734be6 | |||
e9f464debc | |||
ae7ddfb483 | |||
ab16fa1f07 | |||
db7ab961bf | |||
44a49ff45a | |||
1aca63e9e0 | |||
163fd2d76b |
16
.semaphore.sh
Executable file
16
.semaphore.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TEST_SUFFIX=$(date +%s | base64 | head -c 15)
|
||||
|
||||
TEST_OPTS="RELEASE_TEST=y INTEGRATION=y PASSES='build unit release integration_e2e functional' MANUAL_VER=v3.2.9"
|
||||
if [ "$TEST_ARCH" == "386" ]; then
|
||||
TEST_OPTS="GOARCH=386 PASSES='build unit integration_e2e'"
|
||||
fi
|
||||
|
||||
docker run \
|
||||
--rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd \
|
||||
gcr.io/etcd-development/etcd-test:go1.9.2 \
|
||||
/bin/bash -c "${TEST_OPTS} ./test 2>&1 | tee test-${TEST_SUFFIX}.log"
|
||||
|
||||
! egrep "(--- FAIL:|leak|panic: test timed out)" -A10 -B50 test-${TEST_SUFFIX}.log
|
107
.travis.yml
107
.travis.yml
@ -6,7 +6,8 @@ sudo: required
|
||||
services: docker
|
||||
|
||||
go:
|
||||
- 1.8.7
|
||||
- 1.9.2
|
||||
- tip
|
||||
|
||||
notifications:
|
||||
on_success: never
|
||||
@ -14,84 +15,74 @@ notifications:
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- TARGET=linux-amd64-build
|
||||
- TARGET=linux-amd64-unit
|
||||
- TARGET=linux-amd64-integration
|
||||
- TARGET=linux-amd64-functional
|
||||
- TARGET=linux-386-build
|
||||
- TARGET=linux-386-unit
|
||||
- TARGET=darwin-amd64-build
|
||||
- TARGET=windows-amd64-build
|
||||
- TARGET=linux-arm-build
|
||||
- TARGET=linux-arm64-build
|
||||
- TARGET=linux-ppc64le-build
|
||||
- TARGET=amd64
|
||||
- TARGET=amd64-go-tip
|
||||
- TARGET=darwin-amd64
|
||||
- TARGET=windows-amd64
|
||||
- TARGET=arm64
|
||||
- TARGET=arm
|
||||
- TARGET=386
|
||||
- TARGET=ppc64le
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- go: tip
|
||||
env: TARGET=amd64-go-tip
|
||||
exclude:
|
||||
- go: 1.9.2
|
||||
env: TARGET=amd64-go-tip
|
||||
- go: tip
|
||||
env: TARGET=amd64
|
||||
- go: tip
|
||||
env: TARGET=darwin-amd64
|
||||
- go: tip
|
||||
env: TARGET=windows-amd64
|
||||
- go: tip
|
||||
env: TARGET=arm
|
||||
- go: tip
|
||||
env: TARGET=arm64
|
||||
- go: tip
|
||||
env: TARGET=386
|
||||
- go: tip
|
||||
env: TARGET=ppc64le
|
||||
|
||||
before_install:
|
||||
- if [[ $TRAVIS_GO_VERSION == 1.* ]]; then docker pull gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION}; fi
|
||||
- docker pull gcr.io/etcd-development/etcd-test:go1.9.2
|
||||
|
||||
install:
|
||||
- pushd cmd/etcd && go get -t -v ./... && popd
|
||||
|
||||
script:
|
||||
- echo "TRAVIS_GO_VERSION=${TRAVIS_GO_VERSION}"
|
||||
- >
|
||||
case "${TARGET}" in
|
||||
linux-amd64-build)
|
||||
amd64)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=amd64 PASSES='build' ./test"
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go1.9.2 \
|
||||
/bin/bash -c "GOARCH=amd64 ./test"
|
||||
;;
|
||||
linux-amd64-unit)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=amd64 PASSES='unit' ./test"
|
||||
amd64-go-tip)
|
||||
GOARCH=amd64 ./test
|
||||
;;
|
||||
linux-amd64-integration)
|
||||
darwin-amd64)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=amd64 PASSES='integration' ./test"
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go1.9.2 \
|
||||
/bin/bash -c "GO_BUILD_FLAGS='-a -v' GOOS=darwin GOARCH=amd64 ./build"
|
||||
;;
|
||||
linux-amd64-functional)
|
||||
windows-amd64)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "./build && GOARCH=amd64 PASSES='build functional' ./test"
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go1.9.2 \
|
||||
/bin/bash -c "GO_BUILD_FLAGS='-a -v' GOOS=windows GOARCH=amd64 ./build"
|
||||
;;
|
||||
linux-386-build)
|
||||
386)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=386 PASSES='build' ./test"
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go1.9.2 \
|
||||
/bin/bash -c "GOARCH=386 PASSES='build unit' ./test"
|
||||
;;
|
||||
linux-386-unit)
|
||||
*)
|
||||
# test building out of gopath
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GOARCH=386 PASSES='unit' ./test"
|
||||
;;
|
||||
darwin-amd64-build)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GO_BUILD_FLAGS='-v' GOOS=darwin GOARCH=amd64 ./build"
|
||||
;;
|
||||
windows-amd64-build)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GO_BUILD_FLAGS='-v' GOOS=windows GOARCH=amd64 ./build"
|
||||
;;
|
||||
linux-arm-build)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GO_BUILD_FLAGS='-v' GOARCH=arm ./build"
|
||||
;;
|
||||
linux-arm64-build)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GO_BUILD_FLAGS='-v' GOARCH=arm64 ./build"
|
||||
;;
|
||||
linux-ppc64le-build)
|
||||
docker run --rm \
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION} \
|
||||
/bin/bash -c "GO_BUILD_FLAGS='-v' GOARCH=ppc64le ./build"
|
||||
--volume=`pwd`:/go/src/github.com/coreos/etcd gcr.io/etcd-development/etcd-test:go1.9.2 \
|
||||
/bin/bash -c "GO_BUILD_FLAGS='-a -v' GOARCH='${TARGET}' ./build"
|
||||
;;
|
||||
esac
|
||||
|
38
.words
Normal file
38
.words
Normal file
@ -0,0 +1,38 @@
|
||||
ErrCodeEnhanceYourCalm
|
||||
ErrTimeout
|
||||
GoAway
|
||||
RPC
|
||||
RPCs
|
||||
TODO
|
||||
backoff
|
||||
blackhole
|
||||
blackholed
|
||||
cancelable
|
||||
cancelation
|
||||
cluster_proxy
|
||||
defragment
|
||||
defragmenting
|
||||
etcd
|
||||
gRPC
|
||||
goroutine
|
||||
goroutines
|
||||
healthcheck
|
||||
iff
|
||||
inflight
|
||||
keepalive
|
||||
keepalives
|
||||
keyspace
|
||||
linearization
|
||||
localhost
|
||||
mutex
|
||||
prefetching
|
||||
protobuf
|
||||
prometheus
|
||||
repin
|
||||
serializable
|
||||
teardown
|
||||
too_many_pings
|
||||
uncontended
|
||||
unprefixed
|
||||
unlisting
|
||||
|
490
CHANGELOG.md
Normal file
490
CHANGELOG.md
Normal file
@ -0,0 +1,490 @@
|
||||
## [v3.2.10](https://github.com/coreos/etcd/releases/tag/v3.2.10) (2017-11-16)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.2.9...v3.2.10).
|
||||
|
||||
### Fixed
|
||||
|
||||
- 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/coreos/etcd/issues/8009)
|
||||
- Fix clientv3 balancer to handle [network partition](https://github.com/coreos/etcd/issues/8711)
|
||||
- Upgrade [`google.golang.org/grpc`](https://github.com/grpc/grpc-go/releases) v1.2.1 to v1.7.3
|
||||
- Upgrade [`github.com/grpc-ecosystem/grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway/releases) v1.2 to v1.3
|
||||
- Revert [discovery SRV auth `ServerName` with `*.{ROOT_DOMAIN}`](https://github.com/coreos/etcd/pull/8651) to support non-wildcard subject alternative names in the certs (see [issue #8445](https://github.com/coreos/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
|
||||
|
||||
|
||||
## [v3.2.9](https://github.com/coreos/etcd/releases/tag/v3.2.9) (2017-10-06)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.2.8...v3.2.9).
|
||||
|
||||
### Fixed(Security)
|
||||
|
||||
- Compile with [Go 1.8.4](https://groups.google.com/d/msg/golang-nuts/sHfMg4gZNps/a-HDgDDDAAAJ)
|
||||
- Update `golang.org/x/crypto/bcrypt` (See [golang/crypto@6c586e1](https://github.com/golang/crypto/commit/6c586e17d90a7d08bbbc4069984180dce3b04117) for more)
|
||||
- Fix discovery SRV bootstrapping to [authenticate `ServerName` with `*.{ROOT_DOMAIN}`](https://github.com/coreos/etcd/pull/8651), in order to support sub-domain wildcard matching (see [issue #8445](https://github.com/coreos/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
|
||||
|
||||
|
||||
## [v3.2.8](https://github.com/coreos/etcd/releases/tag/v3.2.8) (2017-09-29)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.2.7...v3.2.8).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix v2 client failover to next endpoint on mutable operation
|
||||
- Fix grpc-proxy to respect `KeysOnly` flag
|
||||
|
||||
|
||||
## [v3.2.7](https://github.com/coreos/etcd/releases/tag/v3.2.7) (2017-09-01)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.2.6...v3.2.7).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix server-side auth so concurrent auth operations do not return old revision error
|
||||
- Fix concurrency/stm Put with serializable snapshot
|
||||
- Use store revision from first fetch to resolve write conflicts instead of modified revision
|
||||
|
||||
|
||||
## [v3.2.6](https://github.com/coreos/etcd/releases/tag/v3.2.6) (2017-08-21)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.2.5...v3.2.6).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix watch restore from snapshot
|
||||
- Fix `etcd_debugging_mvcc_keys_total` inconsistency
|
||||
- Fix multiple URLs for `--listen-peer-urls` flag
|
||||
- Add `enable-pprof` to etcd configuration file format
|
||||
|
||||
|
||||
## [v3.2.5](https://github.com/coreos/etcd/releases/tag/v3.2.5) (2017-08-04)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.2.4...v3.2.5).
|
||||
|
||||
### Changed
|
||||
|
||||
- Use reverse lookup to match wildcard DNS SAN
|
||||
- Return non-zero exit code on unhealthy `endpoint health`
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix unreachable /metrics endpoint when `--enable-v2=false`
|
||||
- Fix grpc-proxy to respect `PrevKv` flag
|
||||
|
||||
### Added
|
||||
|
||||
- Add container registry `gcr.io/etcd-development/etcd`
|
||||
|
||||
|
||||
## [v3.2.4](https://github.com/coreos/etcd/releases/tag/v3.2.4) (2017-07-19)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.2.3...v3.2.4).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Do not block on active client stream when stopping server
|
||||
- Fix gRPC proxy Snapshot RPC error handling
|
||||
|
||||
|
||||
## [v3.2.3](https://github.com/coreos/etcd/releases/tag/v3.2.3) (2017-07-14)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.2.2...v3.2.3).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Let clients establish unlimited streams
|
||||
|
||||
### Added
|
||||
|
||||
- Tag docker images with minor versions
|
||||
- e.g. `docker pull quay.io/coreos/etcd:v3.2` to fetch latest v3.2 versions
|
||||
|
||||
|
||||
## [v3.1.10](https://github.com/coreos/etcd/releases/tag/v3.1.10) (2017-07-14)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.1.9...v3.1.10).
|
||||
|
||||
### Changed
|
||||
|
||||
- Compile with Go 1.8.3 to fix panic on `net/http.CloseNotify`
|
||||
|
||||
### Added
|
||||
|
||||
- Tag docker images with minor versions
|
||||
- e.g. `docker pull quay.io/coreos/etcd:v3.1` to fetch latest v3.1 versions
|
||||
|
||||
|
||||
## [v3.2.2](https://github.com/coreos/etcd/releases/tag/v3.2.2) (2017-07-07)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.2.1...v3.2.2).
|
||||
|
||||
### Improved
|
||||
|
||||
- Rate-limit lease revoke on expiration
|
||||
- Extend leases on promote to avoid queueing effect on lease expiration
|
||||
|
||||
### Fixed
|
||||
|
||||
- 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
|
||||
- Accept connection with matched IP SAN but no DNS match
|
||||
- Don't check DNS entries in certs if there's a matching IP
|
||||
- Fix 'tools/benchmark' watch command
|
||||
|
||||
|
||||
## [v3.2.1](https://github.com/coreos/etcd/releases/tag/v3.2.1) (2017-06-23)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.2.0...v3.2.1).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix backend database in-memory index corruption issue on restore (only 3.2.0 is affected)
|
||||
- Fix gRPC gateway Txn marshaling issue
|
||||
- Fix backend database size debugging metrics
|
||||
|
||||
|
||||
## [v3.2.0](https://github.com/coreos/etcd/releases/tag/v3.2.0) (2017-06-09)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.1.10...v3.2.0).
|
||||
See [upgrade 3.2](https://github.com/coreos/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md).
|
||||
|
||||
### Improved
|
||||
|
||||
- Improve backend read concurrency
|
||||
|
||||
### Added
|
||||
|
||||
- Embedded etcd
|
||||
- `Etcd.Peers` field is now `[]*peerListener`
|
||||
- RPCs
|
||||
- Add Election, Lock service
|
||||
- Native client etcdserver/api/v3client
|
||||
- client "embedded" in the server
|
||||
- gRPC proxy
|
||||
- Proxy endpoint discovery
|
||||
- Namespaces
|
||||
- Coalesce lease requests
|
||||
- v3 client
|
||||
- STM prefetching
|
||||
- Add namespace feature
|
||||
- Add `ErrOldCluster` with server version checking
|
||||
- Translate WithPrefix() into WithFromKey() for empty key
|
||||
- v3 etcdctl
|
||||
- Add `check perf` command
|
||||
- Add `--from-key` flag to role grant-permission command
|
||||
- `lock` command takes an optional command to execute
|
||||
- etcd flags
|
||||
- Add `--enable-v2` flag to configure v2 backend (enabled by default)
|
||||
- Add `--auth-token` flag
|
||||
- `etcd gateway`
|
||||
- Support DNS SRV priority
|
||||
- Auth
|
||||
- Support Watch API
|
||||
- JWT tokens
|
||||
- Logging, monitoring
|
||||
- Server warns large snapshot operations
|
||||
- Add `etcd_debugging_server_lease_expired_total` metrics
|
||||
- Security
|
||||
- Deny incoming peer certs with wrong IP SAN
|
||||
- Resolve TLS DNSNames when SAN checking
|
||||
- Reload TLS certificates on every client connection
|
||||
- Release
|
||||
- Annotate acbuild with supports-systemd-notify
|
||||
- Add `nsswitch.conf` to Docker container image
|
||||
- Add ppc64le, arm64(experimental) builds
|
||||
- Compile with Go 1.8.3
|
||||
|
||||
### Changed
|
||||
|
||||
- v3 client
|
||||
- `LeaseTimeToLive` returns TTL=-1 resp on lease not found
|
||||
- `clientv3.NewFromConfigFile` is moved to `clientv3/yaml.NewConfig`
|
||||
- concurrency package's elections updated to match RPC interfaces
|
||||
- let client dial endpoints not in the balancer
|
||||
- Dependencies
|
||||
- Update gRPC to v1.2.1
|
||||
- Update grpc-gateway to v1.2.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Allow v2 snapshot over 512MB
|
||||
|
||||
|
||||
## [v3.1.9](https://github.com/coreos/etcd/releases/tag/v3.1.9) (2017-06-09)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.1.8...v3.1.9).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Allow v2 snapshot over 512MB
|
||||
|
||||
|
||||
## [v3.1.8](https://github.com/coreos/etcd/releases/tag/v3.1.8) (2017-05-19)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.1.7...v3.1.8).
|
||||
|
||||
|
||||
## [v3.1.7](https://github.com/coreos/etcd/releases/tag/v3.1.7) (2017-04-28)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.1.6...v3.1.7).
|
||||
|
||||
|
||||
## [v3.1.6](https://github.com/coreos/etcd/releases/tag/v3.1.6) (2017-04-19)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.1.5...v3.1.6).
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove auth check in Status API
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fill in Auth API response header
|
||||
|
||||
|
||||
## [v3.1.5](https://github.com/coreos/etcd/releases/tag/v3.1.5) (2017-03-27)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.1.4...v3.1.5).
|
||||
|
||||
### Added
|
||||
|
||||
- Add `/etc/nsswitch.conf` file to alpine-based Docker image
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix raft memory leak issue
|
||||
- Fix Windows file path issues
|
||||
|
||||
|
||||
## [v3.1.4](https://github.com/coreos/etcd/releases/tag/v3.1.4) (2017-03-22)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.1.3...v3.1.4).
|
||||
|
||||
|
||||
## [v3.1.3](https://github.com/coreos/etcd/releases/tag/v3.1.3) (2017-03-10)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.1.2...v3.1.3).
|
||||
|
||||
### Changed
|
||||
|
||||
- Use machine default host when advertise URLs are default values(localhost:2379,2380) AND if listen URL is 0.0.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `etcd gateway` schema handling in DNS discovery
|
||||
- Fix sd_notify behaviors in gateway, grpc-proxy
|
||||
|
||||
|
||||
## [v3.1.2](https://github.com/coreos/etcd/releases/tag/v3.1.2) (2017-02-24)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.1.1...v3.1.2).
|
||||
|
||||
### Changed
|
||||
|
||||
- Use IPv4 default host, by default (when IPv4 and IPv6 are available)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `etcd gateway` with multiple endpoints
|
||||
|
||||
|
||||
## [v3.1.1](https://github.com/coreos/etcd/releases/tag/v3.1.1) (2017-02-17)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.1.0...v3.1.1).
|
||||
|
||||
### Changed
|
||||
|
||||
- Compile with Go 1.7.5
|
||||
|
||||
|
||||
## [v2.3.8](https://github.com/coreos/etcd/releases/tag/v2.3.8) (2017-02-17)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v2.3.7...v2.3.8).
|
||||
|
||||
### Changed
|
||||
|
||||
- Compile with Go 1.7.5
|
||||
|
||||
|
||||
## [v3.1.0](https://github.com/coreos/etcd/releases/tag/v3.1.0) (2017-01-20)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.16...v3.1.0).
|
||||
See [upgrade 3.1](https://github.com/coreos/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md).
|
||||
|
||||
### Improved
|
||||
|
||||
- Faster linearizable reads (implements Raft read-index)
|
||||
- v3 authentication API is now stable
|
||||
|
||||
### Added
|
||||
|
||||
- Automatic leadership transfer when leader steps down
|
||||
- etcd flags
|
||||
- `--strict-reconfig-check` flag is set by default
|
||||
- Add `--log-output` flag
|
||||
- Add `--metrics` flag
|
||||
- v3 client
|
||||
- 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
|
||||
- v3 etcdctl
|
||||
- Add lease timetolive command
|
||||
- Add `--print-value-only` flag to get command
|
||||
- Add `--dest-prefix` flag to make-mirror command
|
||||
- command get responses are sorted in ascending order by default
|
||||
- `recipes` now conform to sessions defined in clientv3/concurrency
|
||||
- ACI has symlinks to `/usr/local/bin/etcd*`
|
||||
- experimental gRPC proxy feature
|
||||
|
||||
### Changed
|
||||
|
||||
- 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`
|
||||
- etcd uses default route IP if advertise URL is not given
|
||||
- Cluster rejects removing members if quorum will be lost
|
||||
- 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 in 3.2
|
||||
- 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
|
||||
- Discovery now has upper limit for waiting on retries
|
||||
- Warn on binding listeners through domain names; to be deprecated in 3.2
|
||||
|
||||
|
||||
## [v3.0.16](https://github.com/coreos/etcd/releases/tag/v3.0.16) (2016-11-13)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.15...v3.0.16).
|
||||
|
||||
|
||||
## [v3.0.15](https://github.com/coreos/etcd/releases/tag/v3.0.15) (2016-11-11)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.14...v3.0.15).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix cancel watch request with wrong range end
|
||||
|
||||
|
||||
## [v3.0.14](https://github.com/coreos/etcd/releases/tag/v3.0.14) (2016-11-04)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.13...v3.0.14).
|
||||
|
||||
### Added
|
||||
|
||||
- v3 etcdctl migrate command now supports `--no-ttl` flag to discard keys on transform
|
||||
|
||||
|
||||
## [v3.0.13](https://github.com/coreos/etcd/releases/tag/v3.0.13) (2016-10-24)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.12...v3.0.13).
|
||||
|
||||
|
||||
## [v3.0.12](https://github.com/coreos/etcd/releases/tag/v3.0.12) (2016-10-07)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.11...v3.0.12).
|
||||
|
||||
|
||||
## [v3.0.11](https://github.com/coreos/etcd/releases/tag/v3.0.11) (2016-10-07)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.10...v3.0.11).
|
||||
|
||||
### Added
|
||||
|
||||
- Server returns previous key-value (optional)
|
||||
- `clientv3.WithPrevKV` option
|
||||
- v3 etcdctl put,watch,del `--prev-kv` flag
|
||||
|
||||
|
||||
## [v3.0.10](https://github.com/coreos/etcd/releases/tag/v3.0.10) (2016-09-23)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.9...v3.0.10).
|
||||
|
||||
|
||||
## [v3.0.9](https://github.com/coreos/etcd/releases/tag/v3.0.9) (2016-09-15)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.8...v3.0.9).
|
||||
|
||||
### Added
|
||||
|
||||
- Warn on domain names on listen URLs (v3.2 will reject domain names)
|
||||
|
||||
|
||||
## [v3.0.8](https://github.com/coreos/etcd/releases/tag/v3.0.8) (2016-09-09)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.7...v3.0.8).
|
||||
|
||||
### Changed
|
||||
|
||||
- Allow only IP addresses in listen URLs (domain names are rejected)
|
||||
|
||||
|
||||
## [v3.0.7](https://github.com/coreos/etcd/releases/tag/v3.0.7) (2016-08-31)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.6...v3.0.7).
|
||||
|
||||
### Changed
|
||||
|
||||
- SRV records only allow A records (RFC 2052)
|
||||
|
||||
|
||||
## [v3.0.6](https://github.com/coreos/etcd/releases/tag/v3.0.6) (2016-08-19)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.5...v3.0.6).
|
||||
|
||||
|
||||
## [v3.0.5](https://github.com/coreos/etcd/releases/tag/v3.0.5) (2016-08-19)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.4...v3.0.5).
|
||||
|
||||
### Changed
|
||||
|
||||
- SRV records (e.g., infra1.example.com) must match the discovery domain (i.e., example.com) if no custom certificate authority is given
|
||||
|
||||
|
||||
## [v3.0.4](https://github.com/coreos/etcd/releases/tag/v3.0.4) (2016-07-27)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.3...v3.0.4).
|
||||
|
||||
### Changed
|
||||
|
||||
- v2 auth can now use common name from TLS certificate when `--client-cert-auth` is enabled
|
||||
|
||||
### Added
|
||||
|
||||
- v2 etcdctl ls command now supports `--output=json`
|
||||
- Add /var/lib/etcd directory to etcd official Docker image
|
||||
|
||||
|
||||
## [v3.0.3](https://github.com/coreos/etcd/releases/tag/v3.0.3) (2016-07-15)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.2...v3.0.3).
|
||||
|
||||
### Changed
|
||||
|
||||
- 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
|
||||
|
||||
|
||||
## [v3.0.2](https://github.com/coreos/etcd/releases/tag/v3.0.2) (2016-07-08)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.1...v3.0.2).
|
||||
|
||||
### Changed
|
||||
|
||||
- Dockerfile uses `ENTRYPOINT`, instead of `CMD`, to run etcd without binary path specified
|
||||
|
||||
|
||||
## [v3.0.1](https://github.com/coreos/etcd/releases/tag/v3.0.1) (2016-07-01)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v3.0.0...v3.0.1).
|
||||
|
||||
|
||||
## [v3.0.0](https://github.com/coreos/etcd/releases/tag/v3.0.0) (2016-06-30)
|
||||
|
||||
See [code changes](https://github.com/coreos/etcd/compare/v2.3.8...v3.0.0).
|
||||
See [upgrade 3.0](https://github.com/coreos/etcd/blob/master/Documentation/upgrades/upgrade_3_0.md).
|
63
CODE_OF_CONDUCT.md
Normal file
63
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,63 @@
|
||||
## CoreOS Community Code of Conduct
|
||||
|
||||
### Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, and in the interest of
|
||||
fostering an open and welcoming community, we pledge to respect all people who
|
||||
contribute through reporting issues, posting feature requests, updating
|
||||
documentation, submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free
|
||||
experience for everyone, regardless of level of experience, gender, gender
|
||||
identity and expression, sexual orientation, disability, personal appearance,
|
||||
body size, race, ethnicity, age, religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery
|
||||
* Personal attacks
|
||||
* Trolling or insulting/derogatory comments
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as physical or electronic addresses, without explicit permission
|
||||
* Other unethical or unprofessional conduct.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct. By adopting this Code of Conduct,
|
||||
project maintainers commit themselves to fairly and consistently applying these
|
||||
principles to every aspect of managing this project. Project maintainers who do
|
||||
not follow or enforce the Code of Conduct may be permanently removed from the
|
||||
project team.
|
||||
|
||||
This code of conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting a project maintainer, Brandon Philips
|
||||
<brandon.philips@coreos.com>, and/or Meghan Schofield
|
||||
<meghan.schofield@coreos.com>.
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant
|
||||
(http://contributor-covenant.org), version 1.2.0, available at
|
||||
http://contributor-covenant.org/version/1/2/0/
|
||||
|
||||
### CoreOS Events Code of Conduct
|
||||
|
||||
CoreOS events are working conferences intended for professional networking and
|
||||
collaboration in the CoreOS community. Attendees are expected to behave
|
||||
according to professional standards and in accordance with their employer’s
|
||||
policies on appropriate workplace behavior.
|
||||
|
||||
While at CoreOS events or related social networking opportunities, attendees
|
||||
should not engage in discriminatory or offensive speech or actions including
|
||||
but not limited to gender, sexuality, race, age, disability, or religion.
|
||||
Speakers should be especially aware of these concerns.
|
||||
|
||||
CoreOS does not condone any statements by speakers contrary to these standards.
|
||||
CoreOS reserves the right to deny entrance and/or eject from an event (without
|
||||
refund) any individual found to be engaging in discriminatory or offensive
|
||||
speech or actions.
|
||||
|
||||
Please bring any concerns to the immediate attention of designated on-site
|
||||
staff, Brandon Philips <brandon.philips@coreos.com>, and/or Meghan Schofield
|
||||
<meghan.schofield@coreos.com>.
|
@ -5,7 +5,7 @@ etcd is Apache 2.0 licensed and accepts contributions via GitHub pull requests.
|
||||
# Email and chat
|
||||
|
||||
- Email: [etcd-dev](https://groups.google.com/forum/?hl=en#!forum/etcd-dev)
|
||||
- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org
|
||||
- IRC: #[etcd](irc://irc.freenode.org:6667/#etcd) IRC channel on freenode.org
|
||||
|
||||
## Getting started
|
||||
|
||||
|
@ -51,6 +51,7 @@ RUN go get -v -u -tags spell github.com/chzchzchz/goword \
|
||||
&& go get -v -u honnef.co/go/tools/cmd/staticcheck \
|
||||
&& go get -v -u github.com/wadey/gocovmerge \
|
||||
&& go get -v -u github.com/gordonklaus/ineffassign \
|
||||
&& go get -v -u github.com/alexkohler/nakedret \
|
||||
&& /tmp/install-marker.sh amd64 \
|
||||
&& rm -f /tmp/install-marker.sh \
|
||||
&& curl -s https://codecov.io/bash >/codecov \
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Backwards-compatible bug fixes should target the master branch and subsequently be ported to stable branches.
|
||||
* Once the master branch is ready for release, it will be tagged and become the new stable branch.
|
||||
|
||||
The etcd team has adopted a *rolling release model* and supports one stable version of etcd.
|
||||
The etcd team has adopted a *rolling release model* and supports two stable versions of etcd.
|
||||
|
||||
### Master branch
|
||||
|
||||
@ -21,6 +21,6 @@ Before the release of the next stable version, feature PRs will be frozen. We wi
|
||||
|
||||
All branches with prefix `release-` are considered _stable_ branches.
|
||||
|
||||
After every minor release (http://semver.org/), we will have a new stable branch for that release. We will keep fixing the backwards-compatible bugs for the latest stable release, but not previous releases. The _patch_ release, incorporating any bug fixes, will be once every two weeks, given any patches.
|
||||
After every minor release (http://semver.org/), we will have a new stable branch for that release. We will keep fixing the backwards-compatible bugs for the latest two stable releases. A _patch_ release to each supported release branch, incorporating any bug fixes, will be once every two weeks, given any patches.
|
||||
|
||||
[master]: https://github.com/coreos/etcd/tree/master
|
||||
|
@ -6,9 +6,11 @@ etcd v3 uses [gRPC][grpc] for its messaging protocol. The etcd project includes
|
||||
|
||||
## Using grpc-gateway
|
||||
|
||||
The gateway accepts a [JSON mapping][json-mapping] for etcd's [protocol buffer][api-ref] message definitions. Note that `key` and `value` fields are defined as byte arrays and therefore must be base64 encoded in JSON.
|
||||
The gateway accepts a [JSON mapping][json-mapping] for etcd's [protocol buffer][api-ref] message definitions. Note that `key` and `value` fields are defined as byte arrays and therefore must be base64 encoded in JSON. The following examples use `curl`, but any HTTP/JSON client should work all the same.
|
||||
|
||||
Use `curl` to put and get a key:
|
||||
### Put and get keys
|
||||
|
||||
Use the `/v3beta/kv/range` and `/v3beta/kv/put` services to read and write keys:
|
||||
|
||||
```bash
|
||||
<<COMMENT
|
||||
@ -17,41 +19,88 @@ foo is 'Zm9v' in Base64
|
||||
bar is 'YmFy'
|
||||
COMMENT
|
||||
|
||||
curl -L http://localhost:2379/v3alpha/kv/put \
|
||||
curl -L http://localhost:2379/v3beta/kv/put \
|
||||
-X POST -d '{"key": "Zm9v", "value": "YmFy"}'
|
||||
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"3"}}
|
||||
|
||||
curl -L http://localhost:2379/v3alpha/kv/range \
|
||||
curl -L http://localhost:2379/v3beta/kv/range \
|
||||
-X POST -d '{"key": "Zm9v"}'
|
||||
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"3"},"kvs":[{"key":"Zm9v","create_revision":"2","mod_revision":"2","version":"1","value":"YmFy"}],"count":"1"}
|
||||
|
||||
# get all keys prefixed with "foo"
|
||||
curl -L http://localhost:2379/v3alpha/kv/range \
|
||||
curl -L http://localhost:2379/v3beta/kv/range \
|
||||
-X POST -d '{"key": "Zm9v", "range_end": "Zm9w"}'
|
||||
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"3"},"kvs":[{"key":"Zm9v","create_revision":"2","mod_revision":"2","version":"1","value":"YmFy"}],"count":"1"}
|
||||
```
|
||||
|
||||
Use `curl` to watch a key:
|
||||
### Watch keys
|
||||
|
||||
Use the `/v3beta/watch` service to watch keys:
|
||||
|
||||
```bash
|
||||
curl http://localhost:2379/v3alpha/watch \
|
||||
curl http://localhost:2379/v3beta/watch \
|
||||
-X POST -d '{"create_request": {"key":"Zm9v"} }' &
|
||||
# {"result":{"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"1","raft_term":"2"},"created":true}}
|
||||
|
||||
curl -L http://localhost:2379/v3alpha/kv/put \
|
||||
curl -L http://localhost:2379/v3beta/kv/put \
|
||||
-X POST -d '{"key": "Zm9v", "value": "YmFy"}' >/dev/null 2>&1
|
||||
# {"result":{"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"2"},"events":[{"kv":{"key":"Zm9v","create_revision":"2","mod_revision":"2","version":"1","value":"YmFy"}}]}}
|
||||
```
|
||||
|
||||
Use `curl` to issue a transaction:
|
||||
### Transactions
|
||||
|
||||
Issue a transaction with `/v3beta/kv/txn`:
|
||||
|
||||
```bash
|
||||
curl -L http://localhost:2379/v3alpha/kv/txn \
|
||||
curl -L http://localhost:2379/v3beta/kv/txn \
|
||||
-X POST \
|
||||
-d '{"compare":[{"target":"CREATE","key":"Zm9v","createRevision":"2"}],"success":[{"requestPut":{"key":"Zm9v","value":"YmFy"}}]}'
|
||||
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"3","raft_term":"2"},"succeeded":true,"responses":[{"response_put":{"header":{"revision":"3"}}}]}
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
Set up authentication with the `/v3beta/auth` service:
|
||||
|
||||
```bash
|
||||
# create root user
|
||||
curl -L http://localhost:2379/v3beta/auth/user/add \
|
||||
-X POST -d '{"name": "root", "password": "pass"}'
|
||||
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
|
||||
|
||||
# create root role
|
||||
curl -L http://localhost:2379/v3beta/auth/role/add \
|
||||
-X POST -d '{"name": "root"}'
|
||||
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
|
||||
|
||||
# grant root role
|
||||
curl -L http://localhost:2379/v3beta/auth/user/grant \
|
||||
-X POST -d '{"user": "root", "role": "root"}'
|
||||
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
|
||||
|
||||
# enable auth
|
||||
curl -L http://localhost:2379/v3beta/auth/enable -X POST -d '{}'
|
||||
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
|
||||
```
|
||||
|
||||
Authenticate with etcd for an authentication token using `/v3beta/auth/authenticate`:
|
||||
|
||||
```bash
|
||||
# get the auth token for the root user
|
||||
curl -L http://localhost:2379/v3beta/auth/authenticate \
|
||||
-X POST -d '{"name": "root", "password": "pass"}'
|
||||
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"},"token":"sssvIpwfnLAcWAQH.9"}
|
||||
```
|
||||
|
||||
Set the `Authorization` header to the authentication token to fetch a key using authentication credentials:
|
||||
|
||||
```bash
|
||||
curl -L http://localhost:2379/v3beta/kv/put \
|
||||
-H 'Authorization : sssvIpwfnLAcWAQH.9' \
|
||||
-X POST -d '{"key": "Zm9v", "value": "YmFy"}'
|
||||
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"2","raft_term":"2"}}
|
||||
```
|
||||
|
||||
## Swagger
|
||||
|
||||
Generated [Swagger][swagger] API definitions can be found at [rpc.swagger.json][swagger-doc].
|
||||
|
@ -58,6 +58,7 @@ This is a generated documentation. Please read the proto files for more.
|
||||
| LeaseRevoke | LeaseRevokeRequest | LeaseRevokeResponse | LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted. |
|
||||
| LeaseKeepAlive | LeaseKeepAliveRequest | LeaseKeepAliveResponse | LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client to the server and streaming keep alive responses from the server to the client. |
|
||||
| LeaseTimeToLive | LeaseTimeToLiveRequest | LeaseTimeToLiveResponse | LeaseTimeToLive retrieves lease information. |
|
||||
| LeaseLeases | LeaseLeasesRequest | LeaseLeasesResponse | LeaseLeases lists all existing leases. |
|
||||
|
||||
|
||||
|
||||
@ -68,8 +69,10 @@ This is a generated documentation. Please read the proto files for more.
|
||||
| Alarm | AlarmRequest | AlarmResponse | Alarm activates, deactivates, and queries alarms regarding cluster health. |
|
||||
| Status | StatusRequest | StatusResponse | Status gets the status of the member. |
|
||||
| Defragment | DefragmentRequest | DefragmentResponse | Defragment defragments a member's backend database to recover storage space. |
|
||||
| Hash | HashRequest | HashResponse | Hash returns the hash of the local KV state for consistency checking purpose. This is designed for testing; do not use this in production when there are ongoing transactions. |
|
||||
| Hash | HashRequest | HashResponse | Hash computes the hash of the KV's backend. This is designed for testing; do not use this in production when there are ongoing transactions. |
|
||||
| HashKV | HashKVRequest | HashKVResponse | HashKV computes the hash of all MVCC keys up to a given revision. |
|
||||
| Snapshot | SnapshotRequest | SnapshotResponse | Snapshot sends a snapshot of the entire backend from a member over a stream to a client. |
|
||||
| MoveLeader | MoveLeaderRequest | MoveLeaderResponse | MoveLeader requests current leader node to transfer its leadership to transferee. |
|
||||
|
||||
|
||||
|
||||
@ -401,6 +404,8 @@ CompactionRequest compacts the key-value store up to a given revision. All super
|
||||
| create_revision | create_revision is the creation revision of the given key | int64 |
|
||||
| mod_revision | mod_revision is the last modified revision of the given key. | int64 |
|
||||
| value | value is the value of the given key, in bytes. | bytes |
|
||||
| lease | lease is the lease id of the given key. | int64 |
|
||||
| range_end | range_end compares the given target to all keys in the range [key, range_end). See RangeRequest for more details on key ranges. | bytes |
|
||||
|
||||
|
||||
|
||||
@ -438,6 +443,24 @@ Empty field.
|
||||
|
||||
|
||||
|
||||
##### message `HashKVRequest` (etcdserver/etcdserverpb/rpc.proto)
|
||||
|
||||
| Field | Description | Type |
|
||||
| ----- | ----------- | ---- |
|
||||
| revision | revision is the key-value store revision for the hash operation. | int64 |
|
||||
|
||||
|
||||
|
||||
##### message `HashKVResponse` (etcdserver/etcdserverpb/rpc.proto)
|
||||
|
||||
| Field | Description | Type |
|
||||
| ----- | ----------- | ---- |
|
||||
| header | | ResponseHeader |
|
||||
| hash | hash is the hash value computed from the responding member's MVCC keys up to a given revision. | uint32 |
|
||||
| compact_revision | compact_revision is the compacted revision of key-value store when hash begins. | int64 |
|
||||
|
||||
|
||||
|
||||
##### message `HashRequest` (etcdserver/etcdserverpb/rpc.proto)
|
||||
|
||||
Empty field.
|
||||
@ -449,7 +472,7 @@ Empty field.
|
||||
| Field | Description | Type |
|
||||
| ----- | ----------- | ---- |
|
||||
| header | | ResponseHeader |
|
||||
| hash | hash is the hash value computed from the responding member's key-value store. | uint32 |
|
||||
| hash | hash is the hash value computed from the responding member's KV's backend. | uint32 |
|
||||
|
||||
|
||||
|
||||
@ -491,6 +514,21 @@ Empty field.
|
||||
|
||||
|
||||
|
||||
##### message `LeaseLeasesRequest` (etcdserver/etcdserverpb/rpc.proto)
|
||||
|
||||
Empty field.
|
||||
|
||||
|
||||
|
||||
##### message `LeaseLeasesResponse` (etcdserver/etcdserverpb/rpc.proto)
|
||||
|
||||
| Field | Description | Type |
|
||||
| ----- | ----------- | ---- |
|
||||
| header | | ResponseHeader |
|
||||
| leases | | (slice of) LeaseStatus |
|
||||
|
||||
|
||||
|
||||
##### message `LeaseRevokeRequest` (etcdserver/etcdserverpb/rpc.proto)
|
||||
|
||||
| Field | Description | Type |
|
||||
@ -507,6 +545,14 @@ Empty field.
|
||||
|
||||
|
||||
|
||||
##### message `LeaseStatus` (etcdserver/etcdserverpb/rpc.proto)
|
||||
|
||||
| Field | Description | Type |
|
||||
| ----- | ----------- | ---- |
|
||||
| ID | | int64 |
|
||||
|
||||
|
||||
|
||||
##### message `LeaseTimeToLiveRequest` (etcdserver/etcdserverpb/rpc.proto)
|
||||
|
||||
| Field | Description | Type |
|
||||
@ -607,6 +653,22 @@ Empty field.
|
||||
|
||||
|
||||
|
||||
##### message `MoveLeaderRequest` (etcdserver/etcdserverpb/rpc.proto)
|
||||
|
||||
| Field | Description | Type |
|
||||
| ----- | ----------- | ---- |
|
||||
| targetID | targetID is the node ID for the new leader. | uint64 |
|
||||
|
||||
|
||||
|
||||
##### message `MoveLeaderResponse` (etcdserver/etcdserverpb/rpc.proto)
|
||||
|
||||
| Field | Description | Type |
|
||||
| ----- | ----------- | ---- |
|
||||
| header | | ResponseHeader |
|
||||
|
||||
|
||||
|
||||
##### message `PutRequest` (etcdserver/etcdserverpb/rpc.proto)
|
||||
|
||||
| Field | Description | Type |
|
||||
@ -668,6 +730,7 @@ Empty field.
|
||||
| request_range | | RangeRequest |
|
||||
| request_put | | PutRequest |
|
||||
| request_delete_range | | DeleteRangeRequest |
|
||||
| request_txn | | TxnRequest |
|
||||
|
||||
|
||||
|
||||
@ -690,6 +753,7 @@ Empty field.
|
||||
| response_range | | RangeResponse |
|
||||
| response_put | | PutResponse |
|
||||
| response_delete_range | | DeleteRangeResponse |
|
||||
| response_txn | | TxnResponse |
|
||||
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v3alpha/election/campaign": {
|
||||
"/v3beta/election/campaign": {
|
||||
"post": {
|
||||
"summary": "Campaign waits to acquire leadership in an election, returning a LeaderKey\nrepresenting the leadership if successful. The LeaderKey can then be used\nto issue new values on the election, transactionally guard API requests on\nleadership still being held, and resign from the election.",
|
||||
"operationId": "Campaign",
|
||||
@ -42,7 +42,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v3alpha/election/leader": {
|
||||
"/v3beta/election/leader": {
|
||||
"post": {
|
||||
"summary": "Leader returns the current election proclamation, if any.",
|
||||
"operationId": "Leader",
|
||||
@ -69,7 +69,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v3alpha/election/observe": {
|
||||
"/v3beta/election/observe": {
|
||||
"post": {
|
||||
"summary": "Observe streams election proclamations in-order as made by the election's\nelected leaders.",
|
||||
"operationId": "Observe",
|
||||
@ -96,7 +96,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v3alpha/election/proclaim": {
|
||||
"/v3beta/election/proclaim": {
|
||||
"post": {
|
||||
"summary": "Proclaim updates the leader's posted value with a new value.",
|
||||
"operationId": "Proclaim",
|
||||
@ -123,7 +123,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v3alpha/election/resign": {
|
||||
"/v3beta/election/resign": {
|
||||
"post": {
|
||||
"summary": "Resign releases election leadership so other campaigners may acquire\nleadership on the election.",
|
||||
"operationId": "Resign",
|
||||
|
@ -15,7 +15,7 @@
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v3alpha/lock/lock": {
|
||||
"/v3beta/lock/lock": {
|
||||
"post": {
|
||||
"summary": "Lock acquires a distributed shared lock on a given named lock.\nOn success, it will return a unique key that exists so long as the\nlock is held by the caller. This key can be used in conjunction with\ntransactions to safely ensure updates to etcd only occur while holding\nlock ownership. The lock is held until Unlock is called on the key or the\nlease associate with the owner expires.",
|
||||
"operationId": "Lock",
|
||||
@ -42,7 +42,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v3alpha/lock/unlock": {
|
||||
"/v3beta/lock/unlock": {
|
||||
"post": {
|
||||
"summary": "Unlock takes a key returned by Lock and releases the hold on lock. The\nnext Lock caller waiting for the lock will then be woken up and given\nownership of the lock.",
|
||||
"operationId": "Unlock",
|
||||
|
@ -4,4 +4,4 @@ For the most part, the etcd project is stable, but we are still moving fast! We
|
||||
|
||||
## The current experimental API/features are:
|
||||
|
||||
(none currently)
|
||||
- [KV ordering](https://godoc.org/github.com/coreos/etcd/clientv3/ordering) wrapper. When an etcd client switches endpoints, responses to serializable reads may go backward in time if the new endpoint is lagging behind the rest of the cluster. The ordering wrapper caches the current cluster revision from response headers. If a response revision is less than the cached revision, the client selects another endpoint and reissues the read. Enable in grpcproxy with `--experimental-serializable-ordering`.
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
Users mostly interact with etcd by putting or getting the value of a key. This section describes how to do that by using etcdctl, a command line tool for interacting with etcd server. The concepts described here should apply to the gRPC APIs or client library APIs.
|
||||
|
||||
By default, etcdctl talks to the etcd server with the v2 API for backward compatibility. For etcdctl to speak to etcd using the v3 API, the API version must be set to version 3 via the `ETCDCTL_API` environment variable.
|
||||
By default, etcdctl talks to the etcd server with the v2 API for backward compatibility. For etcdctl to speak to etcd using the v3 API, the API version must be set to version 3 via the `ETCDCTL_API` environment variable. However note that any key that was created using the v2 API will not be able to be queried via the v3 API. A v3 API ```etcdctl get``` of a v2 key will exit with 0 and no key data, this is the expected behaviour.
|
||||
|
||||
|
||||
```bash
|
||||
export ETCDCTL_API=3
|
||||
@ -215,7 +216,7 @@ $ etcdctl del foo foo9
|
||||
Here is the command to delete key `zoo` with the deleted key value pair returned:
|
||||
|
||||
```bash
|
||||
$ etcdctl del --prev-kv zoo
|
||||
$ etcdctl del --prev-kv zoo
|
||||
1 # one key is deleted
|
||||
zoo # deleted key
|
||||
val # the value of the deleted key
|
||||
@ -224,7 +225,7 @@ val # the value of the deleted key
|
||||
Here is the command to delete keys having prefix as `zoo`:
|
||||
|
||||
```bash
|
||||
$ etcdctl del --prefix zoo
|
||||
$ etcdctl del --prefix zoo
|
||||
2 # two keys are deleted
|
||||
```
|
||||
|
||||
@ -290,7 +291,7 @@ barz1
|
||||
Here is the command to watch on multiple keys `foo` and `zoo`:
|
||||
|
||||
```bash
|
||||
$ etcdctl watch -i
|
||||
$ etcdctl watch -i
|
||||
$ watch foo
|
||||
$ watch zoo
|
||||
# in another terminal: etcdctl put foo bar
|
||||
@ -430,9 +431,9 @@ Here is the command to keep the same lease alive:
|
||||
|
||||
```bash
|
||||
$ etcdctl lease keep-alive 32695410dcc0ca06
|
||||
lease 32695410dcc0ca06 keepalived with TTL(100)
|
||||
lease 32695410dcc0ca06 keepalived with TTL(100)
|
||||
lease 32695410dcc0ca06 keepalived with TTL(100)
|
||||
lease 32695410dcc0ca06 keepalived with TTL(10)
|
||||
lease 32695410dcc0ca06 keepalived with TTL(10)
|
||||
lease 32695410dcc0ca06 keepalived with TTL(10)
|
||||
...
|
||||
```
|
||||
|
||||
@ -472,4 +473,3 @@ lease 694d5765fc71500b granted with TTL(500s), remaining(132s), attached keys([z
|
||||
# if the lease has expired or does not exist it will give the below response:
|
||||
Error: etcdserver: requested lease not found
|
||||
```
|
||||
|
||||
|
@ -1,90 +1,163 @@
|
||||
# Setup a local cluster
|
||||
# Set up a local cluster
|
||||
|
||||
For testing and development deployments, the quickest and easiest way is to set up a local cluster. For a production deployment, refer to the [clustering][clustering] section.
|
||||
For testing and development deployments, the quickest and easiest way is to configure a local cluster. For a production deployment, refer to the [clustering][clustering] section.
|
||||
|
||||
## Local standalone cluster
|
||||
|
||||
Deploying an etcd cluster as a standalone cluster is straightforward. Start it with just one command:
|
||||
### Starting a cluster
|
||||
|
||||
Run the following to deploy an etcd cluster as a standalone cluster:
|
||||
|
||||
```
|
||||
$ ./etcd
|
||||
...
|
||||
```
|
||||
|
||||
The started etcd member listens on `localhost:2379` for client requests.
|
||||
If the `etcd` binary is not present in the current working directory, it might be located either at `$GOPATH/bin/etcd` or at `/usr/local/bin/etcd`. Run the command appropriately.
|
||||
|
||||
To interact with the started cluster by using etcdctl:
|
||||
The running etcd member listens on `localhost:2379` for client requests.
|
||||
|
||||
```
|
||||
# use API version 3
|
||||
$ export ETCDCTL_API=3
|
||||
### Interacting with the cluster
|
||||
|
||||
$ ./etcdctl put foo bar
|
||||
OK
|
||||
Use `etcdctl` to interact with the running cluster:
|
||||
|
||||
$ ./etcdctl get foo
|
||||
bar
|
||||
```
|
||||
1. Configure the environment to have `ETCDCTL_API=3` so `etcdctl` uses the etcd API version 3 instead of defaulting to version 2.
|
||||
|
||||
```
|
||||
# use API version 3
|
||||
$ export ETCDCTL_API=3
|
||||
```
|
||||
|
||||
2. Store an example key-value pair in the cluster:
|
||||
|
||||
```
|
||||
$ ./etcdctl put foo bar
|
||||
OK
|
||||
```
|
||||
|
||||
If OK is printed, storing key-value pair is successful.
|
||||
|
||||
3. Retrieve the value of `foo`:
|
||||
|
||||
```
|
||||
$ ./etcdctl get foo
|
||||
bar
|
||||
```
|
||||
|
||||
If `bar` is returned, interaction with the etcd cluster is working as expected.
|
||||
|
||||
## Local multi-member cluster
|
||||
|
||||
A `Procfile` at the base of this git repo is provided to easily set up a local multi-member cluster. To start a multi-member cluster go to the root of an etcd source tree and run:
|
||||
### Starting a cluster
|
||||
|
||||
```
|
||||
# install goreman program to control Profile-based applications.
|
||||
$ go get github.com/mattn/goreman
|
||||
$ goreman -f Procfile start
|
||||
...
|
||||
```
|
||||
A `Procfile` at the base of the etcd git repository is provided to easily configure a local multi-member cluster. To start a multi-member cluster, navigate to the root of the etcd source tree and perform the following:
|
||||
|
||||
The started members listen on `localhost:2379`, `localhost:22379`, and `localhost:32379` for client requests respectively.
|
||||
1. Install `goreman` to control Procfile-based applications:
|
||||
|
||||
To interact with the started cluster by using etcdctl:
|
||||
```
|
||||
$ go get github.com/mattn/goreman
|
||||
```
|
||||
|
||||
```
|
||||
# use API version 3
|
||||
$ export ETCDCTL_API=3
|
||||
2. Start a cluster with `goreman` using etcd's stock Procfile:
|
||||
|
||||
$ etcdctl --write-out=table --endpoints=localhost:2379 member list
|
||||
+------------------+---------+--------+------------------------+------------------------+
|
||||
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
|
||||
+------------------+---------+--------+------------------------+------------------------+
|
||||
| 8211f1d0f64f3269 | started | infra1 | http://127.0.0.1:2380 | http://127.0.0.1:2379 |
|
||||
| 91bc3c398fb3c146 | started | infra2 | http://127.0.0.1:22380 | http://127.0.0.1:22379 |
|
||||
| fd422379fda50e48 | started | infra3 | http://127.0.0.1:32380 | http://127.0.0.1:32379 |
|
||||
+------------------+---------+--------+------------------------+------------------------+
|
||||
```
|
||||
$ goreman -f Procfile start
|
||||
```
|
||||
|
||||
$ etcdctl put foo bar
|
||||
OK
|
||||
```
|
||||
The members start running. They listen on `localhost:2379`, `localhost:22379`, and `localhost:32379` respectively for client requests.
|
||||
|
||||
To exercise etcd's fault tolerance, kill a member:
|
||||
### Interacting with the cluster
|
||||
|
||||
```
|
||||
# kill etcd2
|
||||
$ goreman run stop etcd2
|
||||
Use `etcdctl` to interact with the running cluster:
|
||||
|
||||
$ etcdctl put key hello
|
||||
OK
|
||||
1. Configure the environment to have `ETCDCTL_API=3` so `etcdctl` uses the etcd API version 3 instead of defaulting to version 2.
|
||||
|
||||
$ etcdctl get key
|
||||
hello
|
||||
```
|
||||
# use API version 3
|
||||
$ export ETCDCTL_API=3
|
||||
```
|
||||
|
||||
# try to get key from the killed member
|
||||
$ etcdctl --endpoints=localhost:22379 get key
|
||||
2016/04/18 23:07:35 grpc: Conn.resetTransport failed to create client transport: connection error: desc = "transport: dial tcp 127.0.0.1:22379: getsockopt: connection refused"; Reconnecting to "localhost:22379"
|
||||
Error: grpc: timed out trying to connect
|
||||
2. Print the list of members:
|
||||
|
||||
# restart the killed member
|
||||
$ goreman run restart etcd2
|
||||
```
|
||||
$ etcdctl --write-out=table --endpoints=localhost:2379 member list
|
||||
```
|
||||
The list of etcd members are displayed as follows:
|
||||
|
||||
# get the key from restarted member
|
||||
$ etcdctl --endpoints=localhost:22379 get key
|
||||
hello
|
||||
```
|
||||
```
|
||||
+------------------+---------+--------+------------------------+------------------------+
|
||||
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
|
||||
+------------------+---------+--------+------------------------+------------------------+
|
||||
| 8211f1d0f64f3269 | started | infra1 | http://127.0.0.1:2380 | http://127.0.0.1:2379 |
|
||||
| 91bc3c398fb3c146 | started | infra2 | http://127.0.0.1:22380 | http://127.0.0.1:22379 |
|
||||
| fd422379fda50e48 | started | infra3 | http://127.0.0.1:32380 | http://127.0.0.1:32379 |
|
||||
+------------------+---------+--------+------------------------+------------------------+
|
||||
```
|
||||
|
||||
To learn more about interacting with etcd, read [interacting with etcd section][interacting].
|
||||
3. Store an example key-value pair in the cluster:
|
||||
|
||||
```
|
||||
$ etcdctl put foo bar
|
||||
OK
|
||||
```
|
||||
|
||||
If OK is printed, storing key-value pair is successful.
|
||||
|
||||
### Testing fault tolerance
|
||||
|
||||
To exercise etcd's fault tolerance, kill a member and attempt to retrieve the key.
|
||||
|
||||
1. Identify the process name of the member to be stopped.
|
||||
|
||||
The `Procfile` lists the properties of the multi-member cluster. For example, consider the member with the process name, `etcd2`.
|
||||
|
||||
2. Stop the member:
|
||||
|
||||
```
|
||||
# kill etcd2
|
||||
$ goreman run stop etcd2
|
||||
```
|
||||
|
||||
3. Store a key:
|
||||
|
||||
```
|
||||
$ etcdctl put key hello
|
||||
OK
|
||||
```
|
||||
|
||||
4. Retrieve the key that is stored in the previous step:
|
||||
|
||||
```
|
||||
$ etcdctl get key
|
||||
hello
|
||||
```
|
||||
|
||||
5. Retrieve a key from the stopped member:
|
||||
|
||||
```
|
||||
$ etcdctl --endpoints=localhost:22379 get key
|
||||
```
|
||||
|
||||
The command should display an error caused by connection failure:
|
||||
|
||||
```
|
||||
2017/06/18 23:07:35 grpc: Conn.resetTransport failed to create client transport: connection error: desc = "transport: dial tcp 127.0.0.1:22379: getsockopt: connection refused"; Reconnecting to "localhost:22379"
|
||||
Error: grpc: timed out trying to connect
|
||||
```
|
||||
6. Restart the stopped member:
|
||||
|
||||
```
|
||||
$ goreman run restart etcd2
|
||||
```
|
||||
|
||||
7. Get the key from the restarted member:
|
||||
|
||||
```
|
||||
$ etcdctl --endpoints=localhost:22379 get key
|
||||
hello
|
||||
```
|
||||
|
||||
Restarting the member re-establish the connection. `etcdctl` will now be able to retrieve the key successfully. To learn more about interacting with etcd, read [interacting with etcd section][interacting].
|
||||
|
||||
[interacting]: ./interacting_v3.md
|
||||
[clustering]: ../op-guide/clustering.md
|
||||
|
||||
|
@ -25,8 +25,10 @@ All releases version numbers follow the format of [semantic versioning 2.0.0](ht
|
||||
|
||||
### Patch version release
|
||||
|
||||
- Discuss about commits that are backported to the patch release. The commits should not include merge commits.
|
||||
- Cherry-pick these commits starting from the oldest one into stable branch.
|
||||
- To request a backport, devlopers submit cherrypick PRs targeting the release branch. The commits should not include merge commits. The commits should be restricted to bug fixes and security patches.
|
||||
- The cherrypick PRs should target the appropriate release branch (`base:release-<major>-<minor>`). `hack/patch/cherrypick.sh` may be used to automatically generate cherrypick PRs.
|
||||
- The release patch manager reviews the cherrypick PRs. Please discuss carefully what is backported to the patch release. Each patch release should be strictly better than it's predecessor.
|
||||
- The release patch manager will cherry-pick these commits starting from the oldest one into stable branch.
|
||||
|
||||
## Write release note
|
||||
|
||||
@ -53,7 +55,7 @@ All releases version numbers follow the format of [semantic versioning 2.0.0](ht
|
||||
Run release script in root directory:
|
||||
|
||||
```
|
||||
./scripts/release.sh ${VERSION}
|
||||
TAG=gcr.io/etcd-development/etcd ./scripts/release.sh ${VERSION}
|
||||
```
|
||||
|
||||
It generates all release binaries and images under directory ./release.
|
||||
@ -66,8 +68,8 @@ The following commands are used for public release sign:
|
||||
|
||||
```
|
||||
cd release
|
||||
for i in etcd-*{.zip,.tar.gz}; do gpg2 --default-key $SUBKEYID --armor --output ${i}.asc --detach-sign ${i}; done
|
||||
for i in etcd-*{.zip,.tar.gz}; do gpg2 --verify ${i}.asc ${i}; done
|
||||
for i in etcd-*{.zip,.tar.gz,.aci}; do gpg2 --default-key $SUBKEYID --armor --output ${i}.asc --detach-sign ${i}; done
|
||||
for i in etcd-*{.zip,.tar.gz,.aci}; do gpg2 --verify ${i}.asc ${i}; done
|
||||
|
||||
# sign zipped source code files
|
||||
wget https://github.com/coreos/etcd/archive/${VERSION}.zip
|
||||
@ -90,13 +92,41 @@ The public key for GPG signing can be found at [CoreOS Application Signing Key](
|
||||
- Select whether it is a pre-release.
|
||||
- Publish the release!
|
||||
|
||||
## Publish docker image in gcr.io
|
||||
|
||||
- Push docker image:
|
||||
|
||||
```
|
||||
gcloud docker -- login -u _json_key -p "$(cat /etc/gcp-key-etcd.json)" https://gcr.io
|
||||
|
||||
for TARGET_ARCH in "-arm64" "-ppc64le" ""; do
|
||||
gcloud docker -- push gcr.io/etcd-development/etcd:${VERSION}${TARGET_ARCH}
|
||||
done
|
||||
```
|
||||
|
||||
- Add `latest` tag to the new image on [gcr.io](https://console.cloud.google.com/gcr/images/etcd-development/GLOBAL/etcd?project=etcd-development&authuser=1) if this is a stable release.
|
||||
|
||||
## Publish docker image in Quay.io
|
||||
|
||||
- Build docker images with quay.io:
|
||||
|
||||
```
|
||||
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}
|
||||
done
|
||||
```
|
||||
|
||||
- Push docker image:
|
||||
|
||||
```
|
||||
docker login quay.io
|
||||
docker push quay.io/coreos/etcd:${VERSION}
|
||||
|
||||
for TARGET_ARCH in "-arm64" "-ppc64le" ""; do
|
||||
docker push quay.io/coreos/etcd:${VERSION}${TARGET_ARCH}
|
||||
done
|
||||
```
|
||||
|
||||
- Add `latest` tag to the new image on [quay.io](https://quay.io/repository/coreos/etcd?tag=latest&tab=tags) if this is a stable release.
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
## System requirements
|
||||
|
||||
The etcd performance benchmarks run etcd on 8 vCPU, 16GB RAM, 50GB SSD GCE instances, but any relatively modern machine with low latency storage and a few gigabytes of memory should suffice for most use cases. Applications with large v2 data stores will require more memory than a large v3 data store since data is kept in anonymous memory instead of memory mapped from a file. For running etcd on a cloud provider, we suggest at least a medium instance on AWS or a standard-1 instance on GCE.
|
||||
The etcd performance benchmarks run etcd on 8 vCPU, 16GB RAM, 50GB SSD GCE instances, but any relatively modern machine with low latency storage and a few gigabytes of memory should suffice for most use cases. Applications with large v2 data stores will require more memory than a large v3 data store since data is kept in anonymous memory instead of memory mapped from a file. For running etcd on a cloud provider, see the [Example hardware configuration][example-hardware-configurations] documentation.
|
||||
|
||||
## Download the pre-built binary
|
||||
|
||||
@ -10,7 +10,7 @@ The easiest way to get etcd is to use one of the pre-built release binaries whic
|
||||
|
||||
## Build the latest version
|
||||
|
||||
For those wanting to try the very latest version, build etcd from the `master` branch. [Go](https://golang.org/) version 1.8+ is required to build the latest version of etcd. To ensure etcd is built against well-tested libraries, etcd vendors its dependencies for official release binaries. However, etcd's vendoring is also optional to avoid potential import conflicts when embedding the etcd server or using the etcd client.
|
||||
For those wanting to try the very latest version, build etcd from the `master` branch. [Go](https://golang.org/) version 1.9+ is required to build the latest version of etcd. To ensure etcd is built against well-tested libraries, etcd vendors its dependencies for official release binaries. However, etcd's vendoring is also optional to avoid potential import conflicts when embedding the etcd server or using the etcd client.
|
||||
|
||||
To build `etcd` from the `master` branch without a `GOPATH` using the official `build` script:
|
||||
|
||||
@ -18,7 +18,6 @@ To build `etcd` from the `master` branch without a `GOPATH` using the official `
|
||||
$ git clone https://github.com/coreos/etcd.git
|
||||
$ cd etcd
|
||||
$ ./build
|
||||
$ ./bin/etcd
|
||||
```
|
||||
|
||||
To build a vendored `etcd` from the `master` branch via `go get`:
|
||||
@ -28,7 +27,6 @@ To build a vendored `etcd` from the `master` branch via `go get`:
|
||||
$ echo $GOPATH
|
||||
/Users/example/go
|
||||
$ go get github.com/coreos/etcd/cmd/etcd
|
||||
$ $GOPATH/bin/etcd
|
||||
```
|
||||
|
||||
To build `etcd` from the `master` branch without vendoring (may not build due to upstream conflicts):
|
||||
@ -38,20 +36,28 @@ To build `etcd` from the `master` branch without vendoring (may not build due to
|
||||
$ echo $GOPATH
|
||||
/Users/example/go
|
||||
$ go get github.com/coreos/etcd
|
||||
$ $GOPATH/bin/etcd
|
||||
```
|
||||
|
||||
## Test the installation
|
||||
|
||||
Check the etcd binary is built correctly by starting etcd and setting a key.
|
||||
|
||||
Start etcd:
|
||||
### Starting etcd
|
||||
|
||||
If etcd is built without using GOPATH, run the following:
|
||||
|
||||
```
|
||||
$ ./bin/etcd
|
||||
```
|
||||
If etcd is built using GOPATH, run the following:
|
||||
|
||||
Set a key:
|
||||
```
|
||||
$ $GOPATH/bin/etcd
|
||||
```
|
||||
|
||||
### Setting a key
|
||||
|
||||
Run the following:
|
||||
|
||||
```
|
||||
$ ETCDCTL_API=3 ./bin/etcdctl put foo bar
|
||||
@ -64,4 +70,4 @@ If OK is printed, then etcd is working!
|
||||
[go]: https://golang.org/doc/install
|
||||
[build-script]: ../build
|
||||
[cmd-directory]: ../cmd
|
||||
|
||||
[example-hardware-configurations]: op-guide/hardware.md#example-hardware-configurations
|
||||
|
@ -22,30 +22,29 @@ The easiest way to get started using etcd as a distributed key-value store is to
|
||||
|
||||
## Operating etcd clusters
|
||||
|
||||
Administrators who need to create reliable and scalable key-value stores for the developers they support should begin with a [cluster on multiple machines][clustering].
|
||||
Administrators who need a fault-tolerant etcd cluster for either development or production should begin with a [cluster on multiple machines][clustering].
|
||||
|
||||
- [Setting up etcd clusters][clustering]
|
||||
- [Setting up etcd gateways][gateway]
|
||||
- [Setting up etcd gRPC proxy][grpc_proxy]
|
||||
### Setting up etcd
|
||||
|
||||
- [Configuration flags][conf]
|
||||
- [Multi-member cluster][clustering]
|
||||
- [gRPC proxy][grpc_proxy]
|
||||
- [L4 gateway][gateway]
|
||||
|
||||
### System configuration
|
||||
|
||||
- [Supported systems][supported_platforms]
|
||||
- [Hardware recommendations][hardware]
|
||||
- [Configuration][conf]
|
||||
- [Security][security]
|
||||
- [Authentication][authentication]
|
||||
- [Monitoring][monitoring]
|
||||
- [Maintenance][maintenance]
|
||||
- [Understand failures][failures]
|
||||
- [Disaster recovery][recovery]
|
||||
- [Performance][performance]
|
||||
- [Versioning][versioning]
|
||||
- [Performance benchmarking][performance]
|
||||
- [Tuning][tuning]
|
||||
|
||||
### Platform guides
|
||||
|
||||
- [Supported systems][supported_platforms]
|
||||
- [Docker container][container_docker]
|
||||
- [Container Linux, systemd][container_linux_platform]
|
||||
- [rkt container][container_rkt]
|
||||
- [Amazon Web Services][aws_platform]
|
||||
- [Container Linux, systemd][container_linux_platform]
|
||||
- [FreeBSD][freebsd_platform]
|
||||
- [Docker container][container_docker]
|
||||
- [rkt container][container_rkt]
|
||||
|
||||
### Security
|
||||
|
||||
@ -54,7 +53,7 @@ Administrators who need to create reliable and scalable key-value stores for the
|
||||
|
||||
### Maintenance and troubleshooting
|
||||
|
||||
- [Frequently asked questions][common questions]
|
||||
- [Frequently asked questions][faq]
|
||||
- [Monitoring][monitoring]
|
||||
- [Maintenance][maintenance]
|
||||
- [Failure modes][failures]
|
||||
@ -72,17 +71,13 @@ To learn more about the concepts and internals behind etcd, read the following p
|
||||
- Internals
|
||||
- [Auth subsystem][auth_design]
|
||||
|
||||
## Frequently Asked Questions (FAQ)
|
||||
|
||||
Answers to [common questions] about etcd.
|
||||
|
||||
[api_ref]: dev-guide/api_reference_v3.md
|
||||
[api_concurrency_ref]: dev-guide/api_concurrency_reference_v3.md
|
||||
[api_grpc_gateway]: dev-guide/api_grpc_gateway.md
|
||||
[clustering]: op-guide/clustering.md
|
||||
[conf]: op-guide/configuration.md
|
||||
[system-limit]: dev-guide/limit.md
|
||||
[common questions]: faq.md
|
||||
[faq]: faq.md
|
||||
[why]: learning/why.md
|
||||
[data_model]: learning/data_model.md
|
||||
[demo]: demo.md
|
||||
@ -115,4 +110,5 @@ Answers to [common questions] about etcd.
|
||||
[experimental]: dev-guide/experimental_apis.md
|
||||
[authentication]: op-guide/authentication.md
|
||||
[auth_design]: learning/auth_design.md
|
||||
[tuning]: tuning.md
|
||||
[upgrading]: upgrades/upgrading-etcd.md
|
||||
|
@ -1,36 +1,40 @@
|
||||
## Frequently Asked Questions (FAQ)
|
||||
# Frequently Asked Questions (FAQ)
|
||||
|
||||
### etcd, general
|
||||
## etcd, general
|
||||
|
||||
#### Do clients have to send requests to the etcd leader?
|
||||
### Do clients have to send requests to the etcd leader?
|
||||
|
||||
[Raft][raft] is leader-based; the leader handles all client requests which need cluster consensus. However, the client does not need to know which node is the leader. Any request that requires consensus sent to a follower is automatically forwarded to the leader. Requests that do not require consensus (e.g., serialized reads) can be processed by any cluster member.
|
||||
|
||||
### Configuration
|
||||
## Configuration
|
||||
|
||||
#### What is the difference between listen-<client,peer>-urls, advertise-client-urls or initial-advertise-peer-urls?
|
||||
### What is the difference between listen-<client,peer>-urls, advertise-client-urls or initial-advertise-peer-urls?
|
||||
|
||||
`listen-client-urls` and `listen-peer-urls` specify the local addresses etcd server binds to for accepting incoming connections. To listen on a port for all interfaces, specify `0.0.0.0` as the listen IP address.
|
||||
|
||||
`advertise-client-urls` and `initial-advertise-peer-urls` specify the addresses etcd clients or other etcd members should use to contact the etcd server. The advertise addresses must be reachable from the remote machines. Do not advertise addresses like `localhost` or `0.0.0.0` for a production setup since these addresses are unreachable from remote machines.
|
||||
|
||||
### Deployment
|
||||
### Why doesn't changing `--listen-peer-urls` or `--initial-advertise-peer-urls` update the advertised peer URLs in `etcdctl member list`?
|
||||
|
||||
#### System requirements
|
||||
A member's advertised peer URLs come from `--initial-advertise-peer-urls` on initial cluster boot. Changing the listen peer URLs or the initial advertise peers after booting the member won't affect the exported advertise peer URLs since changes must go through quorum to avoid membership configuration split brain. Use `etcdctl member update` to update a member's peer URLs.
|
||||
|
||||
## Deployment
|
||||
|
||||
### System requirements
|
||||
|
||||
Since etcd writes data to disk, SSD is highly recommended. To prevent performance degradation or unintentionally overloading the key-value store, etcd enforces a 2GB default storage size quota, configurable up to 8GB. To avoid swapping or running out of memory, the machine should have at least as much RAM to cover the quota. At CoreOS, an etcd cluster is usually deployed on dedicated CoreOS Container Linux machines with dual-core processors, 2GB of RAM, and 80GB of SSD *at the very least*. **Note that performance is intrinsically workload dependent; please test before production deployment**. See [hardware][hardware-setup] for more recommendations.
|
||||
|
||||
Most stable production environment is Linux operating system with amd64 architecture; see [supported platform][supported-platform] for more.
|
||||
|
||||
#### Why an odd number of cluster members?
|
||||
### Why an odd number of cluster members?
|
||||
|
||||
An etcd cluster needs a majority of nodes, a quorum, to agree on updates to the cluster state. For a cluster with n members, quorum is (n/2)+1. For any odd-sized cluster, adding one node will always increase the number of nodes necessary for quorum. Although adding a node to an odd-sized cluster appears better since there are more machines, the fault tolerance is worse since exactly the same number of nodes may fail without losing quorum but there are more nodes that can fail. If the cluster is in a state where it can't tolerate any more failures, adding a node before removing nodes is dangerous because if the new node fails to register with the cluster (e.g., the address is misconfigured), quorum will be permanently lost.
|
||||
|
||||
#### What is maximum cluster size?
|
||||
### What is maximum cluster size?
|
||||
|
||||
Theoretically, there is no hard limit. However, an etcd cluster probably should have no more than seven nodes. [Google Chubby lock service][chubby], similar to etcd and widely deployed within Google for many years, suggests running five nodes. A 5-member etcd cluster can tolerate two member failures, which is enough in most cases. Although larger clusters provide better fault tolerance, the write performance suffers because data must be replicated across more machines.
|
||||
|
||||
#### What is failure tolerance?
|
||||
### What is failure tolerance?
|
||||
|
||||
An etcd cluster operates so long as a member quorum can be established. If quorum is lost through transient network failures (e.g., partitions), etcd automatically and safely resumes once the network recovers and restores quorum; Raft enforces cluster consistency. For power loss, etcd persists the Raft log to disk; etcd replays the log to the point of failure and resumes cluster participation. For permanent hardware failure, the node may be removed from the cluster through [runtime reconfiguration][runtime reconfiguration].
|
||||
|
||||
@ -50,19 +54,19 @@ It is recommended to have an odd number of members in a cluster. An odd-size clu
|
||||
|
||||
Adding a member to bring the size of cluster up to an even number doesn't buy additional fault tolerance. Likewise, during a network partition, an odd number of members guarantees that there will always be a majority partition that can continue to operate and be the source of truth when the partition ends.
|
||||
|
||||
#### Does etcd work in cross-region or cross data center deployments?
|
||||
### Does etcd work in cross-region or cross data center deployments?
|
||||
|
||||
Deploying etcd across regions improves etcd's fault tolerance since members are in separate failure domains. The cost is higher consensus request latency from crossing data center boundaries. Since etcd relies on a member quorum for consensus, the latency from crossing data centers will be somewhat pronounced because at least a majority of cluster members must respond to consensus requests. Additionally, cluster data must be replicated across all peers, so there will be bandwidth cost as well.
|
||||
|
||||
With longer latencies, the default etcd configuration may cause frequent elections or heartbeat timeouts. See [tuning] for adjusting timeouts for high latency deployments.
|
||||
|
||||
### Operation
|
||||
## Operation
|
||||
|
||||
#### How to backup a etcd cluster?
|
||||
### How to backup a etcd cluster?
|
||||
|
||||
etcdctl provides a `snapshot` command to create backups. See [backup][backup] for more details.
|
||||
|
||||
#### Should I add a member before removing an unhealthy member?
|
||||
### Should I add a member before removing an unhealthy member?
|
||||
|
||||
When replacing an etcd node, it's important to remove the member first and then add its replacement.
|
||||
|
||||
@ -74,21 +78,21 @@ Additionally, that new member is risky because it may turn out to be misconfigur
|
||||
|
||||
On the other hand, if the downed member is removed from cluster membership first, the number of members becomes 2 and the quorum remains at 2. Following that removal by adding a new member will also keep the quorum steady at 2. So, even if the new node can't be brought up, it's still possible to remove the new member through quorum on the remaining live members.
|
||||
|
||||
#### Why won't etcd accept my membership changes?
|
||||
### Why won't etcd accept my membership changes?
|
||||
|
||||
etcd sets `strict-reconfig-check` in order to reject reconfiguration requests that would cause quorum loss. Abandoning quorum is really risky (especially when the cluster is already unhealthy). Although it may be tempting to disable quorum checking if there's quorum loss to add a new member, this could lead to full fledged cluster inconsistency. For many applications, this will make the problem even worse ("disk geometry corruption" being a candidate for most terrifying).
|
||||
|
||||
#### Why does etcd lose its leader from disk latency spikes?
|
||||
### Why does etcd lose its leader from disk latency spikes?
|
||||
|
||||
This is intentional; disk latency is part of leader liveness. Suppose the cluster leader takes a minute to fsync a raft log update to disk, but the etcd cluster has a one second election timeout. Even though the leader can process network messages within the election interval (e.g., send heartbeats), it's effectively unavailable because it can't commit any new proposals; it's waiting on the slow disk. If the cluster frequently loses its leader due to disk latencies, try [tuning][tuning] the disk settings or etcd time parameters.
|
||||
|
||||
#### What does the etcd warning "request ignored (cluster ID mismatch)" mean?
|
||||
### What does the etcd warning "request ignored (cluster ID mismatch)" mean?
|
||||
|
||||
Every new etcd cluster generates a new cluster ID based on the initial cluster configuration and a user-provided unique `initial-cluster-token` value. By having unique cluster ID's, etcd is protected from cross-cluster interaction which could corrupt the cluster.
|
||||
|
||||
Usually this warning happens after tearing down an old cluster, then reusing some of the peer addresses for the new cluster. If any etcd process from the old cluster is still running it will try to contact the new cluster. The new cluster will recognize a cluster ID mismatch, then ignore the request and emit this warning. This warning is often cleared by ensuring peer addresses among distinct clusters are disjoint.
|
||||
|
||||
#### What does "mvcc: database space exceeded" mean and how do I fix it?
|
||||
### What does "mvcc: database space exceeded" mean and how do I fix it?
|
||||
|
||||
The [multi-version concurrency control][api-mvcc] data model in etcd keeps an exact history of the keyspace. Without periodically compacting this history (e.g., by setting `--auto-compaction`), etcd will eventually exhaust its storage space. If etcd runs low on storage space, it raises a space quota alarm to protect the cluster from further writes. So long as the alarm is raised, etcd responds to write requests with the error `mvcc: database space exceeded`.
|
||||
|
||||
@ -98,13 +102,13 @@ To recover from the low space quota alarm:
|
||||
2. [Defragment][maintenance-defragment] every etcd endpoint.
|
||||
3. [Disarm][maintenance-disarm] the alarm.
|
||||
|
||||
### Performance
|
||||
## Performance
|
||||
|
||||
#### How should I benchmark etcd?
|
||||
### How should I benchmark etcd?
|
||||
|
||||
Try the [benchmark] tool. Current [benchmark results][benchmark-result] are available for comparison.
|
||||
|
||||
#### What does the etcd warning "apply entries took too long" mean?
|
||||
### What does the etcd warning "apply entries took too long" mean?
|
||||
|
||||
After a majority of etcd members agree to commit a request, each etcd server applies the request to its data store and persists the result to disk. Even with a slow mechanical disk or a virtualized network disk, such as Amazon’s EBS or Google’s PD, applying a request should normally take fewer than 50 milliseconds. If the average apply duration exceeds 100 milliseconds, etcd will warn that entries are taking too long to apply.
|
||||
|
||||
@ -116,7 +120,7 @@ Expensive user requests which access too many keys (e.g., fetching the entire ke
|
||||
|
||||
If none of the above suggestions clear the warnings, please [open an issue][new_issue] with detailed logging, monitoring, metrics and optionally workload information.
|
||||
|
||||
#### What does the etcd warning "failed to send out heartbeat on time" mean?
|
||||
### What does the etcd warning "failed to send out heartbeat on time" mean?
|
||||
|
||||
etcd uses a leader-based consensus protocol for consistent data replication and log execution. Cluster members elect a single leader, all other members become followers. The elected leader must periodically send heartbeats to its followers to maintain its leadership. Followers infer leader failure if no heartbeats are received within an election interval and trigger an election. If a leader doesn’t send its heartbeats in time but is still running, the election is spurious and likely caused by insufficient resources. To catch these soft failures, if the leader skips two heartbeat intervals, etcd will warn it failed to send a heartbeat on time.
|
||||
|
||||
@ -128,7 +132,7 @@ A slow network can also cause this issue. If network metrics among the etcd mach
|
||||
|
||||
If none of the above suggestions clear the warnings, please [open an issue][new_issue] with detailed logging, monitoring, metrics and optionally workload information.
|
||||
|
||||
#### What does the etcd warning "snapshotting is taking more than x seconds to finish ..." mean?
|
||||
### What does the etcd warning "snapshotting is taking more than x seconds to finish ..." mean?
|
||||
|
||||
etcd sends a snapshot of its complete key-value store to refresh slow followers and for [backups][backup]. Slow snapshot transfer times increase MTTR; if the cluster is ingesting data with high throughput, slow followers may livelock by needing a new snapshot before finishing receiving a snapshot. To catch slow snapshot performance, etcd warns when sending a snapshot takes more than thirty seconds and exceeds the expected transfer time for a 1Gbps connection.
|
||||
|
||||
|
@ -40,16 +40,18 @@
|
||||
|
||||
**Python libraries**
|
||||
|
||||
- [kragniz/python-etcd3](https://github.com/kragniz/python-etcd3) - Work in progress client for v3
|
||||
- [kragniz/python-etcd3](https://github.com/kragniz/python-etcd3) - Client for v3
|
||||
- [jplana/python-etcd](https://github.com/jplana/python-etcd) - Supports v2
|
||||
- [russellhaering/txetcd](https://github.com/russellhaering/txetcd) - a Twisted Python library
|
||||
- [cholcombe973/autodock](https://github.com/cholcombe973/autodock) - A docker deployment automation tool
|
||||
- [lisael/aioetcd](https://github.com/lisael/aioetcd) - (Python 3.4+) Asyncio coroutines client (Supports v2)
|
||||
- [txaio-etcd](https://github.com/crossbario/txaio-etcd) - Asynchronous etcd v3-only client library for Twisted (today) and asyncio (future)
|
||||
- [dims/etcd3-gateway](https://github.com/dims/etcd3-gateway) - etcd v3 API library using the HTTP grpc gateway
|
||||
- [aioetcd3](https://github.com/gaopeiliang/aioetcd3) - (Python 3.6+) etcd v3 API for asyncio
|
||||
|
||||
**Node libraries**
|
||||
|
||||
- [mixer/etcd3](https://github.com/mixer/etcd3) - Supports v3
|
||||
- [stianeikeland/node-etcd](https://github.com/stianeikeland/node-etcd) - Supports v2 (w Coffeescript)
|
||||
- [lavagetto/nodejs-etcd](https://github.com/lavagetto/nodejs-etcd) - Supports v2
|
||||
- [deedubs/node-etcd-config](https://github.com/deedubs/node-etcd-config) - Supports v2
|
||||
@ -131,7 +133,7 @@
|
||||
- [cloudfoundry/cf-release](https://github.com/cloudfoundry/cf-release/tree/master/jobs/etcd)
|
||||
|
||||
**Projects using etcd**
|
||||
|
||||
- [etcd Raft users](../raft/README.md#notable-users) - projects using etcd's raft library implementation.
|
||||
- [apache/celix](https://github.com/apache/celix) - an implementation of the OSGi specification adapted to C and C++
|
||||
- [binocarlos/yoda](https://github.com/binocarlos/yoda) - etcd + ZeroMQ
|
||||
- [blox/blox](https://github.com/blox/blox) - a collection of open source projects for container management and orchestration with AWS ECS
|
||||
@ -156,3 +158,6 @@
|
||||
- [ryandoyle/nss-etcd](https://github.com/ryandoyle/nss-etcd) - A GNU libc NSS module for resolving names from etcd.
|
||||
- [Gru](https://github.com/dnaeon/gru) - Orchestration made easy with Go
|
||||
- [Vitess](http://vitess.io/) - Vitess is a database clustering system for horizontal scaling of MySQL.
|
||||
- [lclarkmichalek/etcdhcp](https://github.com/lclarkmichalek/etcdhcp) - DHCP server that uses etcd for persistence and coordination.
|
||||
- [openstack/networking-vpp](https://github.com/openstack/networking-vpp) - A networking driver that programs the [FD.io VPP dataplane](https://wiki.fd.io/view/VPP) to provide [OpenStack](https://www.openstack.org/) cloud virtual networking
|
||||
- [openstack](https://github.com/openstack/governance/blob/master/reference/base-services.rst) - OpenStack services can rely on etcd as a base service.
|
||||
|
@ -47,7 +47,7 @@ message ResponseHeader {
|
||||
|
||||
An application may read the Cluster_ID (Member_ID) field to ensure it is communicating with the intended cluster (member).
|
||||
|
||||
Applications can use the `Revision` to know the latest revision of the key-value store. This is especially useful when applications specify a historical revision to make time `travel query` and wishes to know the latest revision at the time of the request.
|
||||
Applications can use the `Revision` to know the latest revision of the key-value store. This is especially useful when applications specify a historical revision to make time `travel query` and wish to know the latest revision at the time of the request.
|
||||
|
||||
Applications can use `Raft_Term` to detect when the cluster completes a new leader election.
|
||||
|
||||
|
@ -2,19 +2,19 @@
|
||||
|
||||
etcd is designed to reliably store infrequently updated data and provide reliable watch queries. etcd exposes previous versions of key-value pairs to support inexpensive snapshots and watch history events (“time travel queries”). A persistent, multi-version, concurrency-control data model is a good fit for these use cases.
|
||||
|
||||
etcd stores data in a multiversion [persistent][persistent-ds] key-value store. The persistent key-value store preserves the previous version of a key-value pair when its value is superseded with new data. The key-value store is effectively immutable; its operations do not update the structure in-place, but instead always generates a new updated structure. All past versions of keys are still accessible and watchable after modification. To prevent the data store from growing indefinitely over time from maintaining old versions, the store may be compacted to shed the oldest versions of superseded data.
|
||||
etcd stores data in a multiversion [persistent][persistent-ds] key-value store. The persistent key-value store preserves the previous version of a key-value pair when its value is superseded with new data. The key-value store is effectively immutable; its operations do not update the structure in-place, but instead always generate a new updated structure. All past versions of keys are still accessible and watchable after modification. To prevent the data store from growing indefinitely over time and from maintaining old versions, the store may be compacted to shed the oldest versions of superseded data.
|
||||
|
||||
### Logical view
|
||||
|
||||
The store’s logical view is a flat binary key space. The key space has a lexically sorted index on byte string keys so range queries are inexpensive.
|
||||
|
||||
The key space maintains multiple revisions. Each atomic mutative operation (e.g., a transaction operation may contain multiple operations) creates a new revision on the key space. All data held by previous revisions remains unchanged. Old versions of key can still be accessed through previous revisions. Likewise, revisions are indexed as well; ranging over revisions with watchers is efficient. If the store is compacted to recover space, revisions before the compact revision will be removed.
|
||||
The key space maintains multiple revisions. Each atomic mutative operation (e.g., a transaction operation may contain multiple operations) creates a new revision on the key space. All data held by previous revisions remains unchanged. Old versions of key can still be accessed through previous revisions. Likewise, revisions are indexed as well; ranging over revisions with watchers is efficient. If the store is compacted to save space, revisions before the compact revision will be removed.
|
||||
|
||||
A key’s lifetime spans a generation. Each key may have one or multiple generations. Creating a key increments the generation of that key, starting at 1 if the key never existed. Deleting a key generates a key tombstone, concluding the key’s current generation. Each modification of a key creates a new version of the key. Once a compaction happens, any generation ended before the given revision will be removed and values set before the compaction revision except the latest one will be removed.
|
||||
A key’s lifetime spans a generation, denoted by its version. Each key may have one or multiple generations. Creating a key increments the version of that key, starting at 1 if the key never existed. Deleting a key generates a key tombstone, concluding the key’s current generation by resetting its version. Each modification of a key increments its version. Once a compaction happens, any version ended before the given revision will be removed and values set before the compaction revision except the latest one will be removed.
|
||||
|
||||
### Physical view
|
||||
|
||||
etcd stores the physical data as key-value pairs in a persistent [b+tree][b+tree]. Each revision of the store’s state only contains the delta from its previous revision to be efficient. A single revision may correspond to multiple keys in the tree.
|
||||
etcd stores the physical data as key-value pairs in a persistent [b+tree][b+tree]. Each revision of the store’s state only contains the delta from its previous revision to be efficient. A single revision may correspond to multiple keys in the tree.
|
||||
|
||||
The key of key-value pair is a 3-tuple (major, sub, type). Major is the store revision holding the key. Sub differentiates among keys within the same revision. Type is an optional suffix for special value (e.g., `t` if the value contains a tombstone). The value of the key-value pair contains the modification from previous revision, thus one delta from previous revision. The b+tree is ordered by key in lexical byte-order. Ranged lookups over revision deltas are fast; this enables quickly finding modifications from one specific revision to another. Compaction removes out-of-date keys-value pairs.
|
||||
|
||||
|
@ -47,7 +47,7 @@ When considering features, support, and stability, new applications planning to
|
||||
|
||||
### Consul
|
||||
|
||||
Consul is an end-to-end service discovery framework. It provides built-in health checking, failure detection, and DNS services. In addition, Consul exposes a key value store with RESTful HTTP APIs. [As it stands in Consul 1.0][dbtester-comparison-results], the storage system does not scale as well as other systems like etcd or Zookeeper in key-value operations; systems requiring millions of keys will suffer from high latencies and memory pressure. The key value API is missing, most notably, multi-version keys, conditional transactions, and reliable streaming watches.
|
||||
Consul bills itself as an end-to-end service discovery framework. To wit, it includes services such as health checking, failure detection, and DNS. Incidentally, Consul also exposes a key value store with mediocre performance and an intricate API. As it stands in Consul 0.7, the storage system does not scales well; systems requiring millions of keys will suffer from high latencies and memory pressure. The key value API is missing, most notably, multi-version keys, conditional transactions, and reliable streaming watches.
|
||||
|
||||
etcd and Consul solve different problems. If looking for a distributed consistent key value store, etcd is a better choice over Consul. If looking for end-to-end cluster service discovery, etcd will not have enough features; choose Kubernetes, Consul, or SmartStack.
|
||||
|
||||
@ -107,10 +107,9 @@ For distributed coordination, choosing etcd can help prevent operational headach
|
||||
[etcd-rbac]: ../op-guide/authentication.md#working-with-roles
|
||||
[zk-acl]: https://zookeeper.apache.org/doc/r3.1.2/zookeeperProgrammers.html#sc_ZooKeeperAccessControl
|
||||
[consul-acl]: https://www.consul.io/docs/internals/acl.html
|
||||
[cockroach-grant]: https://www.cockroachlabs.com/docs/grant.html
|
||||
[cockroach-grant]: https://www.cockroachlabs.com/docs/stable/grant.html
|
||||
[spanner-roles]: https://cloud.google.com/spanner/docs/iam#roles
|
||||
[zk-bindings]: https://zookeeper.apache.org/doc/r3.1.2/zookeeperProgrammers.html#ch_bindings
|
||||
[container-linux]: https://coreos.com/why
|
||||
[locksmith]: https://github.com/coreos/locksmith
|
||||
[kubernetes]: http://kubernetes.io/docs/whatisk8s
|
||||
[dbtester-comparison-results]: https://github.com/coreos/dbtester/tree/master/test-results/2018Q1-02-etcd-zookeeper-consul
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Authentication Guide
|
||||
# Role-based access control
|
||||
|
||||
## Overview
|
||||
|
||||
Authentication was added in etcd 2.1. The etcd v3 API slightly modified the authentication feature's API and user interface to better fit the new data model. This guide is intended to help users set up basic authentication in etcd v3.
|
||||
Authentication was added in etcd 2.1. The etcd v3 API slightly modified the authentication feature's API and user interface to better fit the new data model. This guide is intended to help users set up basic authentication and role-based access control in etcd v3.
|
||||
|
||||
## Special users and roles
|
||||
|
||||
@ -161,4 +161,4 @@ Otherwise, all `etcdctl` commands remain the same. Users and roles can still be
|
||||
|
||||
## Using TLS Common Name
|
||||
|
||||
If an etcd server is launched with the option `--client-cert-auth=true`, the field of Common Name (CN) in the client's TLS cert will be used as an etcd user. In this case, the common name authenticates the user and the client does not need a password.
|
||||
If an etcd server is launched with the option `--client-cert-auth=true`, the field of Common Name (CN) in the client's TLS cert will be used as an etcd user. In this case, the common name authenticates the user and the client does not need a password. Note that if both of 1. `--client-cert-auth=true` is passed and CN is provided by the client, and 2. username and password are provided by the client, the username and password based authentication is prioritized.
|
||||
|
@ -281,7 +281,7 @@ ETCD_DISCOVERY=https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573d
|
||||
--discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
|
||||
```
|
||||
|
||||
**Each member must have a different name flag specified or else discovery will fail due to duplicated names. `Hostname` or `machine-id` can be a good choice. **
|
||||
**Each member must have a different name flag specified or else discovery will fail due to duplicated names. `Hostname` or `machine-id` can be a good choice.**
|
||||
|
||||
Now we start etcd with those relevant flags for each member:
|
||||
|
||||
@ -456,6 +456,8 @@ $ etcd --name infra2 \
|
||||
--listen-peer-urls http://10.0.1.12:2380
|
||||
```
|
||||
|
||||
Since v3.1.0 (except v3.2.9), when `etcd --discovery-srv=example.com` is configured with TLS, server will only authenticate peers/clients when the provided certs have root domain `example.com` as an entry in Subject Alternative Name (SAN) field. See [Notes for DNS SRV][security-guide-dns-srv].
|
||||
|
||||
### Gateway
|
||||
|
||||
etcd gateway is a simple TCP proxy that forwards network data to the etcd cluster. Please read [gateway guide][gateway] for more information.
|
||||
@ -475,5 +477,6 @@ To setup an etcd cluster with proxies of v2 API, please read the the [clustering
|
||||
[proxy]: https://github.com/coreos/etcd/blob/release-2.3/Documentation/proxy.md
|
||||
[clustering_etcd2]: https://github.com/coreos/etcd/blob/release-2.3/Documentation/clustering.md
|
||||
[security-guide]: security.md
|
||||
[security-guide-dns-srv]: security.md#notes-for-dns-srv
|
||||
[tls-setup]: ../../hack/tls-setup
|
||||
[gateway]: gateway.md
|
||||
|
@ -69,9 +69,39 @@ To start etcd automatically using custom settings at startup in Linux, using a [
|
||||
|
||||
### --cors
|
||||
+ Comma-separated white list of origins for CORS (cross-origin resource sharing).
|
||||
+ default: none
|
||||
+ default: ""
|
||||
+ env variable: ETCD_CORS
|
||||
|
||||
### --quota-backend-bytes
|
||||
+ Raise alarms when backend size exceeds the given quota (0 defaults to low space quota).
|
||||
+ default: 0
|
||||
+ env variable: ETCD_QUOTA_BACKEND_BYTES
|
||||
|
||||
### --max-txn-ops
|
||||
+ Maximum number of operations permitted in a transaction.
|
||||
+ default: 128
|
||||
+ env variable: ETCD_MAX_TXN_OPS
|
||||
|
||||
### --max-request-bytes
|
||||
+ Maximum client request size in bytes the server will accept.
|
||||
+ default: 1572864
|
||||
+ env variable: ETCD_MAX_REQUEST_BYTES
|
||||
|
||||
### --grpc-keepalive-min-time
|
||||
+ Minimum duration interval that a client should wait before pinging server.
|
||||
+ default: 5s
|
||||
+ env variable: ETCD_GRPC_KEEPALIVE_MIN_TIME
|
||||
|
||||
### --grpc-keepalive-interval
|
||||
+ Frequency duration of server-to-client ping to check if a connection is alive (0 to disable).
|
||||
+ default: 2h
|
||||
+ env variable: ETCD_GRPC_KEEPALIVE_INTERVAL
|
||||
|
||||
### --grpc-keepalive-timeout
|
||||
+ Additional duration of wait before closing a non-responsive connection (0 to disable).
|
||||
+ default: 20s
|
||||
+ env variable: ETCD_GRPC_KEEPALIVE_TIMEOUT
|
||||
|
||||
## Clustering flags
|
||||
|
||||
`--initial` prefix flags are used in bootstrapping ([static bootstrap][build-cluster], [discovery-service bootstrap][discovery] or [runtime reconfiguration][reconfig]) a new member, and ignored when restarting an existing member.
|
||||
@ -112,12 +142,12 @@ To start etcd automatically using custom settings at startup in Linux, using a [
|
||||
|
||||
### --discovery
|
||||
+ Discovery URL used to bootstrap the cluster.
|
||||
+ default: none
|
||||
+ default: ""
|
||||
+ env variable: ETCD_DISCOVERY
|
||||
|
||||
### --discovery-srv
|
||||
+ DNS srv domain used to bootstrap the cluster.
|
||||
+ default: none
|
||||
+ default: ""
|
||||
+ env variable: ETCD_DISCOVERY_SRV
|
||||
|
||||
### --discovery-fallback
|
||||
@ -127,7 +157,7 @@ To start etcd automatically using custom settings at startup in Linux, using a [
|
||||
|
||||
### --discovery-proxy
|
||||
+ HTTP proxy to use for traffic to discovery service.
|
||||
+ default: none
|
||||
+ default: ""
|
||||
+ env variable: ETCD_DISCOVERY_PROXY
|
||||
|
||||
### --strict-reconfig-check
|
||||
@ -140,6 +170,10 @@ To start etcd automatically using custom settings at startup in Linux, using a [
|
||||
+ default: 0
|
||||
+ env variable: ETCD_AUTO_COMPACTION_RETENTION
|
||||
|
||||
### --auto-compaction-mode
|
||||
+ Interpret 'auto-compaction-retention' one of: periodic|revision. 'periodic' for duration based retention, defaulting to hours if no time unit is provided (e.g. '5m'). 'revision' for revision number based retention.
|
||||
+ default: periodic
|
||||
+ env variable: ETCD_AUTO_COMPACTION_MODE
|
||||
|
||||
### --enable-v2
|
||||
+ Accept etcd V2 client requests
|
||||
@ -185,22 +219,22 @@ To start etcd automatically using custom settings at startup in Linux, using a [
|
||||
|
||||
The security flags help to [build a secure etcd cluster][security].
|
||||
|
||||
### --ca-file
|
||||
### --ca-file
|
||||
|
||||
**DEPRECATED**
|
||||
|
||||
+ Path to the client server TLS CA file. `--ca-file ca.crt` could be replaced by `--trusted-ca-file ca.crt --client-cert-auth` and etcd will perform the same.
|
||||
+ default: none
|
||||
+ default: ""
|
||||
+ env variable: ETCD_CA_FILE
|
||||
|
||||
### --cert-file
|
||||
+ Path to the client server TLS cert file.
|
||||
+ default: none
|
||||
+ default: ""
|
||||
+ env variable: ETCD_CERT_FILE
|
||||
|
||||
### --key-file
|
||||
+ Path to the client server TLS key file.
|
||||
+ default: none
|
||||
+ default: ""
|
||||
+ env variable: ETCD_KEY_FILE
|
||||
|
||||
### --client-cert-auth
|
||||
@ -208,9 +242,14 @@ The security flags help to [build a secure etcd cluster][security].
|
||||
+ default: false
|
||||
+ env variable: ETCD_CLIENT_CERT_AUTH
|
||||
|
||||
### --client-crl-file
|
||||
+ Path to the client certificate revocation list file.
|
||||
+ default: ""
|
||||
+ env variable: ETCD_CLIENT_CRL_FILE
|
||||
|
||||
### --trusted-ca-file
|
||||
+ Path to the client server TLS trusted CA key file.
|
||||
+ default: none
|
||||
+ Path to the client server TLS trusted CA cert file.
|
||||
+ default: ""
|
||||
+ env variable: ETCD_TRUSTED_CA_FILE
|
||||
|
||||
### --auto-tls
|
||||
@ -218,22 +257,22 @@ The security flags help to [build a secure etcd cluster][security].
|
||||
+ default: false
|
||||
+ env variable: ETCD_AUTO_TLS
|
||||
|
||||
### --peer-ca-file
|
||||
### --peer-ca-file
|
||||
|
||||
**DEPRECATED**
|
||||
|
||||
+ Path to the peer server TLS CA file. `--peer-ca-file ca.crt` could be replaced by `--peer-trusted-ca-file ca.crt --peer-client-cert-auth` and etcd will perform the same.
|
||||
+ default: none
|
||||
+ default: ""
|
||||
+ env variable: ETCD_PEER_CA_FILE
|
||||
|
||||
### --peer-cert-file
|
||||
+ Path to the peer server TLS cert file.
|
||||
+ default: none
|
||||
+ default: ""
|
||||
+ env variable: ETCD_PEER_CERT_FILE
|
||||
|
||||
### --peer-key-file
|
||||
+ Path to the peer server TLS key file.
|
||||
+ default: none
|
||||
+ default: ""
|
||||
+ env variable: ETCD_PEER_KEY_FILE
|
||||
|
||||
### --peer-client-cert-auth
|
||||
@ -241,9 +280,14 @@ The security flags help to [build a secure etcd cluster][security].
|
||||
+ default: false
|
||||
+ env variable: ETCD_PEER_CLIENT_CERT_AUTH
|
||||
|
||||
### --peer-crl-file
|
||||
+ Path to the peer certificate revocation list file.
|
||||
+ default: ""
|
||||
+ env variable: ETCD_PEER_CRL_FILE
|
||||
|
||||
### --peer-trusted-ca-file
|
||||
+ Path to the peer server TLS trusted CA file.
|
||||
+ default: none
|
||||
+ default: ""
|
||||
+ env variable: ETCD_PEER_TRUSTED_CA_FILE
|
||||
|
||||
### --peer-auto-tls
|
||||
@ -251,6 +295,11 @@ The security flags help to [build a secure etcd cluster][security].
|
||||
+ default: false
|
||||
+ env variable: ETCD_PEER_AUTO_TLS
|
||||
|
||||
### --peer-cert-allowed-cn
|
||||
+ Allowed CommonName for inter peer authentication.
|
||||
+ default: none
|
||||
+ env variable: ETCD_PEER_CERT_ALLOWED_CN
|
||||
|
||||
## Logging flags
|
||||
|
||||
### --debug
|
||||
@ -260,10 +309,9 @@ The security flags help to [build a secure etcd cluster][security].
|
||||
|
||||
### --log-package-levels
|
||||
+ Set individual etcd subpackages to specific log levels. An example being `etcdserver=WARNING,security=DEBUG`
|
||||
+ default: none (INFO for all packages)
|
||||
+ default: "" (INFO for all packages)
|
||||
+ env variable: ETCD_LOG_PACKAGE_LEVELS
|
||||
|
||||
|
||||
## Unsafe flags
|
||||
|
||||
Please be CAUTIOUS when using unsafe flags because it will break the guarantees given by the consensus protocol.
|
||||
@ -283,7 +331,7 @@ Follow the instructions when using these flags.
|
||||
|
||||
### --config-file
|
||||
+ Load server configuration from a file.
|
||||
+ default: none
|
||||
+ default: ""
|
||||
|
||||
## Profiling flags
|
||||
|
||||
@ -295,6 +343,10 @@ Follow the instructions when using these flags.
|
||||
+ Set level of detail for exported metrics, specify 'extensive' to include histogram metrics.
|
||||
+ default: basic
|
||||
|
||||
### --listen-metrics-urls
|
||||
+ List of URLs to listen on for metrics.
|
||||
+ default: ""
|
||||
|
||||
## Auth flags
|
||||
|
||||
### --auth-token
|
||||
@ -302,6 +354,12 @@ Follow the instructions when using these flags.
|
||||
+ Example option of JWT: '--auth-token jwt,pub-key=app.rsa.pub,priv-key=app.rsa,sign-method=RS512'
|
||||
+ default: "simple"
|
||||
|
||||
## Experimental flags
|
||||
|
||||
### --experimental-corrupt-check-time
|
||||
+ Duration of time between cluster corruption check passes
|
||||
+ default: 0s
|
||||
|
||||
[build-cluster]: clustering.md#static
|
||||
[reconfig]: runtime-configuration.md
|
||||
[discovery]: clustering.md#discovery
|
||||
|
@ -76,18 +76,29 @@ Use the host IP address when configuring etcd:
|
||||
export NODE1=192.168.1.21
|
||||
```
|
||||
|
||||
Configure a Docker volume to store etcd data:
|
||||
|
||||
```
|
||||
docker volume create --name etcd-data
|
||||
export DATA_DIR="etcd-data"
|
||||
```
|
||||
|
||||
Run the latest version of etcd:
|
||||
|
||||
```
|
||||
REGISTRY=quay.io/coreos/etcd
|
||||
# available from v3.2.5
|
||||
REGISTRY=gcr.io/etcd-development/etcd
|
||||
|
||||
docker run \
|
||||
-p 2379:2379 \
|
||||
-p 2380:2380 \
|
||||
--volume=${DATA_DIR}:/etcd-data \
|
||||
--name etcd quay.io/coreos/etcd:latest \
|
||||
--name etcd ${REGISTRY}:latest \
|
||||
/usr/local/bin/etcd \
|
||||
--data-dir=/etcd-data --name node1 \
|
||||
--initial-advertise-peer-urls http://${NODE1}:2380 --listen-peer-urls http://${NODE1}:2380 \
|
||||
--advertise-client-urls http://${NODE1}:2379 --listen-client-urls http://${NODE1}:2379 \
|
||||
--initial-advertise-peer-urls http://${NODE1}:2380 --listen-peer-urls http://0.0.0.0:2380 \
|
||||
--advertise-client-urls http://${NODE1}:2379 --listen-client-urls http://0.0.0.0:2379 \
|
||||
--initial-cluster node1=http://${NODE1}:2380
|
||||
```
|
||||
|
||||
@ -100,6 +111,10 @@ etcdctl --endpoints=http://${NODE1}:2379 member list
|
||||
### Running a 3 node etcd cluster
|
||||
|
||||
```
|
||||
REGISTRY=quay.io/coreos/etcd
|
||||
# available from v3.2.5
|
||||
REGISTRY=gcr.io/etcd-development/etcd
|
||||
|
||||
# For each machine
|
||||
ETCD_VERSION=latest
|
||||
TOKEN=my-etcd-token
|
||||
@ -120,11 +135,11 @@ docker run \
|
||||
-p 2379:2379 \
|
||||
-p 2380:2380 \
|
||||
--volume=${DATA_DIR}:/etcd-data \
|
||||
--name etcd quay.io/coreos/etcd:${ETCD_VERSION} \
|
||||
--name etcd ${REGISTRY}:${ETCD_VERSION} \
|
||||
/usr/local/bin/etcd \
|
||||
--data-dir=/etcd-data --name ${THIS_NAME} \
|
||||
--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \
|
||||
--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \
|
||||
--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
|
||||
--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
|
||||
--initial-cluster ${CLUSTER} \
|
||||
--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
|
||||
|
||||
@ -135,11 +150,11 @@ docker run \
|
||||
-p 2379:2379 \
|
||||
-p 2380:2380 \
|
||||
--volume=${DATA_DIR}:/etcd-data \
|
||||
--name etcd quay.io/coreos/etcd:${ETCD_VERSION} \
|
||||
--name etcd ${REGISTRY}:${ETCD_VERSION} \
|
||||
/usr/local/bin/etcd \
|
||||
--data-dir=/etcd-data --name ${THIS_NAME} \
|
||||
--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \
|
||||
--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \
|
||||
--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
|
||||
--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
|
||||
--initial-cluster ${CLUSTER} \
|
||||
--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
|
||||
|
||||
@ -150,11 +165,11 @@ docker run \
|
||||
-p 2379:2379 \
|
||||
-p 2380:2380 \
|
||||
--volume=${DATA_DIR}:/etcd-data \
|
||||
--name etcd quay.io/coreos/etcd:${ETCD_VERSION} \
|
||||
--name etcd ${REGISTRY}:${ETCD_VERSION} \
|
||||
/usr/local/bin/etcd \
|
||||
--data-dir=/etcd-data --name ${THIS_NAME} \
|
||||
--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \
|
||||
--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \
|
||||
--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
|
||||
--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
|
||||
--initial-cluster ${CLUSTER} \
|
||||
--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
|
||||
```
|
||||
@ -174,21 +189,30 @@ To provision a 3 node etcd cluster on bare-metal, the examples in the [baremetal
|
||||
The etcd release container does not include default root certificates. To use HTTPS with certificates trusted by a root authority (e.g., for discovery), mount a certificate directory into the etcd container:
|
||||
|
||||
```
|
||||
REGISTRY=quay.io/coreos/etcd
|
||||
# available from v3.2.5
|
||||
REGISTRY=docker://gcr.io/etcd-development/etcd
|
||||
|
||||
rkt run \
|
||||
--insecure-options=image \
|
||||
--volume etcd-ssl-certs-bundle,kind=host,source=/etc/ssl/certs/ca-certificates.crt \
|
||||
--mount volume=etcd-ssl-certs-bundle,target=/etc/ssl/certs/ca-certificates.crt \
|
||||
quay.io/coreos/etcd:latest -- --name my-name \
|
||||
${REGISTRY}:latest -- --name my-name \
|
||||
--initial-advertise-peer-urls http://localhost:2380 --listen-peer-urls http://localhost:2380 \
|
||||
--advertise-client-urls http://localhost:2379 --listen-client-urls http://localhost:2379 \
|
||||
--discovery https://discovery.etcd.io/c11fbcdc16972e45253491a24fcf45e1
|
||||
```
|
||||
|
||||
```
|
||||
REGISTRY=quay.io/coreos/etcd
|
||||
# available from v3.2.5
|
||||
REGISTRY=gcr.io/etcd-development/etcd
|
||||
|
||||
docker run \
|
||||
-p 2379:2379 \
|
||||
-p 2380:2380 \
|
||||
--volume=/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt \
|
||||
quay.io/coreos/etcd:latest \
|
||||
${REGISTRY}:latest \
|
||||
/usr/local/bin/etcd --name my-name \
|
||||
--initial-advertise-peer-urls http://localhost:2380 --listen-peer-urls http://localhost:2380 \
|
||||
--advertise-client-urls http://localhost:2379 --listen-client-urls http://localhost:2379 \
|
||||
|
@ -69,14 +69,14 @@ ANNOTATIONS {
|
||||
|
||||
# alert if the 99th percentile of gRPC method calls take more than 150ms
|
||||
ALERT GRPCRequestsSlow
|
||||
IF histogram_quantile(0.99, rate(etcd_grpc_unary_requests_duration_seconds_bucket[5m])) > 0.15
|
||||
IF histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{job="etcd",grpc_type="unary"}[5m])) by (grpc_service, grpc_method, le)) > 0.15
|
||||
FOR 10m
|
||||
LABELS {
|
||||
severity = "critical"
|
||||
}
|
||||
ANNOTATIONS {
|
||||
summary = "slow gRPC requests",
|
||||
description = "on etcd instance {{ $labels.instance }} gRPC requests to {{ $label.grpc_method }} are slow",
|
||||
description = "on etcd instance {{ $labels.instance }} gRPC requests to {{ $labels.grpc_method }} are slow",
|
||||
}
|
||||
|
||||
# HTTP requests alerts
|
||||
@ -84,8 +84,8 @@ ANNOTATIONS {
|
||||
|
||||
# alert if more than 1% of requests to an HTTP endpoint have failed within the last 5 minutes
|
||||
ALERT HighNumberOfFailedHTTPRequests
|
||||
IF sum by(method) (rate(etcd_http_failed_total{job="etcd"}[5m]))
|
||||
/ sum by(method) (rate(etcd_http_received_total{job="etcd"}[5m])) > 0.01
|
||||
IF sum(rate(grpc_server_handled_total{grpc_code!="OK",job="etcd"}[5m])) BY (grpc_service, grpc_method)
|
||||
/ sum(rate(grpc_server_handled_total{job="etcd"}[5m])) BY (grpc_service, grpc_method) > 0.01
|
||||
FOR 10m
|
||||
LABELS {
|
||||
severity = "warning"
|
||||
@ -97,8 +97,8 @@ ANNOTATIONS {
|
||||
|
||||
# alert if more than 5% of requests to an HTTP endpoint have failed within the last 5 minutes
|
||||
ALERT HighNumberOfFailedHTTPRequests
|
||||
IF sum by(method) (rate(etcd_http_failed_total{job="etcd"}[5m]))
|
||||
/ sum by(method) (rate(etcd_http_received_total{job="etcd"}[5m])) > 0.05
|
||||
IF sum(rate(grpc_server_handled_total{grpc_code!="OK",job="etcd"}[5m])) BY (grpc_service, grpc_method)
|
||||
/ sum(rate(grpc_server_handled_total{job="etcd"}[5m])) BY (grpc_service, grpc_method) > 0.05
|
||||
FOR 5m
|
||||
LABELS {
|
||||
severity = "critical"
|
||||
@ -117,7 +117,7 @@ LABELS {
|
||||
}
|
||||
ANNOTATIONS {
|
||||
summary = "slow HTTP requests",
|
||||
description = "on etcd instance {{ $labels.instance }} HTTP requests to {{ $label.method }} are slow",
|
||||
description = "on etcd instance {{ $labels.instance }} HTTP requests to {{ $labels.method }} are slow",
|
||||
}
|
||||
|
||||
# file descriptor alerts
|
||||
@ -161,7 +161,7 @@ LABELS {
|
||||
}
|
||||
ANNOTATIONS {
|
||||
summary = "etcd member communication is slow",
|
||||
description = "etcd instance {{ $labels.instance }} member communication with {{ $label.To }} is slow",
|
||||
description = "etcd instance {{ $labels.instance }} member communication with {{ $labels.To }} is slow",
|
||||
}
|
||||
|
||||
# etcd proposal alerts
|
||||
|
143
Documentation/op-guide/etcd3_alert.rules.yml
Normal file
143
Documentation/op-guide/etcd3_alert.rules.yml
Normal file
@ -0,0 +1,143 @@
|
||||
groups:
|
||||
- name: etcd3_alert.rules
|
||||
rules:
|
||||
- alert: InsufficientMembers
|
||||
expr: count(up{job="etcd"} == 0) > (count(up{job="etcd"}) / 2 - 1)
|
||||
for: 3m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
description: If one more etcd member goes down the cluster will be unavailable
|
||||
summary: etcd cluster insufficient members
|
||||
- alert: NoLeader
|
||||
expr: etcd_server_has_leader{job="etcd"} == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
description: etcd member {{ $labels.instance }} has no leader
|
||||
summary: etcd member has no leader
|
||||
- alert: HighNumberOfLeaderChanges
|
||||
expr: increase(etcd_server_leader_changes_seen_total{job="etcd"}[1h]) > 3
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: etcd instance {{ $labels.instance }} has seen {{ $value }} leader
|
||||
changes within the last hour
|
||||
summary: a high number of leader changes within the etcd cluster are happening
|
||||
- alert: HighNumberOfFailedGRPCRequests
|
||||
expr: sum(rate(grpc_server_handled_total{grpc_code!="OK",job="etcd"}[5m])) BY (grpc_service, grpc_method)
|
||||
/ sum(rate(grpc_server_handled_total{job="etcd"}[5m])) BY (grpc_service, grpc_method) > 0.01
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: '{{ $value }}% of requests for {{ $labels.grpc_method }} failed
|
||||
on etcd instance {{ $labels.instance }}'
|
||||
summary: a high number of gRPC requests are failing
|
||||
- alert: HighNumberOfFailedGRPCRequests
|
||||
expr: sum(rate(grpc_server_handled_total{grpc_code!="OK",job="etcd"}[5m])) BY (grpc_service, grpc_method)
|
||||
/ sum(rate(grpc_server_handled_total{job="etcd"}[5m])) BY (grpc_service, grpc_method) > 0.05
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
description: '{{ $value }}% of requests for {{ $labels.grpc_method }} failed
|
||||
on etcd instance {{ $labels.instance }}'
|
||||
summary: a high number of gRPC requests are failing
|
||||
- alert: GRPCRequestsSlow
|
||||
expr: histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{job="etcd",grpc_type="unary"}[5m])) by (grpc_service, grpc_method, le))
|
||||
> 0.15
|
||||
for: 10m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
description: on etcd instance {{ $labels.instance }} gRPC requests to {{ $labels.grpc_method
|
||||
}} are slow
|
||||
summary: slow gRPC requests
|
||||
- alert: HighNumberOfFailedHTTPRequests
|
||||
expr: sum(rate(etcd_http_failed_total{job="etcd"}[5m])) BY (method) / sum(rate(etcd_http_received_total{job="etcd"}[5m]))
|
||||
BY (method) > 0.01
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: '{{ $value }}% of requests for {{ $labels.method }} failed on etcd
|
||||
instance {{ $labels.instance }}'
|
||||
summary: a high number of HTTP requests are failing
|
||||
- alert: HighNumberOfFailedHTTPRequests
|
||||
expr: sum(rate(etcd_http_failed_total{job="etcd"}[5m])) BY (method) / sum(rate(etcd_http_received_total{job="etcd"}[5m]))
|
||||
BY (method) > 0.05
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
description: '{{ $value }}% of requests for {{ $labels.method }} failed on etcd
|
||||
instance {{ $labels.instance }}'
|
||||
summary: a high number of HTTP requests are failing
|
||||
- alert: HTTPRequestsSlow
|
||||
expr: histogram_quantile(0.99, rate(etcd_http_successful_duration_seconds_bucket[5m]))
|
||||
> 0.15
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: on etcd instance {{ $labels.instance }} HTTP requests to {{ $labels.method
|
||||
}} are slow
|
||||
summary: slow HTTP requests
|
||||
- record: instance:fd_utilization
|
||||
expr: process_open_fds / process_max_fds
|
||||
- alert: FdExhaustionClose
|
||||
expr: predict_linear(instance:fd_utilization[1h], 3600 * 4) > 1
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: '{{ $labels.job }} instance {{ $labels.instance }} will exhaust
|
||||
its file descriptors soon'
|
||||
summary: file descriptors soon exhausted
|
||||
- alert: FdExhaustionClose
|
||||
expr: predict_linear(instance:fd_utilization[10m], 3600) > 1
|
||||
for: 10m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
description: '{{ $labels.job }} instance {{ $labels.instance }} will exhaust
|
||||
its file descriptors soon'
|
||||
summary: file descriptors soon exhausted
|
||||
- alert: EtcdMemberCommunicationSlow
|
||||
expr: histogram_quantile(0.99, rate(etcd_network_member_round_trip_time_seconds_bucket[5m]))
|
||||
> 0.15
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: etcd instance {{ $labels.instance }} member communication with
|
||||
{{ $labels.To }} is slow
|
||||
summary: etcd member communication is slow
|
||||
- alert: HighNumberOfFailedProposals
|
||||
expr: increase(etcd_server_proposals_failed_total{job="etcd"}[1h]) > 5
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: etcd instance {{ $labels.instance }} has seen {{ $value }} proposal
|
||||
failures within the last hour
|
||||
summary: a high number of proposals within the etcd cluster are failing
|
||||
- alert: HighFsyncDurations
|
||||
expr: histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m]))
|
||||
> 0.5
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: etcd instance {{ $labels.instance }} fync durations are high
|
||||
summary: high fsync durations
|
||||
- alert: HighCommitDurations
|
||||
expr: histogram_quantile(0.99, rate(etcd_disk_backend_commit_duration_seconds_bucket[5m]))
|
||||
> 0.25
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: etcd instance {{ $labels.instance }} commit durations are high
|
||||
summary: high commit durations
|
@ -1,4 +1,4 @@
|
||||
# Understand failures
|
||||
# Failure modes
|
||||
|
||||
Failures are common in a large deployment of machines. A machine fails when its hardware or software malfunctions. Multiple machines fail together when there are power failures or network issues. Multiple kinds of failures can also happen at once; it is almost impossible to enumerate all possible failure cases.
|
||||
|
||||
|
@ -550,7 +550,7 @@
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [{
|
||||
"format": "short",
|
||||
"format": "Bps",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
|
@ -90,9 +90,9 @@ The etcd gRPC proxy starts and listens on port 8080. It forwards client requests
|
||||
Sending requests through the proxy:
|
||||
|
||||
```bash
|
||||
$ ETCDCTL_API=3 ./etcdctl --endpoints=127.0.0.1:2379 put foo bar
|
||||
$ ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 put foo bar
|
||||
OK
|
||||
$ ETCDCTL_API=3 ./etcdctl --endpoints=127.0.0.1:2379 get foo
|
||||
$ ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 get foo
|
||||
foo
|
||||
bar
|
||||
```
|
||||
@ -120,7 +120,7 @@ $ etcd grpc-proxy start --endpoints=localhost:2379 \
|
||||
The proxy will list all its members for member list:
|
||||
|
||||
```bash
|
||||
ETCDCTL_API=3 ./bin/etcdctl --endpoints=http://localhost:23790 member list --write-out table
|
||||
ETCDCTL_API=3 etcdctl --endpoints=http://localhost:23790 member list --write-out table
|
||||
|
||||
+----+---------+--------------------------------+------------+-----------------+
|
||||
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
|
||||
@ -155,10 +155,10 @@ $ etcd grpc-proxy start --endpoints=localhost:2379 \
|
||||
--advertise-client-url=127.0.0.1:23792
|
||||
```
|
||||
|
||||
the member list API to the grpc-proxy returns its own `advertise-client-url`:
|
||||
The member list API to the grpc-proxy returns its own `advertise-client-url`:
|
||||
|
||||
```bash
|
||||
ETCDCTL_API=3 ./bin/etcdctl --endpoints=http://localhost:23792 member list --write-out table
|
||||
ETCDCTL_API=3 etcdctl --endpoints=http://localhost:23792 member list --write-out table
|
||||
|
||||
+----+---------+--------------------------------+------------+-----------------+
|
||||
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
|
||||
@ -182,12 +182,44 @@ $ etcd grpc-proxy start --endpoints=localhost:2379 \
|
||||
Accesses to the proxy are now transparently prefixed on the etcd cluster:
|
||||
|
||||
```bash
|
||||
$ ETCDCTL_API=3 ./bin/etcdctl --endpoints=localhost:23790 put my-key abc
|
||||
$ ETCDCTL_API=3 etcdctl --endpoints=localhost:23790 put my-key abc
|
||||
# OK
|
||||
$ ETCDCTL_API=3 ./bin/etcdctl --endpoints=localhost:23790 get my-key
|
||||
$ ETCDCTL_API=3 etcdctl --endpoints=localhost:23790 get my-key
|
||||
# my-key
|
||||
# abc
|
||||
$ ETCDCTL_API=3 ./bin/etcdctl --endpoints=localhost:2379 get my-prefix/my-key
|
||||
$ ETCDCTL_API=3 etcdctl --endpoints=localhost:2379 get my-prefix/my-key
|
||||
# my-prefix/my-key
|
||||
# abc
|
||||
```
|
||||
|
||||
## TLS termination
|
||||
|
||||
Terminate TLS from a secure etcd cluster with the grpc proxy by serving an unencrypted local endpoint.
|
||||
|
||||
To try it out, start a single member etcd cluster with client https:
|
||||
|
||||
```sh
|
||||
$ etcd --listen-client-urls https://localhost:2379 --advertise-client-urls https://localhost:2379 --cert-file=peer.crt --key-file=peer.key --trusted-ca-file=ca.crt --client-cert-auth
|
||||
```
|
||||
|
||||
Confirm the client port is serving https:
|
||||
|
||||
```sh
|
||||
# fails
|
||||
$ ETCDCTL_API=3 etcdctl --endpoints=http://localhost:2379 endpoint status
|
||||
# works
|
||||
$ ETCDCTL_API=3 etcdctl --endpoints=https://localhost:2379 --cert=client.crt --key=client.key --cacert=ca.crt endpoint status
|
||||
```
|
||||
|
||||
Next, start a grpc proxy on `localhost:12379` by connecting to the etcd endpoint `https://localhost:2379` using the client certificates:
|
||||
|
||||
```sh
|
||||
$ etcd grpc-proxy start --endpoints=https://localhost:2379 --listen-addr localhost:12379 --cert client.crt --key client.key --cacert=ca.crt --insecure-skip-tls-verify &
|
||||
```
|
||||
|
||||
Finally, test the TLS termination by putting a key into the proxy over http:
|
||||
|
||||
```sh
|
||||
$ ETCDCTL_API=3 etcdctl --endpoints=http://localhost:12379 put abc def
|
||||
# OK
|
||||
```
|
||||
|
@ -47,9 +47,11 @@ $ etcdctl defrag
|
||||
Finished defragmenting etcd member[127.0.0.1:2379]
|
||||
```
|
||||
|
||||
**Note that defragmentation to a live member blocks the system from reading and writing data while rebuilding its states**.
|
||||
To defragment an etcd data directory directly, while etcd is not running, use the command:
|
||||
|
||||
**Note that defragmentation request does not get replicated over cluster. That is, the request is only applied to the local node. Specify all members in `--endpoints` flag.**
|
||||
``` sh
|
||||
$ etcdctl defrag --data-dir <path-to-etcd-data-dir>
|
||||
```
|
||||
|
||||
## Space quota
|
||||
|
||||
@ -78,7 +80,7 @@ $ ETCDCTL_API=3 etcdctl --write-out=table endpoint status
|
||||
+----------------+------------------+-----------+---------+-----------+-----------+------------+
|
||||
# confirm alarm is raised
|
||||
$ ETCDCTL_API=3 etcdctl alarm list
|
||||
memberID:13803658152347727308 alarm:NOSPACE
|
||||
memberID:13803658152347727308 alarm:NOSPACE
|
||||
```
|
||||
|
||||
Removing excessive keyspace data and defragmenting the backend database will put the cluster back within the quota limits:
|
||||
@ -94,7 +96,7 @@ $ ETCDCTL_API=3 etcdctl defrag
|
||||
Finished defragmenting etcd member[127.0.0.1:2379]
|
||||
# disarm alarm
|
||||
$ ETCDCTL_API=3 etcdctl alarm disarm
|
||||
memberID:13803658152347727308 alarm:NOSPACE
|
||||
memberID:13803658152347727308 alarm:NOSPACE
|
||||
# test puts are allowed again
|
||||
$ ETCDCTL_API=3 etcdctl put newkey 123
|
||||
OK
|
||||
|
@ -41,21 +41,24 @@ When Elapsed (s)
|
||||
17:34:51.999535 . 36 ... sent: header:<cluster_id:14841639068965178418 member_id:10276657743932975437 revision:15 raft_term:17 > kvs:<key:"abc" create_revision:6 mod_revision:14 version:9 value:"asda" > count:1
|
||||
```
|
||||
|
||||
## Metrics endpoint
|
||||
|
||||
Each etcd server exports metrics under the `/metrics` path on its client port and optionally on interfaces given by `--listen-metrics-urls`.
|
||||
|
||||
The metrics can be fetched with `curl`:
|
||||
|
||||
```sh
|
||||
$ curl -L http://localhost:2379/metrics
|
||||
$ curl -L http://localhost:2379/metrics | grep -v debugging # ignore unstable debugging metrics
|
||||
|
||||
# HELP etcd_debugging_mvcc_keys_total Total number of keys.
|
||||
# TYPE etcd_debugging_mvcc_keys_total gauge
|
||||
etcd_debugging_mvcc_keys_total 0
|
||||
# HELP etcd_debugging_mvcc_pending_events_total Total number of pending events to be sent.
|
||||
# TYPE etcd_debugging_mvcc_pending_events_total gauge
|
||||
etcd_debugging_mvcc_pending_events_total 0
|
||||
# HELP etcd_disk_backend_commit_duration_seconds The latency distributions of commit called by backend.
|
||||
# TYPE etcd_disk_backend_commit_duration_seconds histogram
|
||||
etcd_disk_backend_commit_duration_seconds_bucket{le="0.002"} 72756
|
||||
etcd_disk_backend_commit_duration_seconds_bucket{le="0.004"} 401587
|
||||
etcd_disk_backend_commit_duration_seconds_bucket{le="0.008"} 405979
|
||||
etcd_disk_backend_commit_duration_seconds_bucket{le="0.016"} 406464
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
## Prometheus
|
||||
|
||||
Running a [Prometheus][prometheus] monitoring service is the easiest way to ingest and record etcd's metrics.
|
||||
@ -63,7 +66,7 @@ Running a [Prometheus][prometheus] monitoring service is the easiest way to inge
|
||||
First, install Prometheus:
|
||||
|
||||
```sh
|
||||
PROMETHEUS_VERSION="1.3.1"
|
||||
PROMETHEUS_VERSION="2.0.0"
|
||||
wget https://github.com/prometheus/prometheus/releases/download/v$PROMETHEUS_VERSION/prometheus-$PROMETHEUS_VERSION.linux-amd64.tar.gz -O /tmp/prometheus-$PROMETHEUS_VERSION.linux-amd64.tar.gz
|
||||
tar -xvzf /tmp/prometheus-$PROMETHEUS_VERSION.linux-amd64.tar.gz --directory /tmp/ --strip-components=1
|
||||
/tmp/prometheus -version
|
||||
@ -95,13 +98,13 @@ nohup /tmp/prometheus \
|
||||
Now Prometheus will scrape etcd metrics every 10 seconds.
|
||||
|
||||
|
||||
## Alerting
|
||||
### Alerting
|
||||
|
||||
There is a [set of default alerts for etcd v3 clusters](./etcd3_alert.rules).
|
||||
There is a set of default alerts for etcd v3 clusters for [Prometheus 1.x](./etcd3_alert.rules) as well as [Prometheus 2.x](./etcd3_alert.rules.yml).
|
||||
|
||||
> Note: `job` labels may need to be adjusted to fit a particular need. The rules were written to apply to a single cluster so it is recommended to choose labels unique to a cluster.
|
||||
|
||||
## Grafana
|
||||
### Grafana
|
||||
|
||||
[Grafana][grafana] has built-in Prometheus support; just add a Prometheus data source:
|
||||
|
||||
@ -114,6 +117,8 @@ Access: proxy
|
||||
|
||||
Then import the default [etcd dashboard template][template] and customize. For instance, if Prometheus data source name is `my-etcd`, the `datasource` field values in JSON also need to be `my-etcd`.
|
||||
|
||||
See the [demo][demo].
|
||||
|
||||
Sample dashboard:
|
||||
|
||||

|
||||
@ -122,3 +127,4 @@ Sample dashboard:
|
||||
[prometheus]: https://prometheus.io/
|
||||
[grafana]: http://grafana.org/
|
||||
[template]: ./grafana.json
|
||||
[demo]: http://dash.etcd.io/dashboard/db/test-etcd-kubernetes
|
||||
|
@ -1,4 +1,4 @@
|
||||
## Disaster recovery
|
||||
# Disaster recovery
|
||||
|
||||
etcd is designed to withstand machine failures. An etcd cluster automatically recovers from temporary failures (e.g., machine reboots) and tolerates up to *(N-1)/2* permanent failures for a cluster of N members. When a member permanently fails, whether due to hardware failure or disk corruption, it loses access to the cluster. If the cluster permanently loses more than *(N-1)/2* members then it disastrously fails, irrevocably losing quorum. Once quorum is lost, the cluster cannot reach consensus and therefore cannot continue accepting updates.
|
||||
|
||||
@ -6,7 +6,7 @@ To recover from disastrous failure, etcd v3 provides snapshot and restore facili
|
||||
|
||||
[v2_recover]: ../v2/admin_guide.md#disaster-recovery
|
||||
|
||||
### Snapshotting the keyspace
|
||||
## Snapshotting the keyspace
|
||||
|
||||
Recovering a cluster first needs a snapshot of the keyspace from an etcd member. A snapshot may either be taken from a live member with the `etcdctl snapshot save` command or by copying the `member/snap/db` file from an etcd data directory. For example, the following command snapshots the keyspace served by `$ENDPOINT` to the file `snapshot.db`:
|
||||
|
||||
@ -14,7 +14,7 @@ Recovering a cluster first needs a snapshot of the keyspace from an etcd member.
|
||||
$ ETCDCTL_API=3 etcdctl --endpoints $ENDPOINT snapshot save snapshot.db
|
||||
```
|
||||
|
||||
### Restoring a cluster
|
||||
## Restoring a cluster
|
||||
|
||||
To restore a cluster, all that is needed is a single snapshot "db" file. A cluster restore with `etcdctl snapshot restore` creates new etcd data directories; all members should restore using the same snapshot. Restoring overwrites some snapshot metadata (specifically, the member ID and cluster ID); the member loses its former identity. This metadata overwrite prevents the new member from inadvertently joining an existing cluster. Therefore in order to start a cluster from a snapshot, the restore must start a new logical cluster.
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
etcd comes with support for incremental runtime reconfiguration, which allows users to update the membership of the cluster at run time.
|
||||
|
||||
Reconfiguration requests can only be processed when a majority of cluster members are functioning. It is **highly recommended** to always have a cluster size greater than two in production. It is unsafe to remove a member from a two member cluster. The majority of a two member cluster is also two. If there is a failure during the removal process, the cluster might not able to make progress and need to [restart from majority failure][majority failure].
|
||||
Reconfiguration requests can only be processed when a majority of cluster members are functioning. It is **highly recommended** to always have a cluster size greater than two in production. It is unsafe to remove a member from a two member cluster. The majority of a two member cluster is also two. If there is a failure during the removal process, the cluster might not be able to make progress and need to [restart from majority failure][majority failure].
|
||||
|
||||
To better understand the design behind runtime reconfiguration, please read [the runtime reconfiguration document][runtime-reconf].
|
||||
|
||||
@ -41,7 +41,7 @@ Before making any change, a simple majority (quorum) of etcd members must be ava
|
||||
All changes to the cluster must be done sequentially:
|
||||
|
||||
* To update a single member peerURLs, issue an update operation
|
||||
* To replace a healthy single member, add a new member then remove the old member
|
||||
* To replace a healthy single member, remove the old member then add a new member
|
||||
* To increase from 3 to 5 members, issue two add operations
|
||||
* To decrease from 5 to 3, issue two remove operations
|
||||
|
||||
@ -55,9 +55,9 @@ To update the advertise client URLs of a member, simply restart that member with
|
||||
|
||||
#### Update advertise peer URLs
|
||||
|
||||
To update the advertise peer URLs of a member, first update it explicitly via member command and then restart the member. The additional action is required since updating peer URLs changes the cluster wide configuration and can affect the health of the etcd cluster.
|
||||
To update the advertise peer URLs of a member, first update it explicitly via member command and then restart the member. The additional action is required since updating peer URLs changes the cluster wide configuration and can affect the health of the etcd cluster.
|
||||
|
||||
To update the peer URLs, first find the target member's ID. To list all members with `etcdctl`:
|
||||
To update the advertise peer URLs, first find the target member's ID. To list all members with `etcdctl`:
|
||||
|
||||
```sh
|
||||
$ etcdctl member list
|
||||
@ -69,7 +69,7 @@ a8266ecf031671f3: name=node1 peerURLs=http://localhost:23801 clientURLs=http://1
|
||||
This example will `update` a8266ecf031671f3 member ID and change its peerURLs value to `http://10.0.1.10:2380`:
|
||||
|
||||
```sh
|
||||
$ etcdctl member update a8266ecf031671f3 http://10.0.1.10:2380
|
||||
$ etcdctl member update a8266ecf031671f3 --peer-urls=http://10.0.1.10:2380
|
||||
Updated member with ID a8266ecf031671f3 in cluster
|
||||
```
|
||||
|
||||
|
@ -10,13 +10,13 @@ In etcd, every runtime reconfiguration has to go through [two phases][add-member
|
||||
|
||||
Phase 1 - Inform cluster of new configuration
|
||||
|
||||
To add a member into etcd cluster, make an API call to request a new member to be added to the cluster. This is only way to add a new member into an existing cluster. The API call returns when the cluster agrees on the configuration change.
|
||||
To add a member into etcd cluster, make an API call to request a new member to be added to the cluster. This is the only way to add a new member into an existing cluster. The API call returns when the cluster agrees on the configuration change.
|
||||
|
||||
Phase 2 - Start new member
|
||||
|
||||
To join the etcd member into the existing cluster, specify the correct `initial-cluster` and set `initial-cluster-state` to `existing`. When the member starts, it will contact the existing cluster first and verify the current cluster configuration matches the expected one specified in `initial-cluster`. When the new member successfully starts, the cluster has reached the expected configuration.
|
||||
To join the new etcd member into the existing cluster, specify the correct `initial-cluster` and set `initial-cluster-state` to `existing`. When the member starts, it will contact the existing cluster first and verify the current cluster configuration matches the expected one specified in `initial-cluster`. When the new member successfully starts, the cluster has reached the expected configuration.
|
||||
|
||||
By splitting the process into two discrete phases users are forced to be explicit regarding cluster membership changes. This actually gives users more flexibility and makes things easier to reason about. For example, if there is an attempt to add a new member with the same ID as an existing member in an etcd cluster, the action will fail immediately during phase one without impacting the running cluster. Similar protection is provided to prevent adding new members by mistake. If a new etcd member attempts to join the cluster before the cluster has accepted the configuration change,, it will not be accepted by the cluster.
|
||||
By splitting the process into two discrete phases users are forced to be explicit regarding cluster membership changes. This actually gives users more flexibility and makes things easier to reason about. For example, if there is an attempt to add a new member with the same ID as an existing member in an etcd cluster, the action will fail immediately during phase one without impacting the running cluster. Similar protection is provided to prevent adding new members by mistake. If a new etcd member attempts to join the cluster before the cluster has accepted the configuration change, it will not be accepted by the cluster.
|
||||
|
||||
Without the explicit workflow around cluster membership etcd would be vulnerable to unexpected cluster membership changes. For example, if etcd is running under an init system such as systemd, etcd would be restarted after being removed via the membership API, and attempt to rejoin the cluster on startup. This cycle would continue every time a member is removed via the API and systemd is set to restart etcd after failing, which is unexpected.
|
||||
|
||||
@ -26,21 +26,21 @@ We expect runtime reconfiguration to be an infrequent operation. We decided to k
|
||||
|
||||
If a cluster permanently loses a majority of its members, a new cluster will need to be started from an old data directory to recover the previous state.
|
||||
|
||||
It is entirely possible to force removing the failed members from the existing cluster to recover. However, we decided not to support this method since it bypasses the normal consensus committing phase, which is unsafe. If the member to remove is not actually dead or force removed through different members in the same cluster, etcd will end up with a diverged cluster with same clusterID. This is very dangerous and hard to debug/fix afterwards.
|
||||
It is entirely possible to force removing the failed members from the existing cluster to recover. However, we decided not to support this method since it bypasses the normal consensus committing phase, which is unsafe. If the member to remove is not actually dead or force removed through different members in the same cluster, etcd will end up with a diverged cluster with same clusterID. This is very dangerous and hard to debug/fix afterwards.
|
||||
|
||||
With a correct deployment, the possibility of permanent majority lose is very low. But it is a severe enough problem that worth special care. We strongly suggest reading the [disaster recovery documentation][disaster-recovery] and prepare for permanent majority lose before putting etcd into production.
|
||||
With a correct deployment, the possibility of permanent majority lose is very low. But it is a severe enough problem that worth special care. We strongly suggest reading the [disaster recovery documentation][disaster-recovery] and preparing for permanent majority lose before putting etcd into production.
|
||||
|
||||
## Do not use public discovery service for runtime reconfiguration
|
||||
|
||||
The public discovery service should only be used for bootstrapping a cluster. To join member into an existing cluster, use runtime reconfiguration API.
|
||||
The public discovery service should only be used for bootstrapping a cluster. To join member into an existing cluster, use runtime reconfiguration API.
|
||||
|
||||
Discovery service is designed for bootstrapping an etcd cluster in the cloud environment, when the IP addresses of all the members are not known beforehand. After successfully bootstrapping a cluster, the IP addresses of all the members are known. Technically, the discovery service should no longer be needed.
|
||||
|
||||
It seems that using public discovery service is a convenient way to do runtime reconfiguration, after all discovery service already has all the cluster configuration information. However relying on public discovery service brings troubles:
|
||||
It seems that using public discovery service is a convenient way to do runtime reconfiguration, after all discovery service already has all the cluster configuration information. However relying on public discovery service brings troubles:
|
||||
|
||||
1. it introduces external dependencies for the entire life-cycle of the cluster, not just bootstrap time. If there is a network issue between the cluster and public discovery service, the cluster will suffer from it.
|
||||
|
||||
2. public discovery service must reflect correct runtime configuration of the cluster during it life-cycle. It has to provide security mechanism to avoid bad actions, and it is hard.
|
||||
|
||||
2. public discovery service must reflect correct runtime configuration of the cluster during it life-cycle. It has to provide security mechanism to avoid bad actions, and it is hard.
|
||||
|
||||
3. public discovery service has to keep tens of thousands of cluster configurations. Our public discovery service backend is not ready for that workload.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Security model
|
||||
# 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.
|
||||
|
||||
@ -181,6 +181,10 @@ To disable certificate chain checking, invoke curl with the `-k` flag:
|
||||
$ curl -k https://127.0.0.1:2379/v2/keys/foo -Xput -d value=bar -v
|
||||
```
|
||||
|
||||
## Notes for DNS SRV
|
||||
|
||||
Since v3.1.0 (except v3.2.9), discovery SRV bootstrapping authenticates `ServerName` with a root domain name from `--discovery-srv` flag. This is to avoid man-in-the-middle cert attacks, by requiring a certificate to have matching root domain name in its Subject Alternative Name (SAN) field. 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
|
||||
|
||||
## Notes for etcd proxy
|
||||
|
||||
etcd proxy terminates the TLS from its client if the connection is secure, and uses proxy's own key/cert specified in `--peer-key-file` and `--peer-cert-file` to communicate with etcd members.
|
||||
|
@ -1,8 +1,8 @@
|
||||
## Supported platforms
|
||||
# Supported systems
|
||||
|
||||
### Current support
|
||||
## Current support
|
||||
|
||||
The following table lists etcd support status for common architectures and operating systems,
|
||||
The following table lists etcd support status for common architectures and operating systems:
|
||||
|
||||
| Architecture | Operating System | Status | Maintainers |
|
||||
| ------------ | ---------------- | ------------ | --------------------------- |
|
||||
@ -18,7 +18,7 @@ The following table lists etcd support status for common architectures and opera
|
||||
|
||||
Experimental platforms appear to work in practice and have some platform specific code in etcd, but do not fully conform to the stable support policy. Unstable platforms have been lightly tested, but less than experimental. Unlisted architecture and operating system pairs are currently unsupported; caveat emptor.
|
||||
|
||||
### Supporting a new platform
|
||||
## Supporting a new system platform
|
||||
|
||||
For etcd to officially support a new platform as stable, a few requirements are necessary to ensure acceptable quality:
|
||||
|
||||
@ -28,7 +28,7 @@ For etcd to officially support a new platform as stable, a few requirements are
|
||||
4. Set up CI (TravisCI, SemaphoreCI or Jenkins) for running integration tests; etcd must pass intensive tests.
|
||||
5. (Optional) Set up a functional testing cluster; an etcd cluster should survive stress testing.
|
||||
|
||||
### 32-bit and other unsupported systems
|
||||
## 32-bit and other unsupported systems
|
||||
|
||||
etcd has known issues on 32-bit systems due to a bug in the Go runtime. See the [Go issue][go-issue] and [atomic package][go-atomic] for more information.
|
||||
|
||||
|
@ -6,7 +6,7 @@ Migrating an application from the API v2 to the API v3 involves two steps: 1) mi
|
||||
|
||||
## Migrate client library
|
||||
|
||||
API v3 is different from API v2, thus application developers need to use a new client library to send requests to etcd API v3. The documentation of the client v3 is available at https://godoc.org/github.com/coreos/etcd/clientv3.
|
||||
API v3 is different from API v2, thus application developers need to use a new client library to send requests to etcd API v3. The documentation of the client v3 is available at https://godoc.org/github.com/coreos/etcd/clientv3.
|
||||
|
||||
There are some notable differences between API v2 and API v3:
|
||||
|
||||
@ -38,13 +38,17 @@ Second, migrate the v2 keys into v3 with the [migrate][migrate_command] (`ETCDCT
|
||||
|
||||
Restart the etcd members and everything should just work.
|
||||
|
||||
For etcd v3.3+, run `ETCDCTL_API=3 etcdctl endpoint hashkv --cluster` to ensure key-value stores are consistent post migration.
|
||||
|
||||
**Warn**: When v2 store has expiring TTL keys and migrate command intends to preserve TTLs, migration may be inconsistent with the last committed v2 state when run on any member with a raft index less than the last leader's raft index.
|
||||
|
||||
### Online migration
|
||||
|
||||
If the application cannot tolerate any downtime, then it must migrate online. The implementation of online migration will vary from application to application but the overall idea is the same.
|
||||
|
||||
First, write application code using the v3 API. The application must support two modes: a migration mode and a normal mode. The application starts in migration mode. When running in migration mode, the application reads keys using the v3 API first, and, if it cannot find the key, it retries with the API v2. In normal mode, the application only reads keys using the v3 API. The application writes keys over the API v3 in both modes. To acknowledge a switch from migration mode to normal mode, the application watches on a switch mode key. When switch key’s value turns to `true`, the application switches over from migration mode to normal mode.
|
||||
|
||||
Second, start a background job to migrate data from the store v2 to the mvcc store by reading keys from the API v2 and writing keys to the API v3.
|
||||
Second, start a background job to migrate data from the store v2 to the mvcc store by reading keys from the API v2 and writing keys to the API v3.
|
||||
|
||||
After finishing data migration, the background job writes `true` into the switch mode key to notify the application that it may switch modes.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
## Versioning
|
||||
# Versioning
|
||||
|
||||
### Service versioning
|
||||
## Service versioning
|
||||
|
||||
etcd uses [semantic versioning](http://semver.org)
|
||||
New minor versions may add additional features to the API.
|
||||
@ -11,7 +11,7 @@ Get the running etcd cluster version with `etcdctl`:
|
||||
ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 endpoint status
|
||||
```
|
||||
|
||||
### API versioning
|
||||
## API versioning
|
||||
|
||||
The `v3` API responses should not change after the 3.0.0 release but new features will be added over time.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
## Introduction
|
||||
# Amazon Web Services
|
||||
|
||||
This guide assumes operational knowledge of Amazon Web Services (AWS), specifically Amazon Elastic Compute Cloud (EC2). This guide provides an introduction to design considerations when designing an etcd deployment on AWS EC2 and how AWS specific features may be utilized in that context.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Run etcd on Container Linux with systemd
|
||||
# Container Linux with systemd
|
||||
|
||||
The following guide shows how to run etcd with [systemd][systemd-docs] under [Container Linux][container-linux-docs].
|
||||
|
||||
@ -134,13 +134,13 @@ etcd:
|
||||
initial_cluster_state: new
|
||||
client_cert_auth: true
|
||||
trusted_ca_file: /etc/ssl/certs/etcd-root-ca.pem
|
||||
cert-file: /etc/ssl/certs/s1.pem
|
||||
key-file: /etc/ssl/certs/s1-key.pem
|
||||
peer-client-cert-auth: true
|
||||
peer-trusted-ca-file: /etc/ssl/certs/etcd-root-ca.pem
|
||||
peer-cert-file: /etc/ssl/certs/s1.pem
|
||||
peer-key-file: /etc/ssl/certs/s1-key.pem
|
||||
auto-compaction-retention: 1
|
||||
cert_file: /etc/ssl/certs/s1.pem
|
||||
key_file: /etc/ssl/certs/s1-key.pem
|
||||
peer_client_cert_auth: true
|
||||
peer_trusted_ca_file: /etc/ssl/certs/etcd-root-ca.pem
|
||||
peer_cert_file: /etc/ssl/certs/s1.pem
|
||||
peer_key_file: /etc/ssl/certs/s1-key.pem
|
||||
auto_compaction_retention: 1
|
||||
```
|
||||
|
||||
```
|
||||
|
@ -79,9 +79,9 @@ Radius Intelligence uses Kubernetes running CoreOS to containerize and scale int
|
||||
|
||||
PD(Placement Driver) is the central controller in the TiDB cluster. It saves the cluster meta information, schedule the data, allocate the global unique timestamp for the distributed transaction, etc. It embeds etcd to supply high availability and auto failover.
|
||||
|
||||
## Canal
|
||||
## Huawei
|
||||
|
||||
- *Application*: system configuration for overlay network
|
||||
- *Application*: System configuration for overlay network (Canal)
|
||||
- *Launched*: June 2016
|
||||
- *Cluster Size*: 3 members for each cluster
|
||||
- *Order of Data Size*: kilobytes
|
||||
|
@ -8,8 +8,6 @@ Before [starting an upgrade](#upgrade-procedure), read through the rest of this
|
||||
|
||||
### Upgrade checklists
|
||||
|
||||
**NOTE:** When [migrating from v2 with no v3 data](https://github.com/coreos/etcd/issues/9480), etcd server v3.2+ panics when etcd restores from existing snapshots but no v3 `ETCD_DATA_DIR/member/snap/db` file. This happens when the server had migrated from v2 with no previous v3 data. This also prevents accidental v3 data loss (e.g. `db` file might have been moved). etcd requires that post v3 migration can only happen with v3 data. Do not upgrade to newer v3 versions until v3.0 server contains v3 data.
|
||||
|
||||
#### Upgrade requirements
|
||||
|
||||
To upgrade an existing etcd deployment to 3.0, the running cluster must be 2.3 or greater. If it's before 2.3, please upgrade to [2.3](https://github.com/coreos/etcd/releases/tag/v2.3.8) before upgrading to 3.0.
|
||||
|
@ -8,8 +8,6 @@ Before [starting an upgrade](#upgrade-procedure), read through the rest of this
|
||||
|
||||
### Upgrade checklists
|
||||
|
||||
**NOTE:** When [migrating from v2 with no v3 data](https://github.com/coreos/etcd/issues/9480), etcd server v3.2+ panics when etcd restores from existing snapshots but no v3 `ETCD_DATA_DIR/member/snap/db` file. This happens when the server had migrated from v2 with no previous v3 data. This also prevents accidental v3 data loss (e.g. `db` file might have been moved). etcd requires that post v3 migration can only happen with v3 data. Do not upgrade to newer v3 versions until v3.0 server contains v3 data.
|
||||
|
||||
#### Monitoring
|
||||
|
||||
Following metrics from v3.0.x have been deprecated in favor of [go-grpc-prometheus](https://github.com/grpc-ecosystem/go-grpc-prometheus):
|
||||
|
@ -6,44 +6,51 @@ In the general case, upgrading from etcd 3.1 to 3.2 can be a zero-downtime, roll
|
||||
|
||||
Before [starting an upgrade](#upgrade-procedure), read through the rest of this guide to prepare.
|
||||
|
||||
### Upgrade checklists
|
||||
### Client upgrade checklists
|
||||
|
||||
**NOTE:** When [migrating from v2 with no v3 data](https://github.com/coreos/etcd/issues/9480), etcd server v3.2+ panics when etcd restores from existing snapshots but no v3 `ETCD_DATA_DIR/member/snap/db` file. This happens when the server had migrated from v2 with no previous v3 data. This also prevents accidental v3 data loss (e.g. `db` file might have been moved). etcd requires that post v3 migration can only happen with v3 data. Do not upgrade to newer v3 versions until v3.0 server contains v3 data.
|
||||
3.2 introduces two breaking changes.
|
||||
|
||||
Highlighted breaking changes in 3.2.
|
||||
|
||||
#### Change in default `snapshot-count` value
|
||||
|
||||
The default value of `--snapshot-count` has [changed from from 10,000 to 100,000](https://github.com/coreos/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). Higher `--snapshot-count` will be manifested with higher memory usage, while retaining more Raft entries helps with the availabilities of slow followers: leader is still able to replicate its logs to followers, rather than forcing followers to rebuild its stores from leader snapshots.
|
||||
|
||||
#### Change in gRPC dependency (>=3.2.10)
|
||||
|
||||
3.2.10 or later now requires [grpc/grpc-go](https://github.com/grpc/grpc-go/releases) `v1.7.5` (<=3.2.9 requires `v1.2.1`).
|
||||
|
||||
##### Deprecate `grpclog.Logger`
|
||||
|
||||
`grpclog.Logger` has been deprecated in favor of [`grpclog.LoggerV2`](https://github.com/grpc/grpc-go/blob/master/grpclog/loggerv2.go). `clientv3.Logger` is now `grpclog.LoggerV2`.
|
||||
Previously, `clientv3.Lease.TimeToLive` API returned `lease.ErrLeaseNotFound` on non-existent lease ID. 3.2 instead returns TTL=-1 in its response and no error (see [#7305](https://github.com/coreos/etcd/pull/7305)).
|
||||
|
||||
Before
|
||||
|
||||
```go
|
||||
import "github.com/coreos/etcd/clientv3"
|
||||
clientv3.SetLogger(log.New(os.Stderr, "grpc: ", 0))
|
||||
// when leaseID does not exist
|
||||
resp, err := TimeToLive(ctx, leaseID)
|
||||
resp == nil
|
||||
err == lease.ErrLeaseNotFound
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```go
|
||||
import "github.com/coreos/etcd/clientv3"
|
||||
import "google.golang.org/grpc/grpclog"
|
||||
clientv3.SetLogger(grpclog.NewLoggerV2(os.Stderr, os.Stderr, os.Stderr))
|
||||
|
||||
// log.New above cannot be used (not implement grpclog.LoggerV2 interface)
|
||||
// when leaseID does not exist
|
||||
resp, err := TimeToLive(ctx, leaseID)
|
||||
resp.TTL == -1
|
||||
err == nil
|
||||
```
|
||||
|
||||
##### Deprecate `grpc.ErrClientConnTimeout`
|
||||
`clientv3.NewFromConfigFile` is moved to `yaml.NewConfig`.
|
||||
|
||||
Previously, `grpc.ErrClientConnTimeout` error is returned on client dial time-outs. 3.2 instead returns `context.DeadlineExceeded` (see [#8504](https://github.com/coreos/etcd/issues/8504)).
|
||||
Before
|
||||
|
||||
```go
|
||||
import "github.com/coreos/etcd/clientv3"
|
||||
clientv3.NewFromConfigFile
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```go
|
||||
import clientv3yaml "github.com/coreos/etcd/clientv3/yaml"
|
||||
clientv3yaml.NewConfig
|
||||
```
|
||||
|
||||
### Client upgrade checklists (>=3.2.10)
|
||||
|
||||
>=3.2.10 upgrades `grpc/grpc-go` to >=v1.7.x from v1.2.1, which introduces some breaking changes.
|
||||
|
||||
Previously, `grpc.ErrClientConnTimeout` error is returned on client dial time-outs. >=3.2.10 instead returns `context.DeadlineExceeded` (see [#8504](https://github.com/coreos/etcd/issues/8504)).
|
||||
|
||||
Before
|
||||
|
||||
@ -70,148 +77,6 @@ if err == context.DeadlineExceeded {
|
||||
}
|
||||
```
|
||||
|
||||
#### Change in maximum request size limits (>=3.2.10)
|
||||
|
||||
3.2.10 and 3.2.11 allow custom request size limits in server side. >=3.2.12 allows custom request size limits for both server and **client side**. In previous versions(v3.2.10, v3.2.11), client response size was limited to only 4 MiB.
|
||||
|
||||
Server-side request limits can be configured with `--max-request-bytes` flag:
|
||||
|
||||
```bash
|
||||
# limits request size to 1.5 KiB
|
||||
etcd --max-request-bytes 1536
|
||||
|
||||
# client writes exceeding 1.5 KiB will be rejected
|
||||
etcdctl put foo [LARGE VALUE...]
|
||||
# etcdserver: request is too large
|
||||
```
|
||||
|
||||
Or configure `embed.Config.MaxRequestBytes` field:
|
||||
|
||||
```go
|
||||
import "github.com/coreos/etcd/embed"
|
||||
import "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
|
||||
// limit requests to 5 MiB
|
||||
cfg := embed.NewConfig()
|
||||
cfg.MaxRequestBytes = 5 * 1024 * 1024
|
||||
|
||||
// client writes exceeding 5 MiB will be rejected
|
||||
_, err := cli.Put(ctx, "foo", [LARGE VALUE...])
|
||||
err == rpctypes.ErrRequestTooLarge
|
||||
```
|
||||
|
||||
**If not specified, server-side limit defaults to 1.5 MiB**.
|
||||
|
||||
Client-side request limits must be configured based on server-side limits.
|
||||
|
||||
```bash
|
||||
# limits request size to 1 MiB
|
||||
etcd --max-request-bytes 1048576
|
||||
```
|
||||
|
||||
```go
|
||||
import "github.com/coreos/etcd/clientv3"
|
||||
|
||||
cli, _ := clientv3.New(clientv3.Config{
|
||||
Endpoints: []string{"127.0.0.1:2379"},
|
||||
MaxCallSendMsgSize: 2 * 1024 * 1024,
|
||||
MaxCallRecvMsgSize: 3 * 1024 * 1024,
|
||||
})
|
||||
|
||||
|
||||
// client writes exceeding "--max-request-bytes" will be rejected from etcd server
|
||||
_, err := cli.Put(ctx, "foo", strings.Repeat("a", 1*1024*1024+5))
|
||||
err == rpctypes.ErrRequestTooLarge
|
||||
|
||||
|
||||
// client writes exceeding "MaxCallSendMsgSize" will be rejected from client-side
|
||||
_, err = cli.Put(ctx, "foo", strings.Repeat("a", 5*1024*1024))
|
||||
err.Error() == "rpc error: code = ResourceExhausted desc = grpc: trying to send message larger than max (5242890 vs. 2097152)"
|
||||
|
||||
|
||||
// some writes under limits
|
||||
for i := range []int{0,1,2,3,4} {
|
||||
_, err = cli.Put(ctx, fmt.Sprintf("foo%d", i), strings.Repeat("a", 1*1024*1024-500))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// client reads exceeding "MaxCallRecvMsgSize" will be rejected from client-side
|
||||
_, err = cli.Get(ctx, "foo", clientv3.WithPrefix())
|
||||
err.Error() == "rpc error: code = ResourceExhausted desc = grpc: received message larger than max (5240509 vs. 3145728)"
|
||||
```
|
||||
|
||||
**If not specified, client-side send limit defaults to 2 MiB (1.5 MiB + gRPC overhead bytes) and receive limit to `math.MaxInt32`**. Please see [clientv3 godoc](https://godoc.org/github.com/coreos/etcd/clientv3#Config) for more detail.
|
||||
|
||||
#### Change in raw gRPC client wrappers
|
||||
|
||||
3.2.12 or later changes the function signatures of `clientv3` gRPC client wrapper. This change was needed to support [custom `grpc.CallOption` on message size limits](https://github.com/coreos/etcd/pull/9047).
|
||||
|
||||
Before and after
|
||||
|
||||
```diff
|
||||
-func NewKVFromKVClient(remote pb.KVClient) KV {
|
||||
+func NewKVFromKVClient(remote pb.KVClient, c *Client) KV {
|
||||
|
||||
-func NewClusterFromClusterClient(remote pb.ClusterClient) Cluster {
|
||||
+func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster {
|
||||
|
||||
-func NewLeaseFromLeaseClient(remote pb.LeaseClient, keepAliveTimeout time.Duration) Lease {
|
||||
+func NewLeaseFromLeaseClient(remote pb.LeaseClient, c *Client, keepAliveTimeout time.Duration) Lease {
|
||||
|
||||
-func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient) Maintenance {
|
||||
+func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient, c *Client) Maintenance {
|
||||
|
||||
-func NewWatchFromWatchClient(wc pb.WatchClient) Watcher {
|
||||
+func NewWatchFromWatchClient(wc pb.WatchClient, c *Client) Watcher {
|
||||
```
|
||||
|
||||
#### Change in `clientv3.Lease.TimeToLive` API
|
||||
|
||||
Previously, `clientv3.Lease.TimeToLive` API returned `lease.ErrLeaseNotFound` on non-existent lease ID. 3.2 instead returns TTL=-1 in its response and no error (see [#7305](https://github.com/coreos/etcd/pull/7305)).
|
||||
|
||||
Before
|
||||
|
||||
```go
|
||||
// when leaseID does not exist
|
||||
resp, err := TimeToLive(ctx, leaseID)
|
||||
resp == nil
|
||||
err == lease.ErrLeaseNotFound
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```go
|
||||
// when leaseID does not exist
|
||||
resp, err := TimeToLive(ctx, leaseID)
|
||||
resp.TTL == -1
|
||||
err == nil
|
||||
```
|
||||
|
||||
#### Change in `clientv3.NewFromConfigFile`
|
||||
|
||||
`clientv3.NewFromConfigFile` is moved to `yaml.NewConfig`.
|
||||
|
||||
Before
|
||||
|
||||
```go
|
||||
import "github.com/coreos/etcd/clientv3"
|
||||
clientv3.NewFromConfigFile
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```go
|
||||
import clientv3yaml "github.com/coreos/etcd/clientv3/yaml"
|
||||
clientv3yaml.NewConfig
|
||||
```
|
||||
|
||||
#### Change in `--listen-peer-urls` and `--listen-client-urls`
|
||||
|
||||
3.2 now 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. Make sure that those URLs are properly formated as `scheme://IP:port`.
|
||||
|
||||
See [issue #6336](https://github.com/coreos/etcd/issues/6336) for more contexts.
|
||||
|
||||
### Server upgrade checklists
|
||||
|
||||
#### Upgrade requirements
|
||||
|
@ -6,306 +6,9 @@ In the general case, upgrading from etcd 3.2 to 3.3 can be a zero-downtime, roll
|
||||
|
||||
Before [starting an upgrade](#upgrade-procedure), read through the rest of this guide to prepare.
|
||||
|
||||
### Upgrade checklists
|
||||
### Client upgrade checklists
|
||||
|
||||
**NOTE:** When [migrating from v2 with no v3 data](https://github.com/coreos/etcd/issues/9480), etcd server v3.2+ panics when etcd restores from existing snapshots but no v3 `ETCD_DATA_DIR/member/snap/db` file. This happens when the server had migrated from v2 with no previous v3 data. This also prevents accidental v3 data loss (e.g. `db` file might have been moved). etcd requires that post v3 migration can only happen with v3 data. Do not upgrade to newer v3 versions until v3.0 server contains v3 data.
|
||||
|
||||
Highlighted breaking changes in 3.3.
|
||||
|
||||
#### Change in `etcdserver.EtcdServer` struct
|
||||
|
||||
`etcdserver.EtcdServer` has changed the type of its member field `*etcdserver.ServerConfig` to `etcdserver.ServerConfig`. And `etcdserver.NewServer` now takes `etcdserver.ServerConfig`, instead of `*etcdserver.ServerConfig`.
|
||||
|
||||
Before and after (e.g. [k8s.io/kubernetes/test/e2e_node/services/etcd.go](https://github.com/kubernetes/kubernetes/blob/release-1.8/test/e2e_node/services/etcd.go#L50-L55))
|
||||
|
||||
```diff
|
||||
import "github.com/coreos/etcd/etcdserver"
|
||||
|
||||
type EtcdServer struct {
|
||||
*etcdserver.EtcdServer
|
||||
- config *etcdserver.ServerConfig
|
||||
+ config etcdserver.ServerConfig
|
||||
}
|
||||
|
||||
func NewEtcd(dataDir string) *EtcdServer {
|
||||
- config := &etcdserver.ServerConfig{
|
||||
+ config := etcdserver.ServerConfig{
|
||||
DataDir: dataDir,
|
||||
...
|
||||
}
|
||||
return &EtcdServer{config: config}
|
||||
}
|
||||
|
||||
func (e *EtcdServer) Start() error {
|
||||
var err error
|
||||
e.EtcdServer, err = etcdserver.NewServer(e.config)
|
||||
...
|
||||
```
|
||||
|
||||
#### Change in `embed.EtcdServer` struct
|
||||
|
||||
Field `LogOutput` is added to `embed.Config`:
|
||||
|
||||
```diff
|
||||
package embed
|
||||
|
||||
type Config struct {
|
||||
Debug bool `json:"debug"`
|
||||
LogPkgLevels string `json:"log-package-levels"`
|
||||
+ LogOutput string `json:"log-output"`
|
||||
...
|
||||
```
|
||||
|
||||
Before gRPC server warnings were logged in etcdserver.
|
||||
|
||||
```
|
||||
WARNING: 2017/11/02 11:35:51 grpc: addrConn.resetTransport failed to create client transport: connection error: desc = "transport: Error while dialing dial tcp: operation was canceled"; Reconnecting to {localhost:2379 <nil>}
|
||||
WARNING: 2017/11/02 11:35:51 grpc: addrConn.resetTransport failed to create client transport: connection error: desc = "transport: Error while dialing dial tcp: operation was canceled"; Reconnecting to {localhost:2379 <nil>}
|
||||
```
|
||||
|
||||
From v3.3, gRPC server logs are disabled by default.
|
||||
|
||||
```go
|
||||
import "github.com/coreos/etcd/embed"
|
||||
|
||||
cfg := &embed.Config{Debug: false}
|
||||
cfg.SetupLogging()
|
||||
```
|
||||
|
||||
Set `embed.Config.Debug` field to `true` to enable gRPC server logs.
|
||||
|
||||
#### Change in `/health` endpoint response
|
||||
|
||||
Previously, `[endpoint]:[client-port]/health` returned manually marshaled JSON value. 3.3 now defines [`etcdhttp.Health`](https://godoc.org/github.com/coreos/etcd/etcdserver/api/etcdhttp#Health) struct.
|
||||
|
||||
Note that in v3.3.0-rc.0, v3.3.0-rc.1, and v3.3.0-rc.2, `etcdhttp.Health` has boolean type `"health"` and `"errors"` fields. For backward compatibilities, we reverted `"health"` field to `string` type and removed `"errors"` field. Further health information will be provided in separate APIs.
|
||||
|
||||
```bash
|
||||
$ curl http://localhost:2379/health
|
||||
{"health":"true"}
|
||||
```
|
||||
|
||||
#### Change in gRPC gateway HTTP endpoints (replaced `/v3alpha` with `/v3beta`)
|
||||
|
||||
Before
|
||||
|
||||
```bash
|
||||
curl -L http://localhost:2379/v3alpha/kv/put \
|
||||
-X POST -d '{"key": "Zm9v", "value": "YmFy"}'
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```bash
|
||||
curl -L http://localhost:2379/v3beta/kv/put \
|
||||
-X POST -d '{"key": "Zm9v", "value": "YmFy"}'
|
||||
```
|
||||
|
||||
Requests to `/v3alpha` endpoints will redirect to `/v3beta`, and `/v3alpha` will be removed in 3.4 release.
|
||||
|
||||
#### Change in maximum request size limits
|
||||
|
||||
3.3 now allows custom request size limits for both server and **client side**. In previous versions(v3.2.10, v3.2.11), client response size was limited to only 4 MiB.
|
||||
|
||||
Server-side request limits can be configured with `--max-request-bytes` flag:
|
||||
|
||||
```bash
|
||||
# limits request size to 1.5 KiB
|
||||
etcd --max-request-bytes 1536
|
||||
|
||||
# client writes exceeding 1.5 KiB will be rejected
|
||||
etcdctl put foo [LARGE VALUE...]
|
||||
# etcdserver: request is too large
|
||||
```
|
||||
|
||||
Or configure `embed.Config.MaxRequestBytes` field:
|
||||
|
||||
```go
|
||||
import "github.com/coreos/etcd/embed"
|
||||
import "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
|
||||
// limit requests to 5 MiB
|
||||
cfg := embed.NewConfig()
|
||||
cfg.MaxRequestBytes = 5 * 1024 * 1024
|
||||
|
||||
// client writes exceeding 5 MiB will be rejected
|
||||
_, err := cli.Put(ctx, "foo", [LARGE VALUE...])
|
||||
err == rpctypes.ErrRequestTooLarge
|
||||
```
|
||||
|
||||
**If not specified, server-side limit defaults to 1.5 MiB**.
|
||||
|
||||
Client-side request limits must be configured based on server-side limits.
|
||||
|
||||
```bash
|
||||
# limits request size to 1 MiB
|
||||
etcd --max-request-bytes 1048576
|
||||
```
|
||||
|
||||
```go
|
||||
import "github.com/coreos/etcd/clientv3"
|
||||
|
||||
cli, _ := clientv3.New(clientv3.Config{
|
||||
Endpoints: []string{"127.0.0.1:2379"},
|
||||
MaxCallSendMsgSize: 2 * 1024 * 1024,
|
||||
MaxCallRecvMsgSize: 3 * 1024 * 1024,
|
||||
})
|
||||
|
||||
|
||||
// client writes exceeding "--max-request-bytes" will be rejected from etcd server
|
||||
_, err := cli.Put(ctx, "foo", strings.Repeat("a", 1*1024*1024+5))
|
||||
err == rpctypes.ErrRequestTooLarge
|
||||
|
||||
|
||||
// client writes exceeding "MaxCallSendMsgSize" will be rejected from client-side
|
||||
_, err = cli.Put(ctx, "foo", strings.Repeat("a", 5*1024*1024))
|
||||
err.Error() == "rpc error: code = ResourceExhausted desc = grpc: trying to send message larger than max (5242890 vs. 2097152)"
|
||||
|
||||
|
||||
// some writes under limits
|
||||
for i := range []int{0,1,2,3,4} {
|
||||
_, err = cli.Put(ctx, fmt.Sprintf("foo%d", i), strings.Repeat("a", 1*1024*1024-500))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// client reads exceeding "MaxCallRecvMsgSize" will be rejected from client-side
|
||||
_, err = cli.Get(ctx, "foo", clientv3.WithPrefix())
|
||||
err.Error() == "rpc error: code = ResourceExhausted desc = grpc: received message larger than max (5240509 vs. 3145728)"
|
||||
```
|
||||
|
||||
**If not specified, client-side send limit defaults to 2 MiB (1.5 MiB + gRPC overhead bytes) and receive limit to `math.MaxInt32`**. Please see [clientv3 godoc](https://godoc.org/github.com/coreos/etcd/clientv3#Config) for more detail.
|
||||
|
||||
#### Change in raw gRPC client wrappers
|
||||
|
||||
3.3 changes the function signatures of `clientv3` gRPC client wrapper. This change was needed to support [custom `grpc.CallOption` on message size limits](https://github.com/coreos/etcd/pull/9047).
|
||||
|
||||
Before and after
|
||||
|
||||
```diff
|
||||
-func NewKVFromKVClient(remote pb.KVClient) KV {
|
||||
+func NewKVFromKVClient(remote pb.KVClient, c *Client) KV {
|
||||
|
||||
-func NewClusterFromClusterClient(remote pb.ClusterClient) Cluster {
|
||||
+func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster {
|
||||
|
||||
-func NewLeaseFromLeaseClient(remote pb.LeaseClient, keepAliveTimeout time.Duration) Lease {
|
||||
+func NewLeaseFromLeaseClient(remote pb.LeaseClient, c *Client, keepAliveTimeout time.Duration) Lease {
|
||||
|
||||
-func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient) Maintenance {
|
||||
+func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient, c *Client) Maintenance {
|
||||
|
||||
-func NewWatchFromWatchClient(wc pb.WatchClient) Watcher {
|
||||
+func NewWatchFromWatchClient(wc pb.WatchClient, c *Client) Watcher {
|
||||
```
|
||||
|
||||
#### Change in clientv3 `Snapshot` API error type
|
||||
|
||||
Previously, clientv3 `Snapshot` API returned raw [`grpc/*status.statusError`] type error. v3.3 now translates those errors to corresponding public error types, to be consistent with other APIs.
|
||||
|
||||
Before
|
||||
|
||||
```go
|
||||
import "context"
|
||||
|
||||
// reading snapshot with canceled context should error out
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
rc, _ := cli.Snapshot(ctx)
|
||||
cancel()
|
||||
_, err := io.Copy(f, rc)
|
||||
err.Error() == "rpc error: code = Canceled desc = context canceled"
|
||||
|
||||
// reading snapshot with deadline exceeded should error out
|
||||
ctx, cancel = context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
rc, _ = cli.Snapshot(ctx)
|
||||
time.Sleep(2 * time.Second)
|
||||
_, err = io.Copy(f, rc)
|
||||
err.Error() == "rpc error: code = DeadlineExceeded desc = context deadline exceeded"
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```go
|
||||
import "context"
|
||||
|
||||
// reading snapshot with canceled context should error out
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
rc, _ := cli.Snapshot(ctx)
|
||||
cancel()
|
||||
_, err := io.Copy(f, rc)
|
||||
err == context.Canceled
|
||||
|
||||
// reading snapshot with deadline exceeded should error out
|
||||
ctx, cancel = context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
rc, _ = cli.Snapshot(ctx)
|
||||
time.Sleep(2 * time.Second)
|
||||
_, err = io.Copy(f, rc)
|
||||
err == context.DeadlineExceeded
|
||||
```
|
||||
|
||||
#### Change in `etcdctl lease timetolive` command output
|
||||
|
||||
Previously, `lease timetolive LEASE_ID` command on expired lease prints `-1s` for remaining seconds. 3.3 now outputs clearer messages.
|
||||
|
||||
Before
|
||||
|
||||
|
||||
```bash
|
||||
lease 2d8257079fa1bc0c granted with TTL(0s), remaining(-1s)
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```bash
|
||||
lease 2d8257079fa1bc0c already expired
|
||||
```
|
||||
|
||||
#### Change in `golang.org/x/net/context` imports
|
||||
|
||||
`clientv3` has deprecated `golang.org/x/net/context`. If a project vendors `golang.org/x/net/context` in other code (e.g. etcd generated protocol buffer code) and imports `github.com/coreos/etcd/clientv3`, it requires Go 1.9+ to compile.
|
||||
|
||||
Before
|
||||
|
||||
```go
|
||||
import "golang.org/x/net/context"
|
||||
cli.Put(context.Background(), "f", "v")
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```go
|
||||
import "context"
|
||||
cli.Put(context.Background(), "f", "v")
|
||||
```
|
||||
|
||||
#### Change in gRPC dependency
|
||||
|
||||
3.3 now requires [grpc/grpc-go](https://github.com/grpc/grpc-go/releases) `v1.7.5`.
|
||||
|
||||
##### Deprecate `grpclog.Logger`
|
||||
|
||||
`grpclog.Logger` has been deprecated in favor of [`grpclog.LoggerV2`](https://github.com/grpc/grpc-go/blob/master/grpclog/loggerv2.go). `clientv3.Logger` is now `grpclog.LoggerV2`.
|
||||
|
||||
Before
|
||||
|
||||
```go
|
||||
import "github.com/coreos/etcd/clientv3"
|
||||
clientv3.SetLogger(log.New(os.Stderr, "grpc: ", 0))
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```go
|
||||
import "github.com/coreos/etcd/clientv3"
|
||||
import "google.golang.org/grpc/grpclog"
|
||||
clientv3.SetLogger(grpclog.NewLoggerV2(os.Stderr, os.Stderr, os.Stderr))
|
||||
|
||||
// log.New above cannot be used (not implement grpclog.LoggerV2 interface)
|
||||
```
|
||||
|
||||
##### Deprecate `grpc.ErrClientConnTimeout`
|
||||
3.3 introduces breaking changes (TODO: update this before 3.3 release).
|
||||
|
||||
Previously, `grpc.ErrClientConnTimeout` error is returned on client dial time-outs. 3.3 instead returns `context.DeadlineExceeded` (see [#8504](https://github.com/coreos/etcd/issues/8504)).
|
||||
|
||||
@ -334,22 +37,6 @@ if err == context.DeadlineExceeded {
|
||||
}
|
||||
```
|
||||
|
||||
#### Change in official container registry
|
||||
|
||||
etcd now uses [`gcr.io/etcd-development/etcd`](https://gcr.io/etcd-development/etcd) as a primary container registry, and [`quay.io/coreos/etcd`](https://quay.io/coreos/etcd) as secondary.
|
||||
|
||||
Before
|
||||
|
||||
```bash
|
||||
docker pull quay.io/coreos/etcd:v3.2.5
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```bash
|
||||
docker pull gcr.io/etcd-development/etcd:v3.3.0
|
||||
```
|
||||
|
||||
### Server upgrade checklists
|
||||
|
||||
#### Upgrade requirements
|
||||
@ -473,4 +160,4 @@ localhost:22379 is healthy: successfully committed proposal: took = 2.553476ms
|
||||
localhost:32379 is healthy: successfully committed proposal: took = 2.517902ms
|
||||
```
|
||||
|
||||
[etcd-contact]: https://groups.google.com/forum/#!forum/etcd-dev
|
||||
[etcd-contact]: https://groups.google.com/forum/#!forum/etcd-dev
|
||||
|
@ -1,171 +0,0 @@
|
||||
## Upgrade etcd from 3.3 to 3.4
|
||||
|
||||
In the general case, upgrading from etcd 3.3 to 3.4 can be a zero-downtime, rolling upgrade:
|
||||
- one by one, stop the etcd v3.3 processes and replace them with etcd v3.4 processes
|
||||
- after running all v3.4 processes, new features in v3.4 are available to the cluster
|
||||
|
||||
Before [starting an upgrade](#upgrade-procedure), read through the rest of this guide to prepare.
|
||||
|
||||
### Upgrade checklists
|
||||
|
||||
**NOTE:** When [migrating from v2 with no v3 data](https://github.com/coreos/etcd/issues/9480), etcd server v3.2+ panics when etcd restores from existing snapshots but no v3 `ETCD_DATA_DIR/member/snap/db` file. This happens when the server had migrated from v2 with no previous v3 data. This also prevents accidental v3 data loss (e.g. `db` file might have been moved). etcd requires that post v3 migration can only happen with v3 data. Do not upgrade to newer v3 versions until v3.0 server contains v3 data.
|
||||
|
||||
Highlighted breaking changes in 3.4.
|
||||
|
||||
#### Change in `etcd` flags
|
||||
|
||||
`--ca-file` and `--peer-ca-file` flags are deprecated; they have been deprecated since v2.1.
|
||||
|
||||
```diff
|
||||
-etcd --ca-file ca-client.crt
|
||||
+etcd --trusted-ca-file ca-client.crt
|
||||
```
|
||||
|
||||
```diff
|
||||
-etcd --peer-ca-file ca-peer.crt
|
||||
+etcd --peer-trusted-ca-file ca-peer.crt
|
||||
```
|
||||
|
||||
#### Change in ``pkg/transport`
|
||||
|
||||
Deprecated `pkg/transport.TLSInfo.CAFile` field.
|
||||
|
||||
```diff
|
||||
import "github.com/coreos/etcd/pkg/transport"
|
||||
|
||||
tlsInfo := transport.TLSInfo{
|
||||
CertFile: "/tmp/test-certs/test.pem",
|
||||
KeyFile: "/tmp/test-certs/test-key.pem",
|
||||
- CAFile: "/tmp/test-certs/trusted-ca.pem",
|
||||
+ TrustedCAFile: "/tmp/test-certs/trusted-ca.pem",
|
||||
}
|
||||
tlsConfig, err := tlsInfo.ClientConfig()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
```
|
||||
|
||||
### Server upgrade checklists
|
||||
|
||||
#### Upgrade requirements
|
||||
|
||||
To upgrade an existing etcd deployment to 3.4, the running cluster must be 3.3 or greater. If it's before 3.3, please [upgrade to 3.3](upgrade_3_3.md) before upgrading to 3.4.
|
||||
|
||||
Also, to ensure a smooth rolling upgrade, the running cluster must be healthy. Check the health of the cluster by using the `etcdctl endpoint health` command before proceeding.
|
||||
|
||||
#### Preparation
|
||||
|
||||
Before upgrading etcd, always test the services relying on etcd in a staging environment before deploying the upgrade to the production environment.
|
||||
|
||||
Before beginning, [backup the etcd data](../op-guide/maintenance.md#snapshot-backup). Should something go wrong with the upgrade, it is possible to use this backup to [downgrade](#downgrade) back to existing etcd version. Please note that the `snapshot` command only backs up the v3 data. For v2 data, see [backing up v2 datastore](../v2/admin_guide.md#backing-up-the-datastore).
|
||||
|
||||
#### Mixed versions
|
||||
|
||||
While upgrading, an etcd cluster supports mixed versions of etcd members, and operates with the protocol of the lowest common version. The cluster is only considered upgraded once all of its members are upgraded to version 3.4. Internally, etcd members negotiate with each other to determine the overall cluster version, which controls the reported version and the supported features.
|
||||
|
||||
#### Limitations
|
||||
|
||||
Note: If the cluster only has v3 data and no v2 data, it is not subject to this limitation.
|
||||
|
||||
If the cluster is serving a v2 data set larger than 50MB, each newly upgraded member may take up to two minutes to catch up with the existing cluster. Check the size of a recent snapshot to estimate the total data size. In other words, it is safest to wait for 2 minutes between upgrading each member.
|
||||
|
||||
For a much larger total data size, 100MB or more , this one-time process might take even more time. Administrators of very large etcd clusters of this magnitude can feel free to contact the [etcd team][etcd-contact] before upgrading, and we'll be happy to provide advice on the procedure.
|
||||
|
||||
#### Downgrade
|
||||
|
||||
If all members have been upgraded to v3.4, the cluster will be upgraded to v3.4, and downgrade from this completed state is **not possible**. If any single member is still v3.3, however, the cluster and its operations remains "v3.3", and it is possible from this mixed cluster state to return to using a v3.3 etcd binary on all members.
|
||||
|
||||
Please [backup the data directory](../op-guide/maintenance.md#snapshot-backup) of all etcd members to make downgrading the cluster possible even after it has been completely upgraded.
|
||||
|
||||
### Upgrade procedure
|
||||
|
||||
This example shows how to upgrade a 3-member v3.3 ectd cluster running on a local machine.
|
||||
|
||||
#### 1. Check upgrade requirements
|
||||
|
||||
Is the cluster healthy and running v3.3.x?
|
||||
|
||||
```
|
||||
$ ETCDCTL_API=3 etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
|
||||
localhost:2379 is healthy: successfully committed proposal: took = 6.600684ms
|
||||
localhost:22379 is healthy: successfully committed proposal: took = 8.540064ms
|
||||
localhost:32379 is healthy: successfully committed proposal: took = 8.763432ms
|
||||
|
||||
$ curl http://localhost:2379/version
|
||||
{"etcdserver":"3.3.0","etcdcluster":"3.3.0"}
|
||||
```
|
||||
|
||||
#### 2. Stop the existing etcd process
|
||||
|
||||
When each etcd process is stopped, expected errors will be logged by other cluster members. This is normal since a cluster member connection has been (temporarily) broken:
|
||||
|
||||
```
|
||||
14:13:31.491746 I | raft: c89feb932daef420 [term 3] received MsgTimeoutNow from 6d4f535bae3ab960 and starts an election to get leadership.
|
||||
14:13:31.491769 I | raft: c89feb932daef420 became candidate at term 4
|
||||
14:13:31.491788 I | raft: c89feb932daef420 received MsgVoteResp from c89feb932daef420 at term 4
|
||||
14:13:31.491797 I | raft: c89feb932daef420 [logterm: 3, index: 9] sent MsgVote request to 6d4f535bae3ab960 at term 4
|
||||
14:13:31.491805 I | raft: c89feb932daef420 [logterm: 3, index: 9] sent MsgVote request to 9eda174c7df8a033 at term 4
|
||||
14:13:31.491815 I | raft: raft.node: c89feb932daef420 lost leader 6d4f535bae3ab960 at term 4
|
||||
14:13:31.524084 I | raft: c89feb932daef420 received MsgVoteResp from 6d4f535bae3ab960 at term 4
|
||||
14:13:31.524108 I | raft: c89feb932daef420 [quorum:2] has received 2 MsgVoteResp votes and 0 vote rejections
|
||||
14:13:31.524123 I | raft: c89feb932daef420 became leader at term 4
|
||||
14:13:31.524136 I | raft: raft.node: c89feb932daef420 elected leader c89feb932daef420 at term 4
|
||||
14:13:31.592650 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream MsgApp v2 reader)
|
||||
14:13:31.592825 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream Message reader)
|
||||
14:13:31.693275 E | rafthttp: failed to dial 6d4f535bae3ab960 on stream Message (dial tcp [::1]:2380: getsockopt: connection refused)
|
||||
14:13:31.693289 I | rafthttp: peer 6d4f535bae3ab960 became inactive
|
||||
14:13:31.936678 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream Message writer)
|
||||
```
|
||||
|
||||
It's a good idea at this point to [backup the etcd data](../op-guide/maintenance.md#snapshot-backup) to provide a downgrade path should any problems occur:
|
||||
|
||||
```
|
||||
$ etcdctl snapshot save backup.db
|
||||
```
|
||||
|
||||
#### 3. Drop-in etcd v3.4 binary and start the new etcd process
|
||||
|
||||
The new v3.4 etcd will publish its information to the cluster:
|
||||
|
||||
```
|
||||
14:14:25.363225 I | etcdserver: published {Name:s1 ClientURLs:[http://localhost:2379]} to cluster a9ededbffcb1b1f1
|
||||
```
|
||||
|
||||
Verify that each member, and then the entire cluster, becomes healthy with the new v3.4 etcd binary:
|
||||
|
||||
```
|
||||
$ ETCDCTL_API=3 /etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
|
||||
localhost:22379 is healthy: successfully committed proposal: took = 5.540129ms
|
||||
localhost:32379 is healthy: successfully committed proposal: took = 7.321771ms
|
||||
localhost:2379 is healthy: successfully committed proposal: took = 10.629901ms
|
||||
```
|
||||
|
||||
Upgraded members will log warnings like the following until the entire cluster is upgraded. This is expected and will cease after all etcd cluster members are upgraded to v3.4:
|
||||
|
||||
```
|
||||
14:15:17.071804 W | etcdserver: member c89feb932daef420 has a higher version 3.4.0
|
||||
14:15:21.073110 W | etcdserver: the local etcd version 3.3.0 is not up-to-date
|
||||
14:15:21.073142 W | etcdserver: member 6d4f535bae3ab960 has a higher version 3.4.0
|
||||
14:15:21.073157 W | etcdserver: the local etcd version 3.3.0 is not up-to-date
|
||||
14:15:21.073164 W | etcdserver: member c89feb932daef420 has a higher version 3.4.0
|
||||
```
|
||||
|
||||
#### 4. Repeat step 2 to step 3 for all other members
|
||||
|
||||
#### 5. Finish
|
||||
|
||||
When all members are upgraded, the cluster will report upgrading to 3.4 successfully:
|
||||
|
||||
```
|
||||
14:15:54.536901 N | etcdserver/membership: updated the cluster version from 3.3 to 3.4
|
||||
14:15:54.537035 I | etcdserver/api: enabled capabilities for version 3.4
|
||||
```
|
||||
|
||||
```
|
||||
$ ETCDCTL_API=3 /etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
|
||||
localhost:2379 is healthy: successfully committed proposal: took = 2.312897ms
|
||||
localhost:22379 is healthy: successfully committed proposal: took = 2.553476ms
|
||||
localhost:32379 is healthy: successfully committed proposal: took = 2.517902ms
|
||||
```
|
||||
|
||||
[etcd-contact]: https://groups.google.com/forum/#!forum/etcd-dev
|
@ -45,12 +45,12 @@ It is important to monitor your production etcd cluster for healthy information
|
||||
|
||||
#### Health Monitoring
|
||||
|
||||
At lowest level, etcd exposes health information via HTTP at `/health` in JSON format. If it returns `{"health": "true"}`, then the cluster is healthy. Please note the `/health` endpoint is still an experimental one as in etcd 2.2.
|
||||
At lowest level, etcd exposes health information via HTTP at `/health` in JSON format. If it returns `{"health":true}`, then the cluster is healthy.
|
||||
|
||||
```
|
||||
$ curl -L http://127.0.0.1:2379/health
|
||||
|
||||
{"health": "true"}
|
||||
{"health":true}
|
||||
```
|
||||
|
||||
You can also use etcdctl to check the cluster-wide health information. It will contact all the members of the cluster and collect the health information for you.
|
||||
|
@ -205,7 +205,7 @@ The security flags help to [build a secure etcd cluster][security].
|
||||
+ env variable: ETCD_CLIENT_CERT_AUTH
|
||||
|
||||
### --trusted-ca-file
|
||||
+ Path to the client server TLS trusted CA key file.
|
||||
+ Path to the client server TLS trusted CA cert file.
|
||||
+ default: none
|
||||
+ env variable: ETCD_TRUSTED_CA_FILE
|
||||
|
||||
|
@ -62,7 +62,7 @@ ALERT HTTPRequestsSlow
|
||||
}
|
||||
ANNOTATIONS {
|
||||
summary = "slow HTTP requests",
|
||||
description = "on etcd instance {{ $labels.instance }} HTTP requests to {{ $label.method }} are slow",
|
||||
description = "on etcd instance {{ $labels.instance }} HTTP requests to {{ $labels.method }} are slow",
|
||||
}
|
||||
|
||||
### File descriptor alerts ###
|
||||
|
91
Documentation/v2/etcd_alert.rules.yml
Normal file
91
Documentation/v2/etcd_alert.rules.yml
Normal file
@ -0,0 +1,91 @@
|
||||
groups:
|
||||
- name: etcd_alert.rules
|
||||
rules:
|
||||
- alert: InsufficientMembers
|
||||
expr: count(up{job="etcd"} == 0) > (count(up{job="etcd"}) / 2 - 1)
|
||||
for: 3m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
description: If one more etcd member goes down the cluster will be unavailable
|
||||
summary: etcd cluster insufficient members
|
||||
- alert: HighNumberOfFailedHTTPRequests
|
||||
expr: sum(rate(etcd_http_failed_total{code!~"^(?:4[0-9]{2})$",job="etcd"}[5m]))
|
||||
BY (method) / sum(rate(etcd_http_received_total{job="etcd"}[5m])) BY (method)
|
||||
> 0.01
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: '{{ $value }}% of requests for {{ $labels.method }} failed on etcd
|
||||
instance {{ $labels.instance }}'
|
||||
summary: a high number of HTTP requests are failing
|
||||
- alert: HighNumberOfFailedHTTPRequests
|
||||
expr: sum(rate(etcd_http_failed_total{code!~"^(?:4[0-9]{2})$",job="etcd"}[5m]))
|
||||
BY (method) / sum(rate(etcd_http_received_total{job="etcd"}[5m])) BY (method)
|
||||
> 0.05
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
description: '{{ $value }}% of requests for {{ $labels.method }} failed on etcd
|
||||
instance {{ $labels.instance }}'
|
||||
summary: a high number of HTTP requests are failing
|
||||
- alert: HighNumberOfFailedHTTPRequests
|
||||
expr: sum(rate(etcd_http_failed_total{code=~"^(?:4[0-9]{2})$",job="etcd"}[5m]))
|
||||
BY (method) / sum(rate(etcd_http_received_total{job="etcd"}[5m])) BY (method)
|
||||
> 0.5
|
||||
for: 10m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
description: '{{ $value }}% of requests for {{ $labels.method }} failed with
|
||||
4xx responses on etcd instance {{ $labels.instance }}'
|
||||
summary: a high number of HTTP requests are failing
|
||||
- alert: HTTPRequestsSlow
|
||||
expr: histogram_quantile(0.99, rate(etcd_http_successful_duration_second_bucket[5m]))
|
||||
> 0.15
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: on etcd instance {{ $labels.instance }} HTTP requests to {{ $labels.method
|
||||
}} are slow
|
||||
summary: slow HTTP requests
|
||||
- record: instance:fd_utilization
|
||||
expr: process_open_fds / process_max_fds
|
||||
- alert: FdExhaustionClose
|
||||
expr: predict_linear(instance:fd_utilization[1h], 3600 * 4) > 1
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: '{{ $labels.job }} instance {{ $labels.instance }} will exhaust
|
||||
its file descriptors soon'
|
||||
summary: file descriptors soon exhausted
|
||||
- alert: FdExhaustionClose
|
||||
expr: predict_linear(instance:fd_utilization[10m], 3600) > 1
|
||||
for: 10m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
description: '{{ $labels.job }} instance {{ $labels.instance }} will exhaust
|
||||
its file descriptors soon'
|
||||
summary: file descriptors soon exhausted
|
||||
- alert: HighNumberOfFailedProposals
|
||||
expr: increase(etcd_server_proposal_failed_total{job="etcd"}[1h]) > 5
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: etcd instance {{ $labels.instance }} has seen {{ $value }} proposal
|
||||
failures within the last hour
|
||||
summary: a high number of proposals within the etcd cluster are failing
|
||||
- alert: HighFsyncDurations
|
||||
expr: histogram_quantile(0.99, rate(etcd_wal_fsync_durations_seconds_bucket[5m]))
|
||||
> 0.5
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
description: etcd instance {{ $labels.instance }} fync durations are high
|
||||
summary: high fsync durations
|
@ -1,70 +0,0 @@
|
||||
**This is the documentation for etcd2 releases. Read [etcd3 doc][v3-docs] for etcd3 releases.**
|
||||
|
||||
[v3-docs]: ../docs.md#documentation
|
||||
|
||||
|
||||
# FAQ
|
||||
|
||||
## Initial Bootstrapping UX
|
||||
|
||||
etcd initial bootstrapping is done via command line flags such as
|
||||
`--initial-cluster` or `--discovery`. These flags can safely be left on the
|
||||
command line after your cluster is running but they will be ignored if you have
|
||||
a non-empty data dir. So, why did we decide to have this sort of odd UX?
|
||||
|
||||
One of the design goals of etcd is easy bringup of clusters using a one-shot
|
||||
static configuration like AWS Cloud Formation, PXE booting, etc. Essentially we
|
||||
want to describe several virtual machines and bring them all up at once into an
|
||||
etcd cluster.
|
||||
|
||||
To achieve this sort of hands-free cluster bootstrap we had two other options:
|
||||
|
||||
**API to bootstrap**
|
||||
|
||||
This is problematic because it cannot be coordinated from a single service file
|
||||
and we didn't want to have the etcd socket listening but unresponsive to
|
||||
clients for an unbound period of time.
|
||||
|
||||
It would look something like this:
|
||||
|
||||
```
|
||||
ExecStart=/usr/bin/etcd
|
||||
ExecStartPost/usr/bin/etcd init localhost:2379 --cluster=
|
||||
```
|
||||
|
||||
**etcd init subcommand**
|
||||
|
||||
```
|
||||
etcd init --cluster='default=http://localhost:2380,default=http://localhost:7001'...
|
||||
etcd init --discovery https://discovery-example.etcd.io/193e4
|
||||
```
|
||||
|
||||
Then after running an init step you would execute `etcd`. This however
|
||||
introduced problems: we now have to define a hand-off protocol between the etcd
|
||||
init process and the etcd binary itself. This is hard to coordinate in a single
|
||||
service file such as:
|
||||
|
||||
```
|
||||
ExecStartPre=/usr/bin/etcd init --cluster=....
|
||||
ExecStart=/usr/bin/etcd
|
||||
```
|
||||
|
||||
There are several error cases:
|
||||
|
||||
0) Init has already run and the data directory is already configured
|
||||
1) Discovery fails because of network timeout, etc
|
||||
2) Discovery fails because the cluster is already full and etcd needs to fall back to proxy
|
||||
3) Static cluster configuration fails because of conflict, misconfiguration or timeout
|
||||
|
||||
In hindsight we could have made this work by doing:
|
||||
|
||||
```
|
||||
rc status
|
||||
0 Init already ran
|
||||
1 Discovery fails on network timeout, etc
|
||||
0 Discovery fails for cluster full, coordinate via proxy state file
|
||||
1 Static cluster configuration failed
|
||||
```
|
||||
|
||||
Perhaps we can add the init command in a future version and deprecate if the UX
|
||||
continues to confuse people.
|
@ -29,5 +29,5 @@ curl http://10.0.0.10:2379/health
|
||||
```
|
||||
|
||||
```json
|
||||
{"health": "true"}
|
||||
{"health":true}
|
||||
```
|
||||
|
@ -154,5 +154,5 @@ If an error occurs, check the [add member troubleshooting doc][runtime-configura
|
||||
|
||||
[discovery-service]: clustering.md#discovery
|
||||
[goreman]: https://github.com/mattn/goreman
|
||||
[procfile]: https://github.com/coreos/etcd/blob/master/Procfile
|
||||
[procfile]: https://github.com/coreos/etcd/blob/master/Procfile.v2
|
||||
[runtime-configuration]: runtime-configuration.md#error-cases-when-adding-members
|
||||
|
@ -15,13 +15,13 @@ In etcd, every runtime reconfiguration has to go through [two phases][add-member
|
||||
|
||||
Phase 1 - Inform cluster of new configuration
|
||||
|
||||
To add a member into etcd cluster, you need to make an API call to request a new member to be added to the cluster. And this is only way that you can add a new member into an existing cluster. The API call returns when the cluster agrees on the configuration change.
|
||||
To add a member into etcd cluster, you need to make an API call to request a new member to be added to the cluster. And this is the only way that you can add a new member into an existing cluster. The API call returns when the cluster agrees on the configuration change.
|
||||
|
||||
Phase 2 - Start new member
|
||||
|
||||
To join the etcd member into the existing cluster, you need to specify the correct `initial-cluster` and set `initial-cluster-state` to `existing`. When the member starts, it will contact the existing cluster first and verify the current cluster configuration matches the expected one specified in `initial-cluster`. When the new member successfully starts, you know your cluster reached the expected configuration.
|
||||
To join the new etcd member into the existing cluster, you need to specify the correct `initial-cluster` and set `initial-cluster-state` to `existing`. When the member starts, it will contact the existing cluster first and verify the current cluster configuration matches the expected one specified in `initial-cluster`. When the new member successfully starts, you know your cluster reached the expected configuration.
|
||||
|
||||
By splitting the process into two discrete phases users are forced to be explicit regarding cluster membership changes. This actually gives users more flexibility and makes things easier to reason about. For example, if there is an attempt to add a new member with the same ID as an existing member in an etcd cluster, the action will fail immediately during phase one without impacting the running cluster. Similar protection is provided to prevent adding new members by mistake. If a new etcd member attempts to join the cluster before the cluster has accepted the configuration change,, it will not be accepted by the cluster.
|
||||
By splitting the process into two discrete phases users are forced to be explicit regarding cluster membership changes. This actually gives users more flexibility and makes things easier to reason about. For example, if there is an attempt to add a new member with the same ID as an existing member in an etcd cluster, the action will fail immediately during phase one without impacting the running cluster. Similar protection is provided to prevent adding new members by mistake. If a new etcd member attempts to join the cluster before the cluster has accepted the configuration change, it will not be accepted by the cluster.
|
||||
|
||||
Without the explicit workflow around cluster membership etcd would be vulnerable to unexpected cluster membership changes. For example, if etcd is running under an init system such as systemd, etcd would be restarted after being removed via the membership API, and attempt to rejoin the cluster on startup. This cycle would continue every time a member is removed via the API and systemd is set to restart etcd after failing, which is unexpected.
|
||||
|
||||
|
516
Makefile
516
Makefile
@ -1,516 +0,0 @@
|
||||
# run from repository root
|
||||
|
||||
|
||||
|
||||
# Example:
|
||||
# make build
|
||||
# make clean
|
||||
# make docker-clean
|
||||
# make docker-start
|
||||
# make docker-kill
|
||||
# make docker-remove
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
GO_BUILD_FLAGS="-v" ./build
|
||||
./bin/etcd --version
|
||||
ETCDCTL_API=3 ./bin/etcdctl version
|
||||
|
||||
clean:
|
||||
rm -f ./codecov
|
||||
rm -rf ./agent-*
|
||||
rm -rf ./covdir
|
||||
rm -f ./*.log
|
||||
rm -f ./bin/Dockerfile-release
|
||||
rm -rf ./bin/*.etcd
|
||||
rm -rf ./gopath
|
||||
rm -rf ./gopath.proto
|
||||
rm -rf ./release
|
||||
rm -f ./integration/127.0.0.1:* ./integration/localhost:*
|
||||
rm -f ./clientv3/integration/127.0.0.1:* ./clientv3/integration/localhost:*
|
||||
rm -f ./clientv3/ordering/127.0.0.1:* ./clientv3/ordering/localhost:*
|
||||
|
||||
docker-clean:
|
||||
docker images
|
||||
docker image prune --force
|
||||
|
||||
docker-start:
|
||||
service docker restart
|
||||
|
||||
docker-kill:
|
||||
docker kill `docker ps -q` || true
|
||||
|
||||
docker-remove:
|
||||
docker rm --force `docker ps -a -q` || true
|
||||
docker rmi --force `docker images -q` || true
|
||||
|
||||
|
||||
|
||||
GO_VERSION ?= 1.10.1
|
||||
ETCD_VERSION ?= $(shell git rev-parse --short HEAD || echo "GitNotFound")
|
||||
|
||||
TEST_SUFFIX = $(shell date +%s | base64 | head -c 15)
|
||||
TEST_OPTS ?= PASSES='unit'
|
||||
|
||||
TMP_DIR_MOUNT_FLAG = --mount type=tmpfs,destination=/tmp
|
||||
ifdef HOST_TMP_DIR
|
||||
TMP_DIR_MOUNT_FLAG = --mount type=bind,source=$(HOST_TMP_DIR),destination=/tmp
|
||||
endif
|
||||
|
||||
|
||||
|
||||
# Example:
|
||||
# GO_VERSION=1.8.7 make build-docker-test
|
||||
# GO_VERSION=1.9.5 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.8.7 make push-docker-test
|
||||
# GO_VERSION=1.9.5 make push-docker-test
|
||||
# make push-docker-test
|
||||
#
|
||||
# gsutil -m acl ch -u allUsers:R -r gs://artifacts.etcd-development.appspot.com
|
||||
# GO_VERSION=1.9.5 make pull-docker-test
|
||||
# make pull-docker-test
|
||||
|
||||
build-docker-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
@sed -i.bak 's|REPLACE_ME_GO_VERSION|$(GO_VERSION)|g' ./tests/Dockerfile
|
||||
docker build \
|
||||
--tag gcr.io/etcd-development/etcd-test:go$(GO_VERSION) \
|
||||
--file ./tests/Dockerfile .
|
||||
@mv ./tests/Dockerfile.bak ./tests/Dockerfile
|
||||
|
||||
push-docker-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
gcloud docker -- push gcr.io/etcd-development/etcd-test:go$(GO_VERSION)
|
||||
|
||||
pull-docker-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
docker pull gcr.io/etcd-development/etcd-test:go$(GO_VERSION)
|
||||
|
||||
|
||||
|
||||
# Example:
|
||||
# make build-docker-test
|
||||
# make compile-with-docker-test
|
||||
# make compile-setup-gopath-with-docker-test
|
||||
|
||||
compile-with-docker-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
docker run \
|
||||
--rm \
|
||||
--mount type=bind,source=`pwd`,destination=/go/src/github.com/coreos/etcd \
|
||||
gcr.io/etcd-development/etcd-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "GO_BUILD_FLAGS=-v ./build && ./bin/etcd --version"
|
||||
|
||||
compile-setup-gopath-with-docker-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
docker run \
|
||||
--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"
|
||||
|
||||
|
||||
|
||||
# Example:
|
||||
#
|
||||
# Local machine:
|
||||
# TEST_OPTS="PASSES='fmt'" make test
|
||||
# TEST_OPTS="PASSES='fmt bom dep build unit'" make test
|
||||
# TEST_OPTS="PASSES='build unit release integration_e2e functional'" make test
|
||||
# TEST_OPTS="PASSES='build grpcproxy'" make test
|
||||
#
|
||||
# Example (test with docker):
|
||||
# make pull-docker-test
|
||||
# TEST_OPTS="PASSES='fmt'" make docker-test
|
||||
# TEST_OPTS="VERBOSE=2 PASSES='unit'" make docker-test
|
||||
#
|
||||
# Travis CI (test with docker):
|
||||
# TEST_OPTS="PASSES='fmt bom dep build unit'" make docker-test
|
||||
#
|
||||
# Semaphore CI (test with docker):
|
||||
# TEST_OPTS="PASSES='build unit release integration_e2e functional'" make docker-test
|
||||
# HOST_TMP_DIR=/tmp TEST_OPTS="PASSES='build unit release integration_e2e functional'" make docker-test
|
||||
# TEST_OPTS="GOARCH=386 PASSES='build unit integration_e2e'" make docker-test
|
||||
#
|
||||
# grpc-proxy tests (test with docker):
|
||||
# TEST_OPTS="PASSES='build grpcproxy'" make docker-test
|
||||
# HOST_TMP_DIR=/tmp TEST_OPTS="PASSES='build grpcproxy'" make docker-test
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
$(info TEST_OPTS: $(TEST_OPTS))
|
||||
$(info log-file: test-$(TEST_SUFFIX).log)
|
||||
$(TEST_OPTS) ./test 2>&1 | tee test-$(TEST_SUFFIX).log
|
||||
! egrep "(--- FAIL:|panic: test timed out|appears to have leaked)" -B50 -A10 test-$(TEST_SUFFIX).log
|
||||
|
||||
docker-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info ETCD_VERSION: $(ETCD_VERSION))
|
||||
$(info TEST_OPTS: $(TEST_OPTS))
|
||||
$(info log-file: test-$(TEST_SUFFIX).log)
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`,destination=/go/src/github.com/coreos/etcd \
|
||||
gcr.io/etcd-development/etcd-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "$(TEST_OPTS) ./test 2>&1 | tee test-$(TEST_SUFFIX).log"
|
||||
! egrep "(--- FAIL:|panic: test timed out|appears to have leaked)" -B50 -A10 test-$(TEST_SUFFIX).log
|
||||
|
||||
docker-test-coverage:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info ETCD_VERSION: $(ETCD_VERSION))
|
||||
$(info log-file: docker-test-coverage-$(TEST_SUFFIX).log)
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`,destination=/go/src/github.com/coreos/etcd \
|
||||
gcr.io/etcd-development/etcd-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "COVERDIR=covdir PASSES='build build_cov cov' ./test 2>&1 | tee docker-test-coverage-$(TEST_SUFFIX).log && /codecov -t 6040de41-c073-4d6f-bbf8-d89256ef31e1"
|
||||
! egrep "(--- FAIL:|panic: test timed out|appears to have leaked)" -B50 -A10 docker-test-coverage-$(TEST_SUFFIX).log
|
||||
|
||||
|
||||
|
||||
# Example:
|
||||
# make compile-with-docker-test
|
||||
# ETCD_VERSION=v3-test make build-docker-release-master
|
||||
# ETCD_VERSION=v3-test make push-docker-release-master
|
||||
# gsutil -m acl ch -u allUsers:R -r gs://artifacts.etcd-development.appspot.com
|
||||
|
||||
build-docker-release-master:
|
||||
$(info ETCD_VERSION: $(ETCD_VERSION))
|
||||
cp ./Dockerfile-release ./bin/Dockerfile-release
|
||||
docker build \
|
||||
--tag gcr.io/etcd-development/etcd:$(ETCD_VERSION) \
|
||||
--file ./bin/Dockerfile-release \
|
||||
./bin
|
||||
rm -f ./bin/Dockerfile-release
|
||||
|
||||
docker run \
|
||||
--rm \
|
||||
gcr.io/etcd-development/etcd:$(ETCD_VERSION) \
|
||||
/bin/sh -c "/usr/local/bin/etcd --version && ETCDCTL_API=3 /usr/local/bin/etcdctl version"
|
||||
|
||||
push-docker-release-master:
|
||||
$(info ETCD_VERSION: $(ETCD_VERSION))
|
||||
gcloud docker -- push gcr.io/etcd-development/etcd:$(ETCD_VERSION)
|
||||
|
||||
|
||||
|
||||
# Example:
|
||||
# make build-docker-test
|
||||
# make compile-with-docker-test
|
||||
# make build-docker-static-ip-test
|
||||
#
|
||||
# gcloud docker -- login -u _json_key -p "$(cat /etc/gcp-key-etcd-development.json)" https://gcr.io
|
||||
# make push-docker-static-ip-test
|
||||
#
|
||||
# gsutil -m acl ch -u allUsers:R -r gs://artifacts.etcd-development.appspot.com
|
||||
# make pull-docker-static-ip-test
|
||||
#
|
||||
# make docker-static-ip-test-certs-run
|
||||
# make docker-static-ip-test-certs-metrics-proxy-run
|
||||
|
||||
build-docker-static-ip-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
@sed -i.bak 's|REPLACE_ME_GO_VERSION|$(GO_VERSION)|g' ./tests/docker-static-ip/Dockerfile
|
||||
docker build \
|
||||
--tag gcr.io/etcd-development/etcd-static-ip-test:go$(GO_VERSION) \
|
||||
--file ./tests/docker-static-ip/Dockerfile \
|
||||
./tests/docker-static-ip
|
||||
@mv ./tests/docker-static-ip/Dockerfile.bak ./tests/docker-static-ip/Dockerfile
|
||||
|
||||
push-docker-static-ip-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
gcloud docker -- push gcr.io/etcd-development/etcd-static-ip-test:go$(GO_VERSION)
|
||||
|
||||
pull-docker-static-ip-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
docker pull gcr.io/etcd-development/etcd-static-ip-test:go$(GO_VERSION)
|
||||
|
||||
docker-static-ip-test-certs-run:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`/bin,destination=/etcd \
|
||||
--mount type=bind,source=`pwd`/tests/docker-static-ip/certs,destination=/certs \
|
||||
gcr.io/etcd-development/etcd-static-ip-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && /certs/run.sh && rm -rf m*.etcd"
|
||||
|
||||
docker-static-ip-test-certs-metrics-proxy-run:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`/bin,destination=/etcd \
|
||||
--mount type=bind,source=`pwd`/tests/docker-static-ip/certs-metrics-proxy,destination=/certs-metrics-proxy \
|
||||
gcr.io/etcd-development/etcd-static-ip-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && /certs-metrics-proxy/run.sh && rm -rf m*.etcd"
|
||||
|
||||
|
||||
|
||||
# Example:
|
||||
# make build-docker-test
|
||||
# make compile-with-docker-test
|
||||
# make build-docker-dns-test
|
||||
#
|
||||
# gcloud docker -- login -u _json_key -p "$(cat /etc/gcp-key-etcd-development.json)" https://gcr.io
|
||||
# make push-docker-dns-test
|
||||
#
|
||||
# gsutil -m acl ch -u allUsers:R -r gs://artifacts.etcd-development.appspot.com
|
||||
# make pull-docker-dns-test
|
||||
#
|
||||
# make docker-dns-test-insecure-run
|
||||
# make docker-dns-test-certs-run
|
||||
# make docker-dns-test-certs-gateway-run
|
||||
# make docker-dns-test-certs-wildcard-run
|
||||
# make docker-dns-test-certs-common-name-auth-run
|
||||
# make docker-dns-test-certs-common-name-multi-run
|
||||
|
||||
build-docker-dns-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
@sed -i.bak 's|REPLACE_ME_GO_VERSION|$(GO_VERSION)|g' ./tests/docker-dns/Dockerfile
|
||||
docker build \
|
||||
--tag gcr.io/etcd-development/etcd-dns-test:go$(GO_VERSION) \
|
||||
--file ./tests/docker-dns/Dockerfile \
|
||||
./tests/docker-dns
|
||||
@mv ./tests/docker-dns/Dockerfile.bak ./tests/docker-dns/Dockerfile
|
||||
|
||||
docker run \
|
||||
--rm \
|
||||
--dns 127.0.0.1 \
|
||||
gcr.io/etcd-development/etcd-dns-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "/etc/init.d/bind9 start && cat /dev/null >/etc/hosts && dig etcd.local"
|
||||
|
||||
push-docker-dns-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
gcloud docker -- push gcr.io/etcd-development/etcd-dns-test:go$(GO_VERSION)
|
||||
|
||||
pull-docker-dns-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
docker pull gcr.io/etcd-development/etcd-dns-test:go$(GO_VERSION)
|
||||
|
||||
docker-dns-test-insecure-run:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
--dns 127.0.0.1 \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`/bin,destination=/etcd \
|
||||
--mount type=bind,source=`pwd`/tests/docker-dns/insecure,destination=/insecure \
|
||||
gcr.io/etcd-development/etcd-dns-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && /insecure/run.sh && rm -rf m*.etcd"
|
||||
|
||||
docker-dns-test-certs-run:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
--dns 127.0.0.1 \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`/bin,destination=/etcd \
|
||||
--mount type=bind,source=`pwd`/tests/docker-dns/certs,destination=/certs \
|
||||
gcr.io/etcd-development/etcd-dns-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && /certs/run.sh && rm -rf m*.etcd"
|
||||
|
||||
docker-dns-test-certs-gateway-run:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
--dns 127.0.0.1 \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`/bin,destination=/etcd \
|
||||
--mount type=bind,source=`pwd`/tests/docker-dns/certs-gateway,destination=/certs-gateway \
|
||||
gcr.io/etcd-development/etcd-dns-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && /certs-gateway/run.sh && rm -rf m*.etcd"
|
||||
|
||||
docker-dns-test-certs-wildcard-run:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
--dns 127.0.0.1 \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`/bin,destination=/etcd \
|
||||
--mount type=bind,source=`pwd`/tests/docker-dns/certs-wildcard,destination=/certs-wildcard \
|
||||
gcr.io/etcd-development/etcd-dns-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && /certs-wildcard/run.sh && rm -rf m*.etcd"
|
||||
|
||||
docker-dns-test-certs-common-name-auth-run:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
--dns 127.0.0.1 \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`/bin,destination=/etcd \
|
||||
--mount type=bind,source=`pwd`/tests/docker-dns/certs-common-name-auth,destination=/certs-common-name-auth \
|
||||
gcr.io/etcd-development/etcd-dns-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && /certs-common-name-auth/run.sh && rm -rf m*.etcd"
|
||||
|
||||
docker-dns-test-certs-common-name-multi-run:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
--dns 127.0.0.1 \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`/bin,destination=/etcd \
|
||||
--mount type=bind,source=`pwd`/tests/docker-dns/certs-common-name-multi,destination=/certs-common-name-multi \
|
||||
gcr.io/etcd-development/etcd-dns-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && /certs-common-name-multi/run.sh && rm -rf m*.etcd"
|
||||
|
||||
|
||||
|
||||
# Example:
|
||||
# make build-docker-test
|
||||
# make compile-with-docker-test
|
||||
# make build-docker-dns-srv-test
|
||||
# gcloud docker -- login -u _json_key -p "$(cat /etc/gcp-key-etcd-development.json)" https://gcr.io
|
||||
# make push-docker-dns-srv-test
|
||||
# gsutil -m acl ch -u allUsers:R -r gs://artifacts.etcd-development.appspot.com
|
||||
# make pull-docker-dns-srv-test
|
||||
# make docker-dns-srv-test-certs-run
|
||||
# make docker-dns-srv-test-certs-gateway-run
|
||||
# make docker-dns-srv-test-certs-wildcard-run
|
||||
|
||||
build-docker-dns-srv-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
@sed -i.bak 's|REPLACE_ME_GO_VERSION|$(GO_VERSION)|g' ./tests/docker-dns-srv/Dockerfile
|
||||
docker build \
|
||||
--tag gcr.io/etcd-development/etcd-dns-srv-test:go$(GO_VERSION) \
|
||||
--file ./tests/docker-dns-srv/Dockerfile \
|
||||
./tests/docker-dns-srv
|
||||
@mv ./tests/docker-dns-srv/Dockerfile.bak ./tests/docker-dns-srv/Dockerfile
|
||||
|
||||
docker run \
|
||||
--rm \
|
||||
--dns 127.0.0.1 \
|
||||
gcr.io/etcd-development/etcd-dns-srv-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "/etc/init.d/bind9 start && cat /dev/null >/etc/hosts && dig +noall +answer SRV _etcd-client-ssl._tcp.etcd.local && dig +noall +answer SRV _etcd-server-ssl._tcp.etcd.local && dig +noall +answer m1.etcd.local m2.etcd.local m3.etcd.local"
|
||||
|
||||
push-docker-dns-srv-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
gcloud docker -- push gcr.io/etcd-development/etcd-dns-srv-test:go$(GO_VERSION)
|
||||
|
||||
pull-docker-dns-srv-test:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
docker pull gcr.io/etcd-development/etcd-dns-srv-test:go$(GO_VERSION)
|
||||
|
||||
docker-dns-srv-test-certs-run:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
--dns 127.0.0.1 \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`/bin,destination=/etcd \
|
||||
--mount type=bind,source=`pwd`/tests/docker-dns-srv/certs,destination=/certs \
|
||||
gcr.io/etcd-development/etcd-dns-srv-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && /certs/run.sh && rm -rf m*.etcd"
|
||||
|
||||
docker-dns-srv-test-certs-gateway-run:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
--dns 127.0.0.1 \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`/bin,destination=/etcd \
|
||||
--mount type=bind,source=`pwd`/tests/docker-dns-srv/certs-gateway,destination=/certs-gateway \
|
||||
gcr.io/etcd-development/etcd-dns-srv-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && /certs-gateway/run.sh && rm -rf m*.etcd"
|
||||
|
||||
docker-dns-srv-test-certs-wildcard-run:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info HOST_TMP_DIR: $(HOST_TMP_DIR))
|
||||
$(info TMP_DIR_MOUNT_FLAG: $(TMP_DIR_MOUNT_FLAG))
|
||||
docker run \
|
||||
--rm \
|
||||
--tty \
|
||||
--dns 127.0.0.1 \
|
||||
$(TMP_DIR_MOUNT_FLAG) \
|
||||
--mount type=bind,source=`pwd`/bin,destination=/etcd \
|
||||
--mount type=bind,source=`pwd`/tests/docker-dns-srv/certs-wildcard,destination=/certs-wildcard \
|
||||
gcr.io/etcd-development/etcd-dns-srv-test:go$(GO_VERSION) \
|
||||
/bin/bash -c "cd /etcd && /certs-wildcard/run.sh && rm -rf m*.etcd"
|
||||
|
||||
|
||||
|
||||
# Example:
|
||||
# make build-functional
|
||||
# make build-docker-functional
|
||||
# make push-docker-functional
|
||||
# make pull-docker-functional
|
||||
|
||||
build-functional:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info ETCD_VERSION: $(ETCD_VERSION))
|
||||
./functional/build
|
||||
./bin/etcd-agent -help || true && \
|
||||
./bin/etcd-proxy -help || true && \
|
||||
./bin/etcd-runner --help || true && \
|
||||
./bin/etcd-tester -help || true
|
||||
|
||||
build-docker-functional:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info ETCD_VERSION: $(ETCD_VERSION))
|
||||
@sed -i.bak 's|REPLACE_ME_GO_VERSION|$(GO_VERSION)|g' ./functional/Dockerfile
|
||||
docker build \
|
||||
--tag gcr.io/etcd-development/etcd-functional:go$(GO_VERSION) \
|
||||
--file ./functional/Dockerfile \
|
||||
.
|
||||
@mv ./functional/Dockerfile.bak ./functional/Dockerfile
|
||||
|
||||
docker run \
|
||||
--rm \
|
||||
gcr.io/etcd-development/etcd-functional:go$(GO_VERSION) \
|
||||
/bin/bash -c "./bin/etcd --version && \
|
||||
./bin/etcd-failpoints --version && \
|
||||
ETCDCTL_API=3 ./bin/etcdctl version && \
|
||||
./bin/etcd-agent -help || true && \
|
||||
./bin/etcd-proxy -help || true && \
|
||||
./bin/etcd-runner --help || true && \
|
||||
./bin/etcd-tester -help || true && \
|
||||
./bin/benchmark --help || true"
|
||||
|
||||
push-docker-functional:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info ETCD_VERSION: $(ETCD_VERSION))
|
||||
gcloud docker -- push gcr.io/etcd-development/etcd-functional:go$(GO_VERSION)
|
||||
|
||||
pull-docker-functional:
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
$(info ETCD_VERSION: $(ETCD_VERSION))
|
||||
docker pull gcr.io/etcd-development/etcd-functional:go$(GO_VERSION)
|
106
NEWS
106
NEWS
@ -1,106 +0,0 @@
|
||||
etcd v3.1.6 (2017-04-19)
|
||||
- remove auth check in Status API
|
||||
- fill in Auth API response header
|
||||
|
||||
etcd v3.1.5 (2017-03-27)
|
||||
- add '/etc/nsswitch.conf' file to alpine-based Docker image
|
||||
- fix raft memory leak issue
|
||||
- fix Windows file path issues
|
||||
|
||||
etcd v3.1.4 (2017-03-22)
|
||||
|
||||
etcd v3.1.3 (2017-03-10)
|
||||
- use machine default host when advertise URLs are default
|
||||
values(localhost:2379,2380) AND if listen URL is 0.0.0.0
|
||||
- fix 'etcd gateway' schema handling in DNS discovery
|
||||
- fix sd_notify behaviors in gateway, grpc-proxy
|
||||
|
||||
etcd v3.1.2 (2017-02-24)
|
||||
- use IPv4 default host, by default (when IPv4 and IPv6 are available)
|
||||
- fix 'etcd gateway' with multiple endpoints
|
||||
|
||||
etcd v3.1.1 (2017-02-17)
|
||||
|
||||
etcd v2.3.8 (2017-02-17)
|
||||
|
||||
etcd v3.1.0 (2017-01-20)
|
||||
- faster linearizable reads (implements Raft read-index)
|
||||
- automatic leadership transfer when leader steps down
|
||||
- etcd uses default route IP if advertise URL is not given
|
||||
- cluster rejects removing members if quorum will be lost
|
||||
- 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 in 3.2
|
||||
- discovery now has upper limit for waiting on retries
|
||||
- etcd flags
|
||||
- --strict-reconfig-check flag is set by default
|
||||
- add --log-output flag
|
||||
- add --metrics flag
|
||||
- v3 authentication API is now stable
|
||||
- v3 client
|
||||
- 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
|
||||
- v3 etcdctl
|
||||
- add lease timetolive command
|
||||
- add --print-value-only flag to get command
|
||||
- add --dest-prefix flag to make-mirror command
|
||||
- command get responses are sorted in ascending order by default
|
||||
- recipes now conform to sessions defined in clientv3/concurrency
|
||||
- ACI has symlinks to /usr/local/bin/etcd*
|
||||
- warn on binding listeners through domain names; to be deprecated in 3.2
|
||||
- experimental gRPC proxy feature
|
||||
|
||||
etcd v3.0.16 (2017-01-13)
|
||||
|
||||
etcd v3.0.15 (2016-11-11)
|
||||
- fix cancel watch request with wrong range end
|
||||
|
||||
etcd v3.0.14 (2016-11-04)
|
||||
- v3 etcdctl migrate command now supports --no-ttl flag to discard keys on transform
|
||||
|
||||
etcd v3.0.13 (2016-10-24)
|
||||
|
||||
etcd v3.0.12 (2016-10-07)
|
||||
|
||||
etcd v3.0.11 (2016-10-07)
|
||||
- server returns previous key-value (optional)
|
||||
- clientv3 WithPrevKV option
|
||||
- v3 etcdctl put,watch,del --prev-kv flag
|
||||
|
||||
etcd v3.0.10 (2016-09-23)
|
||||
|
||||
etcd v3.0.9 (2016-09-15)
|
||||
- warn on domain names on listen URLs (v3.2 will reject domain names)
|
||||
|
||||
etcd v3.0.8 (2016-09-09)
|
||||
- allow only IP addresses in listen URLs (domain names are rejected)
|
||||
|
||||
etcd v3.0.7 (2016-08-31)
|
||||
- SRV records only allow A records (RFC 2052)
|
||||
|
||||
etcd v3.0.6 (2016-08-19)
|
||||
|
||||
etcd v3.0.5 (2016-08-19)
|
||||
- SRV records (e.g., infra1.example.com) must match the discovery domain
|
||||
(i.e., example.com) if no custom certificate authority is given
|
||||
|
||||
etcd v3.0.4 (2016-07-27)
|
||||
- v2 auth can now use common name from TLS certificate when --client-cert-auth is enabled
|
||||
- v2 etcdctl ls command now supports --output=json
|
||||
- Add /var/lib/etcd directory to etcd official Docker image
|
||||
|
||||
etcd v3.0.3 (2016-07-15)
|
||||
- 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
|
||||
|
||||
etcd v3.0.2 (2016-07-08)
|
||||
- Dockerfile uses ENTRYPOINT, instead of CMD, to run etcd without binary path specified
|
||||
|
||||
etcd v3.0.1 (2016-07-01)
|
||||
|
||||
etcd v3.0.0 (2016-06-30)
|
3
Procfile
3
Procfile
@ -2,5 +2,4 @@
|
||||
etcd1: bin/etcd --name infra1 --listen-client-urls http://127.0.0.1:2379 --advertise-client-urls http://127.0.0.1:2379 --listen-peer-urls http://127.0.0.1:12380 --initial-advertise-peer-urls http://127.0.0.1:12380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof
|
||||
etcd2: bin/etcd --name infra2 --listen-client-urls http://127.0.0.1:22379 --advertise-client-urls http://127.0.0.1:22379 --listen-peer-urls http://127.0.0.1:22380 --initial-advertise-peer-urls http://127.0.0.1:22380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof
|
||||
etcd3: bin/etcd --name infra3 --listen-client-urls http://127.0.0.1:32379 --advertise-client-urls http://127.0.0.1:32379 --listen-peer-urls http://127.0.0.1:32380 --initial-advertise-peer-urls http://127.0.0.1:32380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof
|
||||
# in future, use proxy to listen on 2379
|
||||
#proxy: bin/etcd --name infra-proxy1 --proxy=on --listen-client-urls http://127.0.0.1:2378 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --enable-pprof
|
||||
#proxy: bin/etcd grpc-proxy start --endpoints=127.0.0.1:2379,127.0.0.1:22379,127.0.0.1:32379 --listen-addr=127.0.0.1:23790 --advertise-client-url=127.0.0.1:23790 --enable-pprof
|
||||
|
6
Procfile.v2
Normal file
6
Procfile.v2
Normal file
@ -0,0 +1,6 @@
|
||||
# Use goreman to run `go get github.com/mattn/goreman`
|
||||
etcd1: bin/etcd --name infra1 --listen-client-urls http://127.0.0.1:2379 --advertise-client-urls http://127.0.0.1:2379 --listen-peer-urls http://127.0.0.1:12380 --initial-advertise-peer-urls http://127.0.0.1:12380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof
|
||||
etcd2: bin/etcd --name infra2 --listen-client-urls http://127.0.0.1:22379 --advertise-client-urls http://127.0.0.1:22379 --listen-peer-urls http://127.0.0.1:22380 --initial-advertise-peer-urls http://127.0.0.1:22380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof
|
||||
etcd3: bin/etcd --name infra3 --listen-client-urls http://127.0.0.1:32379 --advertise-client-urls http://127.0.0.1:32379 --listen-peer-urls http://127.0.0.1:32380 --initial-advertise-peer-urls http://127.0.0.1:32380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof
|
||||
# in future, use proxy to listen on 2379
|
||||
#proxy: bin/etcd --name infra-proxy1 --proxy=on --listen-client-urls http://127.0.0.1:2378 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --enable-pprof
|
15
README.md
15
README.md
@ -1,9 +1,12 @@
|
||||
# etcd
|
||||
|
||||
[](https://goreportcard.com/report/github.com/coreos/etcd)
|
||||
[](https://travis-ci.org/coreos/etcd)
|
||||
[](https://semaphoreci.com/coreos/etcd)
|
||||
[](https://quay.io/repository/coreos/etcd-git)
|
||||
[](https://goreportcard.com/report/github.com/coreos/etcd)
|
||||
[](https://codecov.io/gh/coreos/etcd)
|
||||
[](https://travis-ci.org/coreos/etcd)
|
||||
[](https://semaphoreci.com/coreos/etcd)
|
||||
[](https://godoc.org/github.com/coreos/etcd)
|
||||
[](https://github.com/coreos/etcd/releases)
|
||||
[](https://github.com/coreos/etcd/blob/master/LICENSE)
|
||||
|
||||
**Note**: The `master` branch may be in an *unstable or even broken state* during development. Please use [releases][github-release] instead of the `master` branch in order to get stable binaries.
|
||||
|
||||
@ -39,7 +42,7 @@ See [etcdctl][etcdctl] for a simple command line client.
|
||||
|
||||
The easiest way to get etcd is to use one of the pre-built release binaries which are available for OSX, Linux, Windows, [rkt][rkt], and Docker. Instructions for using these binaries are on the [GitHub releases page][github-release].
|
||||
|
||||
For those wanting to try the very latest version, [build the latest version of etcd][dl-build] from the `master` branch. This first needs [*Go*](https://golang.org/) installed (version 1.8+ is required). All development occurs on `master`, including new features and bug fixes. Bug fixes are first targeted at `master` and subsequently ported to release branches, as described in the [branch management][branch-management] guide.
|
||||
For those wanting to try the very latest version, [build the latest version of etcd][dl-build] from the `master` branch. This first needs [*Go*](https://golang.org/) installed (version 1.9+ is required). All development occurs on `master`, including new features and bug fixes. Bug fixes are first targeted at `master` and subsequently ported to release branches, as described in the [branch management][branch-management] guide.
|
||||
|
||||
[rkt]: https://github.com/rkt/rkt/releases/
|
||||
[github-release]: https://github.com/coreos/etcd/releases/
|
||||
@ -87,7 +90,7 @@ Our [Procfile script](./Procfile) will set up a local example cluster. Start it
|
||||
goreman start
|
||||
```
|
||||
|
||||
This will bring up 3 etcd members `infra1`, `infra2` and `infra3` and etcd proxy `proxy`, which runs locally and composes a cluster.
|
||||
This will bring up 3 etcd members `infra1`, `infra2` and `infra3` and etcd `grpc-proxy`, which runs locally and composes a cluster.
|
||||
|
||||
Every cluster member and proxy accepts key value reads and key value writes.
|
||||
|
||||
|
@ -15,11 +15,11 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rsa"
|
||||
"io/ioutil"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type tokenJWT struct {
|
||||
@ -97,7 +97,9 @@ func prepareOpts(opts map[string]string) (jwtSignMethod, jwtPubKeyPath, jwtPrivK
|
||||
return "", "", "", ErrInvalidAuthOpts
|
||||
}
|
||||
}
|
||||
|
||||
if len(jwtSignMethod) == 0 {
|
||||
return "", "", "", ErrInvalidAuthOpts
|
||||
}
|
||||
return jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath, nil
|
||||
}
|
||||
|
||||
|
94
auth/jwt_test.go
Normal file
94
auth/jwt_test.go
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright 2017 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 (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
jwtPubKey = "../integration/fixtures/server.crt"
|
||||
jwtPrivKey = "../integration/fixtures/server.key.insecure"
|
||||
)
|
||||
|
||||
func TestJWTInfo(t *testing.T) {
|
||||
opts := map[string]string{
|
||||
"pub-key": jwtPubKey,
|
||||
"priv-key": jwtPrivKey,
|
||||
"sign-method": "RS256",
|
||||
}
|
||||
jwt, err := newTokenProviderJWT(opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
token, aerr := jwt.assign(context.TODO(), "abc", 123)
|
||||
if aerr != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ai, ok := jwt.info(context.TODO(), token, 123)
|
||||
if !ok {
|
||||
t.Fatalf("failed to authenticate with token %s", token)
|
||||
}
|
||||
if ai.Revision != 123 {
|
||||
t.Fatalf("expected revision 123, got %d", ai.Revision)
|
||||
}
|
||||
ai, ok = jwt.info(context.TODO(), "aaa", 120)
|
||||
if ok || ai != nil {
|
||||
t.Fatalf("expected aaa to fail to authenticate, got %+v", ai)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJWTBad(t *testing.T) {
|
||||
opts := map[string]string{
|
||||
"pub-key": jwtPubKey,
|
||||
"priv-key": jwtPrivKey,
|
||||
"sign-method": "RS256",
|
||||
}
|
||||
// private key instead of public key
|
||||
opts["pub-key"] = jwtPrivKey
|
||||
if _, err := newTokenProviderJWT(opts); err == nil {
|
||||
t.Fatalf("expected failure on missing public key")
|
||||
}
|
||||
opts["pub-key"] = jwtPubKey
|
||||
|
||||
// public key instead of private key
|
||||
opts["priv-key"] = jwtPubKey
|
||||
if _, err := newTokenProviderJWT(opts); err == nil {
|
||||
t.Fatalf("expected failure on missing public key")
|
||||
}
|
||||
opts["priv-key"] = jwtPrivKey
|
||||
|
||||
// missing signing option
|
||||
delete(opts, "sign-method")
|
||||
if _, err := newTokenProviderJWT(opts); err == nil {
|
||||
t.Fatal("expected error on missing option")
|
||||
}
|
||||
opts["sign-method"] = "RS256"
|
||||
|
||||
// bad file for pubkey
|
||||
opts["pub-key"] = "whatever"
|
||||
if _, err := newTokenProviderJWT(opts); err == nil {
|
||||
t.Fatalf("expected failure on missing public key")
|
||||
}
|
||||
opts["pub-key"] = jwtPubKey
|
||||
|
||||
// bad file for private key
|
||||
opts["priv-key"] = "whatever"
|
||||
if _, err := newTokenProviderJWT(opts); err == nil {
|
||||
t.Fatalf("expeceted failure on missing private key")
|
||||
}
|
||||
opts["priv-key"] = jwtPrivKey
|
||||
}
|
@ -18,6 +18,7 @@ package auth
|
||||
// JWT based mechanism will be added in the near future.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
@ -25,8 +26,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -189,9 +188,9 @@ func (t *tokenSimple) info(ctx context.Context, token string, revision uint64) (
|
||||
|
||||
func (t *tokenSimple) assign(ctx context.Context, username string, rev uint64) (string, error) {
|
||||
// rev isn't used in simple token, it is only used in JWT
|
||||
index := ctx.Value("index").(uint64)
|
||||
simpleToken := ctx.Value("simpleToken").(string)
|
||||
token := fmt.Sprintf("%s.%d", simpleToken, index)
|
||||
index := ctx.Value(AuthenticateParamIndex{}).(uint64)
|
||||
simpleTokenPrefix := ctx.Value(AuthenticateParamSimpleTokenPrefix{}).(string)
|
||||
token := fmt.Sprintf("%s.%d", simpleTokenPrefix, index)
|
||||
t.assignSimpleTokenToUser(username, token)
|
||||
|
||||
return token, nil
|
||||
|
@ -29,7 +29,7 @@ func TestSimpleTokenDisabled(t *testing.T) {
|
||||
explicitlyDisabled.disable()
|
||||
|
||||
for _, tp := range []*tokenSimple{initialState, explicitlyDisabled} {
|
||||
ctx := context.WithValue(context.WithValue(context.TODO(), "index", uint64(1)), "simpleToken", "dummy")
|
||||
ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
|
||||
token, err := tp.assign(ctx, "user1", 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -48,7 +48,7 @@ func TestSimpleTokenDisabled(t *testing.T) {
|
||||
func TestSimpleTokenAssign(t *testing.T) {
|
||||
tp := newTokenProviderSimple(dummyIndexWaiter)
|
||||
tp.enable()
|
||||
ctx := context.WithValue(context.WithValue(context.TODO(), "index", uint64(1)), "simpleToken", "dummy")
|
||||
ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
|
||||
token, err := tp.assign(ctx, "user1", 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
145
auth/store.go
145
auth/store.go
@ -16,6 +16,7 @@ package auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"sort"
|
||||
@ -26,9 +27,9 @@ import (
|
||||
"github.com/coreos/etcd/auth/authpb"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
"github.com/coreos/etcd/mvcc/backend"
|
||||
|
||||
"github.com/coreos/pkg/capnslog"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
@ -80,6 +81,12 @@ type AuthInfo struct {
|
||||
Revision uint64
|
||||
}
|
||||
|
||||
// AuthenticateParamIndex is used for a key of context in the parameters of Authenticate()
|
||||
type AuthenticateParamIndex struct{}
|
||||
|
||||
// AuthenticateParamSimpleTokenPrefix is used for a key of context in the parameters of Authenticate()
|
||||
type AuthenticateParamSimpleTokenPrefix struct{}
|
||||
|
||||
type AuthStore interface {
|
||||
// AuthEnable turns on the authentication feature
|
||||
AuthEnable() error
|
||||
@ -162,6 +169,12 @@ type AuthStore interface {
|
||||
|
||||
// AuthInfoFromTLS gets AuthInfo from TLS info of gRPC's context
|
||||
AuthInfoFromTLS(ctx context.Context) *AuthInfo
|
||||
|
||||
// WithRoot generates and installs a token that can be used as a root credential
|
||||
WithRoot(ctx context.Context) context.Context
|
||||
|
||||
// HasRole checks that user has role
|
||||
HasRole(user, role string) bool
|
||||
}
|
||||
|
||||
type TokenProvider interface {
|
||||
@ -460,14 +473,14 @@ func (as *authStore) UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUser
|
||||
func (as *authStore) UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) {
|
||||
tx := as.be.BatchTx()
|
||||
tx.Lock()
|
||||
defer tx.Unlock()
|
||||
|
||||
var resp pb.AuthUserGetResponse
|
||||
|
||||
user := getUser(tx, r.Name)
|
||||
tx.Unlock()
|
||||
|
||||
if user == nil {
|
||||
return nil, ErrUserNotFound
|
||||
}
|
||||
|
||||
var resp pb.AuthUserGetResponse
|
||||
resp.Roles = append(resp.Roles, user.Roles...)
|
||||
return &resp, nil
|
||||
}
|
||||
@ -475,17 +488,14 @@ func (as *authStore) UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse,
|
||||
func (as *authStore) UserList(r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error) {
|
||||
tx := as.be.BatchTx()
|
||||
tx.Lock()
|
||||
defer tx.Unlock()
|
||||
|
||||
var resp pb.AuthUserListResponse
|
||||
|
||||
users := getAllUsers(tx)
|
||||
tx.Unlock()
|
||||
|
||||
for _, u := range users {
|
||||
resp.Users = append(resp.Users, string(u.Name))
|
||||
resp := &pb.AuthUserListResponse{Users: make([]string, len(users))}
|
||||
for i := range users {
|
||||
resp.Users[i] = string(users[i].Name)
|
||||
}
|
||||
|
||||
return &resp, nil
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (as *authStore) UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error) {
|
||||
@ -546,17 +556,14 @@ func (as *authStore) RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse,
|
||||
func (as *authStore) RoleList(r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error) {
|
||||
tx := as.be.BatchTx()
|
||||
tx.Lock()
|
||||
defer tx.Unlock()
|
||||
|
||||
var resp pb.AuthRoleListResponse
|
||||
|
||||
roles := getAllRoles(tx)
|
||||
tx.Unlock()
|
||||
|
||||
for _, r := range roles {
|
||||
resp.Roles = append(resp.Roles, string(r.Name))
|
||||
resp := &pb.AuthRoleListResponse{Roles: make([]string, len(roles))}
|
||||
for i := range roles {
|
||||
resp.Roles[i] = string(roles[i].Name)
|
||||
}
|
||||
|
||||
return &resp, nil
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (as *authStore) RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error) {
|
||||
@ -782,9 +789,9 @@ func (as *authStore) IsAdminPermitted(authInfo *AuthInfo) error {
|
||||
|
||||
tx := as.be.BatchTx()
|
||||
tx.Lock()
|
||||
defer tx.Unlock()
|
||||
|
||||
u := getUser(tx, authInfo.Username)
|
||||
tx.Unlock()
|
||||
|
||||
if u == nil {
|
||||
return ErrUserNotFound
|
||||
}
|
||||
@ -816,18 +823,15 @@ func getAllUsers(tx backend.BatchTx) []*authpb.User {
|
||||
return nil
|
||||
}
|
||||
|
||||
var users []*authpb.User
|
||||
|
||||
for _, v := range vs {
|
||||
users := make([]*authpb.User, len(vs))
|
||||
for i := range vs {
|
||||
user := &authpb.User{}
|
||||
err := user.Unmarshal(v)
|
||||
err := user.Unmarshal(vs[i])
|
||||
if err != nil {
|
||||
plog.Panicf("failed to unmarshal user struct: %s", err)
|
||||
}
|
||||
|
||||
users = append(users, user)
|
||||
users[i] = user
|
||||
}
|
||||
|
||||
return users
|
||||
}
|
||||
|
||||
@ -863,18 +867,15 @@ func getAllRoles(tx backend.BatchTx) []*authpb.Role {
|
||||
return nil
|
||||
}
|
||||
|
||||
var roles []*authpb.Role
|
||||
|
||||
for _, v := range vs {
|
||||
roles := make([]*authpb.Role, len(vs))
|
||||
for i := range vs {
|
||||
role := &authpb.Role{}
|
||||
err := role.Unmarshal(v)
|
||||
err := role.Unmarshal(vs[i])
|
||||
if err != nil {
|
||||
plog.Panicf("failed to unmarshal role struct: %s", err)
|
||||
}
|
||||
|
||||
roles = append(roles, role)
|
||||
roles[i] = role
|
||||
}
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
@ -936,12 +937,9 @@ func NewAuthStore(be backend.Backend, tp TokenProvider) *authStore {
|
||||
}
|
||||
|
||||
func hasRootRole(u *authpb.User) bool {
|
||||
for _, r := range u.Roles {
|
||||
if r == rootRole {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
// u.Roles is sorted in UserGrantRole(), so we can use binary search.
|
||||
idx := sort.SearchStrings(u.Roles, rootRole)
|
||||
return idx != len(u.Roles) && u.Roles[idx] == rootRole
|
||||
}
|
||||
|
||||
func (as *authStore) commitRevision(tx backend.BatchTx) {
|
||||
@ -997,8 +995,12 @@ func (as *authStore) AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ts, tok := md["token"]
|
||||
if !tok {
|
||||
//TODO(mitake|hexfusion) review unifying key names
|
||||
ts, ok := md["token"]
|
||||
if !ok {
|
||||
ts, ok = md["authorization"]
|
||||
}
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -1008,6 +1010,7 @@ func (as *authStore) AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error) {
|
||||
plog.Warningf("invalid auth token: %s", token)
|
||||
return nil, ErrInvalidAuthToken
|
||||
}
|
||||
|
||||
return authInfo, nil
|
||||
}
|
||||
|
||||
@ -1057,3 +1060,55 @@ func NewTokenProvider(tokenOpts string, indexWaiter func(uint64) <-chan struct{}
|
||||
return nil, ErrInvalidAuthOpts
|
||||
}
|
||||
}
|
||||
|
||||
func (as *authStore) WithRoot(ctx context.Context) context.Context {
|
||||
if !as.isAuthEnabled() {
|
||||
return ctx
|
||||
}
|
||||
|
||||
var ctxForAssign context.Context
|
||||
if ts := as.tokenProvider.(*tokenSimple); ts != nil {
|
||||
ctx1 := context.WithValue(ctx, AuthenticateParamIndex{}, uint64(0))
|
||||
prefix, err := ts.genTokenPrefix()
|
||||
if err != nil {
|
||||
plog.Errorf("failed to generate prefix of internally used token")
|
||||
return ctx
|
||||
}
|
||||
ctxForAssign = context.WithValue(ctx1, AuthenticateParamSimpleTokenPrefix{}, prefix)
|
||||
} else {
|
||||
ctxForAssign = ctx
|
||||
}
|
||||
|
||||
token, err := as.tokenProvider.assign(ctxForAssign, "root", as.Revision())
|
||||
if err != nil {
|
||||
// this must not happen
|
||||
plog.Errorf("failed to assign token for lease revoking: %s", err)
|
||||
return ctx
|
||||
}
|
||||
|
||||
mdMap := map[string]string{
|
||||
"token": token,
|
||||
}
|
||||
tokenMD := metadata.New(mdMap)
|
||||
return metadata.NewOutgoingContext(ctx, tokenMD)
|
||||
}
|
||||
|
||||
func (as *authStore) HasRole(user, role string) bool {
|
||||
tx := as.be.BatchTx()
|
||||
tx.Lock()
|
||||
u := getUser(tx, user)
|
||||
tx.Unlock()
|
||||
|
||||
if u == nil {
|
||||
plog.Warningf("tried to check user %s has role %s, but user %s doesn't exist", user, role, user)
|
||||
return false
|
||||
}
|
||||
|
||||
for _, r := range u.Roles {
|
||||
if role == r {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -15,9 +15,11 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@ -27,7 +29,6 @@ import (
|
||||
"github.com/coreos/etcd/mvcc/backend"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
@ -198,7 +199,7 @@ func TestUserChangePassword(t *testing.T) {
|
||||
as, tearDown := setupAuthStore(t)
|
||||
defer tearDown(t)
|
||||
|
||||
ctx1 := context.WithValue(context.WithValue(context.TODO(), "index", uint64(1)), "simpleToken", "dummy")
|
||||
ctx1 := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
|
||||
_, err := as.Authenticate(ctx1, "foo", "bar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -209,7 +210,7 @@ func TestUserChangePassword(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx2 := context.WithValue(context.WithValue(context.TODO(), "index", uint64(2)), "simpleToken", "dummy")
|
||||
ctx2 := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
|
||||
_, err = as.Authenticate(ctx2, "foo", "baz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -460,7 +461,7 @@ func TestAuthInfoFromCtx(t *testing.T) {
|
||||
t.Errorf("expected (nil, nil), got (%v, %v)", ai, err)
|
||||
}
|
||||
|
||||
ctx = context.WithValue(context.WithValue(context.TODO(), "index", uint64(1)), "simpleToken", "dummy")
|
||||
ctx = context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
|
||||
resp, err := as.Authenticate(ctx, "foo", "bar")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@ -493,7 +494,7 @@ func TestAuthDisable(t *testing.T) {
|
||||
defer tearDown(t)
|
||||
|
||||
as.AuthDisable()
|
||||
ctx := context.WithValue(context.WithValue(context.TODO(), "index", uint64(2)), "simpleToken", "dummy")
|
||||
ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
|
||||
_, err := as.Authenticate(ctx, "foo", "bar")
|
||||
if err != ErrAuthNotEnabled {
|
||||
t.Errorf("expected %v, got %v", ErrAuthNotEnabled, err)
|
||||
@ -641,7 +642,7 @@ func TestHammerSimpleAuthenticate(t *testing.T) {
|
||||
go func(user string) {
|
||||
defer wg.Done()
|
||||
token := fmt.Sprintf("%s(%d)", user, i)
|
||||
ctx := context.WithValue(context.WithValue(context.TODO(), "index", uint64(1)), "simpleToken", token)
|
||||
ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, token)
|
||||
if _, err := as.Authenticate(ctx, user, "123"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -654,3 +655,49 @@ func TestHammerSimpleAuthenticate(t *testing.T) {
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
// TestRolesOrder tests authpb.User.Roles is sorted
|
||||
func TestRolesOrder(t *testing.T) {
|
||||
b, tPath := backend.NewDefaultTmpBackend()
|
||||
defer os.Remove(tPath)
|
||||
|
||||
tp, err := NewTokenProvider("simple", dummyIndexWaiter)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
as := NewAuthStore(b, tp)
|
||||
err = enableAuthAndCreateRoot(as)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
username := "user"
|
||||
_, err = as.UserAdd(&pb.AuthUserAddRequest{username, "pass"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
roles := []string{"role1", "role2", "abc", "xyz", "role3"}
|
||||
for _, role := range roles {
|
||||
_, err = as.RoleAdd(&pb.AuthRoleAddRequest{role})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{username, role})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
user, err := as.UserGet(&pb.AuthUserGetRequest{username})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 1; i < len(user.Roles); i++ {
|
||||
if strings.Compare(user.Roles[i-1], user.Roles[i]) != -1 {
|
||||
t.Errorf("User.Roles isn't sorted (%s vs %s)", user.Roles[i-1], user.Roles[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,15 +26,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/cockroachdb/cmux",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "Apache License 2.0",
|
||||
"confidence": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/coreos/bbolt",
|
||||
"licenses": [
|
||||
@ -152,6 +143,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/gorilla/websocket",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "BSD 2-clause \"Simplified\" License",
|
||||
"confidence": 0.9852216748768473
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/grpc-ecosystem/go-grpc-prometheus",
|
||||
"licenses": [
|
||||
@ -264,6 +264,24 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/sirupsen/logrus",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT License",
|
||||
"confidence": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/soheilhy/cmux",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "Apache License 2.0",
|
||||
"confidence": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/spf13/cobra",
|
||||
"licenses": [
|
||||
@ -282,6 +300,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/tmc/grpc-websocket-proxy/wsproxy",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT License",
|
||||
"confidence": 0.9891304347826086
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/ugorji/go/codec",
|
||||
"licenses": [
|
||||
@ -327,6 +354,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "golang.org/x/sys/unix",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "BSD 3-clause \"New\" or \"Revised\" License",
|
||||
"confidence": 0.9663865546218487
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "golang.org/x/text",
|
||||
"licenses": [
|
||||
@ -346,7 +382,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "google.golang.org/genproto/googleapis",
|
||||
"project": "google.golang.org/genproto/googleapis/rpc/status",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "Apache License 2.0",
|
||||
|
20
build
20
build
@ -4,7 +4,7 @@
|
||||
ORG_PATH="github.com/coreos"
|
||||
REPO_PATH="${ORG_PATH}/etcd"
|
||||
|
||||
GIT_SHA=`git rev-parse --short HEAD || echo "GitNotFound"`
|
||||
GIT_SHA=$(git rev-parse --short HEAD || echo "GitNotFound")
|
||||
if [ ! -z "$FAILPOINTS" ]; then
|
||||
GIT_SHA="$GIT_SHA"-FAILPOINTS
|
||||
fi
|
||||
@ -14,10 +14,9 @@ GO_LDFLAGS="$GO_LDFLAGS -X ${REPO_PATH}/cmd/vendor/${REPO_PATH}/version.GitSHA=$
|
||||
|
||||
# enable/disable failpoints
|
||||
toggle_failpoints() {
|
||||
FAILPKGS="etcdserver/ mvcc/backend/"
|
||||
mode="$1"
|
||||
if which gofail >/dev/null 2>&1; then
|
||||
gofail "$mode" $FAILPKGS
|
||||
gofail "$mode" etcdserver/ mvcc/backend/
|
||||
elif [ "$mode" != "disable" ]; then
|
||||
echo "FAILPOINTS set but gofail not found"
|
||||
exit 1
|
||||
@ -34,9 +33,12 @@ etcd_build() {
|
||||
out="bin"
|
||||
if [ -n "${BINDIR}" ]; then out="${BINDIR}"; fi
|
||||
toggle_failpoints_default
|
||||
# Static compilation is useful when etcd is run in a container
|
||||
CGO_ENABLED=0 go build $GO_BUILD_FLAGS -installsuffix cgo -ldflags "$GO_LDFLAGS" -o ${out}/etcd ${REPO_PATH}/cmd/etcd || return
|
||||
CGO_ENABLED=0 go build $GO_BUILD_FLAGS -installsuffix cgo -ldflags "$GO_LDFLAGS" -o ${out}/etcdctl ${REPO_PATH}/cmd/etcdctl || return
|
||||
# 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 -installsuffix cgo -ldflags "$GO_LDFLAGS" -o "${out}/etcd" ${REPO_PATH}/cmd/etcd || return
|
||||
# shellcheck disable=SC2086
|
||||
CGO_ENABLED=0 go build $GO_BUILD_FLAGS -installsuffix cgo -ldflags "$GO_LDFLAGS" -o "${out}/etcdctl" ${REPO_PATH}/cmd/etcdctl || return
|
||||
}
|
||||
|
||||
etcd_setup_gopath() {
|
||||
@ -49,9 +51,9 @@ etcd_setup_gopath() {
|
||||
GOPATH=":$GOPATH"
|
||||
fi
|
||||
export GOPATH=${etcdGOPATH}$GOPATH
|
||||
rm -rf ${etcdGOPATH}/src
|
||||
mkdir -p ${etcdGOPATH}
|
||||
ln -s ${CDIR}/cmd/vendor ${etcdGOPATH}/src
|
||||
rm -rf "${etcdGOPATH}/src"
|
||||
mkdir -p "${etcdGOPATH}"
|
||||
ln -s "${CDIR}/cmd/vendor" "${etcdGOPATH}/src"
|
||||
}
|
||||
|
||||
toggle_failpoints_default
|
||||
|
@ -25,8 +25,8 @@ package main
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
"context"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"github.com/coreos/etcd/client"
|
||||
)
|
||||
|
||||
|
@ -16,11 +16,10 @@ package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type Role struct {
|
||||
|
@ -16,12 +16,11 @@ package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -15,6 +15,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -29,8 +30,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/version"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -671,8 +670,15 @@ func (r *redirectedHTTPAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
}
|
||||
|
||||
func shuffleEndpoints(r *rand.Rand, eps []url.URL) []url.URL {
|
||||
p := r.Perm(len(eps))
|
||||
neps := make([]url.URL, len(eps))
|
||||
// copied from Go 1.9<= rand.Rand.Perm
|
||||
n := len(eps)
|
||||
p := make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
j := r.Intn(i + 1)
|
||||
p[i] = p[j]
|
||||
p[j] = i
|
||||
}
|
||||
neps := make([]url.URL, n)
|
||||
for i, k := range p {
|
||||
neps[i] = eps[k]
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -30,7 +31,6 @@ import (
|
||||
|
||||
"github.com/coreos/etcd/pkg/testutil"
|
||||
"github.com/coreos/etcd/version"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type actionAssertingHTTPClient struct {
|
||||
|
@ -19,9 +19,9 @@ Create a Config and exchange it for a Client:
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"context"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
cfg := client.Config{
|
||||
@ -59,7 +59,7 @@ Use a custom context to set timeouts on your operations:
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// set a new key, ignoring it's previous state
|
||||
// set a new key, ignoring its previous state
|
||||
_, err := kAPI.Set(ctx, "/ping", "pong", nil)
|
||||
if err != nil {
|
||||
if err == context.DeadlineExceeded {
|
||||
|
93
client/example_keys_test.go
Normal file
93
client/example_keys_test.go
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2017 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 client_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
)
|
||||
|
||||
func ExampleKeysAPI_directory() {
|
||||
c, err := client.New(client.Config{
|
||||
Endpoints: exampleEndpoints,
|
||||
Transport: exampleTransport,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
kapi := client.NewKeysAPI(c)
|
||||
|
||||
// Setting '/myNodes' to create a directory that will hold some keys.
|
||||
o := client.SetOptions{Dir: true}
|
||||
resp, err := kapi.Set(context.Background(), "/myNodes", "", &o)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Add keys to /myNodes directory.
|
||||
resp, err = kapi.Set(context.Background(), "/myNodes/key1", "value1", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
resp, err = kapi.Set(context.Background(), "/myNodes/key2", "value2", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// fetch directory
|
||||
resp, err = kapi.Get(context.Background(), "/myNodes", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// print directory keys
|
||||
sort.Sort(resp.Node.Nodes)
|
||||
for _, n := range resp.Node.Nodes {
|
||||
fmt.Printf("Key: %q, Value: %q\n", n.Key, n.Value)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Key: "/myNodes/key1", Value: "value1"
|
||||
// Key: "/myNodes/key2", Value: "value2"
|
||||
}
|
||||
|
||||
func ExampleKeysAPI_setget() {
|
||||
c, err := client.New(client.Config{
|
||||
Endpoints: exampleEndpoints,
|
||||
Transport: exampleTransport,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
kapi := client.NewKeysAPI(c)
|
||||
|
||||
// Set key "/foo" to value "bar".
|
||||
resp, err := kapi.Set(context.Background(), "/foo", "bar", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Get key "/foo"
|
||||
resp, err = kapi.Get(context.Background(), "/foo", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%q key has %q value\n", resp.Node.Key, resp.Node.Value)
|
||||
|
||||
// Output: "/foo" key has "bar" value
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@ -23,8 +24,6 @@ import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/coreos/etcd/integration"
|
||||
"github.com/coreos/etcd/pkg/testutil"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,6 +17,7 @@ package client
|
||||
//go:generate codecgen -d 1819 -r "Node|Response|Nodes" -o keys.generated.go keys.go
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -28,7 +29,6 @@ import (
|
||||
|
||||
"github.com/coreos/etcd/pkg/pathutil"
|
||||
"github.com/ugorji/go/codec"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -653,8 +653,7 @@ func unmarshalHTTPResponse(code int, header http.Header, body []byte) (res *Resp
|
||||
default:
|
||||
err = unmarshalFailedKeysResponse(body)
|
||||
}
|
||||
|
||||
return
|
||||
return res, err
|
||||
}
|
||||
|
||||
func unmarshalSuccessfulKeysResponse(header http.Header, body []byte) (*Response, error) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@ -23,8 +24,6 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestV2KeysURLHelper(t *testing.T) {
|
||||
|
77
client/main_test.go
Normal file
77
client/main_test.go
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright 2017 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 client_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/integration"
|
||||
"github.com/coreos/etcd/pkg/testutil"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
)
|
||||
|
||||
var exampleEndpoints []string
|
||||
var exampleTransport *http.Transport
|
||||
|
||||
// TestMain sets up an etcd cluster if running the examples.
|
||||
func TestMain(m *testing.M) {
|
||||
useCluster, hasRunArg := false, false // default to running only Test*
|
||||
for _, arg := range os.Args {
|
||||
if strings.HasPrefix(arg, "-test.run=") {
|
||||
exp := strings.Split(arg, "=")[1]
|
||||
match, err := regexp.MatchString(exp, "Example")
|
||||
useCluster = (err == nil && match) || strings.Contains(exp, "Example")
|
||||
hasRunArg = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasRunArg {
|
||||
// force only running Test* if no args given to avoid leak false
|
||||
// positives from having a long-running cluster for the examples.
|
||||
os.Args = append(os.Args, "-test.run=Test")
|
||||
}
|
||||
|
||||
var v int
|
||||
if useCluster {
|
||||
tr, trerr := transport.NewTransport(transport.TLSInfo{}, time.Second)
|
||||
if trerr != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v", trerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg := integration.ClusterConfig{Size: 1}
|
||||
clus := integration.NewClusterV3(nil, &cfg)
|
||||
exampleEndpoints = []string{clus.Members[0].URL()}
|
||||
exampleTransport = tr
|
||||
v = m.Run()
|
||||
clus.Terminate(nil)
|
||||
if err := testutil.CheckAfterTest(time.Second); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
v = m.Run()
|
||||
}
|
||||
|
||||
if v == 0 && testutil.CheckLeakedGoroutine() {
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(v)
|
||||
}
|
@ -16,14 +16,13 @@ package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/coreos/etcd/pkg/types"
|
||||
)
|
||||
|
||||
@ -44,7 +43,7 @@ type Member struct {
|
||||
PeerURLs []string `json:"peerURLs"`
|
||||
|
||||
// ClientURLs represents the HTTP(S) endpoints on which this Member
|
||||
// serves it's client-facing APIs.
|
||||
// serves its client-facing APIs.
|
||||
ClientURLs []string `json:"clientURLs"`
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
@ -22,8 +23,6 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/coreos/etcd/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -15,13 +15,13 @@
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/auth/authpb"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
@ -101,65 +101,60 @@ type Auth interface {
|
||||
}
|
||||
|
||||
type auth struct {
|
||||
remote pb.AuthClient
|
||||
callOpts []grpc.CallOption
|
||||
remote pb.AuthClient
|
||||
}
|
||||
|
||||
func NewAuth(c *Client) Auth {
|
||||
api := &auth{remote: RetryAuthClient(c)}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api
|
||||
return &auth{remote: RetryAuthClient(c)}
|
||||
}
|
||||
|
||||
func (auth *auth) AuthEnable(ctx context.Context) (*AuthEnableResponse, error) {
|
||||
resp, err := auth.remote.AuthEnable(ctx, &pb.AuthEnableRequest{}, auth.callOpts...)
|
||||
resp, err := auth.remote.AuthEnable(ctx, &pb.AuthEnableRequest{})
|
||||
return (*AuthEnableResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) AuthDisable(ctx context.Context) (*AuthDisableResponse, error) {
|
||||
resp, err := auth.remote.AuthDisable(ctx, &pb.AuthDisableRequest{}, auth.callOpts...)
|
||||
resp, err := auth.remote.AuthDisable(ctx, &pb.AuthDisableRequest{})
|
||||
return (*AuthDisableResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) UserAdd(ctx context.Context, name string, password string) (*AuthUserAddResponse, error) {
|
||||
resp, err := auth.remote.UserAdd(ctx, &pb.AuthUserAddRequest{Name: name, Password: password}, auth.callOpts...)
|
||||
resp, err := auth.remote.UserAdd(ctx, &pb.AuthUserAddRequest{Name: name, Password: password})
|
||||
return (*AuthUserAddResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) UserDelete(ctx context.Context, name string) (*AuthUserDeleteResponse, error) {
|
||||
resp, err := auth.remote.UserDelete(ctx, &pb.AuthUserDeleteRequest{Name: name}, auth.callOpts...)
|
||||
resp, err := auth.remote.UserDelete(ctx, &pb.AuthUserDeleteRequest{Name: name})
|
||||
return (*AuthUserDeleteResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) UserChangePassword(ctx context.Context, name string, password string) (*AuthUserChangePasswordResponse, error) {
|
||||
resp, err := auth.remote.UserChangePassword(ctx, &pb.AuthUserChangePasswordRequest{Name: name, Password: password}, auth.callOpts...)
|
||||
resp, err := auth.remote.UserChangePassword(ctx, &pb.AuthUserChangePasswordRequest{Name: name, Password: password})
|
||||
return (*AuthUserChangePasswordResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) UserGrantRole(ctx context.Context, user string, role string) (*AuthUserGrantRoleResponse, error) {
|
||||
resp, err := auth.remote.UserGrantRole(ctx, &pb.AuthUserGrantRoleRequest{User: user, Role: role}, auth.callOpts...)
|
||||
resp, err := auth.remote.UserGrantRole(ctx, &pb.AuthUserGrantRoleRequest{User: user, Role: role})
|
||||
return (*AuthUserGrantRoleResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) UserGet(ctx context.Context, name string) (*AuthUserGetResponse, error) {
|
||||
resp, err := auth.remote.UserGet(ctx, &pb.AuthUserGetRequest{Name: name}, auth.callOpts...)
|
||||
resp, err := auth.remote.UserGet(ctx, &pb.AuthUserGetRequest{Name: name})
|
||||
return (*AuthUserGetResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) UserList(ctx context.Context) (*AuthUserListResponse, error) {
|
||||
resp, err := auth.remote.UserList(ctx, &pb.AuthUserListRequest{}, auth.callOpts...)
|
||||
resp, err := auth.remote.UserList(ctx, &pb.AuthUserListRequest{})
|
||||
return (*AuthUserListResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) UserRevokeRole(ctx context.Context, name string, role string) (*AuthUserRevokeRoleResponse, error) {
|
||||
resp, err := auth.remote.UserRevokeRole(ctx, &pb.AuthUserRevokeRoleRequest{Name: name, Role: role}, auth.callOpts...)
|
||||
resp, err := auth.remote.UserRevokeRole(ctx, &pb.AuthUserRevokeRoleRequest{Name: name, Role: role})
|
||||
return (*AuthUserRevokeRoleResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) RoleAdd(ctx context.Context, name string) (*AuthRoleAddResponse, error) {
|
||||
resp, err := auth.remote.RoleAdd(ctx, &pb.AuthRoleAddRequest{Name: name}, auth.callOpts...)
|
||||
resp, err := auth.remote.RoleAdd(ctx, &pb.AuthRoleAddRequest{Name: name})
|
||||
return (*AuthRoleAddResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
@ -169,27 +164,27 @@ func (auth *auth) RoleGrantPermission(ctx context.Context, name string, key, ran
|
||||
RangeEnd: []byte(rangeEnd),
|
||||
PermType: authpb.Permission_Type(permType),
|
||||
}
|
||||
resp, err := auth.remote.RoleGrantPermission(ctx, &pb.AuthRoleGrantPermissionRequest{Name: name, Perm: perm}, auth.callOpts...)
|
||||
resp, err := auth.remote.RoleGrantPermission(ctx, &pb.AuthRoleGrantPermissionRequest{Name: name, Perm: perm})
|
||||
return (*AuthRoleGrantPermissionResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) RoleGet(ctx context.Context, role string) (*AuthRoleGetResponse, error) {
|
||||
resp, err := auth.remote.RoleGet(ctx, &pb.AuthRoleGetRequest{Role: role}, auth.callOpts...)
|
||||
resp, err := auth.remote.RoleGet(ctx, &pb.AuthRoleGetRequest{Role: role})
|
||||
return (*AuthRoleGetResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) RoleList(ctx context.Context) (*AuthRoleListResponse, error) {
|
||||
resp, err := auth.remote.RoleList(ctx, &pb.AuthRoleListRequest{}, auth.callOpts...)
|
||||
resp, err := auth.remote.RoleList(ctx, &pb.AuthRoleListRequest{})
|
||||
return (*AuthRoleListResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) RoleRevokePermission(ctx context.Context, role string, key, rangeEnd string) (*AuthRoleRevokePermissionResponse, error) {
|
||||
resp, err := auth.remote.RoleRevokePermission(ctx, &pb.AuthRoleRevokePermissionRequest{Role: role, Key: key, RangeEnd: rangeEnd}, auth.callOpts...)
|
||||
resp, err := auth.remote.RoleRevokePermission(ctx, &pb.AuthRoleRevokePermissionRequest{Role: role, Key: key, RangeEnd: rangeEnd})
|
||||
return (*AuthRoleRevokePermissionResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *auth) RoleDelete(ctx context.Context, role string) (*AuthRoleDeleteResponse, error) {
|
||||
resp, err := auth.remote.RoleDelete(ctx, &pb.AuthRoleDeleteRequest{Role: role}, auth.callOpts...)
|
||||
resp, err := auth.remote.RoleDelete(ctx, &pb.AuthRoleDeleteRequest{Role: role})
|
||||
return (*AuthRoleDeleteResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
@ -202,13 +197,12 @@ func StrToPermissionType(s string) (PermissionType, error) {
|
||||
}
|
||||
|
||||
type authenticator struct {
|
||||
conn *grpc.ClientConn // conn in-use
|
||||
remote pb.AuthClient
|
||||
callOpts []grpc.CallOption
|
||||
conn *grpc.ClientConn // conn in-use
|
||||
remote pb.AuthClient
|
||||
}
|
||||
|
||||
func (auth *authenticator) authenticate(ctx context.Context, name string, password string) (*AuthenticateResponse, error) {
|
||||
resp, err := auth.remote.Authenticate(ctx, &pb.AuthenticateRequest{Name: name, Password: password}, auth.callOpts...)
|
||||
resp, err := auth.remote.Authenticate(ctx, &pb.AuthenticateRequest{Name: name, Password: password})
|
||||
return (*AuthenticateResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
@ -216,18 +210,14 @@ func (auth *authenticator) close() {
|
||||
auth.conn.Close()
|
||||
}
|
||||
|
||||
func newAuthenticator(endpoint string, opts []grpc.DialOption, c *Client) (*authenticator, error) {
|
||||
func newAuthenticator(endpoint string, opts []grpc.DialOption) (*authenticator, error) {
|
||||
conn, err := grpc.Dial(endpoint, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
api := &authenticator{
|
||||
return &authenticator{
|
||||
conn: conn,
|
||||
remote: pb.NewAuthClient(conn),
|
||||
}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api, nil
|
||||
}, nil
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -27,7 +28,6 @@ import (
|
||||
|
||||
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
@ -56,7 +56,7 @@ type Client struct {
|
||||
cfg Config
|
||||
creds *credentials.TransportCredentials
|
||||
balancer *healthBalancer
|
||||
mu *sync.Mutex
|
||||
mu sync.Mutex
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
@ -67,8 +67,6 @@ type Client struct {
|
||||
Password string
|
||||
// tokenCred is an instance of WithPerRPCCredentials()'s argument
|
||||
tokenCred *authTokenCredential
|
||||
|
||||
callOpts []grpc.CallOption
|
||||
}
|
||||
|
||||
// New creates a new etcdv3 client from a given configuration.
|
||||
@ -297,7 +295,7 @@ func (c *Client) getToken(ctx context.Context) error {
|
||||
endpoint := c.cfg.Endpoints[i]
|
||||
host := getHost(endpoint)
|
||||
// use dial options without dopts to avoid reusing the client balancer
|
||||
auth, err = newAuthenticator(host, c.dialSetupOpts(endpoint), c)
|
||||
auth, err = newAuthenticator(host, c.dialSetupOpts(endpoint))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@ -387,30 +385,11 @@ func newClient(cfg *Config) (*Client, error) {
|
||||
creds: creds,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
mu: new(sync.Mutex),
|
||||
callOpts: defaultCallOpts,
|
||||
}
|
||||
if cfg.Username != "" && cfg.Password != "" {
|
||||
client.Username = cfg.Username
|
||||
client.Password = cfg.Password
|
||||
}
|
||||
if cfg.MaxCallSendMsgSize > 0 || cfg.MaxCallRecvMsgSize > 0 {
|
||||
if cfg.MaxCallRecvMsgSize > 0 && cfg.MaxCallSendMsgSize > cfg.MaxCallRecvMsgSize {
|
||||
return nil, fmt.Errorf("gRPC message recv limit (%d bytes) must be greater than send limit (%d bytes)", cfg.MaxCallRecvMsgSize, cfg.MaxCallSendMsgSize)
|
||||
}
|
||||
callOpts := []grpc.CallOption{
|
||||
defaultFailFast,
|
||||
defaultMaxCallSendMsgSize,
|
||||
defaultMaxCallRecvMsgSize,
|
||||
}
|
||||
if cfg.MaxCallSendMsgSize > 0 {
|
||||
callOpts[1] = grpc.MaxCallSendMsgSize(cfg.MaxCallSendMsgSize)
|
||||
}
|
||||
if cfg.MaxCallRecvMsgSize > 0 {
|
||||
callOpts[2] = grpc.MaxCallRecvMsgSize(cfg.MaxCallRecvMsgSize)
|
||||
}
|
||||
client.callOpts = callOpts
|
||||
}
|
||||
|
||||
client.balancer = newHealthBalancer(cfg.Endpoints, cfg.DialTimeout, func(ep string) (bool, error) {
|
||||
return grpcHealthCheck(client, ep)
|
||||
|
@ -15,6 +15,7 @@
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
@ -22,8 +23,6 @@ import (
|
||||
|
||||
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
"github.com/coreos/etcd/pkg/testutil"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestDialCancel(t *testing.T) {
|
||||
@ -79,8 +78,6 @@ func TestDialCancel(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDialTimeout(t *testing.T) {
|
||||
t.Skip()
|
||||
|
||||
defer testutil.AfterTest(t)
|
||||
|
||||
testCfgs := []Config{
|
||||
|
@ -15,12 +15,11 @@
|
||||
package clientv3util_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/clientv3/clientv3util"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func ExampleKeyExists_put() {
|
||||
|
@ -15,11 +15,9 @@
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
"github.com/coreos/etcd/pkg/types"
|
||||
"context"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -45,34 +43,20 @@ type Cluster interface {
|
||||
}
|
||||
|
||||
type cluster struct {
|
||||
remote pb.ClusterClient
|
||||
callOpts []grpc.CallOption
|
||||
remote pb.ClusterClient
|
||||
}
|
||||
|
||||
func NewCluster(c *Client) Cluster {
|
||||
api := &cluster{remote: RetryClusterClient(c)}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api
|
||||
return &cluster{remote: RetryClusterClient(c)}
|
||||
}
|
||||
|
||||
func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster {
|
||||
api := &cluster{remote: remote}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api
|
||||
func NewClusterFromClusterClient(remote pb.ClusterClient) Cluster {
|
||||
return &cluster{remote: remote}
|
||||
}
|
||||
|
||||
func (c *cluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
|
||||
// fail-fast before panic in rafthttp
|
||||
if _, err := types.NewURLs(peerAddrs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := &pb.MemberAddRequest{PeerURLs: peerAddrs}
|
||||
resp, err := c.remote.MemberAdd(ctx, r, c.callOpts...)
|
||||
resp, err := c.remote.MemberAdd(ctx, r)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
@ -81,7 +65,7 @@ func (c *cluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAdd
|
||||
|
||||
func (c *cluster) MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error) {
|
||||
r := &pb.MemberRemoveRequest{ID: id}
|
||||
resp, err := c.remote.MemberRemove(ctx, r, c.callOpts...)
|
||||
resp, err := c.remote.MemberRemove(ctx, r)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
@ -89,14 +73,9 @@ func (c *cluster) MemberRemove(ctx context.Context, id uint64) (*MemberRemoveRes
|
||||
}
|
||||
|
||||
func (c *cluster) MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error) {
|
||||
// fail-fast before panic in rafthttp
|
||||
if _, err := types.NewURLs(peerAddrs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// it is safe to retry on update.
|
||||
r := &pb.MemberUpdateRequest{ID: id, PeerURLs: peerAddrs}
|
||||
resp, err := c.remote.MemberUpdate(ctx, r, c.callOpts...)
|
||||
resp, err := c.remote.MemberUpdate(ctx, r)
|
||||
if err == nil {
|
||||
return (*MemberUpdateResponse)(resp), nil
|
||||
}
|
||||
@ -105,7 +84,7 @@ func (c *cluster) MemberUpdate(ctx context.Context, id uint64, peerAddrs []strin
|
||||
|
||||
func (c *cluster) MemberList(ctx context.Context) (*MemberListResponse, error) {
|
||||
// it is safe to retry on list.
|
||||
resp, err := c.remote.MemberList(ctx, &pb.MemberListRequest{}, c.callOpts...)
|
||||
resp, err := c.remote.MemberList(ctx, &pb.MemberListRequest{})
|
||||
if err == nil {
|
||||
return (*MemberListResponse)(resp), nil
|
||||
}
|
||||
|
@ -60,6 +60,8 @@ func Compare(cmp Cmp, result string, v interface{}) Cmp {
|
||||
cmp.TargetUnion = &pb.Compare_CreateRevision{CreateRevision: mustInt64(v)}
|
||||
case pb.Compare_MOD:
|
||||
cmp.TargetUnion = &pb.Compare_ModRevision{ModRevision: mustInt64(v)}
|
||||
case pb.Compare_LEASE:
|
||||
cmp.TargetUnion = &pb.Compare_Lease{Lease: mustInt64orLeaseID(v)}
|
||||
default:
|
||||
panic("Unknown compare type")
|
||||
}
|
||||
@ -82,6 +84,12 @@ func ModRevision(key string) Cmp {
|
||||
return Cmp{Key: []byte(key), Target: pb.Compare_MOD}
|
||||
}
|
||||
|
||||
// LeaseValue compares a key's LeaseID to a value of your choosing. The empty
|
||||
// LeaseID is 0, otherwise known as `NoLease`.
|
||||
func LeaseValue(key string) Cmp {
|
||||
return Cmp{Key: []byte(key), Target: pb.Compare_LEASE}
|
||||
}
|
||||
|
||||
// KeyBytes returns the byte slice holding with the comparison key.
|
||||
func (cmp *Cmp) KeyBytes() []byte { return cmp.Key }
|
||||
|
||||
@ -99,6 +107,18 @@ func (cmp *Cmp) ValueBytes() []byte {
|
||||
// WithValueBytes sets the byte slice for the comparison's value.
|
||||
func (cmp *Cmp) WithValueBytes(v []byte) { cmp.TargetUnion.(*pb.Compare_Value).Value = v }
|
||||
|
||||
// WithRange sets the comparison to scan the range [key, end).
|
||||
func (cmp Cmp) WithRange(end string) Cmp {
|
||||
cmp.RangeEnd = []byte(end)
|
||||
return cmp
|
||||
}
|
||||
|
||||
// WithPrefix sets the comparison to scan all keys prefixed by the key.
|
||||
func (cmp Cmp) WithPrefix() Cmp {
|
||||
cmp.RangeEnd = getPrefix(cmp.Key)
|
||||
return cmp
|
||||
}
|
||||
|
||||
// mustInt64 panics if val isn't an int or int64. It returns an int64 otherwise.
|
||||
func mustInt64(val interface{}) int64 {
|
||||
if v, ok := val.(int64); ok {
|
||||
|
@ -15,14 +15,13 @@
|
||||
package concurrency
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
"github.com/coreos/etcd/mvcc/mvccpb"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
|
90
clientv3/concurrency/example_election_test.go
Normal file
90
clientv3/concurrency/example_election_test.go
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright 2017 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 concurrency_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/clientv3/concurrency"
|
||||
)
|
||||
|
||||
func ExampleElection_Campaign() {
|
||||
cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
// create two separate sessions for election competition
|
||||
s1, err := concurrency.NewSession(cli)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer s1.Close()
|
||||
e1 := concurrency.NewElection(s1, "/my-election/")
|
||||
|
||||
s2, err := concurrency.NewSession(cli)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer s2.Close()
|
||||
e2 := concurrency.NewElection(s2, "/my-election/")
|
||||
|
||||
// create competing candidates, with e1 initially losing to e2
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
electc := make(chan *concurrency.Election, 2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// delay candidacy so e2 wins first
|
||||
time.Sleep(3 * time.Second)
|
||||
if err := e1.Campaign(context.Background(), "e1"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
electc <- e1
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := e2.Campaign(context.Background(), "e2"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
electc <- e2
|
||||
}()
|
||||
|
||||
cctx, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
|
||||
e := <-electc
|
||||
fmt.Println("completed first election with", string((<-e.Observe(cctx)).Kvs[0].Value))
|
||||
|
||||
// resign so next candidate can be elected
|
||||
if err := e.Resign(context.TODO()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
e = <-electc
|
||||
fmt.Println("completed second election with", string((<-e.Observe(cctx)).Kvs[0].Value))
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// Output:
|
||||
// completed first election with e2
|
||||
// completed second election with e1
|
||||
}
|
75
clientv3/concurrency/example_mutex_test.go
Normal file
75
clientv3/concurrency/example_mutex_test.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2017 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 concurrency_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/clientv3/concurrency"
|
||||
)
|
||||
|
||||
func ExampleMutex_Lock() {
|
||||
cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
// create two separate sessions for lock competition
|
||||
s1, err := concurrency.NewSession(cli)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer s1.Close()
|
||||
m1 := concurrency.NewMutex(s1, "/my-lock/")
|
||||
|
||||
s2, err := concurrency.NewSession(cli)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer s2.Close()
|
||||
m2 := concurrency.NewMutex(s2, "/my-lock/")
|
||||
|
||||
// acquire lock for s1
|
||||
if err := m1.Lock(context.TODO()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("acquired lock for s1")
|
||||
|
||||
m2Locked := make(chan struct{})
|
||||
go func() {
|
||||
defer close(m2Locked)
|
||||
// wait until s1 is locks /my-lock/
|
||||
if err := m2.Lock(context.TODO()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := m1.Unlock(context.TODO()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("released lock for s1")
|
||||
|
||||
<-m2Locked
|
||||
fmt.Println("acquired lock for s2")
|
||||
|
||||
// Output:
|
||||
// acquired lock for s1
|
||||
// released lock for s1
|
||||
// acquired lock for s2
|
||||
}
|
97
clientv3/concurrency/example_stm_test.go
Normal file
97
clientv3/concurrency/example_stm_test.go
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2017 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 concurrency_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"sync"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/clientv3/concurrency"
|
||||
)
|
||||
|
||||
// ExampleSTM_apply shows how to use STM with a transactional
|
||||
// transfer between balances.
|
||||
func ExampleSTM_apply() {
|
||||
cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
// set up "accounts"
|
||||
totalAccounts := 5
|
||||
for i := 0; i < totalAccounts; i++ {
|
||||
k := fmt.Sprintf("accts/%d", i)
|
||||
if _, err = cli.Put(context.TODO(), k, "100"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
exchange := func(stm concurrency.STM) error {
|
||||
from, to := rand.Intn(totalAccounts), rand.Intn(totalAccounts)
|
||||
if from == to {
|
||||
// nothing to do
|
||||
return nil
|
||||
}
|
||||
// read values
|
||||
fromK, toK := fmt.Sprintf("accts/%d", from), fmt.Sprintf("accts/%d", to)
|
||||
fromV, toV := stm.Get(fromK), stm.Get(toK)
|
||||
fromInt, toInt := 0, 0
|
||||
fmt.Sscanf(fromV, "%d", &fromInt)
|
||||
fmt.Sscanf(toV, "%d", &toInt)
|
||||
|
||||
// transfer amount
|
||||
xfer := fromInt / 2
|
||||
fromInt, toInt = fromInt-xfer, toInt+xfer
|
||||
|
||||
// write back
|
||||
stm.Put(fromK, fmt.Sprintf("%d", fromInt))
|
||||
stm.Put(toK, fmt.Sprintf("%d", toInt))
|
||||
return nil
|
||||
}
|
||||
|
||||
// concurrently exchange values between accounts
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(10)
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if _, serr := concurrency.NewSTM(cli, exchange); serr != nil {
|
||||
log.Fatal(serr)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// confirm account sum matches sum from beginning.
|
||||
sum := 0
|
||||
accts, err := cli.Get(context.TODO(), "accts/", clientv3.WithPrefix())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, kv := range accts.Kvs {
|
||||
v := 0
|
||||
fmt.Sscanf(string(kv.Value), "%d", &v)
|
||||
sum += v
|
||||
}
|
||||
|
||||
fmt.Println("account sum is", sum)
|
||||
// Output:
|
||||
// account sum is 500
|
||||
}
|
@ -15,13 +15,12 @@
|
||||
package concurrency
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
"github.com/coreos/etcd/mvcc/mvccpb"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func waitDelete(ctx context.Context, client *v3.Client, key string, rev int64) error {
|
||||
|
44
clientv3/concurrency/main_test.go
Normal file
44
clientv3/concurrency/main_test.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2017 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 concurrency_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/integration"
|
||||
"github.com/coreos/etcd/pkg/testutil"
|
||||
)
|
||||
|
||||
var endpoints []string
|
||||
|
||||
// TestMain sets up an etcd cluster for running the examples.
|
||||
func TestMain(m *testing.M) {
|
||||
cfg := integration.ClusterConfig{Size: 1}
|
||||
clus := integration.NewClusterV3(nil, &cfg)
|
||||
endpoints = []string{clus.Client(0).Endpoints()[0]}
|
||||
v := m.Run()
|
||||
clus.Terminate(nil)
|
||||
if err := testutil.CheckAfterTest(time.Second); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if v == 0 && testutil.CheckLeakedGoroutine() {
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(v)
|
||||
}
|
@ -15,13 +15,12 @@
|
||||
package concurrency
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Mutex implements the sync Locker interface with etcd
|
||||
|
@ -15,11 +15,10 @@
|
||||
package concurrency
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const defaultSessionTTL = 60
|
||||
|
@ -15,11 +15,10 @@
|
||||
package concurrency
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// STM is an interface for software transactional memory.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user