Compare commits
3644 Commits
RELEASE.20
...
RELEASE.20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d40433bc1 | ||
|
|
3bc39db34e | ||
|
|
a17f14f73a | ||
|
|
6651c655cb | ||
|
|
3ae104edae | ||
|
|
c87a489514 | ||
|
|
a60267501d | ||
|
|
641a56da0d | ||
|
|
59788e25c7 | ||
|
|
a16193bb50 | ||
|
|
132e7413ba | ||
|
|
1966668066 | ||
|
|
064f36ca5a | ||
|
|
15b609ecea | ||
|
|
4a1edfd9aa | ||
|
|
b7f319b62a | ||
|
|
33c101544d | ||
|
|
21cf29330e | ||
|
|
3b21bb5be8 | ||
|
|
6fe2b3f901 | ||
|
|
b368d4cc13 | ||
|
|
0680af7414 | ||
|
|
91805bcab6 | ||
|
|
c0e2886e37 | ||
|
|
4f5dded4d4 | ||
|
|
b3a94c4e85 | ||
|
|
8e618d45fc | ||
|
|
3ef59d2821 | ||
|
|
23db4958f5 | ||
|
|
d9ee668b6d | ||
|
|
2e5d792f0c | ||
|
|
b276651eaa | ||
|
|
3535197f99 | ||
|
|
95f076340a | ||
|
|
698bb93a46 | ||
|
|
2584430141 | ||
|
|
ded373e600 | ||
|
|
e8c54c3d6c | ||
|
|
f944a42886 | ||
|
|
eff0ea43aa | ||
|
|
3b602bb532 | ||
|
|
459985f0fa | ||
|
|
d0080046c2 | ||
|
|
7fcb428622 | ||
|
|
4ea6f94ed8 | ||
|
|
83adc2eebf | ||
|
|
989c318a28 | ||
|
|
ef802f2b2c | ||
|
|
f5d2fbc84c | ||
|
|
e139673969 | ||
|
|
a8c6465f22 | ||
|
|
27538e2d22 | ||
|
|
6c6f0987dc | ||
|
|
5f64658faa | ||
|
|
ce183cb2b4 | ||
|
|
b3bac73c0f | ||
|
|
e726d8ff0f | ||
|
|
f4230777b3 | ||
|
|
380233d646 | ||
|
|
d592bc0c1c | ||
|
|
0d0b0aa599 | ||
|
|
b433bf14ba | ||
|
|
cf371da346 | ||
|
|
107d951893 | ||
|
|
22c53b1c70 | ||
|
|
88926ad8e9 | ||
|
|
32d04091a2 | ||
|
|
b6d4a77b94 | ||
|
|
be84a4fd68 | ||
|
|
2ec1f404ac | ||
|
|
2040559f71 | ||
|
|
ca0ce4c6ef | ||
|
|
757cf413cb | ||
|
|
b35acb3dbc | ||
|
|
e404abf103 | ||
|
|
f7ff19cb18 | ||
|
|
f736702da8 | ||
|
|
91faaa1387 | ||
|
|
68a9f521d5 | ||
|
|
f365a98029 | ||
|
|
47bbc272df | ||
|
|
aebac90013 | ||
|
|
7ca4ba77c4 | ||
|
|
13512170b5 | ||
|
|
154fcaeb56 | ||
|
|
722118386d | ||
|
|
709612cb37 | ||
|
|
b35d083872 | ||
|
|
5e7b243bde | ||
|
|
f8f9fc77ac | ||
|
|
499531f0b5 | ||
|
|
3c2141513f | ||
|
|
602f6a9ad0 | ||
|
|
22c5a5b91b | ||
|
|
41f508765d | ||
|
|
7dccd1f589 | ||
|
|
55ff598b23 | ||
|
|
a22ce4550c | ||
|
|
168ae81b1f | ||
|
|
5f6a25cdd0 | ||
|
|
be97ae4c5d | ||
|
|
4d7d008741 | ||
|
|
2d7a3d1516 | ||
|
|
dfab400d43 | ||
|
|
70078eab10 | ||
|
|
3415c4dd1e | ||
|
|
e200808ab7 | ||
|
|
fae563b85d | ||
|
|
3e6dc02f8f | ||
|
|
95e4cbbfde | ||
|
|
2825294b7b | ||
|
|
bce93b5cfa | ||
|
|
7a4b250c8b | ||
|
|
e5335450a4 | ||
|
|
a6ffdf1dd4 | ||
|
|
69e41f87ef | ||
|
|
ee48f9f206 | ||
|
|
9ba39d7fad | ||
|
|
d2fb371f80 | ||
|
|
2f9018f03b | ||
|
|
eb990f64a9 | ||
|
|
bbb64eaade | ||
|
|
7bd1d899bc | ||
|
|
c91d1ec2e3 | ||
|
|
c50b64027d | ||
|
|
20960b6a2d | ||
|
|
3bd3470d0b | ||
|
|
ba39ed9af7 | ||
|
|
62e6dc950d | ||
|
|
5a5046ce45 | ||
|
|
ad04afe381 | ||
|
|
ba9f0f2480 | ||
|
|
d06b63d056 | ||
|
|
7ce28c3b1d | ||
|
|
e3ac4035b9 | ||
|
|
d21b6daa49 | ||
|
|
76ebb16688 | ||
|
|
55aa431578 | ||
|
|
614981e566 | ||
|
|
b8b956a05d | ||
|
|
d2eed44c78 | ||
|
|
789cbc6fb2 | ||
|
|
0662c90b5c | ||
|
|
a2cab02554 | ||
|
|
6c7a21df6b | ||
|
|
f933b0b708 | ||
|
|
9f305273a7 | ||
|
|
cbd9efcb43 | ||
|
|
29a25a538f | ||
|
|
2dd8faaedc | ||
|
|
f00187033d | ||
|
|
c5141d65ac | ||
|
|
069c4015cd | ||
|
|
2f6e03fb60 | ||
|
|
0fbb945e13 | ||
|
|
b94dd835c9 | ||
|
|
44fc707423 | ||
|
|
5aaef9790f | ||
|
|
7edc352d23 | ||
|
|
850a84b08a | ||
|
|
4148754ce0 | ||
|
|
2107722829 | ||
|
|
d326ba52e9 | ||
|
|
91e1487de4 | ||
|
|
5ffb2a9605 | ||
|
|
17fe91d6d1 | ||
|
|
90a9f2dd70 | ||
|
|
d5e48cfd65 | ||
|
|
d274566463 | ||
|
|
39ac720826 | ||
|
|
21b6204692 | ||
|
|
d98faeb26a | ||
|
|
0a63dc199c | ||
|
|
3ba857dfa1 | ||
|
|
a8554c4022 | ||
|
|
ba54b39c02 | ||
|
|
2a75225569 | ||
|
|
e72429c79c | ||
|
|
c5b3f5553f | ||
|
|
d3ae0aaad3 | ||
|
|
d67bccf861 | ||
|
|
1277ad69a6 | ||
|
|
8f93e81afb | ||
|
|
4af31e654b | ||
|
|
aad50579ba | ||
|
|
38d059b0ae | ||
|
|
bd4eeb4522 | ||
|
|
03e3493288 | ||
|
|
64baedf5a4 | ||
|
|
2f64d5f77e | ||
|
|
f79a4ef4d0 | ||
|
|
2d53854b19 | ||
|
|
e5c83535af | ||
|
|
c904ef966e | ||
|
|
8f266e0772 | ||
|
|
e0fe7cc391 | ||
|
|
9d20dec56a | ||
|
|
597a785253 | ||
|
|
7d75b1e758 | ||
|
|
5f78691fcf | ||
|
|
a591e06ae5 | ||
|
|
443c93c634 | ||
|
|
5659cddc84 | ||
|
|
2a03a34bde | ||
|
|
1654a9b7e6 | ||
|
|
673a521711 | ||
|
|
2e23076688 | ||
|
|
b92ac55250 | ||
|
|
7981509cc8 | ||
|
|
6d5bc045bc | ||
|
|
d38e020b29 | ||
|
|
7d29030292 | ||
|
|
7c7650b7c3 | ||
|
|
ca80eced24 | ||
|
|
d0e0b81d8e | ||
|
|
391baa1c9a | ||
|
|
ae14681c3e | ||
|
|
4d698841f4 | ||
|
|
9906b3ade9 | ||
|
|
bf1769d3e0 | ||
|
|
63e1ad9f29 | ||
|
|
2c7bcee53f | ||
|
|
1fd90c93ff | ||
|
|
e947a844c9 | ||
|
|
4e2d39293a | ||
|
|
1228d6bf1a | ||
|
|
fc4561c64c | ||
|
|
3b7747b42b | ||
|
|
e432e79324 | ||
|
|
08d74819b6 | ||
|
|
aa3fde1784 | ||
|
|
0b3eb7f218 | ||
|
|
69c9496c71 | ||
|
|
b792b36495 | ||
|
|
d3db7d31a3 | ||
|
|
c05ca63158 | ||
|
|
6d3e0c7db6 | ||
|
|
0e59e50b39 | ||
|
|
d4b391de1b | ||
|
|
de4d3dac00 | ||
|
|
534e7161df | ||
|
|
9b219cd646 | ||
|
|
3bab4822f3 | ||
|
|
3c5f2d8916 | ||
|
|
5808190398 | ||
|
|
b2a82248b1 | ||
|
|
4e5fcca8b9 | ||
|
|
c36eaedb93 | ||
|
|
7752b03add | ||
|
|
01bfc78535 | ||
|
|
074d70112d | ||
|
|
e8d14c0d90 | ||
|
|
60d7e8143a | ||
|
|
9667a170de | ||
|
|
abae30f9e1 | ||
|
|
f9311bc9d1 | ||
|
|
b598402738 | ||
|
|
bd026b913f | ||
|
|
72ff69d9bb | ||
|
|
f30417d9a8 | ||
|
|
47a4ad3cd7 | ||
|
|
2f7a10ab31 | ||
|
|
b534dc69ab | ||
|
|
7b7d2ea7d4 | ||
|
|
e00de1c302 | ||
|
|
3549e583a6 | ||
|
|
f5e3eedf34 | ||
|
|
519dbfebf6 | ||
|
|
9a267f9270 | ||
|
|
67bd71b7a5 | ||
|
|
ec49fff583 | ||
|
|
8b660e18f2 | ||
|
|
981497799a | ||
|
|
b9bdc17465 | ||
|
|
b413ff9fdb | ||
|
|
6a15580817 | ||
|
|
39633a5581 | ||
|
|
1e83f15e2f | ||
|
|
888d2bb1d8 | ||
|
|
847ee5ac45 | ||
|
|
9a9a49aa84 | ||
|
|
a03ca80269 | ||
|
|
523bd769f1 | ||
|
|
8ff70ea5a9 | ||
|
|
da3e7747ca | ||
|
|
4afb59e63f | ||
|
|
1526e7ece3 | ||
|
|
6c07bfee8a | ||
|
|
446c760820 | ||
|
|
04f92f1291 | ||
|
|
4a60a7794d | ||
|
|
e5b16adb1c | ||
|
|
402a3ac719 | ||
|
|
f3d61c51fc | ||
|
|
0cde17ae5d | ||
|
|
8c1bba681b | ||
|
|
dbfb5e797b | ||
|
|
08ff702434 | ||
|
|
0e2148264a | ||
|
|
a75f42344b | ||
|
|
7926401cbd | ||
|
|
8161411c5d | ||
|
|
f64dea2aac | ||
|
|
6579304d8c | ||
|
|
6bb10a81a6 | ||
|
|
3cf8a7c888 | ||
|
|
2e38bb5175 | ||
|
|
a372c6a377 | ||
|
|
93b2f8a0c5 | ||
|
|
1a6568a25d | ||
|
|
9e95703efc | ||
|
|
d8e05aca81 | ||
|
|
410a1ac040 | ||
|
|
4caa3422bd | ||
|
|
a658b976f5 | ||
|
|
135874ebdc | ||
|
|
f4f1c42cba | ||
|
|
e7aa26dc29 | ||
|
|
c54ffde568 | ||
|
|
9a3c992d7a | ||
|
|
0c855638de | ||
|
|
943d815783 | ||
|
|
4c0acba62d | ||
|
|
62c3cdee75 | ||
|
|
3212d0c8cd | ||
|
|
1d03bea965 | ||
|
|
fbfeb59658 | ||
|
|
701da1282a | ||
|
|
df93ff92ba | ||
|
|
77d5331e85 | ||
|
|
14cdadfb56 | ||
|
|
f3a52cc195 | ||
|
|
7640cd24c9 | ||
|
|
f7b665347e | ||
|
|
9693c382a8 | ||
|
|
ee1047bd52 | ||
|
|
5ea5ab162b | ||
|
|
b5a09ff96b | ||
|
|
95c65f4e8f | ||
|
|
6bfff7532e | ||
|
|
1aa8896ad6 | ||
|
|
3e32ceb39f | ||
|
|
ca1350b092 | ||
|
|
9205434ed3 | ||
|
|
cd50e9b4bc | ||
|
|
ec816f3840 | ||
|
|
5f774951b1 | ||
|
|
2ca9befd2a | ||
|
|
72f5cb577e | ||
|
|
928c0181bf | ||
|
|
03767d26da | ||
|
|
108e6f92d4 | ||
|
|
d653a59fc0 | ||
|
|
01bfdf949a | ||
|
|
98f7821eb3 | ||
|
|
2d3898e0d5 | ||
|
|
ae46ce9937 | ||
|
|
dfc112c06b | ||
|
|
ca5fab8656 | ||
|
|
6df76ca73c | ||
|
|
f65dd3e5a2 | ||
|
|
a8d601b64a | ||
|
|
73b4794cf7 | ||
|
|
e2709ea129 | ||
|
|
740ec80819 | ||
|
|
d95e054282 | ||
|
|
7c1f9667d1 | ||
|
|
9246990496 | ||
|
|
0cf3d93360 | ||
|
|
cb06aee5ac | ||
|
|
1c70e9ed1b | ||
|
|
f3d6a2dd37 | ||
|
|
d1c58fc2eb | ||
|
|
b8f05b1471 | ||
|
|
e7baf78ee8 | ||
|
|
87299eba10 | ||
|
|
d3a07c29ba | ||
|
|
8d39b715dc | ||
|
|
7e3166475d | ||
|
|
5206c0e883 | ||
|
|
41ec038523 | ||
|
|
08d3d06a06 | ||
|
|
074febd9e1 | ||
|
|
aa8d25797b | ||
|
|
8d7d4adb91 | ||
|
|
ffa91f9794 | ||
|
|
0c31e61343 | ||
|
|
9b926f7dbe | ||
|
|
35d8728990 | ||
|
|
f7ed9a75ba | ||
|
|
9496c17e13 | ||
|
|
ed64e91f06 | ||
|
|
a481825ae1 | ||
|
|
7bb0f32332 | ||
|
|
c6f8dc431e | ||
|
|
78f177b8ee | ||
|
|
787c44c39d | ||
|
|
f06fee0364 | ||
|
|
c957e0d426 | ||
|
|
04101d472f | ||
|
|
51fc145161 | ||
|
|
9d63bb1b41 | ||
|
|
8ff2a7a2b9 | ||
|
|
91f91d8f47 | ||
|
|
a207bd6790 | ||
|
|
96d226c0b1 | ||
|
|
a86d98826d | ||
|
|
1bb670ecba | ||
|
|
c9e9a8e2b9 | ||
|
|
272367ccd2 | ||
|
|
95bf4a57b6 | ||
|
|
2228eb61cb | ||
|
|
5f07eb2d17 | ||
|
|
d96d696841 | ||
|
|
e18c0ab9bf | ||
|
|
faeb2b7e79 | ||
|
|
97ce11cb6b | ||
|
|
d7daae4762 | ||
|
|
3d86ae12bc | ||
|
|
ba46ee5dfa | ||
|
|
912bbb2f1d | ||
|
|
4f660a8eb7 | ||
|
|
ae4fb1b72e | ||
|
|
b435806d91 | ||
|
|
06929258bc | ||
|
|
cb577835d9 | ||
|
|
7f35f74f14 | ||
|
|
3d6194e93c | ||
|
|
72c7845f7e | ||
|
|
1c99597a06 | ||
|
|
feb9d8480b | ||
|
|
4e670458b8 | ||
|
|
48deccdc40 | ||
|
|
2eee744e34 | ||
|
|
3f72439b8a | ||
|
|
468a9fae83 | ||
|
|
d87f91720b | ||
|
|
aa0eec16ab | ||
|
|
d63e603040 | ||
|
|
8222a640ac | ||
|
|
7e45d84ace | ||
|
|
139a606f0a | ||
|
|
289223b6de | ||
|
|
c61dd16a1e | ||
|
|
3e38fa54a5 | ||
|
|
4a02189ba0 | ||
|
|
3d4fc28ec9 | ||
|
|
ec3a3bb10d | ||
|
|
364d3a0ac9 | ||
|
|
cb536a73eb | ||
|
|
428155add9 | ||
|
|
8bce123bba | ||
|
|
0a56dbde2f | ||
|
|
7ff4164d65 | ||
|
|
53a14c7301 | ||
|
|
dc45a5010d | ||
|
|
4b9192034c | ||
|
|
deeadd1a37 | ||
|
|
1fc4203c19 | ||
|
|
15b930be1f | ||
|
|
7fd76dbbb7 | ||
|
|
da81c6cc27 | ||
|
|
a03dac41eb | ||
|
|
b657ffa496 | ||
|
|
55778ae278 | ||
|
|
d990661d1f | ||
|
|
280526caf7 | ||
|
|
1173b26fc8 | ||
|
|
383489d5d9 | ||
|
|
9370b11684 | ||
|
|
999bbd3a14 | ||
|
|
235edd88aa | ||
|
|
b5e074e54c | ||
|
|
4d7068931a | ||
|
|
d7fb6fddf6 | ||
|
|
7213bd7131 | ||
|
|
d4aac7cd72 | ||
|
|
741de4cf94 | ||
|
|
f168ef9989 | ||
|
|
a0de56abb6 | ||
|
|
c201d8bda9 | ||
|
|
d2373d5d6c | ||
|
|
93fb7d62d8 | ||
|
|
485298b680 | ||
|
|
062f0cffad | ||
|
|
ce1c640ce0 | ||
|
|
5c32058ff3 | ||
|
|
24b4f9d748 | ||
|
|
81d7531f1f | ||
|
|
b4a23f720e | ||
|
|
a2f6252b2f | ||
|
|
6c964fede5 | ||
|
|
a25a8312d8 | ||
|
|
b2c5b75efa | ||
|
|
2dfa9adc5d | ||
|
|
88a89213ff | ||
|
|
8e2238ea09 | ||
|
|
2007dd26ae | ||
|
|
31e8f7c525 | ||
|
|
51f62a8da3 | ||
|
|
650efc2e96 | ||
|
|
1787bcfc91 | ||
|
|
2cc4997d24 | ||
|
|
934f6cabf6 | ||
|
|
233cc3905a | ||
|
|
68dd74c5ab | ||
|
|
48b590e14b | ||
|
|
8ab3dac4f2 | ||
|
|
3fb0cbc030 | ||
|
|
837a2a3d4b | ||
|
|
e91a4a414c | ||
|
|
74ccee6619 | ||
|
|
c26b8d4eb8 | ||
|
|
dae9dc4847 | ||
|
|
89f759566c | ||
|
|
5dc1ef0b87 | ||
|
|
cd7551031b | ||
|
|
df57bfcd6c | ||
|
|
dfb1f39b57 | ||
|
|
e3e3d92241 | ||
|
|
1b5f28e99b | ||
|
|
b69bcdcdc4 | ||
|
|
e385f54185 | ||
|
|
9a4d003ac7 | ||
|
|
d5656eeb65 | ||
|
|
8edc67b0a9 | ||
|
|
18b0b7299a | ||
|
|
09b0e7133d | ||
|
|
6d08af61a0 | ||
|
|
a7577da768 | ||
|
|
325fd80687 | ||
|
|
09626d78ff | ||
|
|
8f03c6e0db | ||
|
|
2c2f5d871c | ||
|
|
c599c11e70 | ||
|
|
ef06644799 | ||
|
|
6769d4dd54 | ||
|
|
f3e7c42425 | ||
|
|
f46bee242c | ||
|
|
d7520f0ae6 | ||
|
|
44b70eb646 | ||
|
|
828d4df6f0 | ||
|
|
467714f33b | ||
|
|
f8696cc8f6 | ||
|
|
9a7c7ab2d0 | ||
|
|
40fb3371fa | ||
|
|
51874a5776 | ||
|
|
62ce52c8fd | ||
|
|
2bdb9511bd | ||
|
|
9a012a53ef | ||
|
|
0aae0180fb | ||
|
|
1dd8ef09a6 | ||
|
|
95032e4710 | ||
|
|
b1351e2dee | ||
|
|
30c2596512 | ||
|
|
2b5e4b853c | ||
|
|
85bcb5874a | ||
|
|
92788e4cf4 | ||
|
|
8a698fef71 | ||
|
|
b49ce1713f | ||
|
|
c2b54d92f6 | ||
|
|
f965434022 | ||
|
|
a3ac62596c | ||
|
|
2faba02d6b | ||
|
|
ee158e1610 | ||
|
|
fa68efb1e7 | ||
|
|
8c53a4405a | ||
|
|
c32f699105 | ||
|
|
53aa8f5650 | ||
|
|
56887f3208 | ||
|
|
92180bc793 | ||
|
|
22aa16ab12 | ||
|
|
526b829a09 | ||
|
|
c44f311c4f | ||
|
|
9ea5d08ecd | ||
|
|
35deb1a8e2 | ||
|
|
c7f7c47388 | ||
|
|
cb7dab17cb | ||
|
|
cd419a35fe | ||
|
|
e06168596f | ||
|
|
4c8197a119 | ||
|
|
23c10350f3 | ||
|
|
b6e98aed01 | ||
|
|
00dcba9ddd | ||
|
|
607cafadbc | ||
|
|
68dde2359f | ||
|
|
f9dbf41e27 | ||
|
|
7405760f44 | ||
|
|
7e4a6b4bcd | ||
|
|
b5791e6f28 | ||
|
|
00cb58eaf3 | ||
|
|
f961ec4aaf | ||
|
|
134db72bb7 | ||
|
|
6fd0b434e2 | ||
|
|
effe21f3eb | ||
|
|
1118b285d3 | ||
|
|
912a0031b7 | ||
|
|
a14e192376 | ||
|
|
f8e15e7d09 | ||
|
|
7b9f9e0628 | ||
|
|
ac8e9ce04f | ||
|
|
cfd8645843 | ||
|
|
0c068b15c7 | ||
|
|
30a466aa71 | ||
|
|
4d94609c44 | ||
|
|
6b63123ca9 | ||
|
|
0cc9fb73e1 | ||
|
|
eac4e4b279 | ||
|
|
6d381f7c0a | ||
|
|
4fa06aefc6 | ||
|
|
0e177a44e0 | ||
|
|
afd19de5a9 | ||
|
|
e3fbac9e24 | ||
|
|
a9cf32811c | ||
|
|
53997ecc79 | ||
|
|
8e69f3cb89 | ||
|
|
997ba3a574 | ||
|
|
8e68ff9321 | ||
|
|
62761a23e6 | ||
|
|
404d8b3084 | ||
|
|
6005ad3d48 | ||
|
|
035a3ea4ae | ||
|
|
7ec43bd177 | ||
|
|
a29c66ed74 | ||
|
|
e104b183d8 | ||
|
|
7e082f232e | ||
|
|
d28bf71f25 | ||
|
|
5b1a74b6b2 | ||
|
|
eead4db1d2 | ||
|
|
980fb5e2ab | ||
|
|
9bcc46d93d | ||
|
|
22687c1f50 | ||
|
|
ebc6c9b498 | ||
|
|
630963fa6b | ||
|
|
f674168b8b | ||
|
|
7e023f2d50 | ||
|
|
27d02ea6f7 | ||
|
|
794a7993cb | ||
|
|
6f16d1cb2c | ||
|
|
7aa00bff89 | ||
|
|
e046eb1d17 | ||
|
|
ba975ca320 | ||
|
|
fec13b0ec1 | ||
|
|
100c35c281 | ||
|
|
8414aff424 | ||
|
|
f225ca3312 | ||
|
|
8b68e0bfdc | ||
|
|
6ae97aedc9 | ||
|
|
960d604013 | ||
|
|
63bf5f42a1 | ||
|
|
ff80cfd83d | ||
|
|
99fde2ba85 | ||
|
|
ce0cb913bc | ||
|
|
d99d16e8c3 | ||
|
|
31743789dc | ||
|
|
6fd63e920a | ||
|
|
59cc3e93d6 | ||
|
|
61a4bb38cd | ||
|
|
b192bc348c | ||
|
|
6440d0fbf3 | ||
|
|
ee0055b929 | ||
|
|
24ecc44bac | ||
|
|
0ae4915a93 | ||
|
|
4cd777a5e0 | ||
|
|
65028d4a35 | ||
|
|
caac9d216e | ||
|
|
057192913c | ||
|
|
f25cbdf43c | ||
|
|
6da4a9c7bb | ||
|
|
80ca120088 | ||
|
|
a669946357 | ||
|
|
7ffc162ea8 | ||
|
|
bcfd7fbbcf | ||
|
|
486e2e48ea | ||
|
|
2ddf2ca934 | ||
|
|
403ec7cf21 | ||
|
|
29b1a29044 | ||
|
|
b4ab8e095a | ||
|
|
ff4f4d4649 | ||
|
|
9987ff570b | ||
|
|
cff8235068 | ||
|
|
9ef132c33b | ||
|
|
ff8269575a | ||
|
|
7743d952dc | ||
|
|
944f3c1477 | ||
|
|
1d3bd02089 | ||
|
|
38de8e6936 | ||
|
|
6347fb6636 | ||
|
|
32e668eb94 | ||
|
|
c51f9ef940 | ||
|
|
1a91edecae | ||
|
|
c88308cf0e | ||
|
|
88837fb753 | ||
|
|
d0283ff354 | ||
|
|
f449a7ae2c | ||
|
|
a113b2c394 | ||
|
|
74851834c0 | ||
|
|
e377bb949a | ||
|
|
c905d3fe21 | ||
|
|
b6e9d235fe | ||
|
|
6968f7237a | ||
|
|
4a6c97463f | ||
|
|
6c912ac960 | ||
|
|
708cebe7f0 | ||
|
|
152023e837 | ||
|
|
0f16e19239 | ||
|
|
2c38e44e48 | ||
|
|
82739574b5 | ||
|
|
f78d677ab6 | ||
|
|
e39e2306d6 | ||
|
|
52229a21cb | ||
|
|
961f7dea82 | ||
|
|
feeeef71f1 | ||
|
|
65c4d550cb | ||
|
|
f9b4a8d6e8 | ||
|
|
e11d851aee | ||
|
|
ac81f0248c | ||
|
|
83bf15a703 | ||
|
|
cc960adbee | ||
|
|
c66c5828ea | ||
|
|
19387cafab | ||
|
|
7c0673279b | ||
|
|
7ce0d71a96 | ||
|
|
dd2542e96c | ||
|
|
21d60eab7c | ||
|
|
4d2320ba8b | ||
|
|
a4a74e9844 | ||
|
|
9588978028 | ||
|
|
479940b7d0 | ||
|
|
8cd967803c | ||
|
|
a0e1163fb6 | ||
|
|
8ccd1ee34a | ||
|
|
ca258c04cb | ||
|
|
30bd5e2669 | ||
|
|
38637897ba | ||
|
|
c727c8b684 | ||
|
|
993d96feef | ||
|
|
b2b26d9c95 | ||
|
|
cba3dd276b | ||
|
|
a47fc75c26 | ||
|
|
42cfdf246f | ||
|
|
e5c8794b8b | ||
|
|
ac90a873eb | ||
|
|
5ce68ad7fd | ||
|
|
099e88516d | ||
|
|
82a6ad2c10 | ||
|
|
c1a78224cf | ||
|
|
39f9350697 | ||
|
|
e31081d79d | ||
|
|
f02d282754 | ||
|
|
a89e0bab7d | ||
|
|
3a90af0bcd | ||
|
|
53ceb0791f | ||
|
|
2cd98a0d21 | ||
|
|
a0b10c05e5 | ||
|
|
04135fa6cd | ||
|
|
42dc6329e6 | ||
|
|
9b8ba97f9f | ||
|
|
7705605b5a | ||
|
|
414bcb0c73 | ||
|
|
f4710948c4 | ||
|
|
3f4488c589 | ||
|
|
9434fff215 | ||
|
|
695962fae8 | ||
|
|
8f13c8c3bf | ||
|
|
c1cae51fb5 | ||
|
|
31d16f6cc2 | ||
|
|
a50ea92c64 | ||
|
|
5b2ced0119 | ||
|
|
8a0ba093dd | ||
|
|
fbd8dfe60f | ||
|
|
60aff22931 | ||
|
|
5fc7da345d | ||
|
|
fd2c38fbef | ||
|
|
ba245c6c46 | ||
|
|
496027b589 | ||
|
|
8bd4f6568b | ||
|
|
9d7660b409 | ||
|
|
da55499db0 | ||
|
|
22f8e39b58 | ||
|
|
eba23bbac4 | ||
|
|
4550535cbb | ||
|
|
8432fd5ac2 | ||
|
|
7c948adf88 | ||
|
|
56b7045c20 | ||
|
|
b1a109a611 | ||
|
|
7a311a3b66 | ||
|
|
d55b6b9909 | ||
|
|
f4389fb322 | ||
|
|
331208bec1 | ||
|
|
7680e5f81d | ||
|
|
6acf038a84 | ||
|
|
bdf4e386cf | ||
|
|
ad8a34858f | ||
|
|
162eced7d2 | ||
|
|
bec1f7c26a | ||
|
|
8771617199 | ||
|
|
54bc995f0a | ||
|
|
6c89a81af4 | ||
|
|
8fa2898ff1 | ||
|
|
10ca0a6936 | ||
|
|
b3314e97a6 | ||
|
|
3b9a948045 | ||
|
|
3781a0f9ad | ||
|
|
e79b289325 | ||
|
|
6d4c1156d6 | ||
|
|
3f72c7fcc7 | ||
|
|
d521c84d55 | ||
|
|
946b070744 | ||
|
|
4a21dce2b5 | ||
|
|
5fe7f9fa93 | ||
|
|
65f34cd823 | ||
|
|
196e7e072b | ||
|
|
6f97663174 | ||
|
|
aed7a1818a | ||
|
|
b50d90183e | ||
|
|
6b06da76cb | ||
|
|
6ca6788bb7 | ||
|
|
2e23e61a45 | ||
|
|
9cdf490bc5 | ||
|
|
cfed671ea3 | ||
|
|
53ce92b9ca | ||
|
|
7350a29fec | ||
|
|
5cc2c62c66 | ||
|
|
4bc5ed6c76 | ||
|
|
e99a597899 | ||
|
|
73dde66dbe | ||
|
|
e30c0e7ca3 | ||
|
|
8fc200c0cc | ||
|
|
708296ae1b | ||
|
|
fbb5e75e01 | ||
|
|
f327b21557 | ||
|
|
45b7253f39 | ||
|
|
05bb655efc | ||
|
|
8fdfcfb562 | ||
|
|
e7c144eeac | ||
|
|
e98172d72d | ||
|
|
f2d063e7b9 | ||
|
|
a50f26b7f5 | ||
|
|
69294cf98a | ||
|
|
c397fb6c7a | ||
|
|
961b0b524e | ||
|
|
860fc200b0 | ||
|
|
109a9e3f35 | ||
|
|
5f971fea6e | ||
|
|
0d7abe3b9f | ||
|
|
94fbcd8ebe | ||
|
|
879d5dd236 | ||
|
|
34187e047d | ||
|
|
0ee722f8c3 | ||
|
|
b7d11141e1 | ||
|
|
e9babf3dac | ||
|
|
0bb81f2e9c | ||
|
|
bea0b050cd | ||
|
|
ce62980d4e | ||
|
|
dc88865908 | ||
|
|
9fbd931058 | ||
|
|
b0264bdb90 | ||
|
|
95d6f43cc8 | ||
|
|
9cb94eb4a9 | ||
|
|
bd0819330d | ||
|
|
8d9e83fd99 | ||
|
|
be02333529 | ||
|
|
506f121576 | ||
|
|
ca488cce87 | ||
|
|
11dc723324 | ||
|
|
dd6ea18901 | ||
|
|
3369eeb920 | ||
|
|
9032f49f25 | ||
|
|
fbc6f3f6e8 | ||
|
|
fba883839d | ||
|
|
a93214ea63 | ||
|
|
e6b0fc465b | ||
|
|
70fbcfee4a | ||
|
|
0b074d0fae | ||
|
|
d67e4d5b17 | ||
|
|
891c60d83d | ||
|
|
fe3e49c4eb | ||
|
|
58306a9d34 | ||
|
|
41091d9472 | ||
|
|
a4cfb5e1ed | ||
|
|
51aa59a737 | ||
|
|
8bedb419a9 | ||
|
|
f56a182b71 | ||
|
|
317b40ef90 | ||
|
|
e938ece492 | ||
|
|
02331a612c | ||
|
|
8317557f70 | ||
|
|
1bb7a2a295 | ||
|
|
215ca58d6a | ||
|
|
12f570a307 | ||
|
|
e4b619ce1a | ||
|
|
0a286153bb | ||
|
|
22d59e757d | ||
|
|
0daa2dbf59 | ||
|
|
96c2304ae8 | ||
|
|
343dd2f491 | ||
|
|
38f35463b7 | ||
|
|
5573986e8e | ||
|
|
f3367a1b20 | ||
|
|
a3c2f7b0e8 | ||
|
|
8fbec30998 | ||
|
|
a7466eeb0e | ||
|
|
8b1e819bf3 | ||
|
|
fe63664164 | ||
|
|
4598827dcb | ||
|
|
9afdb05bf4 | ||
|
|
9569a85cee | ||
|
|
54721b7c7b | ||
|
|
91d8bddbd1 | ||
|
|
80adc87a14 | ||
|
|
117ad1b65b | ||
|
|
2229509362 | ||
|
|
6ef8e87492 | ||
|
|
0a25083fdb | ||
|
|
15137d0327 | ||
|
|
8c9974bc0f | ||
|
|
079b6c2b50 | ||
|
|
0924b34a17 | ||
|
|
754f7a8a39 | ||
|
|
64bafe1dfe | ||
|
|
c3e456e7e6 | ||
|
|
57aaeafd2f | ||
|
|
3c2e1a87e2 | ||
|
|
da95a2d13f | ||
|
|
cc5e05fdeb | ||
|
|
a79c390cca | ||
|
|
8a56af439c | ||
|
|
f6e581ce54 | ||
|
|
8953f88780 | ||
|
|
4b4a98d5e5 | ||
|
|
7472818d94 | ||
|
|
ad44fe8d3e | ||
|
|
55e713db0a | ||
|
|
33322e6638 | ||
|
|
a1792ca0d1 | ||
|
|
ac8c43fe9c | ||
|
|
4d40ee00e9 | ||
|
|
06f59ad631 | ||
|
|
877e0cac03 | ||
|
|
ef67c39910 | ||
|
|
508710f4d1 | ||
|
|
3aa3d9cf14 | ||
|
|
c2fedb4c3f | ||
|
|
03dc65e12d | ||
|
|
b8d62a8068 | ||
|
|
dbc2368a7b | ||
|
|
54aed421b8 | ||
|
|
d5e8dac1cf | ||
|
|
96ec8fcba1 | ||
|
|
0663eb69ed | ||
|
|
0594d37230 | ||
|
|
3cc30bcc18 | ||
|
|
99c1a642a4 | ||
|
|
c60f54e5be | ||
|
|
483389f2e2 | ||
|
|
c0f2f84285 | ||
|
|
069d118329 | ||
|
|
a7b1834772 | ||
|
|
6415dec37a | ||
|
|
74253e1ddc | ||
|
|
01b3fb91e5 | ||
|
|
2dc917e87f | ||
|
|
0a284a1a10 | ||
|
|
5c8339e1e8 | ||
|
|
fd37418da2 | ||
|
|
bbfea29c2b | ||
|
|
aa703dc903 | ||
|
|
8cd80fec8c | ||
|
|
780882efcf | ||
|
|
c5636143c6 | ||
|
|
ba6218b354 | ||
|
|
8e32de3ba9 | ||
|
|
e37508fb8f | ||
|
|
b46a717425 | ||
|
|
7926df0b80 | ||
|
|
557df666fd | ||
|
|
f91b257f50 | ||
|
|
28a2d1eb3d | ||
|
|
a0ae1489e5 | ||
|
|
edfb310a59 | ||
|
|
a2312028b9 | ||
|
|
78f1f69d57 | ||
|
|
e1e33077e8 | ||
|
|
b3e7de010d | ||
|
|
f5b04865f4 | ||
|
|
bf1c6edb76 | ||
|
|
2ac7fee017 | ||
|
|
128256e3ab | ||
|
|
a66a7f3e97 | ||
|
|
20b79f8945 | ||
|
|
9a877734b2 | ||
|
|
409c391850 | ||
|
|
763ff085a6 | ||
|
|
5b9656374c | ||
|
|
b32014549c | ||
|
|
9476d212bc | ||
|
|
000928d34e | ||
|
|
6829ae5b13 | ||
|
|
f09756443d | ||
|
|
5512016885 | ||
|
|
21ecb941fe | ||
|
|
77e94087cf | ||
|
|
9ab1f25a47 | ||
|
|
aaab7aefbe | ||
|
|
5b8599e52d | ||
|
|
74e0c9ab9b | ||
|
|
dcce83b288 | ||
|
|
f731e7ea36 | ||
|
|
ec30bb89a4 | ||
|
|
7cd08594f6 | ||
|
|
2b4531f069 | ||
|
|
11544a62aa | ||
|
|
18550387d5 | ||
|
|
efb03e19e6 | ||
|
|
c27d0583d4 | ||
|
|
0de2b9a1b2 | ||
|
|
9dc29d7687 | ||
|
|
72871dbb9a | ||
|
|
4bda4e4e2b | ||
|
|
1971c54a50 | ||
|
|
bb77b89da0 | ||
|
|
b336e9a79f | ||
|
|
a2ab21e91c | ||
|
|
603437e70f | ||
|
|
db3a9a5990 | ||
|
|
24c7e73b4e | ||
|
|
c053e57068 | ||
|
|
6d20ec3bea | ||
|
|
c50627ee3e | ||
|
|
b3cd893f93 | ||
|
|
22d2dbc4e6 | ||
|
|
2b5d9428b1 | ||
|
|
d6446cb096 | ||
|
|
c34bdc33fb | ||
|
|
dd8547e51c | ||
|
|
aec023f537 | ||
|
|
e101eeeda9 | ||
|
|
f29522269d | ||
|
|
3c470a6b8b | ||
|
|
6bc7d711b3 | ||
|
|
10d5dd3a67 | ||
|
|
d9f1df01eb | ||
|
|
cdeab19673 | ||
|
|
22ee678136 | ||
|
|
50a8f13e85 | ||
|
|
6dec60b6e6 | ||
|
|
ac3a19138a | ||
|
|
21e8e071d7 | ||
|
|
57f84a8b4c | ||
|
|
8a672e70a7 | ||
|
|
22041bbcc4 | ||
|
|
5afb459113 | ||
|
|
91ebac0a00 | ||
|
|
5fcb1cfd31 | ||
|
|
3a90fb108c | ||
|
|
4eeb48f8e0 | ||
|
|
373d48c8a3 | ||
|
|
1472875670 | ||
|
|
74cfb207c1 | ||
|
|
6a096e7dc7 | ||
|
|
9788d85ea3 | ||
|
|
69c0e18685 | ||
|
|
3cac927348 | ||
|
|
9081346c40 | ||
|
|
fcfadb0e51 | ||
|
|
2add57cfed | ||
|
|
c5279ec630 | ||
|
|
b73699fad8 | ||
|
|
b8ebe54e53 | ||
|
|
c3d70e0795 | ||
|
|
8c4561b8da | ||
|
|
fd421ddd6f | ||
|
|
9947c01c8e | ||
|
|
a00db4267c | ||
|
|
36385010f5 | ||
|
|
fa6d082bfd | ||
|
|
9fab91852a | ||
|
|
b733e6e83c | ||
|
|
ce05bb69dc | ||
|
|
37aa5934a1 | ||
|
|
1647fc7edc | ||
|
|
7b92687397 | ||
|
|
419e5baf16 | ||
|
|
dc48cd841a | ||
|
|
b0e1776d6d | ||
|
|
7a7068ee47 | ||
|
|
cbc0ef459b | ||
|
|
a2aabfabd9 | ||
|
|
822cbd4b43 | ||
|
|
3c19a9308d | ||
|
|
32890342ce | ||
|
|
ed2c2a285f | ||
|
|
18e23bafd9 | ||
|
|
8b8be2695f | ||
|
|
96fbf18201 | ||
|
|
c8a57a8fa2 | ||
|
|
b1c2dacab3 | ||
|
|
65939913b4 | ||
|
|
08b3a466e8 | ||
|
|
5aa7c38035 | ||
|
|
1df5e31706 | ||
|
|
9f7044aed0 | ||
|
|
41de53996b | ||
|
|
9878031cfd | ||
|
|
e3fbcaeb72 | ||
|
|
ca6dd8be5e | ||
|
|
fba0924b1d | ||
|
|
703ed46d79 | ||
|
|
f7ca6c63c2 | ||
|
|
ad69b9907f | ||
|
|
b9269151a4 | ||
|
|
bfddbb8b40 | ||
|
|
13a2dc8485 | ||
|
|
1e51424e8a | ||
|
|
5b114b43f7 | ||
|
|
812f5a02d7 | ||
|
|
19f70dbfbf | ||
|
|
1c99fb106c | ||
|
|
71c32e9b48 | ||
|
|
380a59520b | ||
|
|
3995355150 | ||
|
|
8208bcb896 | ||
|
|
d665e855de | ||
|
|
18b3655c99 | ||
|
|
6a8d8f34a5 | ||
|
|
b1c1f02132 | ||
|
|
ea93643e6a | ||
|
|
e47e625f73 | ||
|
|
b13fcaf666 | ||
|
|
9458485e43 | ||
|
|
0ce9e00ffa | ||
|
|
c778c381b5 | ||
|
|
0d1fbef751 | ||
|
|
b48bbe08b2 | ||
|
|
cce90cb2b7 | ||
|
|
07b1281046 | ||
|
|
3515b99671 | ||
|
|
6a67c277eb | ||
|
|
1067dd3011 | ||
|
|
7cafdc0512 | ||
|
|
8a57b6bced | ||
|
|
6f0ed2a091 | ||
|
|
53abd25116 | ||
|
|
1ea7826c0e | ||
|
|
97f4cf48f8 | ||
|
|
0cde37be50 | ||
|
|
6aeca54ece | ||
|
|
124e28578c | ||
|
|
62c9e500de | ||
|
|
02cc18ff29 | ||
|
|
ba4566e86d | ||
|
|
87cb0081ec | ||
|
|
4a6af93c83 | ||
|
|
a2f0771fd3 | ||
|
|
af564b8ba0 | ||
|
|
adb8be069e | ||
|
|
7c8746732b | ||
|
|
f506117edb | ||
|
|
1c5af7c31a | ||
|
|
3a0125fa1f | ||
|
|
328cb0a076 | ||
|
|
c3c8441a1d | ||
|
|
fa2a8d7209 | ||
|
|
e3ea97c964 | ||
|
|
7219ae530e | ||
|
|
8f8f8854f0 | ||
|
|
4c6869cd9a | ||
|
|
bc7c0d8624 | ||
|
|
11dfc817f3 | ||
|
|
dde1a12819 | ||
|
|
065fd094d1 | ||
|
|
d09351bb10 | ||
|
|
25d38e030b | ||
|
|
9ebd10d3f4 | ||
|
|
8a9b886011 | ||
|
|
21f0d6b549 | ||
|
|
3ba927edae | ||
|
|
c4ca0a5a57 | ||
|
|
406ea4f281 | ||
|
|
64aa7feabd | ||
|
|
875f4076ec | ||
|
|
4643efe6be | ||
|
|
b760137e1d | ||
|
|
5f56f441bf | ||
|
|
96a22bfcbb | ||
|
|
6c59b33fb1 | ||
|
|
dfaf735073 | ||
|
|
0d2b7bf94d | ||
|
|
7fcfde7f07 | ||
|
|
b1391d1991 | ||
|
|
49c8e16410 | ||
|
|
0e93681589 | ||
|
|
eb55034dfe | ||
|
|
c45bc32d98 | ||
|
|
6e860b6dc5 | ||
|
|
b732a673dc | ||
|
|
b6b6d6e8d8 | ||
|
|
23e4895dfc | ||
|
|
8666c55ca6 | ||
|
|
a3f00c5d5e | ||
|
|
26c23b30f4 | ||
|
|
a436fd513b | ||
|
|
3bc34ffd94 | ||
|
|
533cd8d6df | ||
|
|
cb089dcb52 | ||
|
|
e0329cfdbb | ||
|
|
239ccc9c40 | ||
|
|
b762fbaf21 | ||
|
|
0285df5a02 | ||
|
|
45fb375c41 | ||
|
|
4a4950fe41 | ||
|
|
1664fd8bb1 | ||
|
|
21cdd2bf5d | ||
|
|
0153f96a20 | ||
|
|
a7a7533190 | ||
|
|
311380f8cb | ||
|
|
b0f0e53bba | ||
|
|
004f1e2f66 | ||
|
|
2fa561f22e | ||
|
|
81be718674 | ||
|
|
8162fd1e20 | ||
|
|
49a1e2f98e | ||
|
|
684c46369c | ||
|
|
715c9e3ca9 | ||
|
|
73edd5b8fd | ||
|
|
5e5bdf5432 | ||
|
|
48a3e9bc82 | ||
|
|
f13cfcb83e | ||
|
|
9c0e8cd15b | ||
|
|
ad2a70ba06 | ||
|
|
731e03fe5a | ||
|
|
f9d029c8fa | ||
|
|
7057d00a28 | ||
|
|
114fab4c70 | ||
|
|
c2edbfae55 | ||
|
|
a92cb66468 | ||
|
|
535f97ba61 | ||
|
|
14ebd82dbd | ||
|
|
aea7b08a47 | ||
|
|
47dcfcbdd4 | ||
|
|
bf3901342c | ||
|
|
e1731d9403 | ||
|
|
b28bcad11b | ||
|
|
a7c71e4c6b | ||
|
|
1a42693d68 | ||
|
|
e7b60c4d65 | ||
|
|
f95129894d | ||
|
|
c32c71c836 | ||
|
|
14e1ace552 | ||
|
|
a7fb3a3853 | ||
|
|
2da4bd5f1a | ||
|
|
7e76d66184 | ||
|
|
7764f4a8e3 | ||
|
|
e1094dde08 | ||
|
|
4894c67196 | ||
|
|
d004c45386 | ||
|
|
6624f970c0 | ||
|
|
de684dc122 | ||
|
|
331bdc2245 | ||
|
|
e12ab486a2 | ||
|
|
9eeee92d36 | ||
|
|
756d6aa729 | ||
|
|
bddd53d6d2 | ||
|
|
c0a5bdaed9 | ||
|
|
a99cd825ab | ||
|
|
6426b74770 | ||
|
|
4f257bf1e6 | ||
|
|
73a056999c | ||
|
|
0120ff93bc | ||
|
|
49638fa533 | ||
|
|
76510dac8a | ||
|
|
7a3a7b19e5 | ||
|
|
24e86d0c59 | ||
|
|
9b5c2c386a | ||
|
|
d118031ed6 | ||
|
|
341a89c00d | ||
|
|
df29d25e6b | ||
|
|
3e196fa7b3 | ||
|
|
04c792476f | ||
|
|
005a4a275a | ||
|
|
bdddf597f6 | ||
|
|
bb6921bf9c | ||
|
|
bb63375f1b | ||
|
|
4f89e5bba9 | ||
|
|
183428db03 | ||
|
|
fc6d873758 | ||
|
|
5e2f8d7a42 | ||
|
|
9b9871cfbb | ||
|
|
f80b6926d3 | ||
|
|
6dc55fe5ed | ||
|
|
2d1cda2061 | ||
|
|
a566bcf613 | ||
|
|
f6040dffaf | ||
|
|
9885a0a6af | ||
|
|
f64d62b01d | ||
|
|
82075e8e3a | ||
|
|
5b7c83341b | ||
|
|
8522905d97 | ||
|
|
524ed7ccd0 | ||
|
|
fb49aead9b | ||
|
|
85f5700e4e | ||
|
|
43b3c093ef | ||
|
|
bd6842d917 | ||
|
|
e8c98c3246 | ||
|
|
dfd7cca0d2 | ||
|
|
af3d99e35f | ||
|
|
f6186965c3 | ||
|
|
90c2129f44 | ||
|
|
69e131ee69 | ||
|
|
28a01f0320 | ||
|
|
6d0bc5ab1e | ||
|
|
7af78af1f0 | ||
|
|
45a717a142 | ||
|
|
cb1ec0a0d9 | ||
|
|
abb1f22057 | ||
|
|
73efe436a5 | ||
|
|
f41edb23e2 | ||
|
|
6335a48a53 | ||
|
|
e20aab25ec | ||
|
|
66bea3942a | ||
|
|
08acd9c43d | ||
|
|
ff5988f4e0 | ||
|
|
1bf23374a3 | ||
|
|
899b429094 | ||
|
|
c47ff44f5e | ||
|
|
8af0773baf | ||
|
|
37cbd114de | ||
|
|
2dbb1cff4a | ||
|
|
6efcf9c982 | ||
|
|
0bc34952eb | ||
|
|
f6b48ed02a | ||
|
|
e37c4efc6e | ||
|
|
22f5bc643c | ||
|
|
15fd5ce2fa | ||
|
|
7f782983ca | ||
|
|
9d628346eb | ||
|
|
bde533a9c7 | ||
|
|
2fcb75d86d | ||
|
|
aae6846413 | ||
|
|
5317a0b755 | ||
|
|
73de721a63 | ||
|
|
d2f5c3621f | ||
|
|
1818764840 | ||
|
|
d3e5e607a7 | ||
|
|
c1943ea3af | ||
|
|
2a82c15bf1 | ||
|
|
87b6fb37d6 | ||
|
|
21fbe88e1f | ||
|
|
1f8b9b4bd5 | ||
|
|
fcbed41cc3 | ||
|
|
216069d0da | ||
|
|
eefa047974 | ||
|
|
d8dad5c9ea | ||
|
|
bf8a68879c | ||
|
|
f3248a4b37 | ||
|
|
d315d012a4 | ||
|
|
bd9bf3693f | ||
|
|
15daa2e74a | ||
|
|
74759b05a5 | ||
|
|
82ce78a17c | ||
|
|
9af6c6ceef | ||
|
|
021372cc4c | ||
|
|
b94ab07c2f | ||
|
|
7605d07bb2 | ||
|
|
ccc5801112 | ||
|
|
7c72b25ef0 | ||
|
|
02c2ec3027 | ||
|
|
65c31fab12 | ||
|
|
b6b68be052 | ||
|
|
15911c85f6 | ||
|
|
5a1612fe32 | ||
|
|
bbb7ae156c | ||
|
|
f9b8d1c699 | ||
|
|
1443b5927a | ||
|
|
35ef35b5c1 | ||
|
|
6806537eb3 | ||
|
|
64de61d15d | ||
|
|
22b7c8cd8a | ||
|
|
c4d0c49a5f | ||
|
|
142a5b0dcd | ||
|
|
25db1e4eca | ||
|
|
47a48b6832 | ||
|
|
e98309eb75 | ||
|
|
87051872a7 | ||
|
|
a2aed12dcd | ||
|
|
d8e6e76e89 | ||
|
|
8c33fdf5f4 | ||
|
|
ad4e511026 | ||
|
|
4a562d6732 | ||
|
|
a9082e4f79 | ||
|
|
6278679ffd | ||
|
|
0474791cf8 | ||
|
|
69f819e199 | ||
|
|
f32efd5429 | ||
|
|
22c247a988 | ||
|
|
35d71682f6 | ||
|
|
3d6b88a60e | ||
|
|
26a0803388 | ||
|
|
ae95384dd8 | ||
|
|
0f0dcf0c5e | ||
|
|
6f2406b0b6 | ||
|
|
bb24346e04 | ||
|
|
be45ffd8a4 | ||
|
|
f986b0c493 | ||
|
|
c9e87f0548 | ||
|
|
43468f4d47 | ||
|
|
91987d6f7a | ||
|
|
6b7c98bd0f | ||
|
|
b829e80ecb | ||
|
|
6e38d0f3ab | ||
|
|
38342b1df5 | ||
|
|
dbd4c2425e | ||
|
|
49ce85ee3d | ||
|
|
eba378e4a1 | ||
|
|
442c50ff00 | ||
|
|
d1448adbda | ||
|
|
5a21b1f353 | ||
|
|
123a2fb3a8 | ||
|
|
2f9e2147f5 | ||
|
|
75c6fc4f02 | ||
|
|
f9e07d6143 | ||
|
|
1436858347 | ||
|
|
8030e12ba5 | ||
|
|
a485b923bf | ||
|
|
0649aca219 | ||
|
|
b210ea79bc | ||
|
|
68f80b5fe7 | ||
|
|
e95825a42e | ||
|
|
931712dc46 | ||
|
|
54e544e03e | ||
|
|
f86b9abf32 | ||
|
|
9ef7eda33a | ||
|
|
c9e26401fa | ||
|
|
e53f49e9a9 | ||
|
|
14f6ac9222 | ||
|
|
b8474295af | ||
|
|
817e85a3e0 | ||
|
|
fb5ce3b87a | ||
|
|
6fe028b7c5 | ||
|
|
1cd7f1e38d | ||
|
|
043fd8b536 | ||
|
|
669acbb032 | ||
|
|
086d8f036e | ||
|
|
394690dcfb | ||
|
|
398bca92ff | ||
|
|
fb328b1a64 | ||
|
|
563f667e30 | ||
|
|
c839b64f6a | ||
|
|
6425fec366 | ||
|
|
d5059840ef | ||
|
|
65cba212e8 | ||
|
|
7a69c9c75a | ||
|
|
4a425cbac1 | ||
|
|
615169c4ec | ||
|
|
5cd9dcb844 | ||
|
|
54c5c88fe6 | ||
|
|
443250d135 | ||
|
|
9b5829c16e | ||
|
|
d749aaab69 | ||
|
|
62df731006 | ||
|
|
d0a0eb9738 | ||
|
|
66156b8230 | ||
|
|
5677f73794 | ||
|
|
ef54200db7 | ||
|
|
7875efbf61 | ||
|
|
3e128c116e | ||
|
|
55a3310446 | ||
|
|
fc03be7891 | ||
|
|
b1b00a5055 | ||
|
|
2920b0fc6d | ||
|
|
a30a55f3b1 | ||
|
|
ecfb18b26a | ||
|
|
41fa8fa2d2 | ||
|
|
e94e6adf91 | ||
|
|
7d433f16c4 | ||
|
|
b06d7bf834 | ||
|
|
b784e458cb | ||
|
|
9d96b18df0 | ||
|
|
ad2ab6eb3e | ||
|
|
f037c9b286 | ||
|
|
85912985b6 | ||
|
|
876f51a708 | ||
|
|
f7d29b4a53 | ||
|
|
06557fe8be | ||
|
|
2131046427 | ||
|
|
aaf1abc993 | ||
|
|
413549bcf5 | ||
|
|
9a799065b3 | ||
|
|
fd2959fa3a | ||
|
|
07927e032a | ||
|
|
15bec32bb4 | ||
|
|
e2b7a08c10 | ||
|
|
ef2fc0f99e | ||
|
|
d063596430 | ||
|
|
bd2dc6c670 | ||
|
|
684399433b | ||
|
|
b62791617c | ||
|
|
e07c2ab868 | ||
|
|
203755793c | ||
|
|
883c98e26f | ||
|
|
f5a20a5d06 | ||
|
|
ef7177ebbd | ||
|
|
3637aad36e | ||
|
|
77db9686fb | ||
|
|
c326e5a34e | ||
|
|
c23c982593 | ||
|
|
a3d666356c | ||
|
|
3cdbc2f414 | ||
|
|
b92cdea578 | ||
|
|
5e629a99af | ||
|
|
99c4ffa34f | ||
|
|
a7f266c907 | ||
|
|
57acacd5a7 | ||
|
|
42fb3cd95e | ||
|
|
855ed642c3 | ||
|
|
629503ff73 | ||
|
|
e3a070e3de | ||
|
|
5b364bca1f | ||
|
|
c5c1426262 | ||
|
|
be18d435a2 | ||
|
|
7eea6cdb12 | ||
|
|
824c55b3a4 | ||
|
|
76913a9fd5 | ||
|
|
2f44dac14f | ||
|
|
5569acd95c | ||
|
|
1d0211d395 | ||
|
|
06cd0a636e | ||
|
|
7f7b489a3d | ||
|
|
bb6f4d7633 | ||
|
|
6e24dff26a | ||
|
|
e372e4e592 | ||
|
|
9571b0825e | ||
|
|
90e2cc3d4c | ||
|
|
0c0820caef | ||
|
|
9112ca4e29 | ||
|
|
8203cb9990 | ||
|
|
d5bce978a8 | ||
|
|
ec84bad882 | ||
|
|
b53376a3a4 | ||
|
|
0ec722bc54 | ||
|
|
4640b13c66 | ||
|
|
1704abaf6b | ||
|
|
ab34f0065c | ||
|
|
e8c0a50862 | ||
|
|
b963f69f34 | ||
|
|
02d8f3cdc8 | ||
|
|
7ae69accc0 | ||
|
|
701b89f377 | ||
|
|
46d45a6923 | ||
|
|
7fad0c8b41 | ||
|
|
6e27264c6b | ||
|
|
5c83c9724f | ||
|
|
d5aff735be | ||
|
|
98c26df53e | ||
|
|
2448a9e047 | ||
|
|
b28d391a22 | ||
|
|
c8b92f6067 | ||
|
|
e7cac8acef | ||
|
|
31b5acc245 | ||
|
|
6105997299 | ||
|
|
8c874884fc | ||
|
|
ebfe81e5fd | ||
|
|
0b7ca094e4 | ||
|
|
72802a5972 | ||
|
|
b1f3935c5b | ||
|
|
dbd53af369 | ||
|
|
b09fe0e50e | ||
|
|
fae9000304 | ||
|
|
8fd07bcd51 | ||
|
|
6addc7a35d | ||
|
|
477230c82e | ||
|
|
d1737199ed | ||
|
|
61101d82d9 | ||
|
|
cebb948da2 | ||
|
|
c61c4b71b2 | ||
|
|
84f31ed45d | ||
|
|
8a81e317d6 | ||
|
|
224d9a752f | ||
|
|
0db34e4b85 | ||
|
|
8a9b9832fd | ||
|
|
f66625be67 | ||
|
|
6825bd7e75 | ||
|
|
18515a4e3b | ||
|
|
839b9c9271 | ||
|
|
dd9ed85e22 | ||
|
|
e96c88e914 | ||
|
|
6c1410f7f5 | ||
|
|
f92450d8b3 | ||
|
|
c133979b8e | ||
|
|
a9269cee29 | ||
|
|
cf42ede92c | ||
|
|
958a480e53 | ||
|
|
62151a751d | ||
|
|
f1ab9df2ee | ||
|
|
a42650c065 | ||
|
|
bdad3730f7 | ||
|
|
a5835cecbf | ||
|
|
b19620b324 | ||
|
|
cd6dec49c0 | ||
|
|
d350654aee | ||
|
|
6877578bbc | ||
|
|
10693fddfa | ||
|
|
f3682b6149 | ||
|
|
056ca0c68e | ||
|
|
25f7a8e406 | ||
|
|
1f1c267b6c | ||
|
|
eab1dc927b | ||
|
|
fc94ea1ced | ||
|
|
67d2cf8f30 | ||
|
|
2c85b84cbc | ||
|
|
09a25ea7b7 | ||
|
|
260a63ca73 | ||
|
|
d3f70ea340 | ||
|
|
ceebd35ef7 | ||
|
|
91b6fe1af3 | ||
|
|
9803f68522 | ||
|
|
47b7469a60 | ||
|
|
4c204707fd | ||
|
|
8fd6be0827 | ||
|
|
c06e0bfef9 | ||
|
|
8625a9dbb3 | ||
|
|
2b71b659e0 | ||
|
|
0320ac43cb | ||
|
|
111c7d4026 | ||
|
|
62c3df0ca3 | ||
|
|
ae011663e8 | ||
|
|
12591cd241 | ||
|
|
0499e1c4b0 | ||
|
|
3158f2d12e | ||
|
|
51f7f9aaa3 | ||
|
|
6e359c586e | ||
|
|
699a24f7e5 | ||
|
|
5fa3665074 | ||
|
|
3b7781835e | ||
|
|
5fe1b46bfd | ||
|
|
f65cce4317 | ||
|
|
27d0d22e5d | ||
|
|
407c9ddcbf | ||
|
|
d90d0c8931 | ||
|
|
216a471bbb | ||
|
|
a7b7860e0e | ||
|
|
d703daa480 | ||
|
|
dc8fdcb9c9 | ||
|
|
7a6c4e438e | ||
|
|
c468b4e2a8 | ||
|
|
b04956a676 | ||
|
|
518f6e4d39 | ||
|
|
483b226cc1 | ||
|
|
13151cbb2b | ||
|
|
8e02660a0d | ||
|
|
16feef2a2c | ||
|
|
66ff17e452 | ||
|
|
4c5edacae2 | ||
|
|
8b4d0255b7 | ||
|
|
58c129f94a | ||
|
|
74040b457b | ||
|
|
c259a8ea38 | ||
|
|
2d51e42305 | ||
|
|
5e3bfd2148 | ||
|
|
8b0ab6ead6 | ||
|
|
b1b0aadabf | ||
|
|
ac7d9c449a | ||
|
|
1346561b9d | ||
|
|
4bc52897b2 | ||
|
|
035791669e | ||
|
|
6017b63a06 | ||
|
|
11d04279c8 | ||
|
|
0448728228 | ||
|
|
12047702f5 | ||
|
|
fb1492f531 | ||
|
|
d14ead7bec | ||
|
|
05444a0f6a | ||
|
|
b3c54ec81e | ||
|
|
6c11dbffd5 | ||
|
|
3b5dbf9046 | ||
|
|
09c733677a | ||
|
|
8d6558b236 | ||
|
|
67f4ba154a | ||
|
|
440ad20c1d | ||
|
|
31fba6f434 | ||
|
|
280442e533 | ||
|
|
850a945a18 | ||
|
|
46f9049fb4 | ||
|
|
58266c9e2c | ||
|
|
d1e775313d | ||
|
|
a65df1e67b | ||
|
|
e700be8cd6 | ||
|
|
3fdd574f54 | ||
|
|
e0f4dd6027 | ||
|
|
de02eca467 | ||
|
|
50dbd2cacc | ||
|
|
c2f9cc5824 | ||
|
|
c7f7e67a10 | ||
|
|
628042e65e | ||
|
|
cde7eeb660 | ||
|
|
6305b206e1 | ||
|
|
d85da9236e | ||
|
|
9800760cb3 | ||
|
|
5c087bdcad | ||
|
|
a547bf517d | ||
|
|
b984bf8d1a | ||
|
|
18f9cccfa7 | ||
|
|
fb6ab1cca2 | ||
|
|
56c57e2c53 | ||
|
|
a6057c35cc | ||
|
|
901887e6bf | ||
|
|
ee54643004 | ||
|
|
0a17acdb34 | ||
|
|
72e5212842 | ||
|
|
714283fae2 | ||
|
|
3423028713 | ||
|
|
9d062b37d7 | ||
|
|
4636d3a9c3 | ||
|
|
c95ede35c1 | ||
|
|
7415e1aa56 | ||
|
|
f350953a19 | ||
|
|
958bba5b42 | ||
|
|
0f2b95b497 | ||
|
|
d07089ceac | ||
|
|
47dfa62384 | ||
|
|
3a3265cf88 | ||
|
|
0ff931dc76 | ||
|
|
4d708cebe9 | ||
|
|
4d7c8e3bb8 | ||
|
|
8cde38404d | ||
|
|
fe7bf6cbbc | ||
|
|
8b4eb2304b | ||
|
|
ae029191a3 | ||
|
|
bfedea9bad | ||
|
|
7777d3b43a | ||
|
|
9ed4fc9687 | ||
|
|
b49b39e99d | ||
|
|
cd3a2de5a3 | ||
|
|
6e8960ccdd | ||
|
|
e05f3d5d84 | ||
|
|
94c6cb1323 | ||
|
|
3f81cd1b22 | ||
|
|
8da0f4c5bb | ||
|
|
9acf1024e4 | ||
|
|
b21d3f9b82 | ||
|
|
2bbf380262 | ||
|
|
f678bcf7ba | ||
|
|
a0f06eac2a | ||
|
|
83fe1a2732 | ||
|
|
5c98223c89 | ||
|
|
663a0b7783 | ||
|
|
6efe4d1df6 | ||
|
|
fb17f97cf3 | ||
|
|
6b65ba1551 | ||
|
|
9202c6e26a | ||
|
|
59a5456091 | ||
|
|
8bfe972bab | ||
|
|
fd6622458b | ||
|
|
8a08861dd9 | ||
|
|
82dcfd4e10 | ||
|
|
b66d7dc708 | ||
|
|
eebdd2b31d | ||
|
|
b94733ab31 | ||
|
|
7f2c90a0ed | ||
|
|
84bb7d05a9 | ||
|
|
98a84d88e2 | ||
|
|
e470268c7c | ||
|
|
3a6cd4f73d | ||
|
|
6ea150fd68 | ||
|
|
a7188bc9d0 | ||
|
|
e1e9ddd4a4 | ||
|
|
a1dd08f2e6 | ||
|
|
d136ac0596 | ||
|
|
c33a237067 | ||
|
|
eb7d3da994 | ||
|
|
37134e42d4 | ||
|
|
626a4efaad | ||
|
|
0c1f8b4e0f | ||
|
|
857674c3a0 | ||
|
|
15a75bd79b | ||
|
|
74887c7372 | ||
|
|
31188e9327 | ||
|
|
ee6d96eb46 | ||
|
|
11fe2fd79a | ||
|
|
c2863cc6ef | ||
|
|
d6d01067a0 | ||
|
|
1d3b18c3f4 | ||
|
|
a15b6f21b8 | ||
|
|
689179bf18 | ||
|
|
bf749eec61 | ||
|
|
d0f4cc89a5 | ||
|
|
d65debb6bc | ||
|
|
72daccd468 | ||
|
|
b363400587 | ||
|
|
6b41f941b6 | ||
|
|
b9f5f9ba3f | ||
|
|
c8ffa59d28 | ||
|
|
1141187bf2 | ||
|
|
52aeebebea | ||
|
|
e101384aa4 | ||
|
|
71f02adfca | ||
|
|
9de26531e4 | ||
|
|
fadc46b906 | ||
|
|
b1d98febfd | ||
|
|
1828fb212a | ||
|
|
c97f50e274 | ||
|
|
be92046dfd | ||
|
|
990fc415f7 | ||
|
|
d8daabae9b | ||
|
|
84fe4fd156 | ||
|
|
747d475e76 | ||
|
|
095b518802 | ||
|
|
0319ae756a | ||
|
|
11c7ecb5cf | ||
|
|
422c396d73 | ||
|
|
ffd57fde90 | ||
|
|
a451d1cb8d | ||
|
|
14cf8f1b22 | ||
|
|
5996c8c4d5 | ||
|
|
21885f9457 | ||
|
|
6ac48aff46 | ||
|
|
85ff76e7b0 | ||
|
|
e47a31f9fc | ||
|
|
517fcd423d | ||
|
|
b780359598 | ||
|
|
aa8b9572b9 | ||
|
|
8ca14e6267 | ||
|
|
876e1a91b2 | ||
|
|
0b7989aa4b | ||
|
|
2278fc8f47 | ||
|
|
a91f353621 | ||
|
|
cdb1b48ad9 | ||
|
|
a24037bfec | ||
|
|
2d0f30f062 | ||
|
|
cea2ca8c8e | ||
|
|
f713436dd0 | ||
|
|
b923a62425 | ||
|
|
67fce4a5b3 | ||
|
|
eaa65b7ade | ||
|
|
820d94447c | ||
|
|
ed20134a7b | ||
|
|
d19cbc81b5 | ||
|
|
1fd7946dce | ||
|
|
027ff0f3a8 | ||
|
|
8fa80874a6 | ||
|
|
430669cfad | ||
|
|
54b561898f | ||
|
|
65c104a589 | ||
|
|
0a0416b6ea | ||
|
|
441babdc41 | ||
|
|
1bf1fafc86 | ||
|
|
50d58e9b2d | ||
|
|
e64b9f6751 | ||
|
|
d67a846ec4 | ||
|
|
ca2a1c3f60 | ||
|
|
93fbb228bf | ||
|
|
3683673fb0 | ||
|
|
f37a5b6dae | ||
|
|
31b0decd46 | ||
|
|
eb561e1c05 | ||
|
|
54c9ecff5b | ||
|
|
edcd72585d | ||
|
|
1a17fc17bb | ||
|
|
ddad231921 | ||
|
|
e73894fa50 | ||
|
|
03b94f907f | ||
|
|
3fa7218c44 | ||
|
|
0f591d245d | ||
|
|
1b02e046c2 | ||
|
|
d08e3cc895 | ||
|
|
d98116559b | ||
|
|
71c95ad0d0 | ||
|
|
5c1a4ba5f9 | ||
|
|
698862ec5d | ||
|
|
b4ef5ff294 | ||
|
|
3db658e51e | ||
|
|
5a9f7516d6 | ||
|
|
3039fd4519 | ||
|
|
095fc0561d | ||
|
|
beb1924437 | ||
|
|
c8e1154f1e | ||
|
|
b204c2dbec | ||
|
|
b22b39de96 | ||
|
|
c242e6c391 | ||
|
|
e05205756f | ||
|
|
d03b244fcd | ||
|
|
5ef679d8f1 | ||
|
|
d33c527e39 | ||
|
|
7bc95c47a3 | ||
|
|
475a88b555 | ||
|
|
9815dac48f | ||
|
|
1ece3d1dfe | ||
|
|
2146ed4033 | ||
|
|
52b88b52f0 | ||
|
|
ebd4388cca | ||
|
|
57fd02ee57 | ||
|
|
0333412148 | ||
|
|
1c85652cff | ||
|
|
e0086c1be7 | ||
|
|
b29e159604 | ||
|
|
7883e55da2 | ||
|
|
b197623ed2 | ||
|
|
a15a2556c3 | ||
|
|
14d29b77ae | ||
|
|
a2514ffeed | ||
|
|
f1bbb7fef5 | ||
|
|
72394a8319 | ||
|
|
1cd8e1d8b6 | ||
|
|
62cd918061 | ||
|
|
6a04067514 | ||
|
|
49b3908635 | ||
|
|
75faef888e | ||
|
|
b67d97b1ba | ||
|
|
b8943fdf19 | ||
|
|
f93183f66e | ||
|
|
2937711390 | ||
|
|
aa56c6d51d | ||
|
|
27417459fb | ||
|
|
5b8fe2e89a | ||
|
|
acc9c033ed | ||
|
|
8528b265a9 | ||
|
|
44250f1a52 | ||
|
|
f7560670d9 | ||
|
|
3891885800 | ||
|
|
b882310e2b | ||
|
|
de0b43de32 | ||
|
|
48152a56ac | ||
|
|
29dd7f1d68 | ||
|
|
6423e4c767 | ||
|
|
1dd8f0e8f3 | ||
|
|
2fa35def2c | ||
|
|
34167c51d5 | ||
|
|
a5f8af4efb | ||
|
|
5a218f38a1 | ||
|
|
e57e946206 | ||
|
|
b4f71362e9 | ||
|
|
ed37b7a9d5 | ||
|
|
6511021fbe | ||
|
|
6197ba851b | ||
|
|
3ae1f9d852 | ||
|
|
0db1930f48 | ||
|
|
89db3fdb5d | ||
|
|
80fc3a8a52 | ||
|
|
988a2e8fed | ||
|
|
2433698372 | ||
|
|
5d7e8f79ed | ||
|
|
bad229e16e | ||
|
|
d37e514733 | ||
|
|
c73ea27ed7 | ||
|
|
0159b56717 | ||
|
|
9e6cc847f8 | ||
|
|
709eb283d9 | ||
|
|
76dde82b41 | ||
|
|
939c0100a6 | ||
|
|
2d60bf8c50 | ||
|
|
37e20f6ef2 | ||
|
|
76905b7a67 | ||
|
|
a469e6768d | ||
|
|
2fc182d8e6 | ||
|
|
a2cbeaa9e6 | ||
|
|
444ff20bc5 | ||
|
|
20ef5e7a6a | ||
|
|
c233c8e329 | ||
|
|
e06127566d | ||
|
|
dfe73629a3 | ||
|
|
b03dd1af17 | ||
|
|
4bc367c490 | ||
|
|
3eb2d086b2 | ||
|
|
70986b6e6e | ||
|
|
8edc2faaa9 | ||
|
|
ebe395788b | ||
|
|
12fd6678ee | ||
|
|
90d35b70b4 | ||
|
|
9f71369b67 | ||
|
|
04ae9058ed | ||
|
|
a30cfdd88f | ||
|
|
1bae32dc96 | ||
|
|
932d2c3c62 | ||
|
|
52f4124678 | ||
|
|
8d8d07ac5c | ||
|
|
44735be38e | ||
|
|
1ef1b2ba50 | ||
|
|
6fdbd778d5 | ||
|
|
419f351df3 | ||
|
|
180d6b30ca | ||
|
|
3fd9059b4e | ||
|
|
a713aee3d5 | ||
|
|
a9f5b58a01 | ||
|
|
d882ba2cb4 | ||
|
|
90e37a8745 | ||
|
|
6086f45d25 | ||
|
|
d6351879f3 | ||
|
|
5655272f5a | ||
|
|
9b35c72349 | ||
|
|
98cffbce03 | ||
|
|
1cd875de1e | ||
|
|
5a8df7efb3 | ||
|
|
c84e2939e4 | ||
|
|
641ab24aec | ||
|
|
71133105d7 | ||
|
|
625677b189 | ||
|
|
76943ac05e | ||
|
|
87cbd41265 | ||
|
|
be92cf5959 | ||
|
|
cc1d8f0057 | ||
|
|
1f1dcdce65 | ||
|
|
98a67a3776 | ||
|
|
9b1e70e4f9 | ||
|
|
09d4f8cd0f | ||
|
|
53cbc020b9 | ||
|
|
63fc6ba2cd | ||
|
|
ce53d7f6c2 | ||
|
|
fe8eed963e | ||
|
|
97eb7dbf5f | ||
|
|
59f877fc64 | ||
|
|
f96fe9773c | ||
|
|
04948b4d55 | ||
|
|
98ba622679 | ||
|
|
08103870a5 | ||
|
|
993e586855 | ||
|
|
58ec835af0 | ||
|
|
6aea950d74 | ||
|
|
7198be5be9 | ||
|
|
3661aaf8a1 | ||
|
|
a22b4adf4c | ||
|
|
b7bb122be8 | ||
|
|
8441a3bf5f | ||
|
|
853c4de75a | ||
|
|
3597af789e | ||
|
|
4c9cac0b47 | ||
|
|
1a0b68498b | ||
|
|
5246e3be84 | ||
|
|
8a07000e58 | ||
|
|
3bb82ef60d | ||
|
|
c8a221a9a7 | ||
|
|
91f45c4aa6 | ||
|
|
7c5e4da90c | ||
|
|
d6bc141bd1 | ||
|
|
7ac64ad24a | ||
|
|
14e52f29b0 | ||
|
|
344ae9f84e | ||
|
|
f7db12c7ef | ||
|
|
962d1f1a71 | ||
|
|
6d76db9d6c | ||
|
|
00857f8f59 | ||
|
|
66239f30ce | ||
|
|
bf89f79694 | ||
|
|
ce299b47ea | ||
|
|
6dc7109a9f | ||
|
|
bdcb485740 | ||
|
|
e32b948a49 | ||
|
|
4fe9cbb973 | ||
|
|
5b242f1d11 | ||
|
|
34d28dd79f | ||
|
|
6eef9b4a23 | ||
|
|
5f1999cc71 | ||
|
|
40a2c6b882 | ||
|
|
7ba281728f | ||
|
|
7b7356f04c | ||
|
|
bbc312fce6 | ||
|
|
1b0dfb0f58 | ||
|
|
7260241511 | ||
|
|
3b1a9b9fdf | ||
|
|
52769e1e71 | ||
|
|
72afc2727a | ||
|
|
808739867c | ||
|
|
752e18e795 | ||
|
|
76d822bf1e | ||
|
|
ddeca9f12a | ||
|
|
19d0340ddf | ||
|
|
21251d8c22 | ||
|
|
1f3db03bf0 | ||
|
|
944c62daf4 | ||
|
|
9547b7d0e9 | ||
|
|
76c4ea7682 | ||
|
|
808ecfe0f2 | ||
|
|
2894dd4d1a | ||
|
|
797fa7f97b | ||
|
|
fd8750e959 | ||
|
|
7be65f66b8 | ||
|
|
4f5d38a4b1 | ||
|
|
7e73fc2870 | ||
|
|
d2c9a9e395 | ||
|
|
0d49b365ff | ||
|
|
7721595aa9 | ||
|
|
fd6f6fc8df | ||
|
|
4fb47cd568 | ||
|
|
ecc932d5dd | ||
|
|
b57fbff7c1 | ||
|
|
4892a766a8 | ||
|
|
0303cd8625 | ||
|
|
d765b89a63 | ||
|
|
6e4acf0504 | ||
|
|
71954faa3a | ||
|
|
6d22e74d11 | ||
|
|
dc92bb4646 | ||
|
|
0f0e154315 | ||
|
|
136d41775f | ||
|
|
ec77d28e62 | ||
|
|
86420a1f46 | ||
|
|
7dd8b6c8ed | ||
|
|
8afa6fefd8 | ||
|
|
533c9d4fe3 | ||
|
|
a35ef155fc | ||
|
|
8dd3c41b2a | ||
|
|
4523da6543 | ||
|
|
ce8456a1a9 | ||
|
|
1673778633 | ||
|
|
9ce1884732 | ||
|
|
23b329b9df | ||
|
|
0c34e51a75 | ||
|
|
1633b30979 | ||
|
|
630dabf4b9 | ||
|
|
fc6c794972 | ||
|
|
2e33b99c6b | ||
|
|
3b7292b637 | ||
|
|
e4f469ae7a | ||
|
|
c921dc75c7 | ||
|
|
86d543d0f6 | ||
|
|
e4e90b53c1 | ||
|
|
58d776daa0 | ||
|
|
f6b2e89109 | ||
|
|
ac85c2af76 | ||
|
|
5aba2aedb3 | ||
|
|
bd77f1df4c | ||
|
|
a8332efa94 | ||
|
|
3dbef72dc7 | ||
|
|
2d16e74f38 | ||
|
|
de5070446d | ||
|
|
374abd1e7d | ||
|
|
0506d9e83d | ||
|
|
bd3dfad8b9 | ||
|
|
9fff315555 | ||
|
|
07b6dce1a5 | ||
|
|
18fb86b7be | ||
|
|
58a8275e84 | ||
|
|
196fab6834 | ||
|
|
85fc7cea97 | ||
|
|
328d660106 | ||
|
|
c68910005b | ||
|
|
c79bcc8838 | ||
|
|
0fe58dbb34 | ||
|
|
6cb2f56395 | ||
|
|
59e33b3b21 | ||
|
|
0e3c92c027 | ||
|
|
db7a9b2c37 | ||
|
|
44097faec1 | ||
|
|
ff5fca76ab | ||
|
|
bf3da5081f | ||
|
|
5532982857 | ||
|
|
783dd875f7 | ||
|
|
97112c69be | ||
|
|
b0b573052a | ||
|
|
2939000342 | ||
|
|
41e1654f9a | ||
|
|
e3cb0278ce | ||
|
|
0c81f1bdb3 | ||
|
|
6220875803 | ||
|
|
afd4279cd8 | ||
|
|
0c8dd8046a | ||
|
|
3c4ef4338f | ||
|
|
64cf887b28 | ||
|
|
927a879052 | ||
|
|
dfe0c96b87 | ||
|
|
e856e10ac2 | ||
|
|
6d6a731d6d | ||
|
|
928feb0889 | ||
|
|
b3febe2d24 | ||
|
|
b6b26dba87 | ||
|
|
5c034e26bd | ||
|
|
cef0fb1434 | ||
|
|
158d0e26a2 | ||
|
|
78385bfbeb | ||
|
|
2a13cc28f2 | ||
|
|
4d761fda81 | ||
|
|
4bdf41a6c7 | ||
|
|
3c605c93fe | ||
|
|
121f18a443 | ||
|
|
538aeef27a | ||
|
|
be0d2537b7 | ||
|
|
3307aa1260 | ||
|
|
57cfdfd8fb | ||
|
|
dc6733dacc | ||
|
|
f696a221af | ||
|
|
ed5b67720c | ||
|
|
2aac50571d | ||
|
|
45edd27ad7 | ||
|
|
c302d1cfc8 | ||
|
|
6287e8c571 | ||
|
|
f69a98ce49 | ||
|
|
d44f3526dc | ||
|
|
41b633f5ea | ||
|
|
4f1ff9c4d9 | ||
|
|
86bb48792c | ||
|
|
94dbb4a427 | ||
|
|
048a46ec2a | ||
|
|
1480340830 | ||
|
|
877bd95fa3 | ||
|
|
8ea6fb368d | ||
|
|
5fd5ddea23 | ||
|
|
b04c0697e1 | ||
|
|
50a8ba6a6f | ||
|
|
334f1ed45a | ||
|
|
20c89ebbb3 | ||
|
|
6f56ba80b3 | ||
|
|
6e84283c66 | ||
|
|
9d6fddcfdf | ||
|
|
a83105df9d | ||
|
|
9528b55c25 | ||
|
|
749ce107ee | ||
|
|
b2a67834ac | ||
|
|
aec2aa3497 | ||
|
|
c7dcbfd6c1 | ||
|
|
ff12080ff5 | ||
|
|
0b6175b742 | ||
|
|
cf49da387b | ||
|
|
ac714e7e3d | ||
|
|
79fb79b71c | ||
|
|
98874c3baf | ||
|
|
d89f6af6c4 | ||
|
|
d4bca00df9 | ||
|
|
2c68a19dfd | ||
|
|
9e5853ecc0 | ||
|
|
124544d834 | ||
|
|
b910904fa6 | ||
|
|
eee1ce305c | ||
|
|
5c61c3ccdc | ||
|
|
fb8d512f58 | ||
|
|
a0fb0c1835 | ||
|
|
e152b2a975 | ||
|
|
a71629d4dd | ||
|
|
c22f3ca7a8 | ||
|
|
4a92134235 | ||
|
|
6b9fd256e1 | ||
|
|
d6132b854f | ||
|
|
ff9a74b91f | ||
|
|
b579163802 | ||
|
|
87f0c8e7e8 | ||
|
|
bb855499e1 | ||
|
|
96bfa77856 | ||
|
|
8e997eba4a | ||
|
|
228c6686f8 | ||
|
|
52861d3aea | ||
|
|
cc26911c46 | ||
|
|
7776d064cf | ||
|
|
2d9b5a65f1 | ||
|
|
c240da6568 | ||
|
|
157272dc5b | ||
|
|
9065274d02 | ||
|
|
f4c56026a2 | ||
|
|
37e3f5de10 | ||
|
|
5ea629beb2 | ||
|
|
240164560f | ||
|
|
cf52691959 | ||
|
|
10e75116ef | ||
|
|
f649968c69 | ||
|
|
5ce1448049 | ||
|
|
bcedc2b0d9 | ||
|
|
8e4a45ec41 | ||
|
|
dec942beb6 | ||
|
|
d4e0f13bb3 | ||
|
|
1f28a3bb80 | ||
|
|
3a1d3a7952 | ||
|
|
a9f1ad7924 | ||
|
|
929b9e164e | ||
|
|
97376f6e8f | ||
|
|
92a0a59de2 | ||
|
|
cd18599e7b | ||
|
|
1f22a16b15 | ||
|
|
433b6fa8fe | ||
|
|
d7cd857c7c | ||
|
|
99fbfe2421 | ||
|
|
b1b6264bea | ||
|
|
1fd72d5aea | ||
|
|
18dffb26e7 | ||
|
|
edba7c987b | ||
|
|
b737c83a66 | ||
|
|
97a6322de1 | ||
|
|
037fe4afdc | ||
|
|
afbb63a197 | ||
|
|
8902561f3c | ||
|
|
a67116b5bc | ||
|
|
0f7aa4125f | ||
|
|
b62a5c954c | ||
|
|
b8cdf060c8 | ||
|
|
9fb937986e | ||
|
|
2c48f6a02b | ||
|
|
4155c5b695 | ||
|
|
471467d310 | ||
|
|
c54c13831a | ||
|
|
ae4ee95d25 | ||
|
|
a2e037f0ec | ||
|
|
e9055e9ef7 | ||
|
|
d350b666ff | ||
|
|
21831b3fe2 | ||
|
|
895357607a | ||
|
|
ac240a8477 | ||
|
|
bf38c0c0d1 | ||
|
|
67cf15d036 | ||
|
|
701a82642b | ||
|
|
21fe14201f | ||
|
|
48640b1de2 | ||
|
|
5682685c80 | ||
|
|
9c025b8cce | ||
|
|
eef9f13360 | ||
|
|
fa9b361a3d | ||
|
|
49862ba347 | ||
|
|
ee2afcf70b | ||
|
|
c7d535c648 | ||
|
|
9986e103cf | ||
|
|
c5b3666089 | ||
|
|
d265fe7f9e | ||
|
|
91e6af4470 | ||
|
|
b940fe8fca | ||
|
|
73fe2e95fe | ||
|
|
316c492842 | ||
|
|
74418b542a | ||
|
|
172e63dbb6 | ||
|
|
21bf5b4db7 | ||
|
|
a406bb0288 | ||
|
|
1823ab6808 | ||
|
|
8eec49304d | ||
|
|
ecdc2f2f5f | ||
|
|
6a6c772ff2 | ||
|
|
e178c55bc3 | ||
|
|
2c137c0d04 | ||
|
|
1d35f2b58f | ||
|
|
638c57e466 | ||
|
|
5e4213b3be | ||
|
|
102295f58a | ||
|
|
a0d14f8ff7 | ||
|
|
e0b0a351c6 | ||
|
|
fcd4b3ba9b | ||
|
|
1d2ff46a89 | ||
|
|
8f7c739328 | ||
|
|
1beea3daba | ||
|
|
1ffd063939 | ||
|
|
3d94c38ec4 | ||
|
|
f4af2d3cdc | ||
|
|
b57e7321e7 | ||
|
|
e93867488b | ||
|
|
c08790edd2 | ||
|
|
a46baddbc4 | ||
|
|
3bd9615d0e | ||
|
|
2871cb5775 | ||
|
|
a6e0ec4e6f | ||
|
|
e956369c4e | ||
|
|
76f950c663 | ||
|
|
d774a3309b | ||
|
|
b3edb25377 | ||
|
|
026b87e39b | ||
|
|
53a816b17a | ||
|
|
043aaa792d | ||
|
|
aad9cb208a | ||
|
|
edf081c6a2 | ||
|
|
fd349103e8 | ||
|
|
10b49eb4fb | ||
|
|
3856d078d2 | ||
|
|
6b4cb35f4f | ||
|
|
e6eab2091f | ||
|
|
3cdb609cca | ||
|
|
d6a7f62ff5 | ||
|
|
72f170f5d2 | ||
|
|
824d52a82b | ||
|
|
067ebab9d8 | ||
|
|
6be6c0d2e3 | ||
|
|
aa874010e2 | ||
|
|
8ec888d13d | ||
|
|
916f274c83 | ||
|
|
7ac53c07af | ||
|
|
bc72e4226e | ||
|
|
5e0776e96a | ||
|
|
2f1ef02d35 | ||
|
|
db8442584e | ||
|
|
d46cf50760 | ||
|
|
0357121d17 | ||
|
|
aff236e20e | ||
|
|
cbd70d26b5 | ||
|
|
5e763b71dc | ||
|
|
7e4e7a66af | ||
|
|
906947a285 | ||
|
|
bfc70bc74e | ||
|
|
6b4f833a12 | ||
|
|
426c902b87 | ||
|
|
e4b51235f8 | ||
|
|
4c6498d726 | ||
|
|
0a3b1ad4eb | ||
|
|
f23f442d33 | ||
|
|
e465c3587b | ||
|
|
7109b6d414 | ||
|
|
8c97f3e9bc | ||
|
|
3795b2c8ba | ||
|
|
7725425e05 | ||
|
|
b2f4948bbe | ||
|
|
f802d2ba83 | ||
|
|
ce8548a1a2 | ||
|
|
490dec981a | ||
|
|
39fd7b0b3b | ||
|
|
e83930333b | ||
|
|
b0d70a0e5e | ||
|
|
ff5a5c1ee0 | ||
|
|
290a53d735 | ||
|
|
2393a13f86 | ||
|
|
7d8c8de827 | ||
|
|
3faef829c5 | ||
|
|
7560fb6f9a | ||
|
|
2fddcc6a11 | ||
|
|
69bf39f42e | ||
|
|
f4d5c861f3 | ||
|
|
564a0afae1 | ||
|
|
1e332f0eb1 | ||
|
|
cab8d3d568 | ||
|
|
be8c4cb24a | ||
|
|
65166e4ce4 | ||
|
|
8249cd4406 | ||
|
|
c6ecaf68ed | ||
|
|
d3f89fa6e3 | ||
|
|
ce8397f7d9 | ||
|
|
cae9aeca00 | ||
|
|
f939d1c183 | ||
|
|
242d06274a | ||
|
|
957e3ed729 | ||
|
|
ed02ee4ef4 | ||
|
|
ba9691a0ad | ||
|
|
e7eb94de6b | ||
|
|
b6eb8dff64 | ||
|
|
7da9e3a6f8 | ||
|
|
876970baea | ||
|
|
e68e76e143 | ||
|
|
2bc7ca2d34 | ||
|
|
e94eb9af10 | ||
|
|
3018b21ab8 | ||
|
|
0b605c3383 | ||
|
|
e7ac1ea54c | ||
|
|
5ac6d91525 | ||
|
|
1cd6713e24 | ||
|
|
785b429737 | ||
|
|
4aecd8d039 | ||
|
|
1b339ea062 | ||
|
|
236ef03dbd | ||
|
|
53cc561048 | ||
|
|
bb4b143f3b | ||
|
|
3af41cd37d | ||
|
|
7e32a17742 | ||
|
|
6c265534a4 | ||
|
|
1d42133d44 | ||
|
|
df911c9b9e | ||
|
|
a6f40dd574 | ||
|
|
688215e787 | ||
|
|
1cfa2e04bc | ||
|
|
b4f6901903 | ||
|
|
0149382cdc | ||
|
|
697c9973a7 | ||
|
|
788fd3df81 | ||
|
|
996cac5fed | ||
|
|
0a8b78cb84 | ||
|
|
b4eb74f5ff | ||
|
|
57d1f31054 | ||
|
|
9f02f51b87 | ||
|
|
911a17b149 | ||
|
|
3d969bd2b4 | ||
|
|
f800cee4fa | ||
|
|
b49fc33cb3 | ||
|
|
00e235a1ee | ||
|
|
37a6b2da67 | ||
|
|
913e977c8d | ||
|
|
c2ddcb3b40 | ||
|
|
ab9544c0d3 | ||
|
|
4bfe849409 | ||
|
|
3bdb92fcad | ||
|
|
cf9e3069f2 | ||
|
|
ed0cbfb31e | ||
|
|
32b2f6117e | ||
|
|
ae92521310 | ||
|
|
5802df4365 | ||
|
|
c1901f4e12 | ||
|
|
8d98282afd | ||
|
|
dd839bf295 | ||
|
|
3af6073576 | ||
|
|
2518af5f9e | ||
|
|
7b793d84c8 | ||
|
|
af9bc7ea7d | ||
|
|
ac055b09e9 | ||
|
|
df42914da6 | ||
|
|
2471bdda00 | ||
|
|
c7e01b139d | ||
|
|
9d80ff5a05 | ||
|
|
39b3941892 | ||
|
|
b311abed31 | ||
|
|
ce667ddae0 | ||
|
|
0fee993a4b | ||
|
|
0ea5c9d8e8 | ||
|
|
63ac260bd5 | ||
|
|
a01a39b153 | ||
|
|
f9a4ad7904 | ||
|
|
e60b67d246 | ||
|
|
9004d69c6f | ||
|
|
8856a2d77b | ||
|
|
54a061bdda | ||
|
|
65b4b100a8 | ||
|
|
7cc9286e0f | ||
|
|
2f25639ea0 | ||
|
|
2070c215a2 | ||
|
|
94b98222c2 | ||
|
|
9c605ad153 | ||
|
|
b7c7e59dac | ||
|
|
767c1436d3 | ||
|
|
699cf6ff45 | ||
|
|
9201870f6c | ||
|
|
6722f58668 | ||
|
|
7b9b7cef11 | ||
|
|
7d4fce09dc | ||
|
|
2075501d86 | ||
|
|
bd099f5e71 | ||
|
|
baf257adcb | ||
|
|
4fd1986885 | ||
|
|
e1afac9439 | ||
|
|
580d9db85e | ||
|
|
42e2fd35d8 | ||
|
|
1a40c7c27c | ||
|
|
f3bec41eb9 | ||
|
|
825634d24e | ||
|
|
cb097e6b0a | ||
|
|
1cfb03fb74 | ||
|
|
f293df647c | ||
|
|
10522438b7 | ||
|
|
e2e5bd6f19 | ||
|
|
cd7a0a9757 | ||
|
|
b3eda248a3 | ||
|
|
95b51c48be | ||
|
|
486888f595 | ||
|
|
17ab8145b5 | ||
|
|
b3ebc69034 | ||
|
|
761dde2f1b | ||
|
|
2bb6a3f4d0 | ||
|
|
e83e947ca3 | ||
|
|
73733a8fb9 | ||
|
|
ce6c23a360 | ||
|
|
99d8e6a30f | ||
|
|
2fa1d8ac48 | ||
|
|
ca7e425ce8 | ||
|
|
8b9a19eef1 | ||
|
|
7f629df4d5 | ||
|
|
98ddc3596c | ||
|
|
5d23be6242 | ||
|
|
d15d3a524b | ||
|
|
1e1d9acb1b | ||
|
|
55ee94bed0 | ||
|
|
d228d29944 | ||
|
|
013cc66d8e | ||
|
|
c7ed6eee5e | ||
|
|
8082d1fed6 | ||
|
|
d2a10dbe69 | ||
|
|
14645142db | ||
|
|
f34b2ef90b | ||
|
|
ce894665a8 | ||
|
|
0d00f3a55b | ||
|
|
48ff373ff7 | ||
|
|
e9efee0e64 | ||
|
|
4b3e7aee0b | ||
|
|
dd53b287f2 | ||
|
|
21526efe51 | ||
|
|
7413045f0e | ||
|
|
d76c508566 | ||
|
|
8fb46de5e4 | ||
|
|
af1944f28d | ||
|
|
214ea14f29 | ||
|
|
5fb420c703 | ||
|
|
2420f6c000 | ||
|
|
b0d7332a0c | ||
|
|
f71b56a5d0 | ||
|
|
d55efc791f | ||
|
|
f63645546d | ||
|
|
e2dd3e3587 | ||
|
|
27ab780317 | ||
|
|
e2d4d097e7 | ||
|
|
ac8cb6ba0d | ||
|
|
4ce81fd07f | ||
|
|
df9eeb7f8f | ||
|
|
31c4fdbf79 | ||
|
|
48e367ff7d | ||
|
|
fd02492cb7 | ||
|
|
addfa35d93 | ||
|
|
5afdc56796 | ||
|
|
fb1c333a83 | ||
|
|
c3e1da8e04 | ||
|
|
20a753e2e5 | ||
|
|
3a398775fb | ||
|
|
61a7434379 | ||
|
|
09f5e29327 | ||
|
|
29edb4ccfe | ||
|
|
197d6fb644 | ||
|
|
d4e565e595 | ||
|
|
1fce2b180f | ||
|
|
be6ccd129d | ||
|
|
f7cecf0945 | ||
|
|
7b2198f7e5 | ||
|
|
52221db7ef | ||
|
|
befbf48563 | ||
|
|
56a61bab56 | ||
|
|
d480022711 | ||
|
|
f1abb92f0c | ||
|
|
5792be71fa | ||
|
|
c2630bb3a3 | ||
|
|
5e3010d455 | ||
|
|
ccbf65c8e8 | ||
|
|
9d07cde385 | ||
|
|
464b9d7c80 | ||
|
|
5c81d0d89a | ||
|
|
62cd643868 | ||
|
|
c0bf02b8b2 | ||
|
|
1b7dd70f72 | ||
|
|
372a08be49 | ||
|
|
fd46a1c3b3 | ||
|
|
5aae7178ad | ||
|
|
dea8220eee | ||
|
|
a4be0b88f6 | ||
|
|
d8101573be | ||
|
|
41cdb357bb | ||
|
|
38caddffe7 | ||
|
|
80fe166902 | ||
|
|
0e26f983d6 | ||
|
|
fc08fcab52 | ||
|
|
77dc99e71d | ||
|
|
5041bfcb5c | ||
|
|
5be76856bd | ||
|
|
2a3f5e1ad1 | ||
|
|
f8650a3493 | ||
|
|
90a52a29c5 | ||
|
|
8859c92f80 | ||
|
|
01e5632949 | ||
|
|
18a4276e25 | ||
|
|
c06032f35f | ||
|
|
95a6b2c991 | ||
|
|
ee28f6caaa | ||
|
|
30c9e50701 | ||
|
|
9aadd725d2 | ||
|
|
6cfb1cb6fd | ||
|
|
2dc8ac1e62 | ||
|
|
e952e2a691 | ||
|
|
040ac5cad8 | ||
|
|
05685863e3 | ||
|
|
d324c0a1c3 | ||
|
|
03f8b25b50 | ||
|
|
b0e2c2da78 | ||
|
|
f28a8eca91 | ||
|
|
ca69e54cb6 | ||
|
|
4629abd5a2 | ||
|
|
dc99f4a7a3 | ||
|
|
389ec21d0c | ||
|
|
9341201132 | ||
|
|
88dd83a365 | ||
|
|
74285d50c4 | ||
|
|
60d0611ac2 | ||
|
|
f939222942 | ||
|
|
c293c2e9a3 | ||
|
|
e34ca9acd1 | ||
|
|
83071a3459 | ||
|
|
edf364bf21 | ||
|
|
1e037883b0 | ||
|
|
d909f167ff | ||
|
|
4592aaa3e2 | ||
|
|
95d1a12422 | ||
|
|
62aa42cccf | ||
|
|
5cffd3780a | ||
|
|
def75ffcfe | ||
|
|
ad8e611098 | ||
|
|
3ec1844e4a | ||
|
|
523670ba0d | ||
|
|
35dea24ffd | ||
|
|
e55104a155 | ||
|
|
111745c564 | ||
|
|
c7df1ffc6f | ||
|
|
2b7e75e079 | ||
|
|
bcdaa09c75 | ||
|
|
2fc65dcb99 | ||
|
|
44a3b58e52 | ||
|
|
0a256053ee | ||
|
|
46de9ac03e | ||
|
|
a53dc1d9c8 | ||
|
|
f0462322fd | ||
|
|
c59d2a6288 | ||
|
|
3e3ff2a70b | ||
|
|
16bc11e72e | ||
|
|
2719f1efaa | ||
|
|
cff1be0ae8 | ||
|
|
39ac62a1a1 | ||
|
|
f427dbbd60 | ||
|
|
c3f689a7d9 | ||
|
|
85f3a9f3b0 | ||
|
|
13ba4b433d | ||
|
|
96f27a4965 | ||
|
|
0e502899a8 | ||
|
|
424b44c247 | ||
|
|
01a71c366d | ||
|
|
990fbeb3a4 | ||
|
|
5a9a898ba2 | ||
|
|
fe1fbe0005 | ||
|
|
c56a139fdc | ||
|
|
df50eda811 | ||
|
|
f5d3313210 | ||
|
|
97fcc9ff99 | ||
|
|
8a6b2b4447 | ||
|
|
757eaeae92 | ||
|
|
b7dd61f6bc | ||
|
|
d2a95a04a4 | ||
|
|
0cc993f403 | ||
|
|
3a64580663 | ||
|
|
d087e28dce | ||
|
|
96adfaebe1 | ||
|
|
ddf84f8257 | ||
|
|
507f993075 | ||
|
|
73a6a60785 | ||
|
|
cf4cf58faf | ||
|
|
6bc3c74c0c | ||
|
|
598ce1e354 | ||
|
|
4685b76e08 | ||
|
|
78c9109f6c | ||
|
|
7e248fc0ba | ||
|
|
c526fa9119 | ||
|
|
520e0fd985 | ||
|
|
54a7eba358 | ||
|
|
1494ba2e6e | ||
|
|
a5b3548ede | ||
|
|
8318aa0113 | ||
|
|
e69c42956b | ||
|
|
53ca589c11 | ||
|
|
ca8ff8718e | ||
|
|
e8e48e4c4a | ||
|
|
67e17ed3f8 | ||
|
|
2a6a40e93b | ||
|
|
eda34423d7 | ||
|
|
7ce1f6e736 | ||
|
|
5c53620a72 | ||
|
|
5f94cec1e2 | ||
|
|
646350fa7f | ||
|
|
e162a055cc | ||
|
|
28d3ad3ada | ||
|
|
0bd44a7764 | ||
|
|
8be6d887e2 | ||
|
|
66b14a0d32 | ||
|
|
153a612253 | ||
|
|
1a1b55e133 | ||
|
|
879de20edf | ||
|
|
e77ad3f9bb | ||
|
|
4ce86ff5fa | ||
|
|
e290c010e6 | ||
|
|
33d267fa1b | ||
|
|
601a744159 | ||
|
|
f630d7c3fa | ||
|
|
91bfefcf8c | ||
|
|
a1b01e6d5f | ||
|
|
48594617b5 | ||
|
|
b35b9dcff7 | ||
|
|
a3e317773a | ||
|
|
16431d222c | ||
|
|
ee49a23220 | ||
|
|
a9eef521ec | ||
|
|
901d33b59c | ||
|
|
255116fde7 | ||
|
|
00ebea2536 | ||
|
|
dedf9774c7 | ||
|
|
6b1c62133d | ||
|
|
d4251b2545 | ||
|
|
b9d1698d74 | ||
|
|
7c696e1cb6 | ||
|
|
165d60421d | ||
|
|
c7962118f8 | ||
|
|
892a204013 | ||
|
|
0e6aedc7ed | ||
|
|
c547a4d835 | ||
|
|
fc9668baa5 | ||
|
|
ba17d46f15 | ||
|
|
54a4f93854 | ||
|
|
bdd816488d | ||
|
|
36dcfee2f7 | ||
|
|
4d13ddf6b3 | ||
|
|
9e25475475 | ||
|
|
e955aa7f2a | ||
|
|
81d2b54dfd | ||
|
|
7956ff0313 | ||
|
|
9ff25fb64b | ||
|
|
04df69f633 | ||
|
|
908eb57795 | ||
|
|
ecfae074dc | ||
|
|
be5d394e56 | ||
|
|
849a27ee61 | ||
|
|
062f3ea43a | ||
|
|
5cfedcfe33 | ||
|
|
4d2fc530d0 | ||
|
|
3970204009 | ||
|
|
f046f557fa | ||
|
|
401958938d | ||
|
|
566cffe53d | ||
|
|
028bc2f9be | ||
|
|
813d9bc316 | ||
|
|
79ba458051 | ||
|
|
cf220be9b5 | ||
|
|
c433572585 | ||
|
|
a42b576382 | ||
|
|
fb9b53026d | ||
|
|
2ac54e5a7b | ||
|
|
8eecdc6d1f | ||
|
|
50577e2bd2 | ||
|
|
7bc1f986e8 | ||
|
|
d796621ccc | ||
|
|
751e9fb7be | ||
|
|
f6113264f4 | ||
|
|
a3534a730b | ||
|
|
7f8b8a0e43 | ||
|
|
bd6f7b6d83 | ||
|
|
b0a4beb66a | ||
|
|
472c2d828c | ||
|
|
01ee49045e | ||
|
|
7bd9f821dd | ||
|
|
b20ecc7b54 | ||
|
|
61eb9d4e29 | ||
|
|
43eb5a001c | ||
|
|
f58692abb7 | ||
|
|
c1760fb764 | ||
|
|
e9bc0e7e98 | ||
|
|
ffcadcd99e | ||
|
|
7a733a8d54 | ||
|
|
ce97313fda | ||
|
|
7b81967a3c | ||
|
|
ff811f594b | ||
|
|
0bf80b3c89 | ||
|
|
ae3b369fe1 | ||
|
|
77b15e7194 | ||
|
|
20537f974e | ||
|
|
4476a64bdf | ||
|
|
d4b701576e | ||
|
|
721c053712 | ||
|
|
e3071157f0 | ||
|
|
c07af89e48 | ||
|
|
9c846106fa | ||
|
|
cf94d1f1f1 | ||
|
|
6187440f35 | ||
|
|
57b7c3494f | ||
|
|
dda18c28c5 | ||
|
|
f8d6eaaa96 | ||
|
|
47d4fabb58 | ||
|
|
80039f60d5 | ||
|
|
5a5e9b8a89 | ||
|
|
b7ed3b77bd | ||
|
|
75b925c326 | ||
|
|
91d419ee6c | ||
|
|
23345098ea | ||
|
|
7ce91ea1a1 | ||
|
|
41079f1015 | ||
|
|
712dfa40cd | ||
|
|
decfd6108c | ||
|
|
b890bbfa63 | ||
|
|
0e3a570b85 | ||
|
|
7060c809c0 | ||
|
|
9dbfd84c5b | ||
|
|
fce380a044 | ||
|
|
46ba15ab03 | ||
|
|
1e39ca39c3 | ||
|
|
80ef1ae51c | ||
|
|
4d0715d226 | ||
|
|
8a274169da | ||
|
|
21d8298fe1 | ||
|
|
5d6f6d8d5b | ||
|
|
bacf6156c1 | ||
|
|
1d1b213f1f | ||
|
|
1f11af42f1 | ||
|
|
a026c8748f | ||
|
|
9f7d89b3cd | ||
|
|
92a77cc78e | ||
|
|
b0c84e3de7 | ||
|
|
bbc914e174 | ||
|
|
3fca4055d2 | ||
|
|
66afa16aed | ||
|
|
9b0a8de7de | ||
|
|
04bbede17d | ||
|
|
0e3bafcc54 | ||
|
|
b48f719b8e | ||
|
|
289fcbd08c | ||
|
|
f6875bb893 | ||
|
|
7e803adf13 | ||
|
|
5b5deee5b3 | ||
|
|
7dae4cb685 | ||
|
|
27fad98179 | ||
|
|
58f7e3a829 | ||
|
|
4a15bd8ff8 | ||
|
|
b030ef1aca | ||
|
|
cc46a99f97 | ||
|
|
becec6cb6b | ||
|
|
bc33db9fc0 | ||
|
|
7d4579e737 | ||
|
|
b7c90751b0 | ||
|
|
88fd1cba71 | ||
|
|
e43cc316ff | ||
|
|
e3f24a29fa | ||
|
|
890e526bde | ||
|
|
16ce455fca | ||
|
|
29b7164468 | ||
|
|
acdd03f609 | ||
|
|
03b35ecdd0 | ||
|
|
5307e18085 | ||
|
|
2cea944cdb | ||
|
|
c08540c7b7 | ||
|
|
3934700a08 | ||
|
|
0913eb6655 | ||
|
|
1bfbe354f5 | ||
|
|
2d78e20120 | ||
|
|
77210513c9 | ||
|
|
2e6f8bdf19 | ||
|
|
25144fedd5 | ||
|
|
27f64dd9a4 | ||
|
|
9d7648f02f | ||
|
|
1ef8babfef | ||
|
|
4ea7bf0510 | ||
|
|
5dcf1d13a9 | ||
|
|
94d37d05e5 | ||
|
|
0cbdc458c5 | ||
|
|
c1437c7b46 | ||
|
|
f357f65d04 | ||
|
|
ef8e952fc4 | ||
|
|
a2bc383e15 | ||
|
|
23930355a7 | ||
|
|
bb9f41e613 | ||
|
|
bc110d8055 | ||
|
|
b23b19e5c3 | ||
|
|
65b1a4282e | ||
|
|
1dbb3f6f43 | ||
|
|
af3dc25dfe | ||
|
|
5a0c0079a1 | ||
|
|
af8f563ed3 | ||
|
|
93af4a4864 | ||
|
|
28f188e3ef | ||
|
|
b29224f62f | ||
|
|
d756da41b9 | ||
|
|
cdab4a3b85 | ||
|
|
b88c57ba93 | ||
|
|
1a5496eced | ||
|
|
b264e6a191 | ||
|
|
ae1b495262 | ||
|
|
16939ca192 | ||
|
|
60cd513a33 | ||
|
|
27d94c64ed | ||
|
|
21a0f857d3 | ||
|
|
03a6e8aee2 | ||
|
|
d0862ddf86 | ||
|
|
4afbb89774 | ||
|
|
5ec57a9533 | ||
|
|
f088e8960b | ||
|
|
27dec42ad6 | ||
|
|
b70053090c | ||
|
|
f10e2254ae | ||
|
|
1f92fc3fc0 | ||
|
|
f71b114a84 | ||
|
|
e3e0532613 | ||
|
|
2c0f121550 | ||
|
|
6f41cff75a | ||
|
|
9b39616c1b | ||
|
|
fad3d66093 | ||
|
|
ff99ef74c8 | ||
|
|
6990e73b11 | ||
|
|
860a1237ab | ||
|
|
97b5bf1fb7 | ||
|
|
ed3418c046 | ||
|
|
a2230868e0 | ||
|
|
71bab74148 | ||
|
|
661ea57907 | ||
|
|
1f18efb0ba | ||
|
|
8ae46bce93 | ||
|
|
f19a414e09 | ||
|
|
0ee2933234 | ||
|
|
9890f579f8 | ||
|
|
2ee337ead5 | ||
|
|
362e14fa1a | ||
|
|
524fe62594 | ||
|
|
3c87e1e60d | ||
|
|
0cac868a36 | ||
|
|
2480c66857 | ||
|
|
186c477f3c | ||
|
|
570670be8c | ||
|
|
22b7226581 | ||
|
|
f16f715b59 | ||
|
|
75adb787c4 | ||
|
|
6123377e66 | ||
|
|
778cccb15d | ||
|
|
0256dae657 | ||
|
|
88a93838de | ||
|
|
0855988427 | ||
|
|
84b121bbe1 | ||
|
|
48fb7b0dd7 | ||
|
|
01e550a9be | ||
|
|
63a2e0bab6 | ||
|
|
24657859a8 | ||
|
|
67d07e895c | ||
|
|
d7df6bc738 | ||
|
|
a4e1de93a7 | ||
|
|
41be557f0c | ||
|
|
9417fd933e | ||
|
|
067d21d0f2 | ||
|
|
3882da6ac5 | ||
|
|
77b780b8ca | ||
|
|
127e8bf3b6 | ||
|
|
74faed166a | ||
|
|
dbd05d6e82 | ||
|
|
b5d35c7e09 | ||
|
|
c39eb3bacd | ||
|
|
57fad9148c | ||
|
|
0f88cdc80e | ||
|
|
e2a9949b16 | ||
|
|
38e3c7a8f7 | ||
|
|
67f166fa02 | ||
|
|
c7df5fb119 | ||
|
|
a4be47d7ad | ||
|
|
aaea94a48d | ||
|
|
c3d9c45f58 | ||
|
|
a2a48cc065 | ||
|
|
cf407f7176 | ||
|
|
d6dd17a483 | ||
|
|
a66071099c | ||
|
|
9a6e569412 | ||
|
|
7dfa565d00 | ||
|
|
d2e5f01542 | ||
|
|
f4e373e0d2 | ||
|
|
c8691db2b7 | ||
|
|
affe51cb19 | ||
|
|
57118919d2 | ||
|
|
7db05a80dd | ||
|
|
a8ba71edef | ||
|
|
45a99c3fd3 | ||
|
|
58e6b83e95 | ||
|
|
f556a72fe2 | ||
|
|
cd7a5cab8a | ||
|
|
67b5e0dbe8 | ||
|
|
b68f0cbde4 | ||
|
|
ebc3627c73 | ||
|
|
295730408b | ||
|
|
5a9f133491 | ||
|
|
f30afa4956 | ||
|
|
171cedf0f0 | ||
|
|
27d8ef14f8 | ||
|
|
f6d13f57bb | ||
|
|
5f36167f1a | ||
|
|
8fb4ae916c | ||
|
|
48da4aeee0 | ||
|
|
07df9eecda | ||
|
|
7f214a0e46 | ||
|
|
e1a0a1e73c | ||
|
|
1278b0ec73 | ||
|
|
3e9bd931ed | ||
|
|
9d588319dd | ||
|
|
0012ca8ca5 | ||
|
|
288e276abe | ||
|
|
f22e745514 | ||
|
|
070c31eac5 | ||
|
|
1a56ebea70 | ||
|
|
70e1cbda21 | ||
|
|
1ede3967c1 | ||
|
|
60f2df54e0 | ||
|
|
ba708f51f2 | ||
|
|
b106b1c131 | ||
|
|
0df31f63ab | ||
|
|
64d4da5a37 | ||
|
|
7aec38a73e | ||
|
|
a2fd8caa69 | ||
|
|
f546636c52 | ||
|
|
38ccc4f672 | ||
|
|
04e669a6be | ||
|
|
cc3f139d1f | ||
|
|
d50442da01 | ||
|
|
404b05a44c | ||
|
|
3d7c1ad31d | ||
|
|
d300e775a6 | ||
|
|
7ee2d1c339 | ||
|
|
54a98773f8 | ||
|
|
737a3f0bad | ||
|
|
3bd9636a5b | ||
|
|
76b21de0c6 | ||
|
|
dabb058167 | ||
|
|
f394313fee | ||
|
|
b7c5e45fff | ||
|
|
0a224654c2 | ||
|
|
47e4a36d7e | ||
|
|
e420a1de4d | ||
|
|
62dc0f7698 | ||
|
|
2d31d92271 | ||
|
|
1981fe2072 | ||
|
|
f68bd37acf | ||
|
|
76877eb6fa | ||
|
|
3d66d053c7 | ||
|
|
0d3ae3810f | ||
|
|
0e31cff762 | ||
|
|
c27110e37d | ||
|
|
89441a22aa | ||
|
|
557135185c | ||
|
|
4d39fd4165 | ||
|
|
f4c03e56b8 | ||
|
|
d2b6aa9033 | ||
|
|
5dd40b9377 | ||
|
|
001b77e7e1 | ||
|
|
9d91d32d82 | ||
|
|
a60ac7ca17 | ||
|
|
42ba0da6b0 | ||
|
|
f527c708f2 | ||
|
|
6f474982ed | ||
|
|
fd6cd52728 | ||
|
|
c9e49f4366 | ||
|
|
46fd9f4a53 | ||
|
|
4da641533d | ||
|
|
79df2c7ce7 | ||
|
|
3e28af1723 | ||
|
|
866a95de38 | ||
|
|
6aa0574a53 | ||
|
|
bb97eafa82 | ||
|
|
51d5efee1b | ||
|
|
c980804514 | ||
|
|
b883803b21 | ||
|
|
7e3a7d7044 | ||
|
|
9ad6012782 | ||
|
|
5a96cbbeaa | ||
|
|
416977436e | ||
|
|
54ec0a1308 | ||
|
|
ebd78e983f | ||
|
|
1cf726348f | ||
|
|
41f75e6d1b | ||
|
|
0e3037631f | ||
|
|
6fbf4f96b6 | ||
|
|
e35709a99e | ||
|
|
f3602d7d08 | ||
|
|
526e10a2e0 | ||
|
|
0b21734571 | ||
|
|
499872f31d | ||
|
|
5cc16e098c | ||
|
|
364e27d5f2 | ||
|
|
1f4e0bd17c | ||
|
|
0557e18472 | ||
|
|
cfd66ab8c3 | ||
|
|
691b763613 | ||
|
|
3ddb501190 | ||
|
|
997e808088 | ||
|
|
13441ad0f8 | ||
|
|
aa508591c1 | ||
|
|
818f0201fc | ||
|
|
890f43ffa5 | ||
|
|
e270ab65b3 | ||
|
|
926373f9c1 | ||
|
|
111c6177d2 | ||
|
|
91f72f25ab | ||
|
|
da540ccf8c | ||
|
|
d6396f82fe | ||
|
|
4fa250a6a1 | ||
|
|
b42cfcea60 | ||
|
|
aca6dfbd60 | ||
|
|
5f7e6d03ff | ||
|
|
88ad742da0 | ||
|
|
a8d4042853 | ||
|
|
44a9339c0a | ||
|
|
113c7ff49a | ||
|
|
40dbe243d9 | ||
|
|
d422d24278 | ||
|
|
109c927dad | ||
|
|
de400f3473 | ||
|
|
8144a125ce | ||
|
|
3e34e41a5a | ||
|
|
2270887d43 | ||
|
|
c6431f9a04 | ||
|
|
88c0d0120c | ||
|
|
44fefe5b9f | ||
|
|
878d368cea | ||
|
|
f2bd026d0e | ||
|
|
518612492c | ||
|
|
81e43b87c2 | ||
|
|
a02e17f15c | ||
|
|
c76f86fdbd | ||
|
|
5b7c00ff52 | ||
|
|
b9f0046ee7 | ||
|
|
3b79f7e4ae | ||
|
|
85d2df02b9 | ||
|
|
2f1e8ba612 | ||
|
|
84c690cb07 | ||
|
|
239bbad7ab | ||
|
|
4be8023408 | ||
|
|
83e8da57b8 | ||
|
|
dcff6c996d | ||
|
|
0a66a6f1e5 | ||
|
|
e82a5c5c54 | ||
|
|
92fdcafb66 | ||
|
|
b9aae1aaae | ||
|
|
7d70afc937 | ||
|
|
12b63061c2 | ||
|
|
038fdeea83 | ||
|
|
0b6225bcc3 | ||
|
|
f286ef8e17 | ||
|
|
b120bcb60a | ||
|
|
be34fc9134 | ||
|
|
8591d17d82 | ||
|
|
f6190d6751 | ||
|
|
f0fc77fded | ||
|
|
f56cac6381 | ||
|
|
4f35054d29 | ||
|
|
d29df6714a | ||
|
|
7460fb8349 | ||
|
|
a7c430355a | ||
|
|
1df1517449 | ||
|
|
f7c357ebad | ||
|
|
20c60aae68 | ||
|
|
b14527b7af | ||
|
|
f840080e5b | ||
|
|
2c6983a2f1 | ||
|
|
acfb83ec5e | ||
|
|
3db931dc0e | ||
|
|
8309ddd486 | ||
|
|
21c868a646 | ||
|
|
ffe9acfe4a | ||
|
|
24d904d194 | ||
|
|
b280a37c4d | ||
|
|
906548d0ba | ||
|
|
1485a5bf3b | ||
|
|
9ec197f2e8 | ||
|
|
d21466f595 | ||
|
|
4f3290309e | ||
|
|
d6fe0f61a9 | ||
|
|
5a22f2cf0b | ||
|
|
42d11d9e7d | ||
|
|
e49c184595 | ||
|
|
99d87c5ca2 | ||
|
|
4c0f48c548 | ||
|
|
4ce6d35e30 | ||
|
|
81bf0c66c6 | ||
|
|
34dc725d26 | ||
|
|
a5db4ca092 | ||
|
|
61029fe20b | ||
|
|
fee3f88cb5 | ||
|
|
932500e43d | ||
|
|
55d4cdd464 | ||
|
|
fe3e47b1e8 | ||
|
|
9ca25bd48f | ||
|
|
91e0823ff0 | ||
|
|
142c6b11b3 | ||
|
|
ef0b8367b5 | ||
|
|
d1bfb4d2c0 | ||
|
|
3b5d6f003f | ||
|
|
26c457860b | ||
|
|
d0515031c7 | ||
|
|
08f4a0a816 | ||
|
|
28f95f1fbe | ||
|
|
c791de0e1e | ||
|
|
36b5426f6e | ||
|
|
1e72e9b1cd | ||
|
|
9739e55d0f | ||
|
|
1cddbc80cf | ||
|
|
3da9ee15d3 | ||
|
|
1e2fac054c | ||
|
|
914bfb2d9c | ||
|
|
40244994ad | ||
|
|
556ae07857 | ||
|
|
17fd71164c | ||
|
|
81d19156e9 | ||
|
|
7b82411e6f | ||
|
|
fb268add7a | ||
|
|
7700973538 | ||
|
|
54e25a0251 | ||
|
|
79b3a1fe4e | ||
|
|
faf013ec84 | ||
|
|
7152915318 | ||
|
|
886262e58a | ||
|
|
9c5d9ae376 | ||
|
|
3d2bc15e9a | ||
|
|
20c43c447d | ||
|
|
4caed7cc0d | ||
|
|
5b68f8ea6a | ||
|
|
8378bc9958 | ||
|
|
367cb48096 | ||
|
|
661b263e77 | ||
|
|
07c5e72cdb | ||
|
|
7752cdbfaf | ||
|
|
4545ecad58 | ||
|
|
ac74237f01 | ||
|
|
5a36179c19 | ||
|
|
82d73f387d | ||
|
|
e8c6314770 | ||
|
|
087c1b98dc | ||
|
|
68c5ad83fb | ||
|
|
5acc8c0134 | ||
|
|
c897b6a82d | ||
|
|
d008e90d50 | ||
|
|
ea820b30bf | ||
|
|
03725dc015 | ||
|
|
0a6f9bc1eb | ||
|
|
1946922de3 | ||
|
|
edf1f4233b | ||
|
|
f4b55ea7a7 | ||
|
|
8dfd1f03e9 | ||
|
|
acf26c5ab7 | ||
|
|
d9800c8135 | ||
|
|
02bef7560f | ||
|
|
07dd0692b6 | ||
|
|
4f3317effe | ||
|
|
9afdbe3648 | ||
|
|
fe0df01448 | ||
|
|
12e6907512 | ||
|
|
5aef492b4c | ||
|
|
5d7ed8ff7d | ||
|
|
b1754fc5ff | ||
|
|
19bbf3e142 | ||
|
|
e1755275a0 | ||
|
|
520037e721 | ||
|
|
cbb0828ab8 | ||
|
|
8774d10bdf | ||
|
|
df9f479d58 | ||
|
|
8bb52c9c2a | ||
|
|
947c423824 | ||
|
|
c3d24fb26d | ||
|
|
112f9ae087 | ||
|
|
01b9ff54d9 | ||
|
|
64a1904136 | ||
|
|
bce6864785 | ||
|
|
ecd54b4cba | ||
|
|
ca2b288a4b | ||
|
|
1016fbb8f9 | ||
|
|
be3f81c7ec | ||
|
|
9f3c151c3c | ||
|
|
9735f3d8f2 | ||
|
|
34680c5ccf | ||
|
|
58934e5881 | ||
|
|
ad3f98b8e7 | ||
|
|
7c33a33ef3 | ||
|
|
3dfcca68e6 | ||
|
|
73b74c94a1 | ||
|
|
18338d60d5 | ||
|
|
e106070640 | ||
|
|
091a7ae359 | ||
|
|
70160aeab3 | ||
|
|
1aa08f594d | ||
|
|
14d8a931fe | ||
|
|
30ba85bc67 | ||
|
|
caadcc3ed8 | ||
|
|
26f55472c6 | ||
|
|
79a58e275c | ||
|
|
900e584514 | ||
|
|
bb639d9f29 | ||
|
|
15dcacc1fc | ||
|
|
6d53e3c2d7 | ||
|
|
8ed7346273 | ||
|
|
3c1220adca | ||
|
|
4ed0eb7012 | ||
|
|
2af5445309 | ||
|
|
abb1916bda | ||
|
|
9424dca9e4 | ||
|
|
db84bb9bd3 | ||
|
|
c603f85488 | ||
|
|
2f1ee25f50 | ||
|
|
7bdf9005e5 | ||
|
|
d9c1d79e30 | ||
|
|
bd88b86919 | ||
|
|
8e29ae8c44 | ||
|
|
d158607f8e | ||
|
|
939fbb3c38 | ||
|
|
3b9dfa9d29 | ||
|
|
0c76fb57f2 | ||
|
|
9694fa8d3a | ||
|
|
20761e053e | ||
|
|
29d885b40f | ||
|
|
087dc13965 | ||
|
|
e7f559c582 | ||
|
|
52c5f6e152 | ||
|
|
26ca59859f | ||
|
|
23d6770ff9 | ||
|
|
ac36a377b0 | ||
|
|
1642867136 | ||
|
|
ce40392803 | ||
|
|
5f1af8a69d | ||
|
|
c57ff2640e | ||
|
|
d7fd396b7c | ||
|
|
45d145a823 | ||
|
|
221ef78faa | ||
|
|
d86513cbba | ||
|
|
25b5904b84 | ||
|
|
c2eb60df4a | ||
|
|
feabd0430c | ||
|
|
838de23357 | ||
|
|
d7b7040408 | ||
|
|
44e4bdc6f4 | ||
|
|
779060bc16 | ||
|
|
76239fa1ae | ||
|
|
5e53f767c4 | ||
|
|
974073a2e5 | ||
|
|
d693431183 | ||
|
|
bedf739d16 | ||
|
|
082755de1a | ||
|
|
6299e42aa9 | ||
|
|
129f41cee9 | ||
|
|
415bbc74aa | ||
|
|
13e41f2c68 | ||
|
|
1e117b780a | ||
|
|
f8c5c24159 | ||
|
|
f5a55c44d4 | ||
|
|
91a0e7bdaa | ||
|
|
b07e309627 | ||
|
|
9ea45399ce | ||
|
|
c19b1a143e | ||
|
|
02c24a860d | ||
|
|
9f652708ee | ||
|
|
05fa790584 | ||
|
|
e0db822a9b | ||
|
|
ec0fee6208 | ||
|
|
a188554fe1 | ||
|
|
8d52c7daf3 | ||
|
|
c49ebaaf1a | ||
|
|
acc9645249 | ||
|
|
60f961dfe8 | ||
|
|
0c48b1d993 | ||
|
|
d57b57bddc | ||
|
|
3837d2b94b | ||
|
|
f81a188ef6 | ||
|
|
8e417e28d1 | ||
|
|
4ce6830a7b | ||
|
|
3a7c79e2c7 | ||
|
|
cb2c2905c5 | ||
|
|
421160631a | ||
|
|
60aad1b717 | ||
|
|
72a17bdd76 | ||
|
|
9b9ce1c625 | ||
|
|
d7cb6de820 | ||
|
|
3d5750f31c | ||
|
|
fabf60bc4c | ||
|
|
f5be8ba11f | ||
|
|
94d587e6fc | ||
|
|
2ec44f7620 | ||
|
|
bbbf25201a | ||
|
|
d6a3215fe2 | ||
|
|
75699a3825 | ||
|
|
f3aeed77e5 | ||
|
|
cfbaf7bf1c | ||
|
|
bc6067d195 | ||
|
|
7203d93fb3 | ||
|
|
ffd497673f | ||
|
|
d00ff3c453 | ||
|
|
1d9e91e00f | ||
|
|
7f6ed35347 | ||
|
|
38027c8f52 | ||
|
|
dd5804c10e | ||
|
|
84dcd25a36 | ||
|
|
4519450363 | ||
|
|
3c70eca758 | ||
|
|
68a2d6fc40 | ||
|
|
a5923a5d51 | ||
|
|
769f0b1e24 | ||
|
|
a1271d984f | ||
|
|
db65ec4674 | ||
|
|
a984c55cf9 | ||
|
|
cdd0828c4a | ||
|
|
1984d0671b | ||
|
|
3e4efff73d | ||
|
|
f4d1b7c603 | ||
|
|
200caab82b | ||
|
|
f9b104f37b | ||
|
|
c25b482301 | ||
|
|
31d7cc2cd4 | ||
|
|
19ecdc75a8 | ||
|
|
46724508f8 | ||
|
|
51b0194b8a | ||
|
|
9a27c4a2f0 | ||
|
|
32df742b85 | ||
|
|
8392765213 | ||
|
|
806b10b934 | ||
|
|
1fa0553c71 | ||
|
|
50a68a1791 | ||
|
|
0b55a0423e | ||
|
|
565d95a377 | ||
|
|
f492f72154 | ||
|
|
4d84f0f6f0 | ||
|
|
a0d0c8e4af | ||
|
|
bef748abbd | ||
|
|
c4373ef290 | ||
|
|
0b8c5a6872 | ||
|
|
829ecb2086 | ||
|
|
d3564a4b09 | ||
|
|
a244753f47 | ||
|
|
745782a77a | ||
|
|
246cbe1312 | ||
|
|
6c941122eb | ||
|
|
1a884cd8e1 | ||
|
|
18f008f7c7 | ||
|
|
6d42569ade | ||
|
|
5ed781a330 | ||
|
|
66fcd02aa2 | ||
|
|
1fc0e9a6aa | ||
|
|
91567ba916 | ||
|
|
d80826b05d | ||
|
|
45bcf73185 | ||
|
|
78dc08bdc2 | ||
|
|
f98f115ac2 | ||
|
|
bf409936e7 | ||
|
|
b4364723ef | ||
|
|
bcc6359dec | ||
|
|
787a72a993 | ||
|
|
d9eb962969 | ||
|
|
f221153776 | ||
|
|
67596ef0cc | ||
|
|
bf5bfe589f | ||
|
|
af78c3925a | ||
|
|
d144958669 | ||
|
|
5a64003f6f | ||
|
|
311718309c | ||
|
|
98479d7ffd | ||
|
|
90e505e58f | ||
|
|
410b8dd0fd | ||
|
|
c2f25b6f62 | ||
|
|
03a2a74697 | ||
|
|
2807c11410 | ||
|
|
39d51ce845 | ||
|
|
a216583d95 | ||
|
|
5c448b1b97 | ||
|
|
7f49c38e2d | ||
|
|
0e7fdcee30 | ||
|
|
418f8bed6a | ||
|
|
950fe73c4f | ||
|
|
0892f1e406 | ||
|
|
9af4e7b1da | ||
|
|
198a838d00 | ||
|
|
aaa3fc3805 | ||
|
|
3c2efd9cf3 | ||
|
|
951b1e6a7a | ||
|
|
9c5fd6a776 | ||
|
|
e438dccf19 | ||
|
|
43d2655ee4 | ||
|
|
b2c92cdaaa | ||
|
|
42b1d92b2a | ||
|
|
1250312287 | ||
|
|
308371b434 | ||
|
|
88e6c11746 | ||
|
|
2ee4ae88f9 | ||
|
|
e2b6fb0a6a | ||
|
|
a19e3bc9d9 | ||
|
|
495c55e6a5 | ||
|
|
a366143c5b | ||
|
|
b8f5a7db33 | ||
|
|
f486cfae86 | ||
|
|
0838732df3 | ||
|
|
f422f09dff | ||
|
|
aff196ae57 | ||
|
|
27c9f8be7a | ||
|
|
67b6c945e2 | ||
|
|
c89aee37b9 | ||
|
|
03b7bebc96 | ||
|
|
f89d0f68d0 | ||
|
|
72a288f73f | ||
|
|
0073aee1ed | ||
|
|
0f7a51f461 | ||
|
|
556552340a | ||
|
|
50bce0130a | ||
|
|
5c6dc63577 | ||
|
|
2077d27053 | ||
|
|
76b3d3c559 | ||
|
|
514b2d6f12 | ||
|
|
470553ff5d | ||
|
|
88e0aa1cb2 | ||
|
|
35f2552fc5 | ||
|
|
e05886561d | ||
|
|
2451b9a75a | ||
|
|
06b71c99ee | ||
|
|
ae8f7f11d5 | ||
|
|
ed16ce9b73 | ||
|
|
27f895cf2c | ||
|
|
c11a2ac396 | ||
|
|
2f9ab26372 | ||
|
|
0559f46bbb | ||
|
|
6e5f83c45b | ||
|
|
e1b0582859 | ||
|
|
88d719689c | ||
|
|
200eb8dc0e | ||
|
|
082650bea3 | ||
|
|
abf079135e | ||
|
|
f00e8bc107 | ||
|
|
ce05e67a0c | ||
|
|
fecb1b0489 | ||
|
|
6a7e22386e | ||
|
|
85dfb4351c | ||
|
|
addf15f61f | ||
|
|
bbf3576f70 | ||
|
|
da3f4bd452 | ||
|
|
5eb6f903f2 | ||
|
|
60394ddf83 | ||
|
|
293d261cf9 | ||
|
|
f1cab828ee | ||
|
|
6a8d0fb955 | ||
|
|
c8ca055935 | ||
|
|
e0abb46616 | ||
|
|
170b89f468 | ||
|
|
674c6f7a7b | ||
|
|
db35bcf2ce | ||
|
|
901d1314af | ||
|
|
9e9bfd0255 | ||
|
|
1080609c86 | ||
|
|
8315bcd0d8 | ||
|
|
7fb9301c03 | ||
|
|
63f3e5c3fc | ||
|
|
a75412440f | ||
|
|
47de1d2e0e | ||
|
|
14fe8ecb58 | ||
|
|
0f01e7ef0f | ||
|
|
16f7c64a9f | ||
|
|
00aa9841b7 | ||
|
|
7802088e71 | ||
|
|
6d04c9c585 | ||
|
|
e210cb3670 | ||
|
|
47b577fcc0 | ||
|
|
e9d970154d | ||
|
|
202d0b64eb | ||
|
|
c25816eabc | ||
|
|
ee028a4693 | ||
|
|
9c65168312 | ||
|
|
16aeb68c28 | ||
|
|
6167104644 | ||
|
|
30b77f59b1 | ||
|
|
a690772cc5 | ||
|
|
cf8abd8888 | ||
|
|
17e0de1e87 | ||
|
|
b7e3651d3c | ||
|
|
ef4d023c85 | ||
|
|
654a6e9871 | ||
|
|
2ca5ee026d | ||
|
|
9b7d593e28 | ||
|
|
ad928f0078 | ||
|
|
92bb2928e4 | ||
|
|
47dfc1b1b0 | ||
|
|
7d8413a589 | ||
|
|
24722ddd02 | ||
|
|
f31a00de01 | ||
|
|
d44e4399e6 | ||
|
|
f9ae71fd17 | ||
|
|
ce28e904c9 | ||
|
|
77b8885a24 | ||
|
|
8f2a3efa85 | ||
|
|
89febdb3d6 | ||
|
|
3eac02f676 | ||
|
|
a526ad2e80 | ||
|
|
65b6f4aa31 | ||
|
|
9e88941515 | ||
|
|
3becee9e5d | ||
|
|
40a2fa8e81 | ||
|
|
39f81d2c5b | ||
|
|
59bb54ed6a | ||
|
|
9ab5e0312d | ||
|
|
54ab3a1d5b | ||
|
|
92c94011f1 | ||
|
|
35cbe43b6d | ||
|
|
a2cd3c9a1d | ||
|
|
7b0b0f9101 |
@@ -1,10 +1,17 @@
|
||||
.git
|
||||
.github
|
||||
docs
|
||||
default.etcd
|
||||
*.gz
|
||||
*.tar.gz
|
||||
*.bzip2
|
||||
*.zip
|
||||
browser/node_modules
|
||||
node_modules
|
||||
node_modules
|
||||
docs/debugging/s3-verify/s3-verify
|
||||
docs/debugging/xl-meta/xl-meta
|
||||
docs/debugging/s3-check-md5/s3-check-md5
|
||||
docs/debugging/hash-set/hash-set
|
||||
docs/debugging/healing-bin/healing-bin
|
||||
docs/debugging/inspect/inspect
|
||||
docs/debugging/pprofgoparser/pprofgoparser
|
||||
docs/debugging/reorder-disks/reorder-disks
|
||||
|
||||
5
.github/ISSUE_TEMPLATE.md
vendored
5
.github/ISSUE_TEMPLATE.md
vendored
@@ -7,6 +7,11 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## NOTE
|
||||
All GitHub issues are addressed on a best-effort basis at MinIO's sole discretion. There are no Service Level Agreements (SLA) or Objectives (SLO). Remember our [Code of Conduct](https://github.com/minio/minio/blob/master/code_of_conduct.md) when engaging with MinIO Engineers and the larger community.
|
||||
|
||||
For urgent issues (e.g. production down, etc.), subscribe to [SUBNET](https://min.io/pricing?jmp=github) for direct to engineering support.
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,6 +7,9 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## NOTE
|
||||
If this case is urgent, please subscribe to [Subnet](https://min.io/pricing) so that our 24/7 support team may help you faster.
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,3 +1,9 @@
|
||||
## Community Contribution License
|
||||
All community contributions in this pull request are licensed to the project maintainers
|
||||
under the terms of the [Apache 2 license](https://www.apache.org/licenses/LICENSE-2.0).
|
||||
By creating this pull request I represent that I have the right to license the
|
||||
contributions to the project maintainers under the Apache 2 license.
|
||||
|
||||
## Description
|
||||
|
||||
|
||||
@@ -15,5 +21,6 @@
|
||||
|
||||
## Checklist:
|
||||
- [ ] Fixes a regression (If yes, please add `commit-id` or `PR #` here)
|
||||
- [ ] Documentation updated
|
||||
- [ ] Unit tests added/updated
|
||||
- [ ] Internal documentation updated
|
||||
- [ ] Create a documentation update request [here](https://github.com/minio/docs/issues/new?label=doc-change,title=Doc+Updated+Needed+For+PR+github.com%2fminio%2fminio%2fpull%2fNNNNN)
|
||||
|
||||
39
.github/lock.yml
vendored
39
.github/lock.yml
vendored
@@ -1,39 +0,0 @@
|
||||
# Configuration for Lock Threads - https://github.com/dessant/lock-threads-app
|
||||
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 365
|
||||
|
||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
||||
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
|
||||
skipCreatedBefore: false
|
||||
|
||||
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: true
|
||||
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: >-
|
||||
|
||||
This thread has been automatically locked since there has not been
|
||||
any recent activity after it was closed. Please open a new issue for
|
||||
related bugs.
|
||||
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: true
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: issues
|
||||
|
||||
# Optionally, specify configuration settings just for `issues` or `pulls`
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - help-wanted
|
||||
# lockLabel: outdated
|
||||
|
||||
# pulls:
|
||||
# daysUntilLock: 30
|
||||
|
||||
# Repository to extend settings from
|
||||
# _extends: repo
|
||||
2
.github/stale.yml
vendored
2
.github/stale.yml
vendored
@@ -14,7 +14,7 @@ onlyLabels: []
|
||||
exemptLabels:
|
||||
- "security"
|
||||
- "pending discussion"
|
||||
- "do not close"
|
||||
- "do-not-close"
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
14
.github/workflows/depsreview.yaml
vendored
Normal file
14
.github/workflows/depsreview.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: 'Dependency Review'
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v4
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v4
|
||||
25
.github/workflows/go-cross.yml
vendored
25
.github/workflows/go-cross.yml
vendored
@@ -1,26 +1,33 @@
|
||||
name: Go
|
||||
name: Crosscompile
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: MinIO crosscompile tests on ${{ matrix.go-version }} and ${{ matrix.os }}
|
||||
name: Build Tests with Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x]
|
||||
go-version: [1.22.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12'
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Build on ${{ matrix.os }}
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
|
||||
59
.github/workflows/go-fips.yml
vendored
Normal file
59
.github/workflows/go-fips.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: FIPS Build Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Go BoringCrypto ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.22.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Setup dockerfile for build test
|
||||
run: |
|
||||
GO_VERSION=$(go version | cut -d ' ' -f 3 | sed 's/go//')
|
||||
echo Detected go version $GO_VERSION
|
||||
cat > Dockerfile.fips.test <<EOF
|
||||
FROM golang:${GO_VERSION}
|
||||
COPY . /minio
|
||||
WORKDIR /minio
|
||||
ENV GOEXPERIMENT=boringcrypto
|
||||
RUN make
|
||||
EOF
|
||||
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.fips.test
|
||||
push: false
|
||||
load: true
|
||||
tags: minio/fips-test:latest
|
||||
|
||||
# This should fail if grep returns non-zero exit
|
||||
- name: Test binary
|
||||
run: |
|
||||
docker run --rm minio/fips-test:latest ./minio --version
|
||||
docker run --rm -i minio/fips-test:latest /bin/bash -c 'go tool nm ./minio | grep FIPS | grep -q FIPS'
|
||||
44
.github/workflows/go-healing.yml
vendored
Normal file
44
.github/workflows/go-healing.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Healing Functional Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.22.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Build on ${{ matrix.os }}
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GO111MODULE: on
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:oyArl7zlPECEduNbB1KXgdzDn2Bdpvvw0l8VO51HQnY="
|
||||
MINIO_KMS_AUTO_ENCRYPTION: on
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make verify-healing
|
||||
make verify-healing-inconsistent-versions
|
||||
make verify-healing-with-root-disks
|
||||
make verify-healing-with-rewrite
|
||||
34
.github/workflows/go-lint.yml
vendored
34
.github/workflows/go-lint.yml
vendored
@@ -1,31 +1,43 @@
|
||||
name: Go
|
||||
name: Linters and Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: MinIO tests on ${{ matrix.go-version }} and ${{ matrix.os }}
|
||||
name: Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
go-version: [1.22.x]
|
||||
os: [ubuntu-latest, Windows]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Build on ${{ matrix.os }}
|
||||
if: matrix.os == 'windows-latest'
|
||||
if: matrix.os == 'Windows'
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GO111MODULE: on
|
||||
run: |
|
||||
Set-MpPreference -DisableRealtimeMonitoring $true
|
||||
netsh int ipv4 set dynamicport tcp start=60000 num=61000
|
||||
go build --ldflags="-s -w" -o %GOPATH%\bin\minio.exe
|
||||
go test -v --timeout 50m ./...
|
||||
go test -v --timeout 120m ./...
|
||||
- name: Build on ${{ matrix.os }}
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
@@ -35,8 +47,6 @@ jobs:
|
||||
sudo apt install jq -y
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
nancy_version=$(curl --retry 10 -Ls -o /dev/null -w "%{url_effective}" https://github.com/sonatype-nexus-community/nancy/releases/latest | sed "s/https:\/\/github.com\/sonatype-nexus-community\/nancy\/releases\/tag\///")
|
||||
curl -L -o nancy https://github.com/sonatype-nexus-community/nancy/releases/download/${nancy_version}/nancy-${nancy_version}-linux-amd64 && chmod +x nancy
|
||||
go list -deps -json ./... | jq -s 'unique_by(.Module.Path)|.[]|select(has("Module"))|.Module' | ./nancy sleuth
|
||||
make
|
||||
make test
|
||||
make test-race
|
||||
|
||||
28
.github/workflows/go.yml
vendored
28
.github/workflows/go.yml
vendored
@@ -1,35 +1,41 @@
|
||||
name: Go
|
||||
name: Functional Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: MinIO Setup on ${{ matrix.go-version }} and ${{ matrix.os }}
|
||||
name: Go ${{ matrix.go-version }} on ${{ matrix.os }} - healing
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x]
|
||||
go-version: [1.22.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Build on ${{ matrix.os }}
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GO111MODULE: on
|
||||
MINIO_KMS_KES_CERT_FILE: /home/runner/work/minio/minio/.github/workflows/root.cert
|
||||
MINIO_KMS_KES_KEY_FILE: /home/runner/work/minio/minio/.github/workflows/root.key
|
||||
MINIO_KMS_KES_ENDPOINT: "https://play.min.io:7373"
|
||||
MINIO_KMS_KES_KEY_NAME: "my-minio-key"
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw="
|
||||
MINIO_KMS_AUTO_ENCRYPTION: on
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make verify
|
||||
make verify-healing
|
||||
|
||||
30
.github/workflows/helm-lint.yml
vendored
Normal file
30
.github/workflows/helm-lint.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Helm Chart linting
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v4
|
||||
|
||||
- name: Run helm lint
|
||||
run: |
|
||||
cd helm/minio
|
||||
helm lint .
|
||||
127
.github/workflows/iam-integrations.yaml
vendored
Normal file
127
.github/workflows/iam-integrations.yaml
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
name: IAM integration
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
iam-matrix-test:
|
||||
name: "[Go=${{ matrix.go-version }}|ldap=${{ matrix.ldap }}|etcd=${{ matrix.etcd }}|openid=${{ matrix.openid }}]"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
openldap:
|
||||
image: quay.io/minio/openldap
|
||||
ports:
|
||||
- "389:389"
|
||||
- "636:636"
|
||||
env:
|
||||
LDAP_ORGANIZATION: "MinIO Inc"
|
||||
LDAP_DOMAIN: "min.io"
|
||||
LDAP_ADMIN_PASSWORD: "admin"
|
||||
etcd:
|
||||
image: "quay.io/coreos/etcd:v3.5.1"
|
||||
env:
|
||||
ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
|
||||
ETCD_ADVERTISE_CLIENT_URLS: "http://0.0.0.0:2379"
|
||||
ports:
|
||||
- "2379:2379"
|
||||
options: >-
|
||||
--health-cmd "etcdctl endpoint health"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
openid:
|
||||
image: quay.io/minio/dex
|
||||
ports:
|
||||
- "5556:5556"
|
||||
env:
|
||||
DEX_LDAP_SERVER: "openldap:389"
|
||||
openid2:
|
||||
image: quay.io/minio/dex
|
||||
ports:
|
||||
- "5557:5557"
|
||||
env:
|
||||
DEX_LDAP_SERVER: "openldap:389"
|
||||
DEX_ISSUER: "http://127.0.0.1:5557/dex"
|
||||
DEX_WEB_HTTP: "0.0.0.0:5557"
|
||||
|
||||
strategy:
|
||||
# When ldap, etcd or openid vars are empty below, those external servers
|
||||
# are turned off - i.e. if ldap="", then ldap server is not enabled for
|
||||
# the tests.
|
||||
matrix:
|
||||
go-version: [1.22.x]
|
||||
ldap: ["", "localhost:389"]
|
||||
etcd: ["", "http://localhost:2379"]
|
||||
openid: ["", "http://127.0.0.1:5556/dex"]
|
||||
exclude:
|
||||
# exclude combos where all are empty.
|
||||
- ldap: ""
|
||||
etcd: ""
|
||||
openid: ""
|
||||
# exclude combos where both ldap and openid IDPs are specified.
|
||||
- ldap: "localhost:389"
|
||||
openid: "http://127.0.0.1:5556/dex"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Test LDAP/OpenID/Etcd combo
|
||||
env:
|
||||
_MINIO_LDAP_TEST_SERVER: ${{ matrix.ldap }}
|
||||
_MINIO_ETCD_TEST_SERVER: ${{ matrix.etcd }}
|
||||
_MINIO_OPENID_TEST_SERVER: ${{ matrix.openid }}
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-iam
|
||||
- name: Test with multiple OpenID providers
|
||||
if: matrix.openid == 'http://127.0.0.1:5556/dex'
|
||||
env:
|
||||
_MINIO_LDAP_TEST_SERVER: ${{ matrix.ldap }}
|
||||
_MINIO_ETCD_TEST_SERVER: ${{ matrix.etcd }}
|
||||
_MINIO_OPENID_TEST_SERVER: ${{ matrix.openid }}
|
||||
_MINIO_OPENID_TEST_SERVER_2: "http://127.0.0.1:5557/dex"
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-iam
|
||||
- name: Test with Access Management Plugin enabled
|
||||
env:
|
||||
_MINIO_LDAP_TEST_SERVER: ${{ matrix.ldap }}
|
||||
_MINIO_ETCD_TEST_SERVER: ${{ matrix.etcd }}
|
||||
_MINIO_OPENID_TEST_SERVER: ${{ matrix.openid }}
|
||||
_MINIO_POLICY_PLUGIN_TEST_ENDPOINT: "http://127.0.0.1:8080"
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
go run docs/iam/access-manager-plugin.go &
|
||||
make test-iam
|
||||
- name: Test MinIO Old Version data to IAM import current version
|
||||
if: matrix.ldap == 'ldaphost:389'
|
||||
env:
|
||||
_MINIO_LDAP_TEST_SERVER: ${{ matrix.ldap }}
|
||||
run: |
|
||||
make test-iam-ldap-upgrade-import
|
||||
- name: Test LDAP for automatic site replication
|
||||
if: matrix.ldap == 'localhost:389'
|
||||
run: |
|
||||
make test-site-replication-ldap
|
||||
- name: Test OIDC for automatic site replication
|
||||
if: matrix.openid == 'http://127.0.0.1:5556/dex'
|
||||
run: |
|
||||
make test-site-replication-oidc
|
||||
18
.github/workflows/issues.yaml
vendored
Normal file
18
.github/workflows/issues.yaml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# @format
|
||||
|
||||
name: Issue Workflow
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add issue to project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v0.5.0
|
||||
with:
|
||||
project-url: https://github.com/orgs/miniohq/projects/2
|
||||
github-token: ${{ secrets.BOT_PAT }}
|
||||
24
.github/workflows/lock.yml
vendored
Normal file
24
.github/workflows/lock.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: 'Lock Threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
concurrency:
|
||||
group: lock
|
||||
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v3
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: '365'
|
||||
exclude-any-issue-labels: 'do-not-close'
|
||||
issue-lock-reason: 'resolved'
|
||||
log-output: true
|
||||
81
.github/workflows/mint.yml
vendored
Normal file
81
.github/workflows/mint.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
name: Mint Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
mint-test:
|
||||
runs-on: mint
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- name: cleanup #https://github.com/actions/checkout/issues/273
|
||||
run: |
|
||||
sudo -S rm -rf ${GITHUB_WORKSPACE}
|
||||
mkdir ${GITHUB_WORKSPACE}
|
||||
- name: checkout-step
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: setup-go-step
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
|
||||
- name: github sha short
|
||||
id: vars
|
||||
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: build-minio
|
||||
run: |
|
||||
TAG="quay.io/minio/minio:${{ steps.vars.outputs.sha_short }}" make docker
|
||||
|
||||
- name: multipart uploads test
|
||||
run: |
|
||||
${GITHUB_WORKSPACE}/.github/workflows/multipart/migrate.sh "${{ steps.vars.outputs.sha_short }}"
|
||||
|
||||
- name: compress and encrypt
|
||||
run: |
|
||||
${GITHUB_WORKSPACE}/.github/workflows/run-mint.sh "compress-encrypt" "minio" "minio123" "${{ steps.vars.outputs.sha_short }}"
|
||||
|
||||
- name: multiple pools
|
||||
run: |
|
||||
${GITHUB_WORKSPACE}/.github/workflows/run-mint.sh "pools" "minio" "minio123" "${{ steps.vars.outputs.sha_short }}"
|
||||
|
||||
- name: standalone erasure
|
||||
run: |
|
||||
${GITHUB_WORKSPACE}/.github/workflows/run-mint.sh "erasure" "minio" "minio123" "${{ steps.vars.outputs.sha_short }}"
|
||||
|
||||
# FIXME: renable this back when we have a valid way to add deadlines for PUT()s (internode CreateFile)
|
||||
# - name: resiliency
|
||||
# run: |
|
||||
# ${GITHUB_WORKSPACE}/.github/workflows/run-mint.sh "resiliency" "minio" "minio123" "${{ steps.vars.outputs.sha_short }}"
|
||||
|
||||
- name: The job must cleanup
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
export JOB_NAME=${{ steps.vars.outputs.sha_short }}
|
||||
for mode in $(echo compress-encrypt pools erasure); do
|
||||
docker-compose -f ${GITHUB_WORKSPACE}/.github/workflows/mint/minio-${mode}.yaml down || true
|
||||
docker-compose -f ${GITHUB_WORKSPACE}/.github/workflows/mint/minio-${mode}.yaml rm || true
|
||||
done
|
||||
|
||||
docker-compose -f ${GITHUB_WORKSPACE}/.github/workflows/multipart/docker-compose-site1.yaml rm -s -f || true
|
||||
docker-compose -f ${GITHUB_WORKSPACE}/.github/workflows/multipart/docker-compose-site2.yaml rm -s -f || true
|
||||
for volume in $(docker volume ls -q | grep minio); do
|
||||
docker volume rm ${volume} || true
|
||||
done
|
||||
|
||||
docker rmi -f quay.io/minio/minio:${{ steps.vars.outputs.sha_short }}
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -q -f dangling=true) || true
|
||||
80
.github/workflows/mint/minio-compress-encrypt.yaml
vendored
Normal file
80
.github/workflows/mint/minio-compress-encrypt.yaml
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${JOB_NAME}
|
||||
command: server --console-address ":9001" http://minio{1...4}/cdata{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
- "9001"
|
||||
environment:
|
||||
MINIO_CI_CD: "on"
|
||||
MINIO_ROOT_USER: "minio"
|
||||
MINIO_ROOT_PASSWORD: "minio123"
|
||||
MINIO_COMPRESSION_ENABLE: "on"
|
||||
MINIO_COMPRESSION_MIME_TYPES: "*"
|
||||
MINIO_COMPRESSION_ALLOW_ENCRYPTION: "on"
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw="
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
minio1:
|
||||
<<: *minio-common
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- cdata1-1:/cdata1
|
||||
- cdata1-2:/cdata2
|
||||
|
||||
minio2:
|
||||
<<: *minio-common
|
||||
hostname: minio2
|
||||
volumes:
|
||||
- cdata2-1:/cdata1
|
||||
- cdata2-2:/cdata2
|
||||
|
||||
minio3:
|
||||
<<: *minio-common
|
||||
hostname: minio3
|
||||
volumes:
|
||||
- cdata3-1:/cdata1
|
||||
- cdata3-2:/cdata2
|
||||
|
||||
minio4:
|
||||
<<: *minio-common
|
||||
hostname: minio4
|
||||
volumes:
|
||||
- cdata4-1:/cdata1
|
||||
- cdata4-2:/cdata2
|
||||
|
||||
nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: nginx
|
||||
volumes:
|
||||
- ./nginx-4-node.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- minio1
|
||||
- minio2
|
||||
- minio3
|
||||
- minio4
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
cdata1-1:
|
||||
cdata1-2:
|
||||
cdata2-1:
|
||||
cdata2-2:
|
||||
cdata3-1:
|
||||
cdata3-2:
|
||||
cdata4-1:
|
||||
cdata4-2:
|
||||
51
.github/workflows/mint/minio-erasure.yaml
vendored
Normal file
51
.github/workflows/mint/minio-erasure.yaml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${JOB_NAME}
|
||||
command: server --console-address ":9001" edata{1...4}
|
||||
expose:
|
||||
- "9000"
|
||||
- "9001"
|
||||
environment:
|
||||
MINIO_CI_CD: "on"
|
||||
MINIO_ROOT_USER: "minio"
|
||||
MINIO_ROOT_PASSWORD: "minio123"
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw="
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
minio1:
|
||||
<<: *minio-common
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- edata1-1:/edata1
|
||||
- edata1-2:/edata2
|
||||
- edata1-3:/edata3
|
||||
- edata1-4:/edata4
|
||||
|
||||
nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: nginx
|
||||
volumes:
|
||||
- ./nginx-1-node.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- minio1
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
edata1-1:
|
||||
edata1-2:
|
||||
edata1-3:
|
||||
edata1-4:
|
||||
117
.github/workflows/mint/minio-pools.yaml
vendored
Normal file
117
.github/workflows/mint/minio-pools.yaml
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${JOB_NAME}
|
||||
command: server --console-address ":9001" http://minio{1...4}/pdata{1...2} http://minio{5...8}/pdata{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
- "9001"
|
||||
environment:
|
||||
MINIO_CI_CD: "on"
|
||||
MINIO_ROOT_USER: "minio"
|
||||
MINIO_ROOT_PASSWORD: "minio123"
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw="
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
minio1:
|
||||
<<: *minio-common
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- pdata1-1:/pdata1
|
||||
- pdata1-2:/pdata2
|
||||
|
||||
minio2:
|
||||
<<: *minio-common
|
||||
hostname: minio2
|
||||
volumes:
|
||||
- pdata2-1:/pdata1
|
||||
- pdata2-2:/pdata2
|
||||
|
||||
minio3:
|
||||
<<: *minio-common
|
||||
hostname: minio3
|
||||
volumes:
|
||||
- pdata3-1:/pdata1
|
||||
- pdata3-2:/pdata2
|
||||
|
||||
minio4:
|
||||
<<: *minio-common
|
||||
hostname: minio4
|
||||
volumes:
|
||||
- pdata4-1:/pdata1
|
||||
- pdata4-2:/pdata2
|
||||
|
||||
minio5:
|
||||
<<: *minio-common
|
||||
hostname: minio5
|
||||
volumes:
|
||||
- pdata5-1:/pdata1
|
||||
- pdata5-2:/pdata2
|
||||
|
||||
minio6:
|
||||
<<: *minio-common
|
||||
hostname: minio6
|
||||
volumes:
|
||||
- pdata6-1:/pdata1
|
||||
- pdata6-2:/pdata2
|
||||
|
||||
minio7:
|
||||
<<: *minio-common
|
||||
hostname: minio7
|
||||
volumes:
|
||||
- pdata7-1:/pdata1
|
||||
- pdata7-2:/pdata2
|
||||
|
||||
minio8:
|
||||
<<: *minio-common
|
||||
hostname: minio8
|
||||
volumes:
|
||||
- pdata8-1:/pdata1
|
||||
- pdata8-2:/pdata2
|
||||
|
||||
nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: nginx
|
||||
volumes:
|
||||
- ./nginx-8-node.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- minio1
|
||||
- minio2
|
||||
- minio3
|
||||
- minio4
|
||||
- minio5
|
||||
- minio6
|
||||
- minio7
|
||||
- minio8
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
pdata1-1:
|
||||
pdata1-2:
|
||||
pdata2-1:
|
||||
pdata2-2:
|
||||
pdata3-1:
|
||||
pdata3-2:
|
||||
pdata4-1:
|
||||
pdata4-2:
|
||||
pdata5-1:
|
||||
pdata5-2:
|
||||
pdata6-1:
|
||||
pdata6-2:
|
||||
pdata7-1:
|
||||
pdata7-2:
|
||||
pdata8-1:
|
||||
pdata8-2:
|
||||
78
.github/workflows/mint/minio-resiliency.yaml
vendored
Normal file
78
.github/workflows/mint/minio-resiliency.yaml
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${JOB_NAME}
|
||||
command: server --console-address ":9001" http://minio{1...4}/rdata{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
- "9001"
|
||||
environment:
|
||||
MINIO_CI_CD: "on"
|
||||
MINIO_ROOT_USER: "minio"
|
||||
MINIO_ROOT_PASSWORD: "minio123"
|
||||
MINIO_KMS_SECRET_KEY: "my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw="
|
||||
MINIO_DRIVE_MAX_TIMEOUT: "5s"
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
minio1:
|
||||
<<: *minio-common
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- rdata1-1:/rdata1
|
||||
- rdata1-2:/rdata2
|
||||
|
||||
minio2:
|
||||
<<: *minio-common
|
||||
hostname: minio2
|
||||
volumes:
|
||||
- rdata2-1:/rdata1
|
||||
- rdata2-2:/rdata2
|
||||
|
||||
minio3:
|
||||
<<: *minio-common
|
||||
hostname: minio3
|
||||
volumes:
|
||||
- rdata3-1:/rdata1
|
||||
- rdata3-2:/rdata2
|
||||
|
||||
minio4:
|
||||
<<: *minio-common
|
||||
hostname: minio4
|
||||
volumes:
|
||||
- rdata4-1:/rdata1
|
||||
- rdata4-2:/rdata2
|
||||
|
||||
nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: nginx
|
||||
volumes:
|
||||
- ./nginx-4-node.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- minio1
|
||||
- minio2
|
||||
- minio3
|
||||
- minio4
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
rdata1-1:
|
||||
rdata1-2:
|
||||
rdata2-1:
|
||||
rdata2-2:
|
||||
rdata3-1:
|
||||
rdata3-2:
|
||||
rdata4-1:
|
||||
rdata4-2:
|
||||
100
.github/workflows/mint/nginx-1-node.conf
vendored
Normal file
100
.github/workflows/mint/nginx-1-node.conf
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server minio1:9000;
|
||||
}
|
||||
|
||||
upstream console {
|
||||
ip_hash;
|
||||
server minio1:9001;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9000;
|
||||
listen [::]:9000;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9001;
|
||||
listen [::]:9001;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
|
||||
# This is necessary to pass the correct IP to be hashed
|
||||
real_ip_header X-Real-IP;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
|
||||
# To support websocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://console;
|
||||
}
|
||||
}
|
||||
}
|
||||
105
.github/workflows/mint/nginx-4-node.conf
vendored
Normal file
105
.github/workflows/mint/nginx-4-node.conf
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server minio1:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio2:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio3:9000 max_fails=1 fail_timeout=10s;
|
||||
}
|
||||
|
||||
upstream console {
|
||||
ip_hash;
|
||||
server minio1:9001;
|
||||
server minio2:9001;
|
||||
server minio3:9001;
|
||||
server minio4:9001;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9000;
|
||||
listen [::]:9000;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9001;
|
||||
listen [::]:9001;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
|
||||
# This is necessary to pass the correct IP to be hashed
|
||||
real_ip_header X-Real-IP;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
|
||||
# To support websocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://console;
|
||||
}
|
||||
}
|
||||
}
|
||||
114
.github/workflows/mint/nginx-8-node.conf
vendored
Normal file
114
.github/workflows/mint/nginx-8-node.conf
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server minio1:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio2:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio3:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio4:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio5:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio6:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio7:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio8:9000 max_fails=1 fail_timeout=10s;
|
||||
}
|
||||
|
||||
upstream console {
|
||||
ip_hash;
|
||||
server minio1:9001;
|
||||
server minio2:9001;
|
||||
server minio3:9001;
|
||||
server minio4:9001;
|
||||
server minio5:9001;
|
||||
server minio6:9001;
|
||||
server minio7:9001;
|
||||
server minio8:9001;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9000;
|
||||
listen [::]:9000;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9001;
|
||||
listen [::]:9001;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
|
||||
# This is necessary to pass the correct IP to be hashed
|
||||
real_ip_header X-Real-IP;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
|
||||
# To support websocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://console;
|
||||
}
|
||||
}
|
||||
}
|
||||
106
.github/workflows/mint/nginx.conf
vendored
Normal file
106
.github/workflows/mint/nginx.conf
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server minio1:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio2:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio3:9000 max_fails=1 fail_timeout=10s;
|
||||
server minio4:9000 max_fails=1 fail_timeout=10s;
|
||||
}
|
||||
|
||||
upstream console {
|
||||
ip_hash;
|
||||
server minio1:9001;
|
||||
server minio2:9001;
|
||||
server minio3:9001;
|
||||
server minio4:9001;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9000;
|
||||
listen [::]:9000;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9001;
|
||||
listen [::]:9001;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
|
||||
# This is necessary to pass the correct IP to be hashed
|
||||
real_ip_header X-Real-IP;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
|
||||
# To support websocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://console;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
.github/workflows/multipart/docker-compose-site1.yaml
vendored
Normal file
66
.github/workflows/multipart/docker-compose-site1.yaml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${RELEASE}
|
||||
command: server http://site1-minio{1...4}/data{1...2}
|
||||
environment:
|
||||
- MINIO_PROMETHEUS_AUTH_TYPE=public
|
||||
- CI=true
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
site1-minio1:
|
||||
<<: *minio-common
|
||||
hostname: site1-minio1
|
||||
volumes:
|
||||
- site1-data1-1:/data1
|
||||
- site1-data1-2:/data2
|
||||
|
||||
site1-minio2:
|
||||
<<: *minio-common
|
||||
hostname: site1-minio2
|
||||
volumes:
|
||||
- site1-data2-1:/data1
|
||||
- site1-data2-2:/data2
|
||||
|
||||
site1-minio3:
|
||||
<<: *minio-common
|
||||
hostname: site1-minio3
|
||||
volumes:
|
||||
- site1-data3-1:/data1
|
||||
- site1-data3-2:/data2
|
||||
|
||||
site1-minio4:
|
||||
<<: *minio-common
|
||||
hostname: site1-minio4
|
||||
volumes:
|
||||
- site1-data4-1:/data1
|
||||
- site1-data4-2:/data2
|
||||
|
||||
site1-nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: site1-nginx
|
||||
volumes:
|
||||
- ./nginx-site1.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- site1-minio1
|
||||
- site1-minio2
|
||||
- site1-minio3
|
||||
- site1-minio4
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
site1-data1-1:
|
||||
site1-data1-2:
|
||||
site1-data2-1:
|
||||
site1-data2-2:
|
||||
site1-data3-1:
|
||||
site1-data3-2:
|
||||
site1-data4-1:
|
||||
site1-data4-2:
|
||||
66
.github/workflows/multipart/docker-compose-site2.yaml
vendored
Normal file
66
.github/workflows/multipart/docker-compose-site2.yaml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:${RELEASE}
|
||||
command: server http://site2-minio{1...4}/data{1...2}
|
||||
environment:
|
||||
- MINIO_PROMETHEUS_AUTH_TYPE=public
|
||||
- CI=true
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
site2-minio1:
|
||||
<<: *minio-common
|
||||
hostname: site2-minio1
|
||||
volumes:
|
||||
- site2-data1-1:/data1
|
||||
- site2-data1-2:/data2
|
||||
|
||||
site2-minio2:
|
||||
<<: *minio-common
|
||||
hostname: site2-minio2
|
||||
volumes:
|
||||
- site2-data2-1:/data1
|
||||
- site2-data2-2:/data2
|
||||
|
||||
site2-minio3:
|
||||
<<: *minio-common
|
||||
hostname: site2-minio3
|
||||
volumes:
|
||||
- site2-data3-1:/data1
|
||||
- site2-data3-2:/data2
|
||||
|
||||
site2-minio4:
|
||||
<<: *minio-common
|
||||
hostname: site2-minio4
|
||||
volumes:
|
||||
- site2-data4-1:/data1
|
||||
- site2-data4-2:/data2
|
||||
|
||||
site2-nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
hostname: site2-nginx
|
||||
volumes:
|
||||
- ./nginx-site2.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9002:9002"
|
||||
depends_on:
|
||||
- site2-minio1
|
||||
- site2-minio2
|
||||
- site2-minio3
|
||||
- site2-minio4
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
site2-data1-1:
|
||||
site2-data1-2:
|
||||
site2-data2-1:
|
||||
site2-data2-2:
|
||||
site2-data3-1:
|
||||
site2-data3-2:
|
||||
site2-data4-1:
|
||||
site2-data4-2:
|
||||
147
.github/workflows/multipart/migrate.sh
vendored
Executable file
147
.github/workflows/multipart/migrate.sh
vendored
Executable file
@@ -0,0 +1,147 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
|
||||
## change working directory
|
||||
cd .github/workflows/multipart/
|
||||
|
||||
function cleanup() {
|
||||
docker-compose -f docker-compose-site1.yaml rm -s -f || true
|
||||
docker-compose -f docker-compose-site2.yaml rm -s -f || true
|
||||
for volume in $(docker volume ls -q | grep minio); do
|
||||
docker volume rm ${volume} || true
|
||||
done
|
||||
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -q -f dangling=true) || true
|
||||
}
|
||||
|
||||
cleanup
|
||||
|
||||
if [ ! -f ./mc ]; then
|
||||
wget --quiet -O mc https://dl.minio.io/client/mc/release/linux-amd64/mc &&
|
||||
chmod +x mc
|
||||
fi
|
||||
|
||||
export RELEASE=RELEASE.2023-08-29T23-07-35Z
|
||||
|
||||
docker-compose -f docker-compose-site1.yaml up -d
|
||||
docker-compose -f docker-compose-site2.yaml up -d
|
||||
|
||||
sleep 30s
|
||||
|
||||
./mc alias set site1 http://site1-nginx:9001 minioadmin minioadmin --api s3v4
|
||||
./mc alias set site2 http://site2-nginx:9002 minioadmin minioadmin --api s3v4
|
||||
|
||||
./mc ready site1/
|
||||
./mc ready site2/
|
||||
|
||||
./mc admin replicate add site1 site2
|
||||
./mc mb site1/testbucket/
|
||||
./mc cp -r --quiet /usr/bin site1/testbucket/
|
||||
|
||||
sleep 5
|
||||
|
||||
./s3-check-md5 -h
|
||||
|
||||
failed_count_site1=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site1-nginx:9001 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
failed_count_site2=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site2-nginx:9002 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
|
||||
if [ $failed_count_site1 -ne 0 ]; then
|
||||
echo "failed with multipart on site1 uploads"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $failed_count_site2 -ne 0 ]; then
|
||||
echo "failed with multipart on site2 uploads"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./mc cp -r --quiet /usr/bin site1/testbucket/
|
||||
|
||||
sleep 5
|
||||
|
||||
failed_count_site1=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site1-nginx:9001 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
failed_count_site2=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site2-nginx:9002 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
|
||||
## we do not need to fail here, since we are going to test
|
||||
## upgrading to master, healing and being able to recover
|
||||
## the last version.
|
||||
if [ $failed_count_site1 -ne 0 ]; then
|
||||
echo "failed with multipart on site1 uploads ${failed_count_site1}"
|
||||
fi
|
||||
|
||||
if [ $failed_count_site2 -ne 0 ]; then
|
||||
echo "failed with multipart on site2 uploads ${failed_count_site2}"
|
||||
fi
|
||||
|
||||
export RELEASE=${1}
|
||||
|
||||
docker-compose -f docker-compose-site1.yaml up -d
|
||||
docker-compose -f docker-compose-site2.yaml up -d
|
||||
|
||||
./mc ready site1/
|
||||
./mc ready site2/
|
||||
|
||||
for i in $(seq 1 10); do
|
||||
# mc admin heal -r --remove when used against a LB endpoint
|
||||
# behaves flaky, let this run 10 times before giving up
|
||||
./mc admin heal -r --remove --json site1/ 2>&1 >/dev/null
|
||||
./mc admin heal -r --remove --json site2/ 2>&1 >/dev/null
|
||||
done
|
||||
|
||||
failed_count_site1=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site1-nginx:9001 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
failed_count_site2=$(./s3-check-md5 -versions -access-key minioadmin -secret-key minioadmin -endpoint http://site2-nginx:9002 -bucket testbucket 2>&1 | grep FAILED | wc -l)
|
||||
|
||||
if [ $failed_count_site1 -ne 0 ]; then
|
||||
echo "failed with multipart on site1 uploads"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $failed_count_site2 -ne 0 ]; then
|
||||
echo "failed with multipart on site2 uploads"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Add user group test
|
||||
./mc admin user add site1 site-replication-issue-user site-replication-issue-password
|
||||
./mc admin group add site1 site-replication-issue-group site-replication-issue-user
|
||||
|
||||
max_wait_attempts=30
|
||||
wait_interval=5
|
||||
|
||||
attempt=1
|
||||
while true; do
|
||||
diff <(./mc admin group info site1 site-replication-issue-group) <(./mc admin group info site2 site-replication-issue-group)
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "Outputs are consistent."
|
||||
break
|
||||
fi
|
||||
|
||||
remaining_attempts=$((max_wait_attempts - attempt))
|
||||
if ((attempt >= max_wait_attempts)); then
|
||||
echo "Outputs remain inconsistent after $max_wait_attempts attempts. Exiting with error."
|
||||
exit 1
|
||||
else
|
||||
echo "Outputs are inconsistent. Waiting for $wait_interval seconds (attempt $attempt/$max_wait_attempts)."
|
||||
sleep $wait_interval
|
||||
fi
|
||||
|
||||
((attempt++))
|
||||
done
|
||||
|
||||
status=$(./mc admin group info site1 site-replication-issue-group --json | jq .groupStatus | tr -d '"')
|
||||
|
||||
if [[ $status == "enabled" ]]; then
|
||||
echo "Success"
|
||||
else
|
||||
echo "Expected status: enabled, actual status: $status"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup
|
||||
|
||||
## change working directory
|
||||
cd ../../../
|
||||
61
.github/workflows/multipart/nginx-site1.conf
vendored
Normal file
61
.github/workflows/multipart/nginx-site1.conf
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server site1-minio1:9000;
|
||||
server site1-minio2:9000;
|
||||
server site1-minio3:9000;
|
||||
server site1-minio4:9000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9001;
|
||||
listen [::]:9001;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
.github/workflows/multipart/nginx-site2.conf
vendored
Normal file
61
.github/workflows/multipart/nginx-site2.conf
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server site2-minio1:9000;
|
||||
server site2-minio2:9000;
|
||||
server site2-minio3:9000;
|
||||
server site2-minio4:9000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9002;
|
||||
listen [::]:9002;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
}
|
||||
72
.github/workflows/replication.yaml
vendored
Normal file
72
.github/workflows/replication.yaml
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
name: MinIO advanced tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
replication-test:
|
||||
name: Advanced Tests with Go ${{ matrix.go-version }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.22.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Test Decom
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-decom
|
||||
|
||||
- name: Test ILM
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-ilm
|
||||
|
||||
- name: Test PBAC
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-pbac
|
||||
|
||||
- name: Test Config File
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-configfile
|
||||
|
||||
- name: Test Replication
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-replication
|
||||
|
||||
- name: Test MinIO IDP for automatic site replication
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-site-replication-minio
|
||||
|
||||
- name: Test Versioning
|
||||
run: |
|
||||
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
|
||||
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
|
||||
make test-versioning
|
||||
34
.github/workflows/root-disable.yml
vendored
Normal file
34
.github/workflows/root-disable.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Root lockdown tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.22.x]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Start root lockdown tests
|
||||
run: |
|
||||
make test-root-disable
|
||||
61
.github/workflows/run-mint.sh
vendored
Executable file
61
.github/workflows/run-mint.sh
vendored
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
export MODE="$1"
|
||||
export ACCESS_KEY="$2"
|
||||
export SECRET_KEY="$3"
|
||||
export JOB_NAME="$4"
|
||||
export MINT_MODE="full"
|
||||
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -f dangling=true) || true
|
||||
|
||||
## change working directory
|
||||
cd .github/workflows/mint
|
||||
|
||||
docker-compose -f minio-${MODE}.yaml up -d
|
||||
sleep 1m
|
||||
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -q -f dangling=true) || true
|
||||
|
||||
# Stop two nodes, one of each pool, to check that all S3 calls work while quorum is still there
|
||||
[ "${MODE}" == "pools" ] && docker-compose -f minio-${MODE}.yaml stop minio2
|
||||
[ "${MODE}" == "pools" ] && docker-compose -f minio-${MODE}.yaml stop minio6
|
||||
|
||||
# Pause one node, to check that all S3 calls work while one node goes wrong
|
||||
[ "${MODE}" == "resiliency" ] && docker-compose -f minio-${MODE}.yaml pause minio4
|
||||
|
||||
docker run --rm --net=mint_default \
|
||||
--name="mint-${MODE}-${JOB_NAME}" \
|
||||
-e SERVER_ENDPOINT="nginx:9000" \
|
||||
-e ACCESS_KEY="${ACCESS_KEY}" \
|
||||
-e SECRET_KEY="${SECRET_KEY}" \
|
||||
-e ENABLE_HTTPS=0 \
|
||||
-e MINT_MODE="${MINT_MODE}" \
|
||||
docker.io/minio/mint:edge
|
||||
|
||||
# FIXME: enable this after fixing aws-sdk-java-v2 tests
|
||||
# # unpause the node, to check that all S3 calls work while one node goes wrong
|
||||
# [ "${MODE}" == "resiliency" ] && docker-compose -f minio-${MODE}.yaml unpause minio4
|
||||
# [ "${MODE}" == "resiliency" ] && docker run --rm --net=mint_default \
|
||||
# --name="mint-${MODE}-${JOB_NAME}" \
|
||||
# -e SERVER_ENDPOINT="nginx:9000" \
|
||||
# -e ACCESS_KEY="${ACCESS_KEY}" \
|
||||
# -e SECRET_KEY="${SECRET_KEY}" \
|
||||
# -e ENABLE_HTTPS=0 \
|
||||
# -e MINT_MODE="${MINT_MODE}" \
|
||||
# docker.io/minio/mint:edge
|
||||
|
||||
docker-compose -f minio-${MODE}.yaml down || true
|
||||
sleep 10s
|
||||
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -q -f dangling=true) || true
|
||||
|
||||
## change working directory
|
||||
cd ../../../
|
||||
22
.github/workflows/shfmt.yml
vendored
Normal file
22
.github/workflows/shfmt.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Shell formatting checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: runner / shfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: luizm/action-sh-checker@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SHFMT_OPTS: "-s"
|
||||
with:
|
||||
sh_checker_shellcheck_disable: true # disable for now
|
||||
15
.github/workflows/typos.yml
vendored
Normal file
15
.github/workflows/typos.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: Spelling
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check spelling of repo
|
||||
uses: crate-ci/typos@master
|
||||
|
||||
34
.github/workflows/upgrade-ci-cd.yaml
vendored
Normal file
34
.github/workflows/upgrade-ci-cd.yaml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Upgrade old version tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# This ensures that previous jobs for the PR are canceled when the PR is
|
||||
# updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.22.x]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
- name: Start upgrade tests
|
||||
run: |
|
||||
make test-upgrade
|
||||
30
.github/workflows/vulncheck.yml
vendored
Normal file
30
.github/workflows/vulncheck.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: VulnCheck
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
vulncheck:
|
||||
name: Analysis
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.5
|
||||
- name: Get official govulncheck
|
||||
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
shell: bash
|
||||
- name: Run govulncheck
|
||||
run: govulncheck -show verbose ./...
|
||||
shell: bash
|
||||
37
.gitignore
vendored
37
.gitignore
vendored
@@ -9,8 +9,7 @@ site/
|
||||
/.idea/
|
||||
/Minio.iml
|
||||
**/access.log
|
||||
vendor/**/*.js
|
||||
vendor/**/*.json
|
||||
vendor/
|
||||
.DS_Store
|
||||
*.syso
|
||||
coverage.txt
|
||||
@@ -21,4 +20,36 @@ prime/
|
||||
stage/
|
||||
.sia_temp/
|
||||
config.json
|
||||
node_modules/
|
||||
node_modules/
|
||||
mc.*
|
||||
s3-check-md5*
|
||||
xl-meta*
|
||||
healing-*
|
||||
inspect*.zip
|
||||
200M*
|
||||
hash-set
|
||||
minio.RELEASE*
|
||||
mc
|
||||
nancy
|
||||
inspects/*
|
||||
.bin/
|
||||
*.gz
|
||||
docs/debugging/s3-verify/s3-verify
|
||||
docs/debugging/xl-meta/xl-meta
|
||||
docs/debugging/s3-check-md5/s3-check-md5
|
||||
docs/debugging/hash-set/hash-set
|
||||
docs/debugging/healing-bin/healing-bin
|
||||
docs/debugging/inspect/inspect
|
||||
docs/debugging/pprofgoparser/pprofgoparser
|
||||
docs/debugging/reorder-disks/reorder-disks
|
||||
docs/debugging/populate-hard-links/populate-hardlinks
|
||||
docs/debugging/xattr/xattr
|
||||
hash-set
|
||||
healing-bin
|
||||
inspect
|
||||
pprofgoparser
|
||||
reorder-disks
|
||||
s3-check-md5
|
||||
s3-verify
|
||||
xattr
|
||||
xl-meta
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
linters-settings:
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gofumpt:
|
||||
simplify: true
|
||||
|
||||
misspell:
|
||||
locale: US
|
||||
|
||||
staticcheck:
|
||||
checks: ['all', '-ST1005', '-ST1000', '-SA4000', '-SA9004', '-SA1019', '-SA1008', '-U1000', '-ST1016']
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- typecheck
|
||||
- durationcheck
|
||||
- gocritic
|
||||
- gofumpt
|
||||
- goimports
|
||||
- misspell
|
||||
- govet
|
||||
- revive
|
||||
- ineffassign
|
||||
- gosimple
|
||||
- deadcode
|
||||
- structcheck
|
||||
- gomodguard
|
||||
- gofmt
|
||||
- unused
|
||||
- structcheck
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- revive
|
||||
- staticcheck
|
||||
- tenv
|
||||
- typecheck
|
||||
- unconvert
|
||||
- varcheck
|
||||
- unused
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
exclude:
|
||||
- should have a package comment
|
||||
- error strings should not be capitalized or end with punctuation or a newline
|
||||
|
||||
service:
|
||||
golangci-lint-version: 1.20.0 # use the fixed version to not introduce new linters unexpectedly
|
||||
- "empty-block:"
|
||||
- "unused-parameter:"
|
||||
- "dot-imports:"
|
||||
- should have a package comment
|
||||
- error strings should not be capitalized or end with punctuation or a newline
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
CVE-2020-26160
|
||||
CVE-2020-15136
|
||||
CVE-2020-15115
|
||||
CVE-2020-15114
|
||||
42
.typos.toml
Normal file
42
.typos.toml
Normal file
@@ -0,0 +1,42 @@
|
||||
[files]
|
||||
extend-exclude = [
|
||||
".git/",
|
||||
"docs/",
|
||||
"CREDITS",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
]
|
||||
ignore-hidden = false
|
||||
|
||||
[default]
|
||||
extend-ignore-re = [
|
||||
"Patrick Collison",
|
||||
"Copyright 2014 Unknwon",
|
||||
"[0-9A-Za-z/+=]{64}",
|
||||
"ZXJuZXQxDjAMBgNVBA-some-junk-Q4wDAYDVQQLEwVNaW5pbzEOMAwGA1UEAxMF",
|
||||
"eyJmb28iOiJiYXIifQ",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.*",
|
||||
"MIIDBTCCAe2gAwIBAgIQWHw7h.*",
|
||||
'http\.Header\{"X-Amz-Server-Side-Encryptio":',
|
||||
"ZoEoZdLlzVbOlT9rbhD7ZN7TLyiYXSAlB79uGEge",
|
||||
"ERRO:",
|
||||
]
|
||||
|
||||
[default.extend-words]
|
||||
"encrypter" = "encrypter"
|
||||
"kms" = "kms"
|
||||
"requestor" = "requestor"
|
||||
|
||||
[default.extend-identifiers]
|
||||
"HashiCorp" = "HashiCorp"
|
||||
|
||||
[type.go.extend-identifiers]
|
||||
"bui" = "bui"
|
||||
"dm2nd" = "dm2nd"
|
||||
"ot" = "ot"
|
||||
"ParseND" = "ParseND"
|
||||
"ParseNDStream" = "ParseNDStream"
|
||||
"pn" = "pn"
|
||||
"TestGetPartialObjectMisAligned" = "TestGetPartialObjectMisAligned"
|
||||
"thr" = "thr"
|
||||
"toi" = "toi"
|
||||
7
COMPLIANCE.md
Normal file
7
COMPLIANCE.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# AGPLv3 Compliance
|
||||
|
||||
We have designed MinIO as an Open Source software for the Open Source software community. This requires applications to consider whether their usage of MinIO is in compliance with the GNU AGPLv3 [license](https://github.com/minio/minio/blob/master/LICENSE).
|
||||
|
||||
MinIO cannot make the determination as to whether your application's usage of MinIO is in compliance with the AGPLv3 license requirements. You should instead rely on your own legal counsel or licensing specialists to audit and ensure your application is in compliance with the licenses of MinIO and all other open-source projects with which your application integrates or interacts. We understand that AGPLv3 licensing is complex and nuanced. It is for that reason we strongly encourage using experts in licensing to make any such determinations around compliance instead of relying on apocryphal or anecdotal advice.
|
||||
|
||||
[MinIO Commercial Licensing](https://min.io/pricing) is the best option for applications that trigger AGPLv3 obligations (e.g. open sourcing your application). Applications using MinIO - or any other OSS-licensed code - without validating their usage do so at their own risk.
|
||||
@@ -7,15 +7,17 @@
|
||||
Start by forking the MinIO GitHub repository, make changes in a branch and then send a pull request. We encourage pull requests to discuss code changes. Here are the steps in details:
|
||||
|
||||
### Setup your MinIO GitHub Repository
|
||||
|
||||
Fork [MinIO upstream](https://github.com/minio/minio/fork) source repository to your own personal repository. Copy the URL of your MinIO fork (you will need it for the `git clone` command below).
|
||||
|
||||
```sh
|
||||
$ git clone https://github.com/minio/minio
|
||||
$ go install -v
|
||||
$ ls /go/bin/minio
|
||||
git clone https://github.com/minio/minio
|
||||
go install -v
|
||||
ls /go/bin/minio
|
||||
```
|
||||
|
||||
### Set up git remote as ``upstream``
|
||||
|
||||
```sh
|
||||
$ cd minio
|
||||
$ git remote add upstream https://github.com/minio/minio
|
||||
@@ -25,13 +27,15 @@ $ git merge upstream/master
|
||||
```
|
||||
|
||||
### Create your feature branch
|
||||
|
||||
Before making code changes, make sure you create a separate branch for these changes
|
||||
|
||||
```
|
||||
$ git checkout -b my-new-feature
|
||||
git checkout -b my-new-feature
|
||||
```
|
||||
|
||||
### Test MinIO server changes
|
||||
|
||||
After your code changes, make sure
|
||||
|
||||
- To add test cases for the new code. If you have questions about how to do it, please ask on our [Slack](https://slack.min.io) channel.
|
||||
@@ -40,29 +44,38 @@ After your code changes, make sure
|
||||
- To run `make test` and `make build` completes.
|
||||
|
||||
### Commit changes
|
||||
|
||||
After verification, commit your changes. This is a [great post](https://chris.beams.io/posts/git-commit/) on how to write useful commit messages
|
||||
|
||||
```
|
||||
$ git commit -am 'Add some feature'
|
||||
git commit -am 'Add some feature'
|
||||
```
|
||||
|
||||
### Push to the branch
|
||||
|
||||
Push your locally committed changes to the remote origin (your fork)
|
||||
|
||||
```
|
||||
$ git push origin my-new-feature
|
||||
git push origin my-new-feature
|
||||
```
|
||||
|
||||
### Create a Pull Request
|
||||
|
||||
Pull requests can be created via GitHub. Refer to [this document](https://help.github.com/articles/creating-a-pull-request/) for detailed steps on how to create a pull request. After a Pull Request gets peer reviewed and approved, it will be merged.
|
||||
|
||||
## FAQs
|
||||
### How does ``MinIO`` manages dependencies?
|
||||
|
||||
### How does ``MinIO`` manage dependencies?
|
||||
|
||||
``MinIO`` uses `go mod` to manage its dependencies.
|
||||
|
||||
- Run `go get foo/bar` in the source folder to add the dependency to `go.mod` file.
|
||||
|
||||
To remove a dependency
|
||||
|
||||
- Edit your code and remove the import reference.
|
||||
- Run `go mod tidy` in the source folder to remove dependency from `go.mod` file.
|
||||
|
||||
### What are the coding guidelines for MinIO?
|
||||
|
||||
``MinIO`` is fully conformant with Golang style. Refer: [Effective Go](https://github.com/golang/go/wiki/CodeReviewComments) article from Golang project. If you observe offending code, please feel free to send a pull request or ping us on [Slack](https://slack.min.io).
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
FROM minio/minio:latest
|
||||
|
||||
COPY ./minio /usr/bin/minio
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
|
||||
VOLUME ["/data"]
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
FROM minio/minio:edge
|
||||
FROM minio/minio:latest
|
||||
|
||||
LABEL maintainer="MinIO Inc <dev@min.io>"
|
||||
ENV PATH=/opt/bin:$PATH
|
||||
|
||||
COPY minio /usr/bin/
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/
|
||||
|
||||
RUN chmod +x /usr/bin/minio && \
|
||||
chmod +x /usr/bin/docker-entrypoint.sh
|
||||
|
||||
EXPOSE 9000
|
||||
COPY ./minio /opt/bin/minio
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
|
||||
|
||||
67
Dockerfile.hotfix
Normal file
67
Dockerfile.hotfix
Normal file
@@ -0,0 +1,67 @@
|
||||
FROM golang:1.21-alpine as build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV CGO_ENABLED 0
|
||||
|
||||
# Install curl and minisign
|
||||
RUN apk add -U --no-cache ca-certificates && \
|
||||
apk add -U --no-cache curl && \
|
||||
go install aead.dev/minisign/cmd/minisign@v0.2.1
|
||||
|
||||
# Download minio binary and signature file
|
||||
RUN curl -s -q https://dl.min.io/server/minio/hotfixes/linux-${TARGETARCH}/archive/minio.${RELEASE} -o /go/bin/minio && \
|
||||
curl -s -q https://dl.min.io/server/minio/hotfixes/linux-${TARGETARCH}/archive/minio.${RELEASE}.minisig -o /go/bin/minio.minisig && \
|
||||
chmod +x /go/bin/minio
|
||||
|
||||
# Download mc binary and signature file
|
||||
RUN curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc -o /go/bin/mc && \
|
||||
curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc.minisig -o /go/bin/mc.minisig && \
|
||||
chmod +x /go/bin/mc
|
||||
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
curl -L -s -q https://github.com/moparisthebest/static-curl/releases/latest/download/curl-${TARGETARCH} -o /go/bin/curl; \
|
||||
chmod +x /go/bin/curl; \
|
||||
fi
|
||||
|
||||
# Verify binary signature using public key "RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGavRUN"
|
||||
RUN minisign -Vqm /go/bin/minio -x /go/bin/minio.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav && \
|
||||
minisign -Vqm /go/bin/mc -x /go/bin/mc.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav
|
||||
|
||||
FROM registry.access.redhat.com/ubi9/ubi-micro:latest
|
||||
|
||||
ARG RELEASE
|
||||
|
||||
LABEL name="MinIO" \
|
||||
vendor="MinIO Inc <dev@min.io>" \
|
||||
maintainer="MinIO Inc <dev@min.io>" \
|
||||
version="${RELEASE}" \
|
||||
release="${RELEASE}" \
|
||||
summary="MinIO is a High Performance Object Storage, API compatible with Amazon S3 cloud storage service." \
|
||||
description="MinIO object storage is fundamentally different. Designed for performance and the S3 API, it is 100% open-source. MinIO is ideal for large, private cloud environments with stringent security requirements and delivers mission-critical availability across a diverse range of workloads."
|
||||
|
||||
ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MINIO_SECRET_KEY_FILE=secret_key \
|
||||
MINIO_ROOT_USER_FILE=access_key \
|
||||
MINIO_ROOT_PASSWORD_FILE=secret_key \
|
||||
MINIO_KMS_SECRET_KEY_FILE=kms_master_key \
|
||||
MINIO_UPDATE_MINISIGN_PUBKEY="RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav" \
|
||||
MINIO_CONFIG_ENV_FILE=config.env \
|
||||
MC_CONFIG_DIR=/tmp/.mc
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /go/bin/minio /usr/bin/minio
|
||||
COPY --from=build /go/bin/mc /usr/bin/mc
|
||||
COPY --from=build /go/bin/cur* /usr/bin/
|
||||
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
COPY LICENSE /licenses/LICENSE
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
EXPOSE 9000
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
CMD ["minio"]
|
||||
@@ -1,6 +1,36 @@
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4
|
||||
FROM golang:1.21-alpine as build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV CGO_ENABLED 0
|
||||
|
||||
# Install curl and minisign
|
||||
RUN apk add -U --no-cache ca-certificates && \
|
||||
apk add -U --no-cache curl && \
|
||||
go install aead.dev/minisign/cmd/minisign@v0.2.1
|
||||
|
||||
# Download minio binary and signature file
|
||||
RUN curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE} -o /go/bin/minio && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.minisig -o /go/bin/minio.minisig && \
|
||||
chmod +x /go/bin/minio
|
||||
|
||||
# Download mc binary and signature file
|
||||
RUN curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc -o /go/bin/mc && \
|
||||
curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc.minisig -o /go/bin/mc.minisig && \
|
||||
chmod +x /go/bin/mc
|
||||
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
curl -L -s -q https://github.com/moparisthebest/static-curl/releases/latest/download/curl-${TARGETARCH} -o /go/bin/curl; \
|
||||
chmod +x /go/bin/curl; \
|
||||
fi
|
||||
|
||||
# Verify binary signature using public key "RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGavRUN"
|
||||
RUN minisign -Vqm /go/bin/minio -x /go/bin/minio.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav && \
|
||||
minisign -Vqm /go/bin/mc -x /go/bin/mc.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav
|
||||
|
||||
FROM registry.access.redhat.com/ubi9/ubi-micro:latest
|
||||
|
||||
ARG RELEASE
|
||||
|
||||
@@ -18,31 +48,20 @@ ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MINIO_ROOT_PASSWORD_FILE=secret_key \
|
||||
MINIO_KMS_SECRET_KEY_FILE=kms_master_key \
|
||||
MINIO_UPDATE_MINISIGN_PUBKEY="RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav" \
|
||||
MINIO_CONFIG_ENV_FILE=config.env
|
||||
MINIO_CONFIG_ENV_FILE=config.env \
|
||||
MC_CONFIG_DIR=/tmp/.mc
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /go/bin/minio /usr/bin/minio
|
||||
COPY --from=build /go/bin/mc /usr/bin/mc
|
||||
COPY --from=build /go/bin/cur* /usr/bin/
|
||||
|
||||
COPY dockerscripts/verify-minio.sh /usr/bin/verify-minio.sh
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
COPY LICENSE /licenses/LICENSE
|
||||
|
||||
RUN \
|
||||
microdnf update --nodocs && \
|
||||
microdnf install curl ca-certificates shadow-utils util-linux iproute iputils --nodocs && \
|
||||
rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm && \
|
||||
microdnf install minisign --nodocs && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE} -o /usr/bin/minio && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.sha256sum -o /usr/bin/minio.sha256sum && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.minisig -o /usr/bin/minio.minisig && \
|
||||
microdnf clean all && \
|
||||
chmod +x /usr/bin/minio && \
|
||||
chmod +x /usr/bin/docker-entrypoint.sh && \
|
||||
chmod +x /usr/bin/verify-minio.sh && \
|
||||
/usr/bin/verify-minio.sh
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
EXPOSE 9000
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
CMD ["minio"]
|
||||
|
||||
@@ -1,6 +1,30 @@
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4
|
||||
FROM golang:1.21-alpine as build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV CGO_ENABLED 0
|
||||
|
||||
# Install curl and minisign
|
||||
RUN apk add -U --no-cache ca-certificates && \
|
||||
apk add -U --no-cache curl && \
|
||||
go install aead.dev/minisign/cmd/minisign@v0.2.1
|
||||
|
||||
# Download minio binary and signature file
|
||||
RUN curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.fips -o /go/bin/minio && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.fips.minisig -o /go/bin/minio.minisig && \
|
||||
chmod +x /go/bin/minio
|
||||
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
curl -L -s -q https://github.com/moparisthebest/static-curl/releases/latest/download/curl-${TARGETARCH} -o /go/bin/curl; \
|
||||
chmod +x /go/bin/curl; \
|
||||
fi
|
||||
|
||||
# Verify binary signature using public key "RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGavRUN"
|
||||
RUN minisign -Vqm /go/bin/minio -x /go/bin/minio.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav
|
||||
|
||||
FROM registry.access.redhat.com/ubi9/ubi-micro:latest
|
||||
|
||||
ARG RELEASE
|
||||
|
||||
@@ -20,29 +44,16 @@ ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MINIO_UPDATE_MINISIGN_PUBKEY="RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav" \
|
||||
MINIO_CONFIG_ENV_FILE=config.env
|
||||
|
||||
COPY dockerscripts/verify-minio.sh /usr/bin/verify-minio.sh
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /go/bin/minio /usr/bin/minio
|
||||
COPY --from=build /go/bin/cur* /usr/bin/
|
||||
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
COPY LICENSE /licenses/LICENSE
|
||||
|
||||
RUN \
|
||||
microdnf update --nodocs && \
|
||||
microdnf install curl ca-certificates shadow-utils util-linux iproute iputils --nodocs && \
|
||||
rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm && \
|
||||
microdnf install minisign --nodocs && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.fips -o /usr/bin/minio && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.fips.sha256sum -o /usr/bin/minio.sha256sum && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.fips.minisig -o /usr/bin/minio.minisig && \
|
||||
microdnf clean all && \
|
||||
chmod +x /usr/bin/minio && \
|
||||
chmod +x /usr/bin/docker-entrypoint.sh && \
|
||||
chmod +x /usr/bin/verify-minio.sh && \
|
||||
/usr/bin/verify-minio.sh
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
EXPOSE 9000
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
CMD ["minio"]
|
||||
|
||||
67
Dockerfile.release.old_cpu
Normal file
67
Dockerfile.release.old_cpu
Normal file
@@ -0,0 +1,67 @@
|
||||
FROM golang:1.21-alpine as build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV CGO_ENABLED 0
|
||||
|
||||
# Install curl and minisign
|
||||
RUN apk add -U --no-cache ca-certificates && \
|
||||
apk add -U --no-cache curl && \
|
||||
go install aead.dev/minisign/cmd/minisign@v0.2.1
|
||||
|
||||
# Download minio binary and signature file
|
||||
RUN curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE} -o /go/bin/minio && \
|
||||
curl -s -q https://dl.min.io/server/minio/release/linux-${TARGETARCH}/archive/minio.${RELEASE}.minisig -o /go/bin/minio.minisig && \
|
||||
chmod +x /go/bin/minio
|
||||
|
||||
# Download mc binary and signature file
|
||||
RUN curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc -o /go/bin/mc && \
|
||||
curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc.minisig -o /go/bin/mc.minisig && \
|
||||
chmod +x /go/bin/mc
|
||||
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
curl -L -s -q https://github.com/moparisthebest/static-curl/releases/latest/download/curl-${TARGETARCH} -o /go/bin/curl; \
|
||||
chmod +x /go/bin/curl; \
|
||||
fi
|
||||
|
||||
# Verify binary signature using public key "RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGavRUN"
|
||||
RUN minisign -Vqm /go/bin/minio -x /go/bin/minio.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav && \
|
||||
minisign -Vqm /go/bin/mc -x /go/bin/mc.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav
|
||||
|
||||
FROM registry.access.redhat.com/ubi8/ubi-micro:latest
|
||||
|
||||
ARG RELEASE
|
||||
|
||||
LABEL name="MinIO" \
|
||||
vendor="MinIO Inc <dev@min.io>" \
|
||||
maintainer="MinIO Inc <dev@min.io>" \
|
||||
version="${RELEASE}" \
|
||||
release="${RELEASE}" \
|
||||
summary="MinIO is a High Performance Object Storage, API compatible with Amazon S3 cloud storage service." \
|
||||
description="MinIO object storage is fundamentally different. Designed for performance and the S3 API, it is 100% open-source. MinIO is ideal for large, private cloud environments with stringent security requirements and delivers mission-critical availability across a diverse range of workloads."
|
||||
|
||||
ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MINIO_SECRET_KEY_FILE=secret_key \
|
||||
MINIO_ROOT_USER_FILE=access_key \
|
||||
MINIO_ROOT_PASSWORD_FILE=secret_key \
|
||||
MINIO_KMS_SECRET_KEY_FILE=kms_master_key \
|
||||
MINIO_UPDATE_MINISIGN_PUBKEY="RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav" \
|
||||
MINIO_CONFIG_ENV_FILE=config.env \
|
||||
MC_CONFIG_DIR=/tmp/.mc
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /go/bin/minio /usr/bin/minio
|
||||
COPY --from=build /go/bin/mc /usr/bin/mc
|
||||
COPY --from=build /go/bin/cur* /usr/bin/
|
||||
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
COPY LICENSE /licenses/LICENSE
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
EXPOSE 9000
|
||||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
|
||||
CMD ["minio"]
|
||||
5
Dockerfile.scratch
Normal file
5
Dockerfile.scratch
Normal file
@@ -0,0 +1,5 @@
|
||||
FROM scratch
|
||||
|
||||
COPY minio /minio
|
||||
|
||||
CMD ["/minio"]
|
||||
204
Makefile
204
Makefile
@@ -6,86 +6,218 @@ GOARCH := $(shell go env GOARCH)
|
||||
GOOS := $(shell go env GOOS)
|
||||
|
||||
VERSION ?= $(shell git describe --tags)
|
||||
TAG ?= "minio/minio:$(VERSION)"
|
||||
REPO ?= quay.io/minio
|
||||
TAG ?= $(REPO)/minio:$(VERSION)
|
||||
|
||||
GOLANGCI_DIR = .bin/golangci/$(GOLANGCI_VERSION)
|
||||
GOLANGCI = $(GOLANGCI_DIR)/golangci-lint
|
||||
|
||||
all: build
|
||||
|
||||
checks:
|
||||
checks: ## check dependencies
|
||||
@echo "Checking dependencies"
|
||||
@(env bash $(PWD)/buildscripts/checkdeps.sh)
|
||||
|
||||
getdeps:
|
||||
@mkdir -p ${GOPATH}/bin
|
||||
@echo "Installing golangci-lint" && curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOPATH)/bin v1.40.1
|
||||
@which msgp 1>/dev/null || (echo "Installing msgp" && go install -v github.com/tinylib/msgp@v1.1.3)
|
||||
@which stringer 1>/dev/null || (echo "Installing stringer" && go install -v golang.org/x/tools/cmd/stringer)
|
||||
help: ## print this help
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-40s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
crosscompile:
|
||||
getdeps: ## fetch necessary dependencies
|
||||
@mkdir -p ${GOPATH}/bin
|
||||
@echo "Installing golangci-lint" && curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOLANGCI_DIR)
|
||||
@echo "Installing msgp" && go install -v github.com/tinylib/msgp@v1.1.10-0.20240227114326-6d6f813fff1b
|
||||
@echo "Installing stringer" && go install -v golang.org/x/tools/cmd/stringer@latest
|
||||
|
||||
crosscompile: ## cross compile minio
|
||||
@(env bash $(PWD)/buildscripts/cross-compile.sh)
|
||||
|
||||
verifiers: getdeps lint check-gen
|
||||
verifiers: lint check-gen
|
||||
|
||||
check-gen:
|
||||
check-gen: ## check for updated autogenerated files
|
||||
@go generate ./... >/dev/null
|
||||
@go mod tidy -compat=1.21
|
||||
@(! git diff --name-only | grep '_gen.go$$') || (echo "Non-committed changes in auto-generated code is detected, please commit them to proceed." && false)
|
||||
@(! git diff --name-only | grep 'go.sum') || (echo "Non-committed changes in auto-generated go.sum is detected, please commit them to proceed." && false)
|
||||
|
||||
lint:
|
||||
lint: getdeps ## runs golangci-lint suite of linters
|
||||
@echo "Running $@ check"
|
||||
@GO111MODULE=on ${GOPATH}/bin/golangci-lint cache clean
|
||||
@GO111MODULE=on ${GOPATH}/bin/golangci-lint run --build-tags kqueue --timeout=10m --config ./.golangci.yml
|
||||
@$(GOLANGCI) run --build-tags kqueue --timeout=10m --config ./.golangci.yml
|
||||
@command typos && typos ./ || echo "typos binary is not found.. skipping.."
|
||||
|
||||
lint-fix: getdeps ## runs golangci-lint suite of linters with automatic fixes
|
||||
@echo "Running $@ check"
|
||||
@$(GOLANGCI) run --build-tags kqueue --timeout=10m --config ./.golangci.yml --fix
|
||||
|
||||
# Builds minio, runs the verifiers then runs the tests.
|
||||
check: test
|
||||
test: verifiers build
|
||||
test: verifiers build ## builds minio, runs linters, tests
|
||||
@echo "Running unit tests"
|
||||
@GOGC=25 GO111MODULE=on CGO_ENABLED=0 go test -tags kqueue ./... 1>/dev/null
|
||||
@MINIO_API_REQUESTS_MAX=10000 CGO_ENABLED=0 go test -v -tags kqueue,dev ./...
|
||||
|
||||
test-race: verifiers build
|
||||
test-root-disable: install-race
|
||||
@echo "Running minio root lockdown tests"
|
||||
@env bash $(PWD)/buildscripts/disable-root.sh
|
||||
|
||||
test-ilm: install-race
|
||||
@echo "Running ILM tests"
|
||||
@env bash $(PWD)/docs/bucket/replication/setup_ilm_expiry_replication.sh
|
||||
|
||||
test-pbac: install-race
|
||||
@echo "Running bucket policies tests"
|
||||
@env bash $(PWD)/docs/iam/policies/pbac-tests.sh
|
||||
|
||||
test-decom: install-race
|
||||
@echo "Running minio decom tests"
|
||||
@env bash $(PWD)/docs/distributed/decom.sh
|
||||
@env bash $(PWD)/docs/distributed/decom-encrypted.sh
|
||||
@env bash $(PWD)/docs/distributed/decom-encrypted-sse-s3.sh
|
||||
@env bash $(PWD)/docs/distributed/decom-compressed-sse-s3.sh
|
||||
@env bash $(PWD)/docs/distributed/decom-encrypted-kes.sh
|
||||
|
||||
test-versioning: install-race
|
||||
@echo "Running minio versioning tests"
|
||||
@env bash $(PWD)/docs/bucket/versioning/versioning-tests.sh
|
||||
|
||||
test-configfile: install-race
|
||||
@env bash $(PWD)/docs/distributed/distributed-from-config-file.sh
|
||||
|
||||
test-upgrade: install-race
|
||||
@echo "Running minio upgrade tests"
|
||||
@(env bash $(PWD)/buildscripts/minio-upgrade.sh)
|
||||
|
||||
test-race: verifiers build ## builds minio, runs linters, tests (race)
|
||||
@echo "Running unit tests under -race"
|
||||
@(env bash $(PWD)/buildscripts/race.sh)
|
||||
|
||||
# Verify minio binary
|
||||
verify:
|
||||
test-iam: install-race ## verify IAM (external IDP, etcd backends)
|
||||
@echo "Running tests for IAM (external IDP, etcd backends)"
|
||||
@MINIO_API_REQUESTS_MAX=10000 CGO_ENABLED=0 go test -timeout 15m -tags kqueue,dev -v -run TestIAM* ./cmd
|
||||
@echo "Running tests for IAM (external IDP, etcd backends) with -race"
|
||||
@MINIO_API_REQUESTS_MAX=10000 GORACE=history_size=7 CGO_ENABLED=1 go test -timeout 15m -race -tags kqueue,dev -v -run TestIAM* ./cmd
|
||||
|
||||
test-iam-ldap-upgrade-import: install-race ## verify IAM (external LDAP IDP)
|
||||
@echo "Running upgrade tests for IAM (LDAP backend)"
|
||||
@env bash $(PWD)/buildscripts/minio-iam-ldap-upgrade-import-test.sh
|
||||
|
||||
test-sio-error:
|
||||
@(env bash $(PWD)/docs/bucket/replication/sio-error.sh)
|
||||
|
||||
test-replication-2site:
|
||||
@(env bash $(PWD)/docs/bucket/replication/setup_2site_existing_replication.sh)
|
||||
|
||||
test-replication-3site:
|
||||
@(env bash $(PWD)/docs/bucket/replication/setup_3site_replication.sh)
|
||||
|
||||
test-delete-replication:
|
||||
@(env bash $(PWD)/docs/bucket/replication/delete-replication.sh)
|
||||
|
||||
test-delete-marker-proxying:
|
||||
@(env bash $(PWD)/docs/bucket/replication/test_del_marker_proxying.sh)
|
||||
|
||||
test-replication: install-race test-replication-2site test-replication-3site test-delete-replication test-sio-error test-delete-marker-proxying ## verify multi site replication
|
||||
@echo "Running tests for replicating three sites"
|
||||
|
||||
test-site-replication-ldap: install-race ## verify automatic site replication
|
||||
@echo "Running tests for automatic site replication of IAM (with LDAP)"
|
||||
@(env bash $(PWD)/docs/site-replication/run-multi-site-ldap.sh)
|
||||
|
||||
test-site-replication-oidc: install-race ## verify automatic site replication
|
||||
@echo "Running tests for automatic site replication of IAM (with OIDC)"
|
||||
@(env bash $(PWD)/docs/site-replication/run-multi-site-oidc.sh)
|
||||
|
||||
test-site-replication-minio: install-race ## verify automatic site replication
|
||||
@echo "Running tests for automatic site replication of IAM (with MinIO IDP)"
|
||||
@(env bash $(PWD)/docs/site-replication/run-multi-site-minio-idp.sh)
|
||||
@echo "Running tests for automatic site replication of SSE-C objects"
|
||||
@(env bash $(PWD)/docs/site-replication/run-ssec-object-replication.sh)
|
||||
@echo "Running tests for automatic site replication of SSE-C objects with SSE-KMS enabled for bucket"
|
||||
@(env bash $(PWD)/docs/site-replication/run-sse-kms-object-replication.sh)
|
||||
@echo "Running tests for automatic site replication of SSE-C objects with compression enabled for site"
|
||||
@(env bash $(PWD)/docs/site-replication/run-ssec-object-replication-with-compression.sh)
|
||||
|
||||
verify: install-race ## verify minio various setups
|
||||
@echo "Verifying build with race"
|
||||
@GO111MODULE=on CGO_ENABLED=1 go build -race -tags kqueue -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/minio 1>/dev/null
|
||||
@(env bash $(PWD)/buildscripts/verify-build.sh)
|
||||
|
||||
# Verify healing of disks with minio binary
|
||||
verify-healing:
|
||||
verify-healing: install-race ## verify healing and replacing disks with minio binary
|
||||
@echo "Verify healing build with race"
|
||||
@GO111MODULE=on CGO_ENABLED=1 go build -race -tags kqueue -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/minio 1>/dev/null
|
||||
@(env bash $(PWD)/buildscripts/verify-healing.sh)
|
||||
@(env bash $(PWD)/buildscripts/verify-healing-empty-erasure-set.sh)
|
||||
@(env bash $(PWD)/buildscripts/heal-inconsistent-versions.sh)
|
||||
|
||||
# Builds minio locally.
|
||||
build: checks
|
||||
verify-healing-with-root-disks: install-race ## verify healing root disks
|
||||
@echo "Verify healing with root drives"
|
||||
@(env bash $(PWD)/buildscripts/verify-healing-with-root-disks.sh)
|
||||
|
||||
verify-healing-with-rewrite: install-race ## verify healing to rewrite old xl.meta -> new xl.meta
|
||||
@echo "Verify healing with rewrite"
|
||||
@(env bash $(PWD)/buildscripts/rewrite-old-new.sh)
|
||||
|
||||
verify-healing-inconsistent-versions: install-race ## verify resolving inconsistent versions
|
||||
@echo "Verify resolving inconsistent versions build with race"
|
||||
@(env bash $(PWD)/buildscripts/resolve-right-versions.sh)
|
||||
|
||||
build-debugging:
|
||||
@(env bash $(PWD)/docs/debugging/build.sh)
|
||||
|
||||
build: checks build-debugging ## builds minio to $(PWD)
|
||||
@echo "Building minio binary to './minio'"
|
||||
@GO111MODULE=on CGO_ENABLED=0 go build -tags kqueue -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/minio 1>/dev/null
|
||||
@CGO_ENABLED=0 go build -tags kqueue -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/minio 1>/dev/null
|
||||
|
||||
hotfix-vars:
|
||||
$(eval LDFLAGS := $(shell MINIO_RELEASE="RELEASE" MINIO_HOTFIX="hotfix.$(shell git rev-parse --short HEAD)" go run buildscripts/gen-ldflags.go $(shell git describe --tags --abbrev=0 | \
|
||||
sed 's#RELEASE\.\([0-9]\+\)-\([0-9]\+\)-\([0-9]\+\)T\([0-9]\+\)-\([0-9]\+\)-\([0-9]\+\)Z#\1-\2-\3T\4:\5:\6Z#')))
|
||||
$(eval TAG := "minio/minio:$(shell git describe --tags --abbrev=0).hotfix.$(shell git rev-parse --short HEAD)")
|
||||
hotfix: hotfix-vars install
|
||||
$(eval VERSION := $(shell git describe --tags --abbrev=0).hotfix.$(shell git rev-parse --short HEAD))
|
||||
|
||||
docker-hotfix: hotfix checks
|
||||
hotfix: hotfix-vars clean install ## builds minio binary with hotfix tags
|
||||
@wget -q -c https://github.com/minio/pkger/releases/download/v2.3.1/pkger_2.3.1_linux_amd64.deb
|
||||
@wget -q -c https://raw.githubusercontent.com/minio/minio-service/v1.0.1/linux-systemd/distributed/minio.service
|
||||
@sudo apt install ./pkger_2.3.1_linux_amd64.deb --yes
|
||||
@mkdir -p minio-release/$(GOOS)-$(GOARCH)/archive
|
||||
@cp -af ./minio minio-release/$(GOOS)-$(GOARCH)/minio
|
||||
@cp -af ./minio minio-release/$(GOOS)-$(GOARCH)/minio.$(VERSION)
|
||||
@minisign -qQSm minio-release/$(GOOS)-$(GOARCH)/minio.$(VERSION) -s "${CRED_DIR}/minisign.key" < "${CRED_DIR}/minisign-passphrase"
|
||||
@sha256sum < minio-release/$(GOOS)-$(GOARCH)/minio.$(VERSION) | sed 's, -,minio.$(VERSION),g' > minio-release/$(GOOS)-$(GOARCH)/minio.$(VERSION).sha256sum
|
||||
@cp -af minio-release/$(GOOS)-$(GOARCH)/minio.$(VERSION)* minio-release/$(GOOS)-$(GOARCH)/archive/
|
||||
@pkger -r $(VERSION) --ignore
|
||||
|
||||
hotfix-push: hotfix
|
||||
@scp -q -r minio-release/$(GOOS)-$(GOARCH)/* minio@dl-0.minio.io:~/releases/server/minio/hotfixes/linux-amd64/
|
||||
@scp -q -r minio-release/$(GOOS)-$(GOARCH)/* minio@dl-0.minio.io:~/releases/server/minio/hotfixes/linux-amd64/archive
|
||||
@scp -q -r minio-release/$(GOOS)-$(GOARCH)/* minio@dl-1.minio.io:~/releases/server/minio/hotfixes/linux-amd64/
|
||||
@scp -q -r minio-release/$(GOOS)-$(GOARCH)/* minio@dl-1.minio.io:~/releases/server/minio/hotfixes/linux-amd64/archive
|
||||
@echo "Published new hotfix binaries at https://dl.min.io/server/minio/hotfixes/linux-amd64/archive/minio.$(VERSION)"
|
||||
|
||||
docker-hotfix-push: docker-hotfix
|
||||
@docker push -q $(TAG) && echo "Published new container $(TAG)"
|
||||
|
||||
docker-hotfix: hotfix-push checks ## builds minio docker container with hotfix tags
|
||||
@echo "Building minio docker image '$(TAG)'"
|
||||
@docker build -t $(TAG) . -f Dockerfile.dev
|
||||
@docker build -q --no-cache -t $(TAG) --build-arg RELEASE=$(VERSION) . -f Dockerfile.hotfix
|
||||
|
||||
docker: build checks
|
||||
docker: build ## builds minio docker container
|
||||
@echo "Building minio docker image '$(TAG)'"
|
||||
@docker build -t $(TAG) . -f Dockerfile.dev
|
||||
@docker build -q --no-cache -t $(TAG) . -f Dockerfile
|
||||
|
||||
# Builds minio and installs it to $GOPATH/bin.
|
||||
install: build
|
||||
install-race: checks build-debugging ## builds minio to $(PWD)
|
||||
@echo "Building minio binary with -race to './minio'"
|
||||
@GORACE=history_size=7 CGO_ENABLED=1 go build -tags kqueue,dev -race -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/minio 1>/dev/null
|
||||
@echo "Installing minio binary with -race to '$(GOPATH)/bin/minio'"
|
||||
@mkdir -p $(GOPATH)/bin && cp -af $(PWD)/minio $(GOPATH)/bin/minio
|
||||
|
||||
install: build ## builds minio and installs it to $GOPATH/bin.
|
||||
@echo "Installing minio binary to '$(GOPATH)/bin/minio'"
|
||||
@mkdir -p $(GOPATH)/bin && cp -f $(PWD)/minio $(GOPATH)/bin/minio
|
||||
@mkdir -p $(GOPATH)/bin && cp -af $(PWD)/minio $(GOPATH)/bin/minio
|
||||
@echo "Installation successful. To learn more, try \"minio --help\"."
|
||||
|
||||
clean:
|
||||
clean: ## cleanup all generated assets
|
||||
@echo "Cleaning up all the generated files"
|
||||
@find . -name '*.test' | xargs rm -fv
|
||||
@find . -name '*~' | xargs rm -fv
|
||||
@find . -name '.#*#' | xargs rm -fv
|
||||
@find . -name '#*#' | xargs rm -fv
|
||||
@rm -rvf minio
|
||||
@rm -rvf build
|
||||
@rm -rvf release
|
||||
@rm -rvf .verify*
|
||||
@rm -rvf minio-release
|
||||
@rm -rvf minio.RELEASE*.hotfix.*
|
||||
@rm -rvf pkger_*.deb
|
||||
|
||||
2
NOTICE
2
NOTICE
@@ -1,4 +1,4 @@
|
||||
MinIO Project, (C) 2015-2021 MinIO, Inc.
|
||||
MinIO Project, (C) 2015-2023 MinIO, Inc.
|
||||
|
||||
This product includes software developed at MinIO, Inc.
|
||||
(https://min.io/).
|
||||
|
||||
7
README.fips.md
Normal file
7
README.fips.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# MinIO FIPS Builds
|
||||
|
||||
MinIO creates FIPS builds using a patched version of the Go compiler (that uses BoringCrypto, from BoringSSL, which is [FIPS 140-2 validated](https://csrc.nist.gov/csrc/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp2964.pdf)) published by the Golang Team [here](https://github.com/golang/go/tree/dev.boringcrypto/misc/boring).
|
||||
|
||||
MinIO FIPS executables are available at <http://dl.min.io> - they are only published for `linux-amd64` architecture as binary files with the suffix `.fips`. We also publish corresponding container images to our official image repositories.
|
||||
|
||||
We are not making any statements or representations about the suitability of this code or build in relation to the FIPS 140-2 standard. Interested users will have to evaluate for themselves whether this is useful for their own purposes.
|
||||
151
README.md
151
README.md
@@ -1,49 +1,48 @@
|
||||
# MinIO Quickstart Guide
|
||||
|
||||
[](https://slack.min.io) [](https://hub.docker.com/r/minio/minio/) [](https://github.com/minio/minio/blob/master/LICENSE)
|
||||
|
||||
[](https://min.io)
|
||||
|
||||
MinIO is a High Performance Object Storage released under GNU Affero General Public License v3.0. It is API compatible with Amazon S3 cloud storage service. Use MinIO to build high performance infrastructure for machine learning, analytics and application data workloads.
|
||||
|
||||
This README provides quickstart instructions on running MinIO on baremetal hardware, including container-based installations. For Kubernetes environments, use the [MinIO Kubernetes Operator](https://github.com/minio/operator/blob/master/README.md).
|
||||
This README provides quickstart instructions on running MinIO on bare metal hardware, including container-based installations. For Kubernetes environments, use the [MinIO Kubernetes Operator](https://github.com/minio/operator/blob/master/README.md).
|
||||
|
||||
# Container Installation
|
||||
## Container Installation
|
||||
|
||||
Use the following commands to run a standalone MinIO server as a container.
|
||||
|
||||
Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication
|
||||
require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically,
|
||||
with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Quickstart Guide](https://docs.min.io/docs/minio-erasure-code-quickstart-guide.html)
|
||||
with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html)
|
||||
for more complete documentation.
|
||||
|
||||
## Stable
|
||||
### Stable
|
||||
|
||||
Run the following command to run the latest stable image of MinIO as a container using an ephemeral data volume:
|
||||
|
||||
```sh
|
||||
podman run \
|
||||
-p 9000:9000 \
|
||||
-p 9001:9001 \
|
||||
minio/minio server /data --console-address ":9001"
|
||||
podman run -p 9000:9000 -p 9001:9001 \
|
||||
quay.io/minio/minio server /data --console-address ":9001"
|
||||
```
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded
|
||||
object browser built into MinIO Server. Point a web browser running on the host machine to http://127.0.0.1:9000 and log in with the
|
||||
object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the
|
||||
root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See
|
||||
[Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers,
|
||||
see https://docs.min.io/docs/ and click **MinIO SDKs** in the navigation to view MinIO SDKs for supported languages.
|
||||
see <https://min.io/docs/minio/linux/developers/minio-drivers.html> to view MinIO SDKs for supported languages.
|
||||
|
||||
> NOTE: To deploy MinIO on with persistent storage, you must map local persistent directories from the host OS to the container using the `podman -v` option. For example, `-v /mnt/data:/data` maps the host OS drive at `/mnt/data` to `/data` on the container.
|
||||
|
||||
# macOS
|
||||
## macOS
|
||||
|
||||
Use the following commands to run a standalone MinIO server on macOS.
|
||||
|
||||
Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Quickstart Guide](https://docs.min.io/docs/minio-erasure-code-quickstart-guide.html) for more complete documentation.
|
||||
Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html) for more complete documentation.
|
||||
|
||||
## Homebrew (recommended)
|
||||
### Homebrew (recommended)
|
||||
|
||||
Run the following command to install the latest stable MinIO package using [Homebrew](https://brew.sh/). Replace ``/data`` with the path to the drive or directory in which you want MinIO to store data.
|
||||
|
||||
@@ -59,11 +58,11 @@ brew uninstall minio
|
||||
brew install minio/stable/minio
|
||||
```
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to http://127.0.0.1:9000 and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see https://docs.min.io/docs/ and click **MinIO SDKs** in the navigation to view MinIO SDKs for supported languages.
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see <https://min.io/docs/minio/linux/developers/minio-drivers.html/> to view MinIO SDKs for supported languages.
|
||||
|
||||
## Binary Download
|
||||
### Binary Download
|
||||
|
||||
Use the following command to download and run a standalone MinIO server on macOS. Replace ``/data`` with the path to the drive or directory in which you want MinIO to store data.
|
||||
|
||||
@@ -73,11 +72,11 @@ chmod +x minio
|
||||
./minio server /data
|
||||
```
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to http://127.0.0.1:9000 and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see https://docs.min.io/docs/ and click **MinIO SDKs** in the navigation to view MinIO SDKs for supported languages.
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see <https://min.io/docs/minio/linux/developers/minio-drivers.html> to view MinIO SDKs for supported languages.
|
||||
|
||||
# GNU/Linux
|
||||
## GNU/Linux
|
||||
|
||||
Use the following command to run a standalone MinIO server on Linux hosts running 64-bit Intel/AMD architectures. Replace ``/data`` with the path to the drive or directory in which you want MinIO to store data.
|
||||
|
||||
@@ -87,24 +86,22 @@ chmod +x minio
|
||||
./minio server /data
|
||||
```
|
||||
|
||||
Replace ``/data`` with the path to the drive or directory in which you want MinIO to store data.
|
||||
|
||||
The following table lists supported architectures. Replace the `wget` URL with the architecture for your Linux host.
|
||||
|
||||
| Architecture | URL |
|
||||
| -------- | ------ |
|
||||
| 64-bit Intel/AMD | https://dl.min.io/server/minio/release/linux-amd64/minio |
|
||||
| 64-bit ARM | https://dl.min.io/server/minio/release/linux-arm64/minio |
|
||||
| 64-bit PowerPC LE (ppc64le) | https://dl.min.io/server/minio/release/linux-ppc64le/minio |
|
||||
| IBM Z-Series (S390X) | https://dl.min.io/server/minio/release/linux-s390x/minio |
|
||||
| 64-bit Intel/AMD | <https://dl.min.io/server/minio/release/linux-amd64/minio> |
|
||||
| 64-bit ARM | <https://dl.min.io/server/minio/release/linux-arm64/minio> |
|
||||
| 64-bit PowerPC LE (ppc64le) | <https://dl.min.io/server/minio/release/linux-ppc64le/minio> |
|
||||
| IBM Z-Series (S390X) | <https://dl.min.io/server/minio/release/linux-s390x/minio> |
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to http://127.0.0.1:9000 and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see https://docs.min.io/docs/ and click **MinIO SDKs** in the navigation to view MinIO SDKs for supported languages.
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see <https://min.io/docs/minio/linux/developers/minio-drivers.html> to view MinIO SDKs for supported languages.
|
||||
|
||||
> NOTE: Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Quickstart Guide](https://docs.min.io/docs/minio-erasure-code-quickstart-guide.html) for more complete documentation.
|
||||
> NOTE: Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html#) for more complete documentation.
|
||||
|
||||
# Microsoft Windows
|
||||
## Microsoft Windows
|
||||
|
||||
To run MinIO on 64-bit Windows hosts, download the MinIO executable from the following URL:
|
||||
|
||||
@@ -118,31 +115,31 @@ Use the following command to run a standalone MinIO server on the Windows host.
|
||||
minio.exe server D:\
|
||||
```
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to http://127.0.0.1:9000 and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see https://docs.min.io/docs/ and click **MinIO SDKs** in the navigation to view MinIO SDKs for supported languages.
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see <https://min.io/docs/minio/linux/developers/minio-drivers.html> to view MinIO SDKs for supported languages.
|
||||
|
||||
> NOTE: Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Quickstart Guide](https://docs.min.io/docs/minio-erasure-code-quickstart-guide.html) for more complete documentation.
|
||||
> NOTE: Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html#) for more complete documentation.
|
||||
|
||||
# Install from Source
|
||||
## Install from Source
|
||||
|
||||
Use the following commands to compile and run a standalone MinIO server from source. Source installation is only intended for developers and advanced users. If you do not have a working Golang environment, please follow [How to install Golang](https://golang.org/doc/install). Minimum version required is [go1.16](https://golang.org/dl/#stable)
|
||||
Use the following commands to compile and run a standalone MinIO server from source. Source installation is only intended for developers and advanced users. If you do not have a working Golang environment, please follow [How to install Golang](https://golang.org/doc/install). Minimum version required is [go1.21](https://golang.org/dl/#stable)
|
||||
|
||||
```sh
|
||||
GO111MODULE=on go install github.com/minio/minio@latest
|
||||
go install github.com/minio/minio@latest
|
||||
```
|
||||
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to http://127.0.0.1:9000 and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
The MinIO deployment starts using default root credentials `minioadmin:minioadmin`. You can test the deployment using the MinIO Console, an embedded web-based object browser built into MinIO Server. Point a web browser running on the host machine to <http://127.0.0.1:9000> and log in with the root credentials. You can use the Browser to create buckets, upload objects, and browse the contents of the MinIO server.
|
||||
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see https://docs.min.io/docs/ and click **MinIO SDKs** in the navigation to view MinIO SDKs for supported languages.
|
||||
You can also connect using any S3-compatible tool, such as the MinIO Client `mc` commandline tool. See [Test using MinIO Client `mc`](#test-using-minio-client-mc) for more information on using the `mc` commandline tool. For application developers, see <https://min.io/docs/minio/linux/developers/minio-drivers.html> to view MinIO SDKs for supported languages.
|
||||
|
||||
> NOTE: Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Quickstart Guide](https://docs.min.io/docs/minio-erasure-code-quickstart-guide.html) for more complete documentation.
|
||||
> NOTE: Standalone MinIO servers are best suited for early development and evaluation. Certain features such as versioning, object locking, and bucket replication require distributed deploying MinIO with Erasure Coding. For extended development and production, deploy MinIO with Erasure Coding enabled - specifically, with a *minimum* of 4 drives per MinIO server. See [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html) for more complete documentation.
|
||||
|
||||
MinIO strongly recommends *against* using compiled-from-source MinIO servers for production environments.
|
||||
|
||||
# Deployment Recommendations
|
||||
## Deployment Recommendations
|
||||
|
||||
## Allow port access for Firewalls
|
||||
### Allow port access for Firewalls
|
||||
|
||||
By default MinIO uses the port 9000 to listen for incoming connections. If your platform blocks the port by default, you may need to enable access to the port.
|
||||
|
||||
@@ -197,67 +194,67 @@ iptables -A INPUT -p tcp --dport 9000:9010 -j ACCEPT
|
||||
service iptables restart
|
||||
```
|
||||
|
||||
## Pre-existing data
|
||||
When deployed on a single drive, MinIO server lets clients access any pre-existing data in the data directory. For example, if MinIO is started with the command `minio server /mnt/data`, any pre-existing data in the `/mnt/data` directory would be accessible to the clients.
|
||||
## Test MinIO Connectivity
|
||||
|
||||
The above statement is also valid for all gateway backends.
|
||||
### Test using MinIO Console
|
||||
|
||||
# Test MinIO Connectivity
|
||||
MinIO Server comes with an embedded web based object browser. Point your web browser to <http://127.0.0.1:9000> to ensure your server has started successfully.
|
||||
|
||||
## Test using MinIO Console
|
||||
MinIO Server comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 to ensure your server has started successfully.
|
||||
|
||||
> NOTE: MinIO runs console on random port by default if you wish choose a specific port use `--console-address` to pick a specific interface and port.
|
||||
> NOTE: MinIO runs console on random port by default, if you wish to choose a specific port use `--console-address` to pick a specific interface and port.
|
||||
|
||||
### Things to consider
|
||||
|
||||
MinIO redirects browser access requests to the configured server port (i.e. `127.0.0.1:9000`) to the configured Console port. MinIO uses the hostname or IP address specified in the request when building the redirect URL. The URL and port *must* be accessible by the client for the redirection to work.
|
||||
|
||||
For deployments behind a load balancer, proxy, or ingress rule where the MinIO host IP address or port is not public, use the `MINIO_BROWSER_REDIRECT_URL` environment variable to specify the external hostname for the redirect. The LB/Proxy must have rules for directing traffic to the Console port specifically.
|
||||
|
||||
For example, consider a MinIO deployment behind a proxy `https://minio.example.net`, `https://console.minio.example.net` with rules for forwarding traffic on port :9000 and :9001 to MinIO and the MinIO Console respectively on the internal network. Set `MINIO_BROWSER_REDIRECT_URL` to `https://console.minio.example.net` to ensure the browser receives a valid reachable URL.
|
||||
|
||||
Similarly, if your TLS certificates do not have the IP SAN for the MinIO server host, the MinIO Console may fail to validate the connection to the server. Use the `MINIO_SERVER_URL` environment variable and specify the proxy-accessible hostname of the MinIO server to allow the Console to use the MinIO server API using the TLS certificate.
|
||||
|
||||
For example: `export MINIO_SERVER_URL="https://minio.example.net"`
|
||||
|
||||
|
||||
| Dashboard | Creating a bucket |
|
||||
| ------------- | ------------- |
|
||||
|  |  |
|
||||
|
||||
## Test using MinIO Client `mc`
|
||||
`mc` provides a modern alternative to UNIX commands like ls, cat, cp, mirror, diff etc. It supports filesystems and Amazon S3 compatible cloud storage services. Follow the MinIO Client [Quickstart Guide](https://docs.min.io/docs/minio-client-quickstart-guide) for further instructions.
|
||||
|
||||
# Upgrading MinIO
|
||||
MinIO server supports rolling upgrades, i.e. you can update one MinIO instance at a time in a distributed cluster. This allows upgrades with no downtime. Upgrades can be done manually by replacing the binary with the latest release and restarting all servers in a rolling fashion. However, we recommend all our users to use [`mc admin update`](https://docs.min.io/docs/minio-admin-complete-guide.html#update) from the client. This will update all the nodes in the cluster simultaneously and restart them, as shown in the following command from the MinIO client (mc):
|
||||
`mc` provides a modern alternative to UNIX commands like ls, cat, cp, mirror, diff etc. It supports filesystems and Amazon S3 compatible cloud storage services. Follow the MinIO Client [Quickstart Guide](https://min.io/docs/minio/linux/reference/minio-mc.html#quickstart) for further instructions.
|
||||
|
||||
```
|
||||
## Upgrading MinIO
|
||||
|
||||
Upgrades require zero downtime in MinIO, all upgrades are non-disruptive, all transactions on MinIO are atomic. So upgrading all the servers simultaneously is the recommended way to upgrade MinIO.
|
||||
|
||||
> NOTE: requires internet access to update directly from <https://dl.min.io>, optionally you can host any mirrors at <https://my-artifactory.example.com/minio/>
|
||||
|
||||
- For deployments that installed the MinIO server binary by hand, use [`mc admin update`](https://min.io/docs/minio/linux/reference/minio-mc-admin/mc-admin-update.html)
|
||||
|
||||
```sh
|
||||
mc admin update <minio alias, e.g., myminio>
|
||||
```
|
||||
|
||||
> NOTE: some releases might not allow rolling upgrades, this is always called out in the release notes and it is generally advised to read release notes before upgrading. In such a situation `mc admin update` is the recommended upgrading mechanism to upgrade all servers at once.
|
||||
- For deployments without external internet access (e.g. airgapped environments), download the binary from <https://dl.min.io> and replace the existing MinIO binary let's say for example `/opt/bin/minio`, apply executable permissions `chmod +x /opt/bin/minio` and proceed to perform `mc admin service restart alias/`.
|
||||
|
||||
## Important things to remember during MinIO upgrades
|
||||
- For installations using Systemd MinIO service, upgrade via RPM/DEB packages **parallelly** on all servers or replace the binary lets say `/opt/bin/minio` on all nodes, apply executable permissions `chmod +x /opt/bin/minio` and process to perform `mc admin service restart alias/`.
|
||||
|
||||
- `mc admin update` will only work if the user running MinIO has write access to the parent directory where the binary is located, for example if the current binary is at `/usr/local/bin/minio`, you would need write access to `/usr/local/bin`.
|
||||
- `mc admin update` updates and restarts all servers simultaneously, applications would retry and continue their respective operations upon upgrade.
|
||||
- `mc admin update` is disabled in kubernetes/container environments, container environments provide their own mechanisms to rollout of updates.
|
||||
- In the case of federated setups `mc admin update` should be run against each cluster individually. Avoid updating `mc` to any new releases until all clusters have been successfully updated.
|
||||
- If using `kes` as KMS with MinIO, just replace the binary and restart `kes` more information about `kes` can be found [here](https://github.com/minio/kes/wiki)
|
||||
- If using Vault as KMS with MinIO, ensure you have followed the Vault upgrade procedure outlined here: https://www.vaultproject.io/docs/upgrading/index.html
|
||||
- If using etcd with MinIO for the federation, ensure you have followed the etcd upgrade procedure outlined here: https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrading-etcd.md
|
||||
### Upgrade Checklist
|
||||
|
||||
# Explore Further
|
||||
- [MinIO Erasure Code QuickStart Guide](https://docs.min.io/docs/minio-erasure-code-quickstart-guide)
|
||||
- [Use `mc` with MinIO Server](https://docs.min.io/docs/minio-client-quickstart-guide)
|
||||
- [Use `aws-cli` with MinIO Server](https://docs.min.io/docs/aws-cli-with-minio)
|
||||
- [Use `s3cmd` with MinIO Server](https://docs.min.io/docs/s3cmd-with-minio)
|
||||
- [Use `minio-go` SDK with MinIO Server](https://docs.min.io/docs/golang-client-quickstart-guide)
|
||||
- [The MinIO documentation website](https://docs.min.io)
|
||||
- Test all upgrades in a lower environment (DEV, QA, UAT) before applying to production. Performing blind upgrades in production environments carries significant risk.
|
||||
- Read the release notes for MinIO *before* performing any upgrade, there is no forced requirement to upgrade to latest release upon every release. Some release may not be relevant to your setup, avoid upgrading production environments unnecessarily.
|
||||
- If you plan to use `mc admin update`, MinIO process must have write access to the parent directory where the binary is present on the host system.
|
||||
- `mc admin update` is not supported and should be avoided in kubernetes/container environments, please upgrade containers by upgrading relevant container images.
|
||||
- **We do not recommend upgrading one MinIO server at a time, the product is designed to support parallel upgrades please follow our recommended guidelines.**
|
||||
|
||||
## Explore Further
|
||||
|
||||
- [MinIO Erasure Code Overview](https://min.io/docs/minio/linux/operations/concepts/erasure-coding.html)
|
||||
- [Use `mc` with MinIO Server](https://min.io/docs/minio/linux/reference/minio-mc.html)
|
||||
- [Use `minio-go` SDK with MinIO Server](https://min.io/docs/minio/linux/developers/go/minio-go.html)
|
||||
- [The MinIO documentation website](https://min.io/docs/minio/linux/index.html)
|
||||
|
||||
## Contribute to MinIO Project
|
||||
|
||||
# Contribute to MinIO Project
|
||||
Please follow MinIO [Contributor's Guide](https://github.com/minio/minio/blob/master/CONTRIBUTING.md)
|
||||
|
||||
# License
|
||||
MinIO source is licensed under the GNU AGPLv3 license that can be found in the [LICENSE](https://github.com/minio/minio/blob/master/LICENSE) file.
|
||||
MinIO [Documentation](https://github.com/minio/minio/tree/master/docs) © 2021 by MinIO, Inc is licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
|
||||
## License
|
||||
|
||||
- MinIO source is licensed under the [GNU AGPLv3](https://github.com/minio/minio/blob/master/LICENSE).
|
||||
- MinIO [documentation](https://github.com/minio/minio/tree/master/docs) is licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
|
||||
- [License Compliance](https://github.com/minio/minio/blob/master/COMPLIANCE.md)
|
||||
|
||||
@@ -18,9 +18,10 @@ you need access credentials for a successful exploit).
|
||||
|
||||
If you have not received a reply to your email within 48 hours or you have not heard from the security team
|
||||
for the past five days please contact the security team directly:
|
||||
- Primary security coordinator: aead@min.io
|
||||
- Secondary coordinator: harsha@min.io
|
||||
- If you receive no response: dev@min.io
|
||||
|
||||
- Primary security coordinator: aead@min.io
|
||||
- Secondary coordinator: harsha@min.io
|
||||
- If you receive no response: dev@min.io
|
||||
|
||||
### Disclosure Process
|
||||
|
||||
@@ -32,7 +33,7 @@ MinIO uses the following disclosure process:
|
||||
If the report is rejected the response explains why.
|
||||
3. Code is audited to find any potential similar problems.
|
||||
4. Fixes are prepared for the latest release.
|
||||
5. On the date that the fixes are applied a security advisory will be published on https://blog.min.io.
|
||||
5. On the date that the fixes are applied a security advisory will be published on <https://blog.min.io>.
|
||||
Please inform us in your report email whether MinIO should mention your contribution w.r.t. fixing
|
||||
the security issue. By default MinIO will **not** publish this information to protect your privacy.
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
## Vulnerability Management Policy
|
||||
# Vulnerability Management Policy
|
||||
|
||||
This document formally describes the process of addressing and managing a
|
||||
reported vulnerability that has been found in the MinIO server code base,
|
||||
any directly connected ecosystem component or a direct / indirect dependency
|
||||
of the code base.
|
||||
|
||||
### Scope
|
||||
## Scope
|
||||
|
||||
The vulnerability management policy described in this document covers the
|
||||
process of investigating, assessing and resolving a vulnerability report
|
||||
@@ -14,13 +14,13 @@ opened by a MinIO employee or an external third party.
|
||||
Therefore, it lists pre-conditions and actions that should be performed to
|
||||
resolve and fix a reported vulnerability.
|
||||
|
||||
### Vulnerability Management Process
|
||||
## Vulnerability Management Process
|
||||
|
||||
The vulnerability management process requires that the vulnerability report
|
||||
contains the following information:
|
||||
|
||||
- The project / component that contains the reported vulnerability.
|
||||
- A description of the vulnerability. In particular, the type of the
|
||||
- The project / component that contains the reported vulnerability.
|
||||
- A description of the vulnerability. In particular, the type of the
|
||||
reported vulnerability and how it might be exploited. Alternatively,
|
||||
a well-established vulnerability identifier, e.g. CVE number, can be
|
||||
used instead.
|
||||
@@ -28,12 +28,11 @@ contains the following information:
|
||||
Based on the description mentioned above, a MinIO engineer or security team
|
||||
member investigates:
|
||||
|
||||
- Whether the reported vulnerability exists.
|
||||
- The conditions that are required such that the vulnerability can be exploited.
|
||||
- The steps required to fix the vulnerability.
|
||||
- Whether the reported vulnerability exists.
|
||||
- The conditions that are required such that the vulnerability can be exploited.
|
||||
- The steps required to fix the vulnerability.
|
||||
|
||||
In general, if the vulnerability exists in one of the MinIO code bases
|
||||
itself - not in a code dependency - then MinIO will, if possible, fix
|
||||
the vulnerability or implement reasonable countermeasures such that the
|
||||
vulnerability cannot be exploited anymore.
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
**/*.swp
|
||||
cover.out
|
||||
*~
|
||||
minio
|
||||
!*/
|
||||
site/
|
||||
**/*.test
|
||||
**/*.sublime-workspace
|
||||
/.idea/
|
||||
/Minio.iml
|
||||
**/access.log
|
||||
build
|
||||
vendor/**/*.js
|
||||
vendor/**/*.json
|
||||
.DS_Store
|
||||
*.syso
|
||||
coverage.txt
|
||||
node_modules
|
||||
169
buildscripts/checkdeps.sh
Normal file → Executable file
169
buildscripts/checkdeps.sh
Normal file → Executable file
@@ -3,19 +3,19 @@
|
||||
|
||||
_init() {
|
||||
|
||||
shopt -s extglob
|
||||
shopt -s extglob
|
||||
|
||||
## Minimum required versions for build dependencies
|
||||
GIT_VERSION="1.0"
|
||||
GO_VERSION="1.16"
|
||||
OSX_VERSION="10.8"
|
||||
KNAME=$(uname -s)
|
||||
ARCH=$(uname -m)
|
||||
case "${KNAME}" in
|
||||
SunOS )
|
||||
ARCH=$(isainfo -k)
|
||||
;;
|
||||
esac
|
||||
## Minimum required versions for build dependencies
|
||||
GIT_VERSION="1.0"
|
||||
GO_VERSION="1.16"
|
||||
OSX_VERSION="10.8"
|
||||
KNAME=$(uname -s)
|
||||
ARCH=$(uname -m)
|
||||
case "${KNAME}" in
|
||||
SunOS)
|
||||
ARCH=$(isainfo -k)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
## FIXME:
|
||||
@@ -28,24 +28,23 @@ _init() {
|
||||
## }
|
||||
##
|
||||
readlink() {
|
||||
TARGET_FILE=$1
|
||||
TARGET_FILE=$1
|
||||
|
||||
cd `dirname $TARGET_FILE`
|
||||
TARGET_FILE=`basename $TARGET_FILE`
|
||||
cd $(dirname $TARGET_FILE)
|
||||
TARGET_FILE=$(basename $TARGET_FILE)
|
||||
|
||||
# Iterate down a (possible) chain of symlinks
|
||||
while [ -L "$TARGET_FILE" ]
|
||||
do
|
||||
TARGET_FILE=$(env readlink $TARGET_FILE)
|
||||
cd `dirname $TARGET_FILE`
|
||||
TARGET_FILE=`basename $TARGET_FILE`
|
||||
done
|
||||
# Iterate down a (possible) chain of symlinks
|
||||
while [ -L "$TARGET_FILE" ]; do
|
||||
TARGET_FILE=$(env readlink $TARGET_FILE)
|
||||
cd $(dirname $TARGET_FILE)
|
||||
TARGET_FILE=$(basename $TARGET_FILE)
|
||||
done
|
||||
|
||||
# Compute the canonicalized name by finding the physical path
|
||||
# for the directory we're in and appending the target file.
|
||||
PHYS_DIR=`pwd -P`
|
||||
RESULT=$PHYS_DIR/$TARGET_FILE
|
||||
echo $RESULT
|
||||
# Compute the canonicalized name by finding the physical path
|
||||
# for the directory we're in and appending the target file.
|
||||
PHYS_DIR=$(pwd -P)
|
||||
RESULT=$PHYS_DIR/$TARGET_FILE
|
||||
echo $RESULT
|
||||
}
|
||||
|
||||
## FIXME:
|
||||
@@ -59,84 +58,86 @@ readlink() {
|
||||
## }
|
||||
##
|
||||
check_minimum_version() {
|
||||
IFS='.' read -r -a varray1 <<< "$1"
|
||||
IFS='.' read -r -a varray2 <<< "$2"
|
||||
IFS='.' read -r -a varray1 <<<"$1"
|
||||
IFS='.' read -r -a varray2 <<<"$2"
|
||||
|
||||
for i in "${!varray1[@]}"; do
|
||||
if [[ ${varray1[i]} -lt ${varray2[i]} ]]; then
|
||||
return 0
|
||||
elif [[ ${varray1[i]} -gt ${varray2[i]} ]]; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
for i in "${!varray1[@]}"; do
|
||||
if [[ ${varray1[i]} -lt ${varray2[i]} ]]; then
|
||||
return 0
|
||||
elif [[ ${varray1[i]} -gt ${varray2[i]} ]]; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
return 0
|
||||
}
|
||||
|
||||
assert_is_supported_arch() {
|
||||
case "${ARCH}" in
|
||||
x86_64 | amd64 | aarch64 | ppc64le | arm* | s390x )
|
||||
return
|
||||
;;
|
||||
*)
|
||||
echo "Arch '${ARCH}' is not supported. Supported Arch: [x86_64, amd64, aarch64, ppc64le, arm*, s390x]"
|
||||
exit 1
|
||||
esac
|
||||
case "${ARCH}" in
|
||||
x86_64 | amd64 | aarch64 | ppc64le | arm* | s390x | loong64 | loongarch64)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
echo "Arch '${ARCH}' is not supported. Supported Arch: [x86_64, amd64, aarch64, ppc64le, arm*, s390x, loong64, loongarch64]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
assert_is_supported_os() {
|
||||
case "${KNAME}" in
|
||||
Linux | FreeBSD | OpenBSD | NetBSD | DragonFly | SunOS )
|
||||
return
|
||||
;;
|
||||
Darwin )
|
||||
osx_host_version=$(env sw_vers -productVersion)
|
||||
if ! check_minimum_version "${OSX_VERSION}" "${osx_host_version}"; then
|
||||
echo "OSX version '${osx_host_version}' is not supported. Minimum supported version: ${OSX_VERSION}"
|
||||
exit 1
|
||||
fi
|
||||
return
|
||||
;;
|
||||
*)
|
||||
echo "OS '${KNAME}' is not supported. Supported OS: [Linux, FreeBSD, OpenBSD, NetBSD, Darwin, DragonFly]"
|
||||
exit 1
|
||||
esac
|
||||
case "${KNAME}" in
|
||||
Linux | FreeBSD | OpenBSD | NetBSD | DragonFly | SunOS)
|
||||
return
|
||||
;;
|
||||
Darwin)
|
||||
osx_host_version=$(env sw_vers -productVersion)
|
||||
if ! check_minimum_version "${OSX_VERSION}" "${osx_host_version}"; then
|
||||
echo "OSX version '${osx_host_version}' is not supported. Minimum supported version: ${OSX_VERSION}"
|
||||
exit 1
|
||||
fi
|
||||
return
|
||||
;;
|
||||
*)
|
||||
echo "OS '${KNAME}' is not supported. Supported OS: [Linux, FreeBSD, OpenBSD, NetBSD, Darwin, DragonFly]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
assert_check_golang_env() {
|
||||
if ! which go >/dev/null 2>&1; then
|
||||
echo "Cannot find go binary in your PATH configuration, please refer to Go installation document at https://golang.org/doc/install"
|
||||
exit 1
|
||||
fi
|
||||
if ! which go >/dev/null 2>&1; then
|
||||
echo "Cannot find go binary in your PATH configuration, please refer to Go installation document at https://golang.org/doc/install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
installed_go_version=$(go version | sed 's/^.* go\([0-9.]*\).*$/\1/')
|
||||
if ! check_minimum_version "${GO_VERSION}" "${installed_go_version}"; then
|
||||
echo "Go runtime version '${installed_go_version}' is unsupported. Minimum supported version: ${GO_VERSION} to compile."
|
||||
exit 1
|
||||
fi
|
||||
installed_go_version=$(go version | sed 's/^.* go\([0-9.]*\).*$/\1/')
|
||||
if ! check_minimum_version "${GO_VERSION}" "${installed_go_version}"; then
|
||||
echo "Go runtime version '${installed_go_version}' is unsupported. Minimum supported version: ${GO_VERSION} to compile."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_check_deps() {
|
||||
# support unusual Git versions such as: 2.7.4 (Apple Git-66)
|
||||
installed_git_version=$(git version | perl -ne '$_ =~ m/git version (.*?)( |$)/; print "$1\n";')
|
||||
if ! check_minimum_version "${GIT_VERSION}" "${installed_git_version}"; then
|
||||
echo "Git version '${installed_git_version}' is not supported. Minimum supported version: ${GIT_VERSION}"
|
||||
exit 1
|
||||
fi
|
||||
# support unusual Git versions such as: 2.7.4 (Apple Git-66)
|
||||
installed_git_version=$(git version | perl -ne '$_ =~ m/git version (.*?)( |$)/; print "$1\n";')
|
||||
if ! check_minimum_version "${GIT_VERSION}" "${installed_git_version}"; then
|
||||
echo "Git version '${installed_git_version}' is not supported. Minimum supported version: ${GIT_VERSION}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
## Check for supported arch
|
||||
assert_is_supported_arch
|
||||
## Check for supported arch
|
||||
assert_is_supported_arch
|
||||
|
||||
## Check for supported os
|
||||
assert_is_supported_os
|
||||
## Check for supported os
|
||||
assert_is_supported_os
|
||||
|
||||
## Check for Go environment
|
||||
assert_check_golang_env
|
||||
## Check for Go environment
|
||||
assert_check_golang_env
|
||||
|
||||
## Check for dependencies
|
||||
assert_check_deps
|
||||
## Check for dependencies
|
||||
assert_check_deps
|
||||
}
|
||||
|
||||
_init && main "$@"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
buildscripts/cicd-corpus/disk2/bucket/testobj/xl.meta
Normal file
BIN
buildscripts/cicd-corpus/disk2/bucket/testobj/xl.meta
Normal file
Binary file not shown.
Binary file not shown.
BIN
buildscripts/cicd-corpus/disk3/bucket/testobj/xl.meta
Normal file
BIN
buildscripts/cicd-corpus/disk3/bucket/testobj/xl.meta
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
buildscripts/cicd-corpus/disk4/bucket/testobj/xl.meta
Normal file
BIN
buildscripts/cicd-corpus/disk4/bucket/testobj/xl.meta
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
buildscripts/cicd-corpus/disk5/bucket/testobj/xl.meta
Normal file
BIN
buildscripts/cicd-corpus/disk5/bucket/testobj/xl.meta
Normal file
Binary file not shown.
@@ -5,33 +5,33 @@ set -e
|
||||
[ -n "$BASH_XTRACEFD" ] && set -x
|
||||
|
||||
function _init() {
|
||||
## All binaries are static make sure to disable CGO.
|
||||
export CGO_ENABLED=0
|
||||
## All binaries are static make sure to disable CGO.
|
||||
export CGO_ENABLED=0
|
||||
|
||||
## List of architectures and OS to test coss compilation.
|
||||
SUPPORTED_OSARCH="linux/ppc64le linux/mips64 linux/arm64 linux/s390x darwin/arm64 darwin/amd64 freebsd/amd64 windows/amd64 linux/arm linux/386 netbsd/amd64 linux/mips openbsd/amd64"
|
||||
## List of architectures and OS to test coss compilation.
|
||||
SUPPORTED_OSARCH="linux/ppc64le linux/mips64 linux/amd64 linux/arm64 linux/s390x darwin/arm64 darwin/amd64 freebsd/amd64 windows/amd64 linux/arm linux/386 netbsd/amd64 linux/mips openbsd/amd64"
|
||||
}
|
||||
|
||||
function _build() {
|
||||
local osarch=$1
|
||||
IFS=/ read -r -a arr <<<"$osarch"
|
||||
os="${arr[0]}"
|
||||
arch="${arr[1]}"
|
||||
package=$(go list -f '{{.ImportPath}}')
|
||||
printf -- "--> %15s:%s\n" "${osarch}" "${package}"
|
||||
local osarch=$1
|
||||
IFS=/ read -r -a arr <<<"$osarch"
|
||||
os="${arr[0]}"
|
||||
arch="${arr[1]}"
|
||||
package=$(go list -f '{{.ImportPath}}')
|
||||
printf -- "--> %15s:%s\n" "${osarch}" "${package}"
|
||||
|
||||
# go build -trimpath to build the binary.
|
||||
export GOOS=$os
|
||||
export GOARCH=$arch
|
||||
export GO111MODULE=on
|
||||
go build -trimpath -tags kqueue -o /dev/null
|
||||
# go build -trimpath to build the binary.
|
||||
export GOOS=$os
|
||||
export GOARCH=$arch
|
||||
export GO111MODULE=on
|
||||
go build -trimpath -tags kqueue -o /dev/null
|
||||
}
|
||||
|
||||
function main() {
|
||||
echo "Testing builds for OS/Arch: ${SUPPORTED_OSARCH}"
|
||||
for each_osarch in ${SUPPORTED_OSARCH}; do
|
||||
_build "${each_osarch}"
|
||||
done
|
||||
echo "Testing builds for OS/Arch: ${SUPPORTED_OSARCH}"
|
||||
for each_osarch in ${SUPPORTED_OSARCH}; do
|
||||
_build "${each_osarch}"
|
||||
done
|
||||
}
|
||||
|
||||
_init && main "$@"
|
||||
|
||||
124
buildscripts/disable-root.sh
Executable file
124
buildscripts/disable-root.sh
Executable file
@@ -0,0 +1,124 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
|
||||
export MINIO_CI_CD=1
|
||||
killall -9 minio
|
||||
|
||||
rm -rf ${HOME}/tmp/dist
|
||||
|
||||
scheme="http"
|
||||
nr_servers=4
|
||||
|
||||
addr="localhost"
|
||||
args=""
|
||||
for ((i = 0; i < $((nr_servers)); i++)); do
|
||||
args="$args $scheme://$addr:$((9100 + i))/${HOME}/tmp/dist/path1/$i"
|
||||
done
|
||||
|
||||
echo $args
|
||||
|
||||
for ((i = 0; i < $((nr_servers)); i++)); do
|
||||
(minio server --address ":$((9100 + i))" $args 2>&1 >/tmp/log$i.txt) &
|
||||
done
|
||||
|
||||
sleep 10s
|
||||
|
||||
if [ ! -f ./mc ]; then
|
||||
wget --quiet -O ./mc https://dl.minio.io/client/mc/release/linux-amd64/./mc &&
|
||||
chmod +x mc
|
||||
fi
|
||||
|
||||
set +e
|
||||
|
||||
export MC_HOST_minioadm=http://minioadmin:minioadmin@localhost:9100/
|
||||
./mc ready minioadm
|
||||
|
||||
./mc ls minioadm/
|
||||
|
||||
./mc admin config set minioadm/ api root_access=off
|
||||
|
||||
sleep 3s # let things settle a little
|
||||
|
||||
./mc ls minioadm/
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "listing succeeded, 'minioadmin' was not disabled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
killall -9 minio
|
||||
|
||||
export MINIO_API_ROOT_ACCESS=on
|
||||
for ((i = 0; i < $((nr_servers)); i++)); do
|
||||
(minio server --address ":$((9100 + i))" $args 2>&1 >/tmp/log$i.txt) &
|
||||
done
|
||||
|
||||
set +e
|
||||
|
||||
./mc ready minioadm/
|
||||
|
||||
./mc ls minioadm/
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "listing failed, 'minioadmin' should be enabled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
killall -9 minio
|
||||
|
||||
rm -rf /tmp/multisitea/
|
||||
rm -rf /tmp/multisiteb/
|
||||
|
||||
echo "Setup site-replication and then disable root credentials"
|
||||
|
||||
minio server --address 127.0.0.1:9001 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9002/tmp/multisitea/data/disterasure/xl{5...8}" >/tmp/sitea_1.log 2>&1 &
|
||||
minio server --address 127.0.0.1:9002 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9002/tmp/multisitea/data/disterasure/xl{5...8}" >/tmp/sitea_2.log 2>&1 &
|
||||
|
||||
minio server --address 127.0.0.1:9003 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9004/tmp/multisiteb/data/disterasure/xl{5...8}" >/tmp/siteb_1.log 2>&1 &
|
||||
minio server --address 127.0.0.1:9004 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9004/tmp/multisiteb/data/disterasure/xl{5...8}" >/tmp/siteb_2.log 2>&1 &
|
||||
|
||||
export MC_HOST_sitea=http://minioadmin:minioadmin@127.0.0.1:9001
|
||||
export MC_HOST_siteb=http://minioadmin:minioadmin@127.0.0.1:9004
|
||||
|
||||
./mc ready sitea
|
||||
./mc ready siteb
|
||||
|
||||
./mc admin replicate add sitea siteb
|
||||
|
||||
./mc admin user add sitea foobar foo12345
|
||||
|
||||
./mc admin policy attach sitea/ consoleAdmin --user=foobar
|
||||
|
||||
./mc admin user info siteb foobar
|
||||
|
||||
killall -9 minio
|
||||
|
||||
echo "turning off root access, however site replication must continue"
|
||||
export MINIO_API_ROOT_ACCESS=off
|
||||
|
||||
minio server --address 127.0.0.1:9001 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9002/tmp/multisitea/data/disterasure/xl{5...8}" >/tmp/sitea_1.log 2>&1 &
|
||||
minio server --address 127.0.0.1:9002 "http://127.0.0.1:9001/tmp/multisitea/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9002/tmp/multisitea/data/disterasure/xl{5...8}" >/tmp/sitea_2.log 2>&1 &
|
||||
|
||||
minio server --address 127.0.0.1:9003 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9004/tmp/multisiteb/data/disterasure/xl{5...8}" >/tmp/siteb_1.log 2>&1 &
|
||||
minio server --address 127.0.0.1:9004 "http://127.0.0.1:9003/tmp/multisiteb/data/disterasure/xl{1...4}" \
|
||||
"http://127.0.0.1:9004/tmp/multisiteb/data/disterasure/xl{5...8}" >/tmp/siteb_2.log 2>&1 &
|
||||
|
||||
export MC_HOST_sitea=http://foobar:foo12345@127.0.0.1:9001
|
||||
export MC_HOST_siteb=http://foobar:foo12345@127.0.0.1:9004
|
||||
|
||||
./mc ready sitea
|
||||
./mc ready siteb
|
||||
|
||||
./mc admin user add sitea foobar-admin foo12345
|
||||
|
||||
sleep 2s
|
||||
|
||||
./mc admin user info siteb foobar-admin
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
@@ -23,14 +24,18 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func genLDFlags(version string) string {
|
||||
releaseTag, date := releaseTag(version)
|
||||
copyrightYear := strconv.Itoa(date.Year())
|
||||
ldflagsStr := "-s -w"
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.Version=" + version
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.ReleaseTag=" + releaseTag(version)
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.CopyrightYear=" + copyrightYear
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.ReleaseTag=" + releaseTag
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.CommitID=" + commitID()
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.ShortCommitID=" + commitID()[:12]
|
||||
ldflagsStr += " -X github.com/minio/minio/cmd.GOPATH=" + os.Getenv("GOPATH")
|
||||
@@ -39,7 +44,7 @@ func genLDFlags(version string) string {
|
||||
}
|
||||
|
||||
// genReleaseTag prints release tag to the console for easy git tagging.
|
||||
func releaseTag(version string) string {
|
||||
func releaseTag(version string) (string, time.Time) {
|
||||
relPrefix := "DEVELOPMENT"
|
||||
if prefix := os.Getenv("MINIO_RELEASE"); prefix != "" {
|
||||
relPrefix = prefix
|
||||
@@ -52,14 +57,17 @@ func releaseTag(version string) string {
|
||||
|
||||
relTag := strings.Replace(version, " ", "-", -1)
|
||||
relTag = strings.Replace(relTag, ":", "-", -1)
|
||||
t, err := time.Parse("2006-01-02T15-04-05Z", relTag)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
relTag = strings.Replace(relTag, ",", "", -1)
|
||||
relTag = relPrefix + "." + relTag
|
||||
|
||||
if relSuffix != "" {
|
||||
relTag += "." + relSuffix
|
||||
}
|
||||
|
||||
return relTag
|
||||
return relTag, t
|
||||
}
|
||||
|
||||
// commitID returns the abbreviated commit-id hash of the last commit.
|
||||
|
||||
92
buildscripts/heal-inconsistent-versions.sh
Executable file
92
buildscripts/heal-inconsistent-versions.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
set -E
|
||||
set -o pipefail
|
||||
set -x
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function start_minio_4drive() {
|
||||
start_port=$1
|
||||
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
export MC_HOST_minio="http://minio:minio123@127.0.0.1:${start_port}/"
|
||||
unset MINIO_KMS_AUTO_ENCRYPTION # do not auto-encrypt objects
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
mkdir ${WORK_DIR}
|
||||
C_PWD=${PWD}
|
||||
if [ ! -x "$PWD/mc" ]; then
|
||||
MC_BUILD_DIR="mc-$RANDOM"
|
||||
if ! git clone --quiet https://github.com/minio/mc "$MC_BUILD_DIR"; then
|
||||
echo "failed to download https://github.com/minio/mc"
|
||||
purge "${MC_BUILD_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "${MC_BUILD_DIR}" && go build -o "$C_PWD/mc")
|
||||
|
||||
# remove mc source.
|
||||
purge "${MC_BUILD_DIR}"
|
||||
fi
|
||||
|
||||
"${MINIO[@]}" --address ":$start_port" "${WORK_DIR}/disk{1...4}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
pid=$!
|
||||
disown $pid
|
||||
sleep 5
|
||||
|
||||
if ! ps -p ${pid} 1>&2 >/dev/null; then
|
||||
echo "server1 log:"
|
||||
cat "${WORK_DIR}/server1.log"
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"${PWD}/mc" mb --with-versioning minio/bucket
|
||||
|
||||
for i in $(seq 1 4); do
|
||||
"${PWD}/mc" cp /etc/hosts minio/bucket/testobj
|
||||
|
||||
sudo chown -R root. "${WORK_DIR}/disk${i}"
|
||||
|
||||
"${PWD}/mc" cp /etc/hosts minio/bucket/testobj
|
||||
|
||||
sudo chown -R ${USER}. "${WORK_DIR}/disk${i}"
|
||||
done
|
||||
|
||||
for vid in $("${PWD}/mc" ls --json --versions minio/bucket/testobj | jq -r .versionId); do
|
||||
"${PWD}/mc" cat --vid "${vid}" minio/bucket/testobj | md5sum
|
||||
done
|
||||
|
||||
pkill minio
|
||||
sleep 3
|
||||
}
|
||||
|
||||
function main() {
|
||||
start_port=$(shuf -i 10000-65000 -n 1)
|
||||
|
||||
start_minio_4drive ${start_port}
|
||||
}
|
||||
|
||||
function purge() {
|
||||
rm -rf "$1"
|
||||
}
|
||||
|
||||
(main "$@")
|
||||
rv=$?
|
||||
purge "$WORK_DIR"
|
||||
exit "$rv"
|
||||
86
buildscripts/heal-manual.go
Normal file
86
buildscripts/heal-manual.go
Normal file
@@ -0,0 +1,86 @@
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
//
|
||||
// MinIO Object Storage (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Note: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY are
|
||||
// dummy values, please replace them with original values.
|
||||
|
||||
// API requests are secure (HTTPS) if secure=true and insecure (HTTP) otherwise.
|
||||
// New returns an MinIO Admin client object.
|
||||
madmClnt, err := madmin.New(os.Args[1], os.Args[2], os.Args[3], false)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
opts := madmin.HealOpts{
|
||||
Recursive: true, // recursively heal all objects at 'prefix'
|
||||
Remove: true, // remove content that has lost quorum and not recoverable
|
||||
ScanMode: madmin.HealNormalScan, // by default do not do 'deep' scanning
|
||||
}
|
||||
|
||||
start, _, err := madmClnt.Heal(context.Background(), "healing-rewrite-bucket", "", opts, "", false, false)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Println("Healstart sequence ===")
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
if err = enc.Encode(&start); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
for {
|
||||
_, status, err := madmClnt.Heal(context.Background(), "healing-rewrite-bucket", "", opts, start.ClientToken, false, false)
|
||||
if status.Summary == "finished" {
|
||||
fmt.Println("Healstatus on items ===")
|
||||
for _, item := range status.Items {
|
||||
if err = enc.Encode(&item); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
if status.Summary == "stopped" {
|
||||
fmt.Println("Healstatus on items ===")
|
||||
fmt.Println("Heal failed with", status.FailureDetail)
|
||||
break
|
||||
}
|
||||
|
||||
for _, item := range status.Items {
|
||||
if err = enc.Encode(&item); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
127
buildscripts/minio-iam-ldap-upgrade-import-test.sh
Executable file
127
buildscripts/minio-iam-ldap-upgrade-import-test.sh
Executable file
@@ -0,0 +1,127 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is used to test the migration of IAM content from old minio
|
||||
# instance to new minio instance.
|
||||
#
|
||||
# To run it locally, start the LDAP server in github.com/minio/minio-iam-testing
|
||||
# repo (e.g. make podman-run), and then run this script.
|
||||
#
|
||||
# This script assumes that LDAP server is at:
|
||||
#
|
||||
# `localhost:1389`
|
||||
#
|
||||
# if this is not the case, set the environment variable
|
||||
# `_MINIO_LDAP_TEST_SERVER`.
|
||||
|
||||
OLD_VERSION=RELEASE.2024-03-26T22-10-45Z
|
||||
OLD_BINARY_LINK=https://dl.min.io/server/minio/release/linux-amd64/archive/minio.${OLD_VERSION}
|
||||
|
||||
__init__() {
|
||||
if which curl &>/dev/null; then
|
||||
echo "curl is already installed"
|
||||
else
|
||||
echo "Installing curl:"
|
||||
sudo apt install curl -y
|
||||
fi
|
||||
|
||||
export GOPATH=/tmp/gopath
|
||||
export PATH="${PATH}":"${GOPATH}"/bin
|
||||
|
||||
if which mc &>/dev/null; then
|
||||
echo "mc is already installed"
|
||||
else
|
||||
echo "Installing mc:"
|
||||
go install github.com/minio/mc@latest
|
||||
fi
|
||||
|
||||
if [ ! -x ./minio.${OLD_VERSION} ]; then
|
||||
echo "Downloading minio.${OLD_VERSION} binary"
|
||||
curl -o minio.${OLD_VERSION} ${OLD_BINARY_LINK}
|
||||
chmod +x minio.${OLD_VERSION}
|
||||
fi
|
||||
|
||||
if [ -z "$_MINIO_LDAP_TEST_SERVER" ]; then
|
||||
export _MINIO_LDAP_TEST_SERVER=localhost:1389
|
||||
echo "Using default LDAP endpoint: $_MINIO_LDAP_TEST_SERVER"
|
||||
fi
|
||||
|
||||
rm -rf /tmp/data
|
||||
}
|
||||
|
||||
create_iam_content_in_old_minio() {
|
||||
echo "Creating IAM content in old minio instance."
|
||||
|
||||
MINIO_CI_CD=1 ./minio.${OLD_VERSION} server /tmp/data/{1...4} &
|
||||
sleep 5
|
||||
|
||||
set -x
|
||||
mc alias set old-minio http://localhost:9000 minioadmin minioadmin
|
||||
mc ready old-minio
|
||||
mc idp ldap add old-minio \
|
||||
server_addr=localhost:1389 \
|
||||
server_insecure=on \
|
||||
lookup_bind_dn=cn=admin,dc=min,dc=io \
|
||||
lookup_bind_password=admin \
|
||||
user_dn_search_base_dn=dc=min,dc=io \
|
||||
user_dn_search_filter="(uid=%s)" \
|
||||
group_search_base_dn=ou=swengg,dc=min,dc=io \
|
||||
group_search_filter="(&(objectclass=groupOfNames)(member=%d))"
|
||||
mc admin service restart old-minio
|
||||
|
||||
mc idp ldap policy attach old-minio readwrite --user=UID=dillon,ou=people,ou=swengg,dc=min,dc=io
|
||||
mc idp ldap policy attach old-minio readwrite --group=CN=project.c,ou=groups,ou=swengg,dc=min,dc=io
|
||||
|
||||
mc idp ldap policy entities old-minio
|
||||
|
||||
mc admin cluster iam export old-minio
|
||||
set +x
|
||||
|
||||
mc admin service stop old-minio
|
||||
}
|
||||
|
||||
import_iam_content_in_new_minio() {
|
||||
echo "Importing IAM content in new minio instance."
|
||||
# Assume current minio binary exists.
|
||||
MINIO_CI_CD=1 ./minio server /tmp/data/{1...4} &
|
||||
sleep 5
|
||||
|
||||
set -x
|
||||
mc alias set new-minio http://localhost:9000 minioadmin minioadmin
|
||||
echo "BEFORE IMPORT mappings:"
|
||||
mc ready new-minio
|
||||
mc idp ldap policy entities new-minio
|
||||
mc admin cluster iam import new-minio ./old-minio-iam-info.zip
|
||||
echo "AFTER IMPORT mappings:"
|
||||
mc idp ldap policy entities new-minio
|
||||
set +x
|
||||
|
||||
# mc admin service stop new-minio
|
||||
}
|
||||
|
||||
verify_iam_content_in_new_minio() {
|
||||
output=$(mc idp ldap policy entities new-minio --json)
|
||||
|
||||
groups=$(echo "$output" | jq -r '.result.policyMappings[] | select(.policy == "readwrite") | .groups[]')
|
||||
if [ "$groups" != "cn=project.c,ou=groups,ou=swengg,dc=min,dc=io" ]; then
|
||||
echo "Failed to verify groups: $groups"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
users=$(echo "$output" | jq -r '.result.policyMappings[] | select(.policy == "readwrite") | .users[]')
|
||||
if [ "$users" != "uid=dillon,ou=people,ou=swengg,dc=min,dc=io" ]; then
|
||||
echo "Failed to verify users: $users"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mc admin service stop new-minio
|
||||
}
|
||||
|
||||
main() {
|
||||
create_iam_content_in_old_minio
|
||||
|
||||
import_iam_content_in_new_minio
|
||||
|
||||
verify_iam_content_in_new_minio
|
||||
}
|
||||
|
||||
(__init__ "$@" && main "$@")
|
||||
111
buildscripts/minio-upgrade.sh
Executable file
111
buildscripts/minio-upgrade.sh
Executable file
@@ -0,0 +1,111 @@
|
||||
#!/bin/bash
|
||||
|
||||
trap 'cleanup $LINENO' ERR
|
||||
|
||||
# shellcheck disable=SC2120
|
||||
cleanup() {
|
||||
MINIO_VERSION=dev /tmp/gopath/bin/docker-compose \
|
||||
-f "buildscripts/upgrade-tests/compose.yml" \
|
||||
down || true
|
||||
|
||||
MINIO_VERSION=dev /tmp/gopath/bin/docker-compose \
|
||||
-f "buildscripts/upgrade-tests/compose.yml" \
|
||||
rm || true
|
||||
|
||||
for volume in $(docker volume ls -q | grep upgrade); do
|
||||
docker volume rm ${volume} || true
|
||||
done
|
||||
|
||||
docker volume prune -f
|
||||
docker system prune -f || true
|
||||
docker volume prune -f || true
|
||||
docker volume rm $(docker volume ls -q -f dangling=true) || true
|
||||
}
|
||||
|
||||
verify_checksum_after_heal() {
|
||||
local sum1
|
||||
sum1=$(curl -s "$2" | sha256sum)
|
||||
mc admin heal --json -r "$1" >/dev/null # test after healing
|
||||
local sum1_heal
|
||||
sum1_heal=$(curl -s "$2" | sha256sum)
|
||||
|
||||
if [ "${sum1_heal}" != "${sum1}" ]; then
|
||||
echo "mismatch expected ${sum1_heal}, got ${sum1}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
verify_checksum_mc() {
|
||||
local expected
|
||||
expected=$(mc cat "$1" | sha256sum)
|
||||
local got
|
||||
got=$(mc cat "$2" | sha256sum)
|
||||
|
||||
if [ "${expected}" != "${got}" ]; then
|
||||
echo "mismatch - expected ${expected}, got ${got}"
|
||||
exit 1
|
||||
fi
|
||||
echo "matches - ${expected}, got ${got}"
|
||||
}
|
||||
|
||||
add_alias() {
|
||||
for i in $(seq 1 4); do
|
||||
echo "... attempting to add alias $i"
|
||||
until (mc alias set minio http://127.0.0.1:9000 minioadmin minioadmin); do
|
||||
echo "...waiting... for 5secs" && sleep 5
|
||||
done
|
||||
done
|
||||
|
||||
echo "Sleeping for nginx"
|
||||
sleep 20
|
||||
}
|
||||
|
||||
__init__() {
|
||||
sudo apt install curl -y
|
||||
export GOPATH=/tmp/gopath
|
||||
export PATH=${PATH}:${GOPATH}/bin
|
||||
|
||||
go install github.com/minio/mc@latest
|
||||
|
||||
## this is needed because github actions don't have
|
||||
## docker-compose on all runners
|
||||
go install github.com/docker/compose/v2/cmd@latest
|
||||
mv -v /tmp/gopath/bin/cmd /tmp/gopath/bin/docker-compose
|
||||
|
||||
cleanup
|
||||
|
||||
TAG=minio/minio:dev make docker
|
||||
|
||||
MINIO_VERSION=RELEASE.2019-12-19T22-52-26Z docker-compose \
|
||||
-f "buildscripts/upgrade-tests/compose.yml" \
|
||||
up -d --build
|
||||
|
||||
add_alias
|
||||
|
||||
mc mb minio/minio-test/
|
||||
mc cp ./minio minio/minio-test/to-read/
|
||||
mc cp /etc/hosts minio/minio-test/to-read/hosts
|
||||
mc anonymous set download minio/minio-test
|
||||
|
||||
verify_checksum_mc ./minio minio/minio-test/to-read/minio
|
||||
|
||||
curl -s http://127.0.0.1:9000/minio-test/to-read/hosts | sha256sum
|
||||
|
||||
MINIO_VERSION=dev /tmp/gopath/bin/docker-compose -f "buildscripts/upgrade-tests/compose.yml" stop
|
||||
}
|
||||
|
||||
main() {
|
||||
MINIO_VERSION=dev /tmp/gopath/bin/docker-compose -f "buildscripts/upgrade-tests/compose.yml" up -d --build
|
||||
|
||||
add_alias
|
||||
|
||||
verify_checksum_after_heal minio/minio-test http://127.0.0.1:9000/minio-test/to-read/hosts
|
||||
|
||||
verify_checksum_mc ./minio minio/minio-test/to-read/minio
|
||||
|
||||
verify_checksum_mc /etc/hosts minio/minio-test/to-read/hosts
|
||||
|
||||
cleanup
|
||||
}
|
||||
|
||||
(__init__ "$@" && main "$@")
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
set -e
|
||||
|
||||
for d in $(go list ./... | grep -v browser); do
|
||||
CGO_ENABLED=1 go test -v -tags kqueue -race --timeout 100m "$d"
|
||||
export GORACE="history_size=7"
|
||||
export MINIO_API_REQUESTS_MAX=10000
|
||||
|
||||
for d in $(go list ./...); do
|
||||
CGO_ENABLED=1 go test -v -race --timeout 100m "$d"
|
||||
done
|
||||
|
||||
72
buildscripts/resolve-right-versions.sh
Executable file
72
buildscripts/resolve-right-versions.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
set -E
|
||||
set -o pipefail
|
||||
set -x
|
||||
set -e
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function start_minio_5drive() {
|
||||
start_port=$1
|
||||
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
export MC_HOST_minio="http://minio:minio123@127.0.0.1:${start_port}/"
|
||||
unset MINIO_KMS_AUTO_ENCRYPTION # do not auto-encrypt objects
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
MC_BUILD_DIR="mc-$RANDOM"
|
||||
if ! git clone --quiet https://github.com/minio/mc "$MC_BUILD_DIR"; then
|
||||
echo "failed to download https://github.com/minio/mc"
|
||||
purge "${MC_BUILD_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "${MC_BUILD_DIR}" && go build -o "$WORK_DIR/mc")
|
||||
|
||||
# remove mc source.
|
||||
purge "${MC_BUILD_DIR}"
|
||||
|
||||
"${WORK_DIR}/mc" cp --quiet -r "buildscripts/cicd-corpus/" "${WORK_DIR}/cicd-corpus/"
|
||||
|
||||
"${MINIO[@]}" --address ":$start_port" "${WORK_DIR}/cicd-corpus/disk{1...5}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
pid=$!
|
||||
disown $pid
|
||||
sleep 5
|
||||
|
||||
if ! ps -p ${pid} 1>&2 >/dev/null; then
|
||||
echo "server1 log:"
|
||||
cat "${WORK_DIR}/server1.log"
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"${WORK_DIR}/mc" stat minio/bucket/testobj
|
||||
|
||||
pkill minio
|
||||
sleep 3
|
||||
}
|
||||
|
||||
function main() {
|
||||
start_port=$(shuf -i 10000-65000 -n 1)
|
||||
|
||||
start_minio_5drive ${start_port}
|
||||
}
|
||||
|
||||
function purge() {
|
||||
rm -rf "$1"
|
||||
}
|
||||
|
||||
(main "$@")
|
||||
rv=$?
|
||||
purge "$WORK_DIR"
|
||||
exit "$rv"
|
||||
157
buildscripts/rewrite-old-new.sh
Executable file
157
buildscripts/rewrite-old-new.sh
Executable file
@@ -0,0 +1,157 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
set -E
|
||||
set -o pipefail
|
||||
set -x
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO_OLD=("$PWD/minio.RELEASE.2020-10-28T08-16-50Z" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function download_old_release() {
|
||||
if [ ! -f minio.RELEASE.2020-10-28T08-16-50Z ]; then
|
||||
curl --silent -O https://dl.minio.io/server/minio/release/linux-amd64/archive/minio.RELEASE.2020-10-28T08-16-50Z
|
||||
chmod a+x minio.RELEASE.2020-10-28T08-16-50Z
|
||||
fi
|
||||
}
|
||||
|
||||
function verify_rewrite() {
|
||||
start_port=$1
|
||||
|
||||
export MINIO_ACCESS_KEY=minio
|
||||
export MINIO_SECRET_KEY=minio123
|
||||
export MC_HOST_minio="http://minio:minio123@127.0.0.1:${start_port}/"
|
||||
unset MINIO_KMS_AUTO_ENCRYPTION # do not auto-encrypt objects
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
MC_BUILD_DIR="mc-$RANDOM"
|
||||
if ! git clone --quiet https://github.com/minio/mc "$MC_BUILD_DIR"; then
|
||||
echo "failed to download https://github.com/minio/mc"
|
||||
purge "${MC_BUILD_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "${MC_BUILD_DIR}" && go build -o "$WORK_DIR/mc")
|
||||
|
||||
# remove mc source.
|
||||
purge "${MC_BUILD_DIR}"
|
||||
|
||||
"${MINIO_OLD[@]}" --address ":$start_port" "${WORK_DIR}/xl{1...16}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
pid=$!
|
||||
disown $pid
|
||||
|
||||
"${WORK_DIR}/mc" ready minio/
|
||||
|
||||
if ! ps -p ${pid} 1>&2 >/dev/null; then
|
||||
echo "server1 log:"
|
||||
cat "${WORK_DIR}/server1.log"
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"${WORK_DIR}/mc" mb minio/healing-rewrite-bucket --quiet --with-lock
|
||||
"${WORK_DIR}/mc" cp \
|
||||
buildscripts/verify-build.sh \
|
||||
minio/healing-rewrite-bucket/ \
|
||||
--disable-multipart --quiet
|
||||
|
||||
"${WORK_DIR}/mc" cp \
|
||||
buildscripts/verify-build.sh \
|
||||
minio/healing-rewrite-bucket/ \
|
||||
--disable-multipart --quiet
|
||||
|
||||
"${WORK_DIR}/mc" cp \
|
||||
buildscripts/verify-build.sh \
|
||||
minio/healing-rewrite-bucket/ \
|
||||
--disable-multipart --quiet
|
||||
|
||||
kill ${pid}
|
||||
sleep 3
|
||||
|
||||
"${MINIO[@]}" --address ":$start_port" "${WORK_DIR}/xl{1...16}" >"${WORK_DIR}/server1.log" 2>&1 &
|
||||
pid=$!
|
||||
disown $pid
|
||||
|
||||
"${WORK_DIR}/mc" ready minio/
|
||||
|
||||
if ! ps -p ${pid} 1>&2 >/dev/null; then
|
||||
echo "server1 log:"
|
||||
cat "${WORK_DIR}/server1.log"
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! ./s3-check-md5 \
|
||||
-debug \
|
||||
-versions \
|
||||
-access-key minio \
|
||||
-secret-key minio123 \
|
||||
-endpoint "http://127.0.0.1:${start_port}/" 2>&1 | grep INTACT; then
|
||||
echo "server1 log:"
|
||||
cat "${WORK_DIR}/server1.log"
|
||||
echo "FAILED"
|
||||
mkdir -p inspects
|
||||
(
|
||||
cd inspects
|
||||
"${WORK_DIR}/mc" admin inspect minio/healing-rewrite-bucket/verify-build.sh/**
|
||||
)
|
||||
|
||||
"${WORK_DIR}/mc" mb play/inspects
|
||||
"${WORK_DIR}/mc" mirror inspects play/inspects
|
||||
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
go run ./buildscripts/heal-manual.go "127.0.0.1:${start_port}" "minio" "minio123"
|
||||
sleep 1
|
||||
|
||||
if ! ./s3-check-md5 \
|
||||
-debug \
|
||||
-versions \
|
||||
-access-key minio \
|
||||
-secret-key minio123 \
|
||||
-endpoint http://127.0.0.1:${start_port}/ 2>&1 | grep INTACT; then
|
||||
echo "server1 log:"
|
||||
cat "${WORK_DIR}/server1.log"
|
||||
echo "FAILED"
|
||||
mkdir -p inspects
|
||||
(
|
||||
cd inspects
|
||||
"${WORK_DIR}/mc" admin inspect minio/healing-rewrite-bucket/verify-build.sh/**
|
||||
)
|
||||
|
||||
"${WORK_DIR}/mc" mb play/inspects
|
||||
"${WORK_DIR}/mc" mirror inspects play/inspects
|
||||
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
kill ${pid}
|
||||
}
|
||||
|
||||
function main() {
|
||||
download_old_release
|
||||
|
||||
start_port=$(shuf -i 10000-65000 -n 1)
|
||||
|
||||
verify_rewrite ${start_port}
|
||||
}
|
||||
|
||||
function purge() {
|
||||
rm -rf "$1"
|
||||
}
|
||||
|
||||
(main "$@")
|
||||
rv=$?
|
||||
purge "$WORK_DIR"
|
||||
exit "$rv"
|
||||
74
buildscripts/upgrade-tests/compose.yml
Normal file
74
buildscripts/upgrade-tests/compose.yml
Normal file
@@ -0,0 +1,74 @@
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: minio/minio:${MINIO_VERSION}
|
||||
command: server http://minio{1...4}/data{1...3}
|
||||
env_file:
|
||||
- ./minio.env
|
||||
expose:
|
||||
- "9000"
|
||||
- "9001"
|
||||
|
||||
# starts 4 docker containers running minio server instances.
|
||||
# using nginx reverse proxy, load balancing, you can access
|
||||
# it through port 9000.
|
||||
services:
|
||||
minio1:
|
||||
<<: *minio-common
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- data1-1:/data1
|
||||
- data1-2:/data2
|
||||
- data1-3:/data3
|
||||
|
||||
minio2:
|
||||
<<: *minio-common
|
||||
hostname: minio2
|
||||
volumes:
|
||||
- data2-1:/data1
|
||||
- data2-2:/data2
|
||||
- data2-3:/data3
|
||||
|
||||
minio3:
|
||||
<<: *minio-common
|
||||
hostname: minio3
|
||||
volumes:
|
||||
- data3-1:/data1
|
||||
- data3-2:/data2
|
||||
- data3-3:/data3
|
||||
|
||||
minio4:
|
||||
<<: *minio-common
|
||||
hostname: minio4
|
||||
volumes:
|
||||
- data4-1:/data1
|
||||
- data4-2:/data2
|
||||
- data4-3:/data3
|
||||
|
||||
nginx:
|
||||
image: nginx:1.19.2-alpine
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
depends_on:
|
||||
- minio1
|
||||
- minio2
|
||||
- minio3
|
||||
- minio4
|
||||
|
||||
## By default this config uses default local driver,
|
||||
## For custom volumes replace with volume driver configuration.
|
||||
volumes:
|
||||
data1-1:
|
||||
data1-2:
|
||||
data1-3:
|
||||
data2-1:
|
||||
data2-2:
|
||||
data2-3:
|
||||
data3-1:
|
||||
data3-2:
|
||||
data3-3:
|
||||
data4-1:
|
||||
data4-2:
|
||||
data4-3:
|
||||
3
buildscripts/upgrade-tests/minio.env
Normal file
3
buildscripts/upgrade-tests/minio.env
Normal file
@@ -0,0 +1,3 @@
|
||||
MINIO_ACCESS_KEY=minioadmin
|
||||
MINIO_SECRET_KEY=minioadmin
|
||||
MINIO_BROWSER=off
|
||||
68
buildscripts/upgrade-tests/nginx.conf
Normal file
68
buildscripts/upgrade-tests/nginx.conf
Normal file
@@ -0,0 +1,68 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
# include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
upstream minio {
|
||||
server minio1:9000;
|
||||
server minio2:9000;
|
||||
server minio3:9000;
|
||||
server minio4:9000;
|
||||
}
|
||||
|
||||
# main minio
|
||||
server {
|
||||
listen 9000;
|
||||
listen [::]:9000;
|
||||
server_name localhost;
|
||||
|
||||
# To allow special characters in headers
|
||||
ignore_invalid_headers off;
|
||||
# Allow any size file to be uploaded.
|
||||
# Set to a value such as 1000m; to restrict file size to a specific value
|
||||
client_max_body_size 0;
|
||||
# To disable buffering
|
||||
proxy_buffering off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
proxy_pass http://minio;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ set -E
|
||||
set -o pipefail
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
exit 1
|
||||
echo "minio executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
@@ -15,292 +15,284 @@ WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
export MINT_MODE=core
|
||||
export MINT_DATA_DIR="$WORK_DIR/data"
|
||||
export SERVER_ENDPOINT="127.0.0.1:9000"
|
||||
export MC_HOST_verify="http://minio:minio123@${SERVER_ENDPOINT}/"
|
||||
export MC_HOST_verify_ipv6="http://minio:minio123@[::1]:9000/"
|
||||
export ACCESS_KEY="minio"
|
||||
export SECRET_KEY="minio123"
|
||||
export ENABLE_HTTPS=0
|
||||
export GO111MODULE=on
|
||||
export GOGC=25
|
||||
export ENABLE_ADMIN=1
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=( "$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" )
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR")
|
||||
|
||||
FILE_1_MB="$MINT_DATA_DIR/datafile-1-MB"
|
||||
FILE_65_MB="$MINT_DATA_DIR/datafile-65-MB"
|
||||
|
||||
FUNCTIONAL_TESTS="$WORK_DIR/functional-tests.sh"
|
||||
|
||||
function start_minio_fs()
|
||||
{
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
"${MINIO[@]}" server "${WORK_DIR}/fs-disk" >"$WORK_DIR/fs-minio.log" 2>&1 &
|
||||
sleep 10
|
||||
function start_minio_fs() {
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
"${MINIO[@]}" server "${WORK_DIR}/fs-disk" >"$WORK_DIR/fs-minio.log" 2>&1 &
|
||||
|
||||
"${WORK_DIR}/mc" ready verify
|
||||
}
|
||||
|
||||
function start_minio_erasure()
|
||||
{
|
||||
"${MINIO[@]}" server "${WORK_DIR}/erasure-disk1" "${WORK_DIR}/erasure-disk2" "${WORK_DIR}/erasure-disk3" "${WORK_DIR}/erasure-disk4" >"$WORK_DIR/erasure-minio.log" 2>&1 &
|
||||
sleep 15
|
||||
function start_minio_erasure() {
|
||||
"${MINIO[@]}" server "${WORK_DIR}/erasure-disk1" "${WORK_DIR}/erasure-disk2" "${WORK_DIR}/erasure-disk3" "${WORK_DIR}/erasure-disk4" >"$WORK_DIR/erasure-minio.log" 2>&1 &
|
||||
|
||||
"${WORK_DIR}/mc" ready verify
|
||||
}
|
||||
|
||||
function start_minio_erasure_sets()
|
||||
{
|
||||
export MINIO_ENDPOINTS="${WORK_DIR}/erasure-disk-sets{1...32}"
|
||||
"${MINIO[@]}" server > "$WORK_DIR/erasure-minio-sets.log" 2>&1 &
|
||||
sleep 15
|
||||
function start_minio_erasure_sets() {
|
||||
export MINIO_ENDPOINTS="${WORK_DIR}/erasure-disk-sets{1...32}"
|
||||
"${MINIO[@]}" server >"$WORK_DIR/erasure-minio-sets.log" 2>&1 &
|
||||
|
||||
"${WORK_DIR}/mc" ready verify
|
||||
}
|
||||
|
||||
function start_minio_pool_erasure_sets()
|
||||
{
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
export MINIO_ENDPOINTS="http://127.0.0.1:9000${WORK_DIR}/pool-disk-sets{1...4} http://127.0.0.1:9001${WORK_DIR}/pool-disk-sets{5...8}"
|
||||
"${MINIO[@]}" server --address ":9000" > "$WORK_DIR/pool-minio-9000.log" 2>&1 &
|
||||
"${MINIO[@]}" server --address ":9001" > "$WORK_DIR/pool-minio-9001.log" 2>&1 &
|
||||
function start_minio_pool_erasure_sets() {
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
export MINIO_ENDPOINTS="http://127.0.0.1:9000${WORK_DIR}/pool-disk-sets{1...4} http://127.0.0.1:9001${WORK_DIR}/pool-disk-sets{5...8}"
|
||||
"${MINIO[@]}" server --address ":9000" >"$WORK_DIR/pool-minio-9000.log" 2>&1 &
|
||||
"${MINIO[@]}" server --address ":9001" >"$WORK_DIR/pool-minio-9001.log" 2>&1 &
|
||||
|
||||
sleep 40
|
||||
"${WORK_DIR}/mc" ready verify
|
||||
}
|
||||
|
||||
function start_minio_pool_erasure_sets_ipv6()
|
||||
{
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
export MINIO_ENDPOINTS="http://[::1]:9000${WORK_DIR}/pool-disk-sets{1...4} http://[::1]:9001${WORK_DIR}/pool-disk-sets{5...8}"
|
||||
"${MINIO[@]}" server --address="[::1]:9000" > "$WORK_DIR/pool-minio-ipv6-9000.log" 2>&1 &
|
||||
"${MINIO[@]}" server --address="[::1]:9001" > "$WORK_DIR/pool-minio-ipv6-9001.log" 2>&1 &
|
||||
function start_minio_pool_erasure_sets_ipv6() {
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
export MINIO_ENDPOINTS="http://[::1]:9000${WORK_DIR}/pool-disk-sets-ipv6{1...4} http://[::1]:9001${WORK_DIR}/pool-disk-sets-ipv6{5...8}"
|
||||
"${MINIO[@]}" server --address="[::1]:9000" >"$WORK_DIR/pool-minio-ipv6-9000.log" 2>&1 &
|
||||
"${MINIO[@]}" server --address="[::1]:9001" >"$WORK_DIR/pool-minio-ipv6-9001.log" 2>&1 &
|
||||
|
||||
sleep 40
|
||||
"${WORK_DIR}/mc" ready verify_ipv6
|
||||
}
|
||||
|
||||
function start_minio_dist_erasure()
|
||||
{
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
export MINIO_ENDPOINTS="http://127.0.0.1:9000${WORK_DIR}/dist-disk1 http://127.0.0.1:9001${WORK_DIR}/dist-disk2 http://127.0.0.1:9002${WORK_DIR}/dist-disk3 http://127.0.0.1:9003${WORK_DIR}/dist-disk4"
|
||||
for i in $(seq 0 3); do
|
||||
"${MINIO[@]}" server --address ":900${i}" > "$WORK_DIR/dist-minio-900${i}.log" 2>&1 &
|
||||
done
|
||||
function start_minio_dist_erasure() {
|
||||
export MINIO_ROOT_USER=$ACCESS_KEY
|
||||
export MINIO_ROOT_PASSWORD=$SECRET_KEY
|
||||
export MINIO_ENDPOINTS="http://127.0.0.1:9000${WORK_DIR}/dist-disk1 http://127.0.0.1:9001${WORK_DIR}/dist-disk2 http://127.0.0.1:9002${WORK_DIR}/dist-disk3 http://127.0.0.1:9003${WORK_DIR}/dist-disk4"
|
||||
for i in $(seq 0 3); do
|
||||
"${MINIO[@]}" server --address ":900${i}" >"$WORK_DIR/dist-minio-900${i}.log" 2>&1 &
|
||||
done
|
||||
|
||||
sleep 40
|
||||
"${WORK_DIR}/mc" ready verify
|
||||
}
|
||||
|
||||
function run_test_fs()
|
||||
{
|
||||
start_minio_fs
|
||||
function run_test_fs() {
|
||||
start_minio_fs
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
sleep 3
|
||||
pkill minio
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
cat "$WORK_DIR/fs-minio.log"
|
||||
fi
|
||||
rm -f "$WORK_DIR/fs-minio.log"
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
cat "$WORK_DIR/fs-minio.log"
|
||||
fi
|
||||
rm -f "$WORK_DIR/fs-minio.log"
|
||||
|
||||
return "$rv"
|
||||
return "$rv"
|
||||
}
|
||||
|
||||
function run_test_erasure_sets()
|
||||
{
|
||||
start_minio_erasure_sets
|
||||
function run_test_erasure_sets() {
|
||||
start_minio_erasure_sets
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
sleep 3
|
||||
pkill minio
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
cat "$WORK_DIR/erasure-minio-sets.log"
|
||||
fi
|
||||
rm -f "$WORK_DIR/erasure-minio-sets.log"
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
cat "$WORK_DIR/erasure-minio-sets.log"
|
||||
fi
|
||||
rm -f "$WORK_DIR/erasure-minio-sets.log"
|
||||
|
||||
return "$rv"
|
||||
return "$rv"
|
||||
}
|
||||
|
||||
function run_test_pool_erasure_sets()
|
||||
{
|
||||
start_minio_pool_erasure_sets
|
||||
function run_test_pool_erasure_sets() {
|
||||
start_minio_pool_erasure_sets
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
sleep 3
|
||||
pkill minio
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
for i in $(seq 0 1); do
|
||||
echo "server$i log:"
|
||||
cat "$WORK_DIR/pool-minio-900$i.log"
|
||||
done
|
||||
fi
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
for i in $(seq 0 1); do
|
||||
echo "server$i log:"
|
||||
cat "$WORK_DIR/pool-minio-900$i.log"
|
||||
done
|
||||
fi
|
||||
|
||||
for i in $(seq 0 1); do
|
||||
rm -f "$WORK_DIR/pool-minio-900$i.log"
|
||||
done
|
||||
for i in $(seq 0 1); do
|
||||
rm -f "$WORK_DIR/pool-minio-900$i.log"
|
||||
done
|
||||
|
||||
return "$rv"
|
||||
return "$rv"
|
||||
}
|
||||
|
||||
function run_test_pool_erasure_sets_ipv6()
|
||||
{
|
||||
start_minio_pool_erasure_sets_ipv6
|
||||
function run_test_pool_erasure_sets_ipv6() {
|
||||
start_minio_pool_erasure_sets_ipv6
|
||||
|
||||
export SERVER_ENDPOINT="[::1]:9000"
|
||||
export SERVER_ENDPOINT="[::1]:9000"
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
sleep 3
|
||||
pkill minio
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
for i in $(seq 0 1); do
|
||||
echo "server$i log:"
|
||||
cat "$WORK_DIR/pool-minio-ipv6-900$i.log"
|
||||
done
|
||||
fi
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
for i in $(seq 0 1); do
|
||||
echo "server$i log:"
|
||||
cat "$WORK_DIR/pool-minio-ipv6-900$i.log"
|
||||
done
|
||||
fi
|
||||
|
||||
for i in $(seq 0 1); do
|
||||
rm -f "$WORK_DIR/pool-minio-ipv6-900$i.log"
|
||||
done
|
||||
for i in $(seq 0 1); do
|
||||
rm -f "$WORK_DIR/pool-minio-ipv6-900$i.log"
|
||||
done
|
||||
|
||||
return "$rv"
|
||||
return "$rv"
|
||||
}
|
||||
|
||||
function run_test_erasure()
|
||||
{
|
||||
start_minio_erasure
|
||||
function run_test_erasure() {
|
||||
start_minio_erasure
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
sleep 3
|
||||
pkill minio
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
cat "$WORK_DIR/erasure-minio.log"
|
||||
fi
|
||||
rm -f "$WORK_DIR/erasure-minio.log"
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
cat "$WORK_DIR/erasure-minio.log"
|
||||
fi
|
||||
rm -f "$WORK_DIR/erasure-minio.log"
|
||||
|
||||
return "$rv"
|
||||
return "$rv"
|
||||
}
|
||||
|
||||
function run_test_dist_erasure()
|
||||
{
|
||||
start_minio_dist_erasure
|
||||
function run_test_dist_erasure() {
|
||||
start_minio_dist_erasure
|
||||
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
(cd "$WORK_DIR" && "$FUNCTIONAL_TESTS")
|
||||
rv=$?
|
||||
|
||||
pkill minio
|
||||
sleep 3
|
||||
pkill minio
|
||||
sleep 3
|
||||
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
echo "server1 log:"
|
||||
cat "$WORK_DIR/dist-minio-9000.log"
|
||||
echo "server2 log:"
|
||||
cat "$WORK_DIR/dist-minio-9001.log"
|
||||
echo "server3 log:"
|
||||
cat "$WORK_DIR/dist-minio-9002.log"
|
||||
echo "server4 log:"
|
||||
cat "$WORK_DIR/dist-minio-9003.log"
|
||||
fi
|
||||
if [ "$rv" -ne 0 ]; then
|
||||
echo "server1 log:"
|
||||
cat "$WORK_DIR/dist-minio-9000.log"
|
||||
echo "server2 log:"
|
||||
cat "$WORK_DIR/dist-minio-9001.log"
|
||||
echo "server3 log:"
|
||||
cat "$WORK_DIR/dist-minio-9002.log"
|
||||
echo "server4 log:"
|
||||
cat "$WORK_DIR/dist-minio-9003.log"
|
||||
fi
|
||||
|
||||
rm -f "$WORK_DIR/dist-minio-9000.log" "$WORK_DIR/dist-minio-9001.log" "$WORK_DIR/dist-minio-9002.log" "$WORK_DIR/dist-minio-9003.log"
|
||||
rm -f "$WORK_DIR/dist-minio-9000.log" "$WORK_DIR/dist-minio-9001.log" "$WORK_DIR/dist-minio-9002.log" "$WORK_DIR/dist-minio-9003.log"
|
||||
|
||||
return "$rv"
|
||||
return "$rv"
|
||||
}
|
||||
|
||||
function purge()
|
||||
{
|
||||
rm -rf "$1"
|
||||
function purge() {
|
||||
rm -rf "$1"
|
||||
}
|
||||
|
||||
function __init__()
|
||||
{
|
||||
echo "Initializing environment"
|
||||
mkdir -p "$WORK_DIR"
|
||||
mkdir -p "$MINIO_CONFIG_DIR"
|
||||
mkdir -p "$MINT_DATA_DIR"
|
||||
function __init__() {
|
||||
echo "Initializing environment"
|
||||
mkdir -p "$WORK_DIR"
|
||||
mkdir -p "$MINIO_CONFIG_DIR"
|
||||
mkdir -p "$MINT_DATA_DIR"
|
||||
|
||||
MC_BUILD_DIR="mc-$RANDOM"
|
||||
if ! git clone --quiet https://github.com/minio/mc "$MC_BUILD_DIR"; then
|
||||
echo "failed to download https://github.com/minio/mc"
|
||||
purge "${MC_BUILD_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
MC_BUILD_DIR="mc-$RANDOM"
|
||||
if ! git clone --quiet https://github.com/minio/mc "$MC_BUILD_DIR"; then
|
||||
echo "failed to download https://github.com/minio/mc"
|
||||
purge "${MC_BUILD_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "${MC_BUILD_DIR}" && go build -o "$WORK_DIR/mc")
|
||||
(cd "${MC_BUILD_DIR}" && go build -o "${WORK_DIR}/mc")
|
||||
|
||||
# remove mc source.
|
||||
purge "${MC_BUILD_DIR}"
|
||||
# remove mc source.
|
||||
purge "${MC_BUILD_DIR}"
|
||||
|
||||
shred -n 1 -s 1M - 1>"$FILE_1_MB" 2>/dev/null
|
||||
shred -n 1 -s 65M - 1>"$FILE_65_MB" 2>/dev/null
|
||||
shred -n 1 -s 1M - 1>"$FILE_1_MB" 2>/dev/null
|
||||
shred -n 1 -s 65M - 1>"$FILE_65_MB" 2>/dev/null
|
||||
|
||||
## version is purposefully set to '3' for minio to migrate configuration file
|
||||
echo '{"version": "3", "credential": {"accessKey": "minio", "secretKey": "minio123"}, "region": "us-east-1"}' > "$MINIO_CONFIG_DIR/config.json"
|
||||
## version is purposefully set to '3' for minio to migrate configuration file
|
||||
echo '{"version": "3", "credential": {"accessKey": "minio", "secretKey": "minio123"}, "region": "us-east-1"}' >"$MINIO_CONFIG_DIR/config.json"
|
||||
|
||||
if ! wget -q -O "$FUNCTIONAL_TESTS" https://raw.githubusercontent.com/minio/mc/master/functional-tests.sh; then
|
||||
echo "failed to download https://raw.githubusercontent.com/minio/mc/master/functional-tests.sh"
|
||||
exit 1
|
||||
fi
|
||||
if ! wget -q -O "$FUNCTIONAL_TESTS" https://raw.githubusercontent.com/minio/mc/master/functional-tests.sh; then
|
||||
echo "failed to download https://raw.githubusercontent.com/minio/mc/master/functional-tests.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sed -i 's|-sS|-sSg|g' "$FUNCTIONAL_TESTS"
|
||||
chmod a+x "$FUNCTIONAL_TESTS"
|
||||
sed -i 's|-sS|-sSg|g' "$FUNCTIONAL_TESTS"
|
||||
chmod a+x "$FUNCTIONAL_TESTS"
|
||||
}
|
||||
|
||||
function main()
|
||||
{
|
||||
echo "Testing in FS setup"
|
||||
if ! run_test_fs; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
function main() {
|
||||
echo "Testing in FS setup"
|
||||
if ! run_test_fs; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Testing in Erasure setup"
|
||||
if ! run_test_erasure; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
echo "Testing in Erasure setup"
|
||||
if ! run_test_erasure; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Testing in Distributed Erasure setup"
|
||||
if ! run_test_dist_erasure; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
echo "Testing in Distributed Erasure setup"
|
||||
if ! run_test_dist_erasure; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Testing in Erasure setup as sets"
|
||||
if ! run_test_erasure_sets; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
echo "Testing in Erasure setup as sets"
|
||||
if ! run_test_erasure_sets; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Testing in Distributed Eraure expanded setup"
|
||||
if ! run_test_pool_erasure_sets; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
echo "Testing in Distributed Eraure expanded setup"
|
||||
if ! run_test_pool_erasure_sets; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Testing in Distributed Erasure expanded setup with ipv6"
|
||||
if ! run_test_pool_erasure_sets_ipv6; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
echo "Testing in Distributed Erasure expanded setup with ipv6"
|
||||
if ! run_test_pool_erasure_sets_ipv6; then
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
purge "$WORK_DIR"
|
||||
purge "$WORK_DIR"
|
||||
}
|
||||
|
||||
( __init__ "$@" && main "$@" )
|
||||
(__init__ "$@" && main "$@")
|
||||
rv=$?
|
||||
purge "$WORK_DIR"
|
||||
exit "$rv"
|
||||
|
||||
151
buildscripts/verify-healing-empty-erasure-set.sh
Executable file
151
buildscripts/verify-healing-empty-erasure-set.sh
Executable file
@@ -0,0 +1,151 @@
|
||||
#!/bin/bash -e
|
||||
#
|
||||
|
||||
set -E
|
||||
set -o pipefail
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
|
||||
function start_minio_3_node() {
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
export MINIO_ERASURE_SET_DRIVE_COUNT=6
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
start_port=$1
|
||||
args=""
|
||||
for i in $(seq 1 3); do
|
||||
args="$args http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/1/ http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/2/ http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/3/ http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/4/ http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/5/ http://127.0.0.1:$((start_port + i))${WORK_DIR}/$i/6/"
|
||||
done
|
||||
|
||||
"${MINIO[@]}" --address ":$((start_port + 1))" $args >"${WORK_DIR}/dist-minio-server1.log" 2>&1 &
|
||||
pid1=$!
|
||||
disown ${pid1}
|
||||
|
||||
"${MINIO[@]}" --address ":$((start_port + 2))" $args >"${WORK_DIR}/dist-minio-server2.log" 2>&1 &
|
||||
pid2=$!
|
||||
disown $pid2
|
||||
|
||||
"${MINIO[@]}" --address ":$((start_port + 3))" $args >"${WORK_DIR}/dist-minio-server3.log" 2>&1 &
|
||||
pid3=$!
|
||||
disown $pid3
|
||||
|
||||
export MC_HOST_myminio="http://minio:minio123@127.0.0.1:$((start_port + 1))"
|
||||
|
||||
timeout 15m /tmp/mc ready myminio || fail
|
||||
|
||||
# Wait for all drives to be online and formatted
|
||||
while [ $(/tmp/mc admin info --json myminio | jq '.info.servers[].drives[].state | select(. != "ok")' | wc -l) -gt 0 ]; do sleep 1; done
|
||||
# Wait for all drives to be healed
|
||||
while [ $(/tmp/mc admin info --json myminio | jq '.info.servers[].drives[].healing | select(. != null) | select(. == true)' | wc -l) -gt 0 ]; do sleep 1; done
|
||||
|
||||
# Wait for Status: in MinIO output
|
||||
while true; do
|
||||
rv=$(check_online)
|
||||
if [ "$rv" != "1" ]; then
|
||||
# success
|
||||
break
|
||||
fi
|
||||
|
||||
# Check if we should retry
|
||||
retry=$((retry + 1))
|
||||
if [ $retry -le 20 ]; then
|
||||
sleep 5
|
||||
continue
|
||||
fi
|
||||
|
||||
# Failure
|
||||
fail
|
||||
done
|
||||
|
||||
if ! ps -p $pid1 1>&2 >/dev/null; then
|
||||
echo "minio-server-1 is not running." && fail
|
||||
fi
|
||||
|
||||
if ! ps -p $pid2 1>&2 >/dev/null; then
|
||||
echo "minio-server-2 is not running." && fail
|
||||
fi
|
||||
|
||||
if ! ps -p $pid3 1>&2 >/dev/null; then
|
||||
echo "minio-server-3 is not running." && fail
|
||||
fi
|
||||
|
||||
if ! pkill minio; then
|
||||
fail
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
if pgrep minio; then
|
||||
# forcibly killing, to proceed further properly.
|
||||
if ! pkill -9 minio; then
|
||||
echo "no minio process running anymore, proceed."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function fail() {
|
||||
for i in $(seq 1 3); do
|
||||
echo "server$i log:"
|
||||
cat "${WORK_DIR}/dist-minio-server$i.log"
|
||||
done
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function check_online() {
|
||||
if ! grep -q 'API:' ${WORK_DIR}/dist-minio-*.log; then
|
||||
echo "1"
|
||||
fi
|
||||
}
|
||||
|
||||
function purge() {
|
||||
echo rm -rf "$1"
|
||||
}
|
||||
|
||||
function __init__() {
|
||||
echo "Initializing environment"
|
||||
mkdir -p "$WORK_DIR"
|
||||
mkdir -p "$MINIO_CONFIG_DIR"
|
||||
|
||||
## version is purposefully set to '3' for minio to migrate configuration file
|
||||
echo '{"version": "3", "credential": {"accessKey": "minio", "secretKey": "minio123"}, "region": "us-east-1"}' >"$MINIO_CONFIG_DIR/config.json"
|
||||
|
||||
if [ ! -f /tmp/mc ]; then
|
||||
wget --quiet -O /tmp/mc https://dl.minio.io/client/mc/release/linux-amd64/mc &&
|
||||
chmod +x /tmp/mc
|
||||
fi
|
||||
}
|
||||
|
||||
function perform_test() {
|
||||
start_minio_3_node $2
|
||||
|
||||
echo "Testing Distributed Erasure setup healing of drives"
|
||||
echo "Remove the contents of the disks belonging to '${1}' erasure set"
|
||||
|
||||
rm -rf ${WORK_DIR}/${1}/*/
|
||||
|
||||
set -x
|
||||
start_minio_3_node $2
|
||||
}
|
||||
|
||||
function main() {
|
||||
# use same ports for all tests
|
||||
start_port=$(shuf -i 10000-65000 -n 1)
|
||||
|
||||
perform_test "2" ${start_port}
|
||||
perform_test "1" ${start_port}
|
||||
perform_test "3" ${start_port}
|
||||
}
|
||||
|
||||
(__init__ "$@" && main "$@")
|
||||
rv=$?
|
||||
purge "$WORK_DIR"
|
||||
exit "$rv"
|
||||
96
buildscripts/verify-healing-with-root-disks.sh
Executable file
96
buildscripts/verify-healing-with-root-disks.sh
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
set -E
|
||||
set -o pipefail
|
||||
set -x
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
|
||||
function start_minio() {
|
||||
start_port=$1
|
||||
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
unset MINIO_KMS_AUTO_ENCRYPTION # do not auto-encrypt objects
|
||||
unset MINIO_CI_CD
|
||||
unset CI
|
||||
|
||||
args=()
|
||||
for i in $(seq 1 4); do
|
||||
args+=("http://localhost:$((start_port + i))${WORK_DIR}/mnt/disk$i/ ")
|
||||
done
|
||||
|
||||
for i in $(seq 1 4); do
|
||||
"${MINIO[@]}" --address ":$((start_port + i))" ${args[@]} 2>&1 >"${WORK_DIR}/server$i.log" &
|
||||
done
|
||||
|
||||
# Wait until all nodes return 403
|
||||
for i in $(seq 1 4); do
|
||||
while [ "$(curl -m 1 -s -o /dev/null -w "%{http_code}" http://localhost:$((start_port + i)))" -ne "403" ]; do
|
||||
echo -n "."
|
||||
sleep 1
|
||||
done
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
# Prepare fake disks with losetup
|
||||
function prepare_block_devices() {
|
||||
set -e
|
||||
mkdir -p ${WORK_DIR}/disks/ ${WORK_DIR}/mnt/
|
||||
sudo modprobe loop
|
||||
for i in 1 2 3 4; do
|
||||
dd if=/dev/zero of=${WORK_DIR}/disks/img.${i} bs=1M count=2000
|
||||
device=$(sudo losetup --find --show ${WORK_DIR}/disks/img.${i})
|
||||
sudo mkfs.ext4 -F ${device}
|
||||
mkdir -p ${WORK_DIR}/mnt/disk${i}/
|
||||
sudo mount ${device} ${WORK_DIR}/mnt/disk${i}/
|
||||
sudo chown "$(id -u):$(id -g)" ${device} ${WORK_DIR}/mnt/disk${i}/
|
||||
done
|
||||
set +e
|
||||
}
|
||||
|
||||
# Start a distributed MinIO setup, unmount one disk and check if it is formatted
|
||||
function main() {
|
||||
start_port=$(shuf -i 10000-65000 -n 1)
|
||||
start_minio ${start_port}
|
||||
|
||||
# Unmount the disk, after the unmount the device id
|
||||
# /tmp/xxx/mnt/disk4 will be the same as '/' and it
|
||||
# will be detected as root disk
|
||||
while [ "$u" != "0" ]; do
|
||||
sudo umount ${WORK_DIR}/mnt/disk4/
|
||||
u=$?
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Wait until MinIO self heal kicks in
|
||||
sleep 60
|
||||
|
||||
if [ -f ${WORK_DIR}/mnt/disk4/.minio.sys/format.json ]; then
|
||||
echo "A root disk is formatted unexpectedely"
|
||||
cat "${WORK_DIR}/server4.log"
|
||||
exit -1
|
||||
fi
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
pkill minio
|
||||
sudo umount ${WORK_DIR}/mnt/disk{1..3}/
|
||||
sudo rm /dev/minio-loopdisk*
|
||||
rm -rf "$WORK_DIR"
|
||||
}
|
||||
|
||||
(prepare_block_devices)
|
||||
(main "$@")
|
||||
rv=$?
|
||||
|
||||
cleanup
|
||||
exit "$rv"
|
||||
@@ -1,123 +1,167 @@
|
||||
#!/bin/bash
|
||||
#!/bin/bash -e
|
||||
#
|
||||
|
||||
set -e
|
||||
set -E
|
||||
set -o pipefail
|
||||
|
||||
if [ ! -x "$PWD/minio" ]; then
|
||||
echo "minio executable binary not found in current directory"
|
||||
exit 1
|
||||
echo "minio executable binary not found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORK_DIR="$PWD/.verify-$RANDOM"
|
||||
MINIO_CONFIG_DIR="$WORK_DIR/.minio"
|
||||
MINIO=( "$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server )
|
||||
|
||||
export GOGC=25
|
||||
MINIO=("$PWD/minio" --config-dir "$MINIO_CONFIG_DIR" server)
|
||||
GOPATH=/tmp/gopath
|
||||
|
||||
function start_minio_3_node() {
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
export MINIO_ERASURE_SET_DRIVE_COUNT=6
|
||||
for i in $(seq 1 3); do
|
||||
rm "${WORK_DIR}/dist-minio-server$i.log"
|
||||
done
|
||||
|
||||
start_port=$(shuf -i 10000-65000 -n 1)
|
||||
args=""
|
||||
for i in $(seq 1 3); do
|
||||
args="$args http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/1/ http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/2/ http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/3/ http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/4/ http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/5/ http://127.0.0.1:$[$start_port+$i]${WORK_DIR}/$i/6/"
|
||||
done
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
export MINIO_ERASURE_SET_DRIVE_COUNT=6
|
||||
export MINIO_CI_CD=1
|
||||
|
||||
"${MINIO[@]}" --address ":$[$start_port+1]" $args > "${WORK_DIR}/dist-minio-server1.log" 2>&1 &
|
||||
disown $!
|
||||
first_time=$(find ${WORK_DIR}/ | grep format.json | wc -l)
|
||||
|
||||
"${MINIO[@]}" --address ":$[$start_port+2]" $args > "${WORK_DIR}/dist-minio-server2.log" 2>&1 &
|
||||
disown $!
|
||||
start_port=$1
|
||||
args=""
|
||||
for d in $(seq 1 3 5); do
|
||||
args="$args http://127.0.0.1:$((start_port + 1))${WORK_DIR}/1/${d}/ http://127.0.0.1:$((start_port + 2))${WORK_DIR}/2/${d}/ http://127.0.0.1:$((start_port + 3))${WORK_DIR}/3/${d}/ "
|
||||
d=$((d + 1))
|
||||
args="$args http://127.0.0.1:$((start_port + 1))${WORK_DIR}/1/${d}/ http://127.0.0.1:$((start_port + 2))${WORK_DIR}/2/${d}/ http://127.0.0.1:$((start_port + 3))${WORK_DIR}/3/${d}/ "
|
||||
done
|
||||
|
||||
"${MINIO[@]}" --address ":$[$start_port+3]" $args > "${WORK_DIR}/dist-minio-server3.log" 2>&1 &
|
||||
disown $!
|
||||
"${MINIO[@]}" --address ":$((start_port + 1))" $args >"${WORK_DIR}/dist-minio-server1.log" 2>&1 &
|
||||
pid1=$!
|
||||
disown ${pid1}
|
||||
|
||||
sleep "$1"
|
||||
if [ "$(pgrep -c minio)" -ne 3 ]; then
|
||||
for i in $(seq 1 3); do
|
||||
echo "server$i log:"
|
||||
cat "${WORK_DIR}/dist-minio-server$i.log"
|
||||
done
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
if ! pkill minio; then
|
||||
for i in $(seq 1 3); do
|
||||
echo "server$i log:"
|
||||
cat "${WORK_DIR}/dist-minio-server$i.log"
|
||||
done
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
"${MINIO[@]}" --address ":$((start_port + 2))" $args >"${WORK_DIR}/dist-minio-server2.log" 2>&1 &
|
||||
pid2=$!
|
||||
disown $pid2
|
||||
|
||||
sleep 1;
|
||||
if pgrep minio; then
|
||||
# forcibly killing, to proceed further properly.
|
||||
if ! pkill -9 minio; then
|
||||
echo "no minio process running anymore, proceed."
|
||||
fi
|
||||
fi
|
||||
"${MINIO[@]}" --address ":$((start_port + 3))" $args >"${WORK_DIR}/dist-minio-server3.log" 2>&1 &
|
||||
pid3=$!
|
||||
disown $pid3
|
||||
|
||||
export MC_HOST_myminio="http://minio:minio123@127.0.0.1:$((start_port + 1))"
|
||||
timeout 15m /tmp/mc ready myminio || fail
|
||||
|
||||
[ ${first_time} -eq 0 ] && upload_objects
|
||||
[ ${first_time} -ne 0 ] && sleep 120
|
||||
|
||||
if ! ps -p $pid1 1>&2 >/dev/null; then
|
||||
echo "minio server 1 is not running" && fail
|
||||
fi
|
||||
|
||||
if ! ps -p $pid2 1>&2 >/dev/null; then
|
||||
echo "minio server 2 is not running" && fail
|
||||
fi
|
||||
|
||||
if ! ps -p $pid3 1>&2 >/dev/null; then
|
||||
echo "minio server 3 is not running" && fail
|
||||
fi
|
||||
|
||||
if ! pkill minio; then
|
||||
fail
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
if pgrep minio; then
|
||||
# forcibly killing, to proceed further properly.
|
||||
if ! pkill -9 minio; then
|
||||
echo "no minio process running anymore, proceed."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function check_heal() {
|
||||
if ! grep -q 'API:' ${WORK_DIR}/dist-minio-*.log; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
function check_online() {
|
||||
if grep -q 'Unable to initialize sub-systems' ${WORK_DIR}/dist-minio-*.log; then
|
||||
echo "1"
|
||||
fi
|
||||
for ((i = 0; i < 20; i++)); do
|
||||
test -f ${WORK_DIR}/$1/1/.minio.sys/format.json
|
||||
v1=$?
|
||||
nextInES=$(($1 + 1)) && [ $nextInES -gt 3 ] && nextInES=1
|
||||
foundFiles1=$(find ${WORK_DIR}/$1/1/ | grep -v .minio.sys | grep xl.meta | wc -l)
|
||||
foundFiles2=$(find ${WORK_DIR}/$nextInES/1/ | grep -v .minio.sys | grep xl.meta | wc -l)
|
||||
test $foundFiles1 -eq $foundFiles2
|
||||
v2=$?
|
||||
[ $v1 == 0 -a $v2 == 0 ] && return 0
|
||||
sleep 10
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
function purge()
|
||||
{
|
||||
rm -rf "$1"
|
||||
function purge() {
|
||||
rm -rf "$1"
|
||||
}
|
||||
|
||||
function __init__()
|
||||
{
|
||||
echo "Initializing environment"
|
||||
mkdir -p "$WORK_DIR"
|
||||
mkdir -p "$MINIO_CONFIG_DIR"
|
||||
function fail() {
|
||||
for i in $(seq 1 3); do
|
||||
echo "server$i log:"
|
||||
cat "${WORK_DIR}/dist-minio-server$i.log"
|
||||
done
|
||||
pkill -9 minio
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
}
|
||||
|
||||
## version is purposefully set to '3' for minio to migrate configuration file
|
||||
echo '{"version": "3", "credential": {"accessKey": "minio", "secretKey": "minio123"}, "region": "us-east-1"}' > "$MINIO_CONFIG_DIR/config.json"
|
||||
function __init__() {
|
||||
echo "Initializing environment"
|
||||
mkdir -p "$WORK_DIR"
|
||||
mkdir -p "$MINIO_CONFIG_DIR"
|
||||
|
||||
## version is purposefully set to '3' for minio to migrate configuration file
|
||||
echo '{"version": "3", "credential": {"accessKey": "minio", "secretKey": "minio123"}, "region": "us-east-1"}' >"$MINIO_CONFIG_DIR/config.json"
|
||||
|
||||
if [ ! -f /tmp/mc ]; then
|
||||
wget --quiet -O /tmp/mc https://dl.minio.io/client/mc/release/linux-amd64/mc &&
|
||||
chmod +x /tmp/mc
|
||||
fi
|
||||
}
|
||||
|
||||
function upload_objects() {
|
||||
/tmp/mc mb myminio/testbucket/
|
||||
for ((i = 0; i < 20; i++)); do
|
||||
echo "my content" | /tmp/mc pipe myminio/testbucket/file-$i
|
||||
done
|
||||
}
|
||||
|
||||
function perform_test() {
|
||||
start_minio_3_node 60
|
||||
start_port=$2
|
||||
|
||||
echo "Testing Distributed Erasure setup healing of drives"
|
||||
echo "Remove the contents of the disks belonging to '${1}' erasure set"
|
||||
start_minio_3_node $start_port
|
||||
|
||||
rm -rf ${WORK_DIR}/${1}/*/
|
||||
echo "Testing Distributed Erasure setup healing of drives"
|
||||
echo "Remove the contents of the disks belonging to '${1}' node"
|
||||
|
||||
start_minio_3_node 60
|
||||
rm -rf ${WORK_DIR}/${1}/*/
|
||||
|
||||
rv=$(check_online)
|
||||
if [ "$rv" == "1" ]; then
|
||||
pkill -9 minio
|
||||
for i in $(seq 1 3); do
|
||||
echo "server$i log:"
|
||||
cat "${WORK_DIR}/dist-minio-server$i.log"
|
||||
done
|
||||
echo "FAILED"
|
||||
purge "$WORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
set -x
|
||||
start_minio_3_node $start_port
|
||||
|
||||
check_heal ${1}
|
||||
rv=$?
|
||||
if [ "$rv" == "1" ]; then
|
||||
fail
|
||||
fi
|
||||
}
|
||||
|
||||
function main()
|
||||
{
|
||||
perform_test "2"
|
||||
perform_test "1"
|
||||
perform_test "3"
|
||||
function main() {
|
||||
# use same ports for all tests
|
||||
start_port=$(shuf -i 10000-65000 -n 1)
|
||||
|
||||
perform_test "2" ${start_port}
|
||||
perform_test "1" ${start_port}
|
||||
perform_test "3" ${start_port}
|
||||
}
|
||||
|
||||
( __init__ "$@" && main "$@" )
|
||||
(__init__ "$@" && main "$@")
|
||||
rv=$?
|
||||
purge "$WORK_DIR"
|
||||
exit "$rv"
|
||||
|
||||
@@ -22,10 +22,10 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
xhttp "github.com/minio/minio/internal/http"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/minio/pkg/bucket/policy"
|
||||
"github.com/minio/mux"
|
||||
"github.com/minio/pkg/v3/policy"
|
||||
)
|
||||
|
||||
// Data types used for returning dummy access control
|
||||
@@ -80,7 +80,7 @@ func (api objectAPIHandlers) PutBucketACLHandler(w http.ResponseWriter, r *http.
|
||||
}
|
||||
|
||||
// Before proceeding validate if bucket exists.
|
||||
_, err := objAPI.GetBucketInfo(ctx, bucket)
|
||||
_, err := objAPI.GetBucketInfo(ctx, bucket, BucketOptions{})
|
||||
if err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
@@ -90,8 +90,8 @@ func (api objectAPIHandlers) PutBucketACLHandler(w http.ResponseWriter, r *http.
|
||||
if aclHeader == "" {
|
||||
acl := &accessControlPolicy{}
|
||||
if err = xmlDecoder(r.Body, acl, r.ContentLength); err != nil {
|
||||
if err == io.EOF {
|
||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingSecurityHeader),
|
||||
if terr, ok := err.(*xml.SyntaxError); ok && terr.Msg == io.EOF.Error() {
|
||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedXML),
|
||||
r.URL)
|
||||
return
|
||||
}
|
||||
@@ -114,8 +114,6 @@ func (api objectAPIHandlers) PutBucketACLHandler(w http.ResponseWriter, r *http.
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, NotImplemented{}), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
// GetBucketACLHandler - GET Bucket ACL
|
||||
@@ -144,7 +142,7 @@ func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.
|
||||
}
|
||||
|
||||
// Before proceeding validate if bucket exists.
|
||||
_, err := objAPI.GetBucketInfo(ctx, bucket)
|
||||
_, err := objAPI.GetBucketInfo(ctx, bucket, BucketOptions{})
|
||||
if err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
@@ -164,8 +162,6 @@ func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
// PutObjectACLHandler - PUT Object ACL
|
||||
@@ -229,8 +225,6 @@ func (api objectAPIHandlers) PutObjectACLHandler(w http.ResponseWriter, r *http.
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, NotImplemented{}), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
// GetObjectACLHandler - GET Object ACL
|
||||
@@ -283,6 +277,4 @@ func (api objectAPIHandlers) GetObjectACLHandler(w http.ResponseWriter, r *http.
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
263
cmd/admin-handler-utils.go
Normal file
263
cmd/admin-handler-utils.go
Normal file
@@ -0,0 +1,263 @@
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/kms-go/kes"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/minio/internal/config"
|
||||
"github.com/minio/pkg/v3/policy"
|
||||
)
|
||||
|
||||
// validateAdminReq will validate request against and return whether it is allowed.
|
||||
// If any of the supplied actions are allowed it will be successful.
|
||||
// If nil ObjectLayer is returned, the operation is not permitted.
|
||||
// When nil ObjectLayer has been returned an error has always been sent to w.
|
||||
func validateAdminReq(ctx context.Context, w http.ResponseWriter, r *http.Request, actions ...policy.AdminAction) (ObjectLayer, auth.Credentials) {
|
||||
// Get current object layer instance.
|
||||
objectAPI := newObjectLayerFn()
|
||||
if objectAPI == nil || globalNotificationSys == nil {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
||||
return nil, auth.Credentials{}
|
||||
}
|
||||
|
||||
for _, action := range actions {
|
||||
// Validate request signature.
|
||||
cred, adminAPIErr := checkAdminRequestAuth(ctx, r, action, "")
|
||||
switch adminAPIErr {
|
||||
case ErrNone:
|
||||
return objectAPI, cred
|
||||
case ErrAccessDenied:
|
||||
// Try another
|
||||
continue
|
||||
default:
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL)
|
||||
return nil, cred
|
||||
}
|
||||
}
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||
return nil, auth.Credentials{}
|
||||
}
|
||||
|
||||
// AdminError - is a generic error for all admin APIs.
|
||||
type AdminError struct {
|
||||
Code string
|
||||
Message string
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
func (ae AdminError) Error() string {
|
||||
return ae.Message
|
||||
}
|
||||
|
||||
func toAdminAPIErr(ctx context.Context, err error) APIError {
|
||||
if err == nil {
|
||||
return noError
|
||||
}
|
||||
|
||||
var apiErr APIError
|
||||
switch e := err.(type) {
|
||||
case policy.Error:
|
||||
apiErr = APIError{
|
||||
Code: "XMinioMalformedIAMPolicy",
|
||||
Description: e.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case config.ErrConfigNotFound:
|
||||
apiErr = APIError{
|
||||
Code: "XMinioConfigNotFoundError",
|
||||
Description: e.Error(),
|
||||
HTTPStatusCode: http.StatusNotFound,
|
||||
}
|
||||
case config.ErrConfigGeneric:
|
||||
apiErr = APIError{
|
||||
Code: "XMinioConfigError",
|
||||
Description: e.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case AdminError:
|
||||
apiErr = APIError{
|
||||
Code: e.Code,
|
||||
Description: e.Message,
|
||||
HTTPStatusCode: e.StatusCode,
|
||||
}
|
||||
case SRError:
|
||||
apiErr = errorCodes.ToAPIErrWithErr(e.Code, e.Cause)
|
||||
case decomError:
|
||||
apiErr = APIError{
|
||||
Code: "XMinioDecommissionNotAllowed",
|
||||
Description: e.Err,
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
default:
|
||||
switch {
|
||||
case errors.Is(err, errTooManyPolicies):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioAdminInvalidRequest",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, errDecommissionAlreadyRunning):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioDecommissionNotAllowed",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, errDecommissionComplete):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioDecommissionNotAllowed",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, errDecommissionRebalanceAlreadyRunning):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioDecommissionNotAllowed",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, errRebalanceDecommissionAlreadyRunning):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioRebalanceNotAllowed",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, errConfigNotFound):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioConfigError",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusNotFound,
|
||||
}
|
||||
case errors.Is(err, errIAMActionNotAllowed):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioIAMActionNotAllowed",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusForbidden,
|
||||
}
|
||||
case errors.Is(err, errIAMServiceAccountNotAllowed):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioIAMServiceAccountNotAllowed",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, errIAMNotInitialized):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioIAMNotInitialized",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusServiceUnavailable,
|
||||
}
|
||||
case errors.Is(err, errPolicyInUse):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioIAMPolicyInUse",
|
||||
Description: "The policy cannot be removed, as it is in use",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, errSessionPolicyTooLarge):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioIAMServiceAccountSessionPolicyTooLarge",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, kes.ErrKeyExists):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioKMSKeyExists",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusConflict,
|
||||
}
|
||||
|
||||
// Tier admin API errors
|
||||
case errors.Is(err, madmin.ErrTierNameEmpty):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioAdminTierNameEmpty",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, madmin.ErrTierInvalidConfig):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioAdminTierInvalidConfig",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, madmin.ErrTierInvalidConfigVersion):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioAdminTierInvalidConfigVersion",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, madmin.ErrTierTypeUnsupported):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioAdminTierTypeUnsupported",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errIsTierPermError(err):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioAdminTierInsufficientPermissions",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
case errors.Is(err, errTierInvalidConfig):
|
||||
apiErr = APIError{
|
||||
Code: "XMinioAdminTierInvalidConfig",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
default:
|
||||
apiErr = errorCodes.ToAPIErrWithErr(toAdminAPIErrCode(ctx, err), err)
|
||||
}
|
||||
}
|
||||
return apiErr
|
||||
}
|
||||
|
||||
// toAdminAPIErrCode - converts errErasureWriteQuorum error to admin API
|
||||
// specific error.
|
||||
func toAdminAPIErrCode(ctx context.Context, err error) APIErrorCode {
|
||||
if errors.Is(err, errErasureWriteQuorum) {
|
||||
return ErrAdminConfigNoQuorum
|
||||
}
|
||||
return toAPIErrorCode(ctx, err)
|
||||
}
|
||||
|
||||
// wraps export error for more context
|
||||
func exportError(ctx context.Context, err error, fname, entity string) APIError {
|
||||
if entity == "" {
|
||||
return toAPIError(ctx, fmt.Errorf("error exporting %s with: %w", fname, err))
|
||||
}
|
||||
return toAPIError(ctx, fmt.Errorf("error exporting %s from %s with: %w", entity, fname, err))
|
||||
}
|
||||
|
||||
// wraps import error for more context
|
||||
func importError(ctx context.Context, err error, fname, entity string) APIError {
|
||||
if entity == "" {
|
||||
return toAPIError(ctx, fmt.Errorf("error importing %s with: %w", fname, err))
|
||||
}
|
||||
return toAPIError(ctx, fmt.Errorf("error importing %s from %s with: %w", entity, fname, err))
|
||||
}
|
||||
|
||||
// wraps import error for more context
|
||||
func importErrorWithAPIErr(ctx context.Context, apiErr APIErrorCode, err error, fname, entity string) APIError {
|
||||
if entity == "" {
|
||||
return errorCodes.ToAPIErrWithErr(apiErr, fmt.Errorf("error importing %s with: %w", fname, err))
|
||||
}
|
||||
return errorCodes.ToAPIErrWithErr(apiErr, fmt.Errorf("error importing %s from %s with: %w", entity, fname, err))
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
// Copyright (c) 2015-2023 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
@@ -26,45 +26,25 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/config"
|
||||
"github.com/minio/minio/internal/config/cache"
|
||||
"github.com/minio/minio/internal/config/etcd"
|
||||
xldap "github.com/minio/minio/internal/config/identity/ldap"
|
||||
"github.com/minio/minio/internal/config/identity/openid"
|
||||
"github.com/minio/minio/internal/config/policy/opa"
|
||||
idplugin "github.com/minio/minio/internal/config/identity/plugin"
|
||||
polplugin "github.com/minio/minio/internal/config/policy/plugin"
|
||||
"github.com/minio/minio/internal/config/storageclass"
|
||||
"github.com/minio/minio/internal/config/subnet"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
"github.com/minio/mux"
|
||||
"github.com/minio/pkg/v3/policy"
|
||||
)
|
||||
|
||||
func validateAdminReqConfigKV(ctx context.Context, w http.ResponseWriter, r *http.Request) (auth.Credentials, ObjectLayer) {
|
||||
// Get current object layer instance.
|
||||
objectAPI := newObjectLayerFn()
|
||||
if objectAPI == nil {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
||||
return auth.Credentials{}, nil
|
||||
}
|
||||
|
||||
// Validate request signature.
|
||||
cred, adminAPIErr := checkAdminRequestAuth(ctx, r, iampolicy.ConfigUpdateAdminAction, "")
|
||||
if adminAPIErr != ErrNone {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL)
|
||||
return cred, nil
|
||||
}
|
||||
|
||||
return cred, objectAPI
|
||||
}
|
||||
|
||||
// DelConfigKVHandler - DELETE /minio/admin/v3/del-config-kv
|
||||
func (a adminAPIHandlers) DelConfigKVHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "DeleteConfigKV")
|
||||
ctx := r.Context()
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
@@ -78,12 +58,18 @@ func (a adminAPIHandlers) DelConfigKVHandler(w http.ResponseWriter, r *http.Requ
|
||||
password := cred.SecretKey
|
||||
kvBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err, logger.Application)
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := readServerConfig(ctx, objectAPI)
|
||||
subSys, _, _, err := config.GetSubSys(string(kvBytes))
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := readServerConfig(ctx, objectAPI, nil)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
@@ -94,19 +80,75 @@ func (a adminAPIHandlers) DelConfigKVHandler(w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
|
||||
if err = validateConfig(ctx, cfg, subSys); err != nil {
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if subnet proxy being deleted and if so the value of proxy of subnet
|
||||
// target of logger webhook configuration also should be deleted
|
||||
loggerWebhookProxyDeleted := setLoggerWebhookSubnetProxy(subSys, cfg)
|
||||
|
||||
if err = saveServerConfig(ctx, objectAPI, cfg); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// freshly retrieve the config so that default values are loaded for reset config
|
||||
if cfg, err = getValidConfig(objectAPI); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
dynamic := config.SubSystemsDynamic.Contains(subSys)
|
||||
if dynamic {
|
||||
applyDynamic(ctx, objectAPI, cfg, subSys, r, w)
|
||||
if subSys == config.SubnetSubSys && loggerWebhookProxyDeleted {
|
||||
// Logger webhook proxy deleted, apply the dynamic changes
|
||||
applyDynamic(ctx, objectAPI, cfg, config.LoggerWebhookSubSys, r, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func applyDynamic(ctx context.Context, objectAPI ObjectLayer, cfg config.Config, subSys string,
|
||||
r *http.Request, w http.ResponseWriter,
|
||||
) {
|
||||
// Apply dynamic values.
|
||||
if err := applyDynamicConfigForSubSys(GlobalContext, objectAPI, cfg, subSys); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
globalNotificationSys.SignalConfigReload(subSys)
|
||||
// Tell the client that dynamic config was applied.
|
||||
w.Header().Set(madmin.ConfigAppliedHeader, madmin.ConfigAppliedTrue)
|
||||
}
|
||||
|
||||
type badConfigErr struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error - return the error message
|
||||
func (bce badConfigErr) Error() string {
|
||||
return bce.Err.Error()
|
||||
}
|
||||
|
||||
// Unwrap the error to its underlying error.
|
||||
func (bce badConfigErr) Unwrap() error {
|
||||
return bce.Err
|
||||
}
|
||||
|
||||
type setConfigResult struct {
|
||||
Cfg config.Config
|
||||
SubSys string
|
||||
Dynamic bool
|
||||
LoggerWebhookCfgUpdated bool
|
||||
}
|
||||
|
||||
// SetConfigKVHandler - PUT /minio/admin/v3/set-config-kv
|
||||
func (a adminAPIHandlers) SetConfigKVHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "SetConfigKV")
|
||||
ctx := r.Context()
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
@@ -120,75 +162,120 @@ func (a adminAPIHandlers) SetConfigKVHandler(w http.ResponseWriter, r *http.Requ
|
||||
password := cred.SecretKey
|
||||
kvBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err, logger.Application)
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := readServerConfig(ctx, objectAPI)
|
||||
result, err := setConfigKV(ctx, objectAPI, kvBytes)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
dynamic, err := cfg.ReadConfig(bytes.NewReader(kvBytes))
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err = validateConfig(cfg, objectAPI.SetDriveCounts()); err != nil {
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Update the actual server config on disk.
|
||||
if err = saveServerConfig(ctx, objectAPI, cfg); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Write to the config input KV to history.
|
||||
if err = saveServerConfigHistory(ctx, objectAPI, kvBytes); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if dynamic {
|
||||
// Apply dynamic values.
|
||||
if err := applyDynamicConfig(GlobalContext, objectAPI, cfg); err != nil {
|
||||
switch err.(type) {
|
||||
case badConfigErr:
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL)
|
||||
default:
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
globalNotificationSys.SignalService(serviceReloadDynamic)
|
||||
// If all values were dynamic, tell the client.
|
||||
w.Header().Set(madmin.ConfigAppliedHeader, madmin.ConfigAppliedTrue)
|
||||
return
|
||||
}
|
||||
|
||||
if result.Dynamic {
|
||||
applyDynamic(ctx, objectAPI, result.Cfg, result.SubSys, r, w)
|
||||
// If logger webhook config updated (proxy due to callhome), explicitly dynamically
|
||||
// apply the config
|
||||
if result.LoggerWebhookCfgUpdated {
|
||||
applyDynamic(ctx, objectAPI, result.Cfg, config.LoggerWebhookSubSys, r, w)
|
||||
}
|
||||
}
|
||||
|
||||
writeSuccessResponseHeadersOnly(w)
|
||||
}
|
||||
|
||||
func setConfigKV(ctx context.Context, objectAPI ObjectLayer, kvBytes []byte) (result setConfigResult, err error) {
|
||||
result.Cfg, err = readServerConfig(ctx, objectAPI, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.Dynamic, err = result.Cfg.ReadConfig(bytes.NewReader(kvBytes))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.SubSys, _, _, err = config.GetSubSys(string(kvBytes))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tgts, err := config.ParseConfigTargetID(bytes.NewReader(kvBytes))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ctx = context.WithValue(ctx, config.ContextKeyForTargetFromConfig, tgts)
|
||||
if verr := validateConfig(ctx, result.Cfg, result.SubSys); verr != nil {
|
||||
err = badConfigErr{Err: verr}
|
||||
return
|
||||
}
|
||||
|
||||
// Check if subnet proxy being set and if so set the same value to proxy of subnet
|
||||
// target of logger webhook configuration
|
||||
result.LoggerWebhookCfgUpdated = setLoggerWebhookSubnetProxy(result.SubSys, result.Cfg)
|
||||
|
||||
// Update the actual server config on disk.
|
||||
if err = saveServerConfig(ctx, objectAPI, result.Cfg); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Write the config input KV to history.
|
||||
err = saveServerConfigHistory(ctx, objectAPI, kvBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// GetConfigKVHandler - GET /minio/admin/v3/get-config-kv?key={key}
|
||||
//
|
||||
// `key` can be one of three forms:
|
||||
// 1. `subsys:target` -> request for config of a single subsystem and target pair.
|
||||
// 2. `subsys:` -> request for config of a single subsystem and the default target.
|
||||
// 3. `subsys` -> request for config of all targets for the given subsystem.
|
||||
//
|
||||
// This is a reporting API and config secrets are redacted in the response.
|
||||
func (a adminAPIHandlers) GetConfigKVHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "GetConfigKV")
|
||||
ctx := r.Context()
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cfg := globalServerConfig.Clone()
|
||||
vars := mux.Vars(r)
|
||||
var buf = &bytes.Buffer{}
|
||||
cw := config.NewConfigWriteTo(cfg, vars["key"])
|
||||
if _, err := cw.WriteTo(buf); err != nil {
|
||||
key := vars["key"]
|
||||
|
||||
var subSys, target string
|
||||
{
|
||||
ws := strings.SplitN(key, madmin.SubSystemSeparator, 2)
|
||||
subSys = ws[0]
|
||||
if len(ws) == 2 {
|
||||
if ws[1] == "" {
|
||||
target = madmin.Default
|
||||
} else {
|
||||
target = ws[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subSysConfigs, err := cfg.GetSubsysInfo(subSys, target, true)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
var s strings.Builder
|
||||
for _, subSysConfig := range subSysConfigs {
|
||||
subSysConfig.WriteTo(&s, false)
|
||||
}
|
||||
|
||||
password := cred.SecretKey
|
||||
econfigData, err := madmin.EncryptData(password, buf.Bytes())
|
||||
econfigData, err := madmin.EncryptData(password, []byte(s.String()))
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
@@ -198,11 +285,9 @@ func (a adminAPIHandlers) GetConfigKVHandler(w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
|
||||
func (a adminAPIHandlers) ClearConfigHistoryKVHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "ClearConfigHistoryKV")
|
||||
ctx := r.Context()
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
_, objectAPI := validateAdminReqConfigKV(ctx, w, r)
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
@@ -225,21 +310,17 @@ func (a adminAPIHandlers) ClearConfigHistoryKVHandler(w http.ResponseWriter, r *
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := delServerConfigHistory(ctx, objectAPI, restoreID); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
} else if err := delServerConfigHistory(ctx, objectAPI, restoreID); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// RestoreConfigHistoryKVHandler - restores a config with KV settings for the given KV id.
|
||||
func (a adminAPIHandlers) RestoreConfigHistoryKVHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "RestoreConfigHistoryKV")
|
||||
ctx := r.Context()
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
_, objectAPI := validateAdminReqConfigKV(ctx, w, r)
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
@@ -257,7 +338,7 @@ func (a adminAPIHandlers) RestoreConfigHistoryKVHandler(w http.ResponseWriter, r
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := readServerConfig(ctx, objectAPI)
|
||||
cfg, err := readServerConfig(ctx, objectAPI, nil)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
@@ -268,7 +349,7 @@ func (a adminAPIHandlers) RestoreConfigHistoryKVHandler(w http.ResponseWriter, r
|
||||
return
|
||||
}
|
||||
|
||||
if err = validateConfig(cfg, objectAPI.SetDriveCounts()); err != nil {
|
||||
if err = validateConfig(ctx, cfg, ""); err != nil {
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -283,11 +364,9 @@ func (a adminAPIHandlers) RestoreConfigHistoryKVHandler(w http.ResponseWriter, r
|
||||
|
||||
// ListConfigHistoryKVHandler - lists all the KV ids.
|
||||
func (a adminAPIHandlers) ListConfigHistoryKVHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "ListConfigHistoryKV")
|
||||
ctx := r.Context()
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
@@ -323,11 +402,9 @@ func (a adminAPIHandlers) ListConfigHistoryKVHandler(w http.ResponseWriter, r *h
|
||||
|
||||
// HelpConfigKVHandler - GET /minio/admin/v3/help-config-kv?subSys={subSys}&key={key}
|
||||
func (a adminAPIHandlers) HelpConfigKVHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "HelpConfigKV")
|
||||
ctx := r.Context()
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
_, objectAPI := validateAdminReqConfigKV(ctx, w, r)
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
@@ -337,7 +414,7 @@ func (a adminAPIHandlers) HelpConfigKVHandler(w http.ResponseWriter, r *http.Req
|
||||
subSys := vars["subSys"]
|
||||
key := vars["key"]
|
||||
|
||||
_, envOnly := r.URL.Query()["env"]
|
||||
_, envOnly := r.Form["env"]
|
||||
|
||||
rd, err := GetHelp(subSys, key, envOnly)
|
||||
if err != nil {
|
||||
@@ -346,16 +423,13 @@ func (a adminAPIHandlers) HelpConfigKVHandler(w http.ResponseWriter, r *http.Req
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(rd)
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
// SetConfigHandler - PUT /minio/admin/v3/config
|
||||
func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "SetConfig")
|
||||
ctx := r.Context()
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
@@ -369,7 +443,7 @@ func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Reques
|
||||
password := cred.SecretKey
|
||||
kvBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err, logger.Application)
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -380,7 +454,7 @@ func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Reques
|
||||
return
|
||||
}
|
||||
|
||||
if err = validateConfig(cfg, objectAPI.SetDriveCounts()); err != nil {
|
||||
if err = validateConfig(ctx, cfg, ""); err != nil {
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -401,13 +475,13 @@ func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
|
||||
// GetConfigHandler - GET /minio/admin/v3/config
|
||||
// Get config.json of this minio setup.
|
||||
//
|
||||
// This endpoint is mainly for exporting and backing up the configuration.
|
||||
// Secrets are not redacted.
|
||||
func (a adminAPIHandlers) GetConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "GetConfig")
|
||||
ctx := r.Context()
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
cred, objectAPI := validateAdminReqConfigKV(ctx, w, r)
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
@@ -416,43 +490,29 @@ func (a adminAPIHandlers) GetConfigHandler(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
var s strings.Builder
|
||||
hkvs := config.HelpSubSysMap[""]
|
||||
var count int
|
||||
for _, hkv := range hkvs {
|
||||
count += len(cfg[hkv.Key])
|
||||
}
|
||||
for _, hkv := range hkvs {
|
||||
v := cfg[hkv.Key]
|
||||
for target, kv := range v {
|
||||
off := kv.Get(config.Enable) == config.EnableOff
|
||||
// We ignore the error below, as we cannot get one.
|
||||
cfgSubsysItems, _ := cfg.GetSubsysInfo(hkv.Key, "", false)
|
||||
|
||||
for _, item := range cfgSubsysItems {
|
||||
off := item.Config.Get(config.Enable) == config.EnableOff
|
||||
switch hkv.Key {
|
||||
case config.EtcdSubSys:
|
||||
off = !etcd.Enabled(kv)
|
||||
case config.CacheSubSys:
|
||||
off = !cache.Enabled(kv)
|
||||
off = !etcd.Enabled(item.Config)
|
||||
case config.StorageClassSubSys:
|
||||
off = !storageclass.Enabled(kv)
|
||||
case config.PolicyOPASubSys:
|
||||
off = !opa.Enabled(kv)
|
||||
off = !storageclass.Enabled(item.Config)
|
||||
case config.PolicyPluginSubSys:
|
||||
off = !polplugin.Enabled(item.Config)
|
||||
case config.IdentityOpenIDSubSys:
|
||||
off = !openid.Enabled(kv)
|
||||
off = !openid.Enabled(item.Config)
|
||||
case config.IdentityLDAPSubSys:
|
||||
off = !xldap.Enabled(kv)
|
||||
}
|
||||
if off {
|
||||
s.WriteString(config.KvComment)
|
||||
s.WriteString(config.KvSpaceSeparator)
|
||||
}
|
||||
s.WriteString(hkv.Key)
|
||||
if target != config.Default {
|
||||
s.WriteString(config.SubSystemSeparator)
|
||||
s.WriteString(target)
|
||||
}
|
||||
s.WriteString(config.KvSpaceSeparator)
|
||||
s.WriteString(kv.String())
|
||||
count--
|
||||
if count > 0 {
|
||||
s.WriteString(config.KvNewline)
|
||||
off = !xldap.Enabled(item.Config)
|
||||
case config.IdentityTLSSubSys:
|
||||
off = !globalIAMSys.STSTLSConfig.Enabled
|
||||
case config.IdentityPluginSubSys:
|
||||
off = !idplugin.Enabled(item.Config)
|
||||
}
|
||||
item.WriteTo(&s, off)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,3 +525,18 @@ func (a adminAPIHandlers) GetConfigHandler(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
writeSuccessResponseJSON(w, econfigData)
|
||||
}
|
||||
|
||||
// setLoggerWebhookSubnetProxy - Sets the logger webhook's subnet proxy value to
|
||||
// one being set for subnet proxy
|
||||
func setLoggerWebhookSubnetProxy(subSys string, cfg config.Config) bool {
|
||||
if subSys == config.SubnetSubSys || subSys == config.LoggerWebhookSubSys {
|
||||
subnetWebhookCfg := cfg[config.LoggerWebhookSubSys][subnet.LoggerWebhookName]
|
||||
loggerWebhookSubnetProxy := subnetWebhookCfg.Get(logger.Proxy)
|
||||
subnetProxy := cfg[config.SubnetSubSys][config.Default].Get(logger.Proxy)
|
||||
if loggerWebhookSubnetProxy != subnetProxy {
|
||||
subnetWebhookCfg.Set(logger.Proxy, subnetProxy)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
441
cmd/admin-handlers-idp-config.go
Normal file
441
cmd/admin-handlers-idp-config.go
Normal file
@@ -0,0 +1,441 @@
|
||||
// Copyright (c) 2015-2022 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
"github.com/minio/minio/internal/config"
|
||||
cfgldap "github.com/minio/minio/internal/config/identity/ldap"
|
||||
"github.com/minio/minio/internal/config/identity/openid"
|
||||
"github.com/minio/mux"
|
||||
"github.com/minio/pkg/v3/ldap"
|
||||
"github.com/minio/pkg/v3/policy"
|
||||
)
|
||||
|
||||
func addOrUpdateIDPHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, isUpdate bool) {
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 {
|
||||
// More than maxConfigSize bytes were available
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure body content type is opaque to ensure that request body has not
|
||||
// been interpreted as form data.
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
if contentType != "application/octet-stream" {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
password := cred.SecretKey
|
||||
reqBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
|
||||
if err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
idpCfgType := mux.Vars(r)["type"]
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpCfgType) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigInvalidIDPType), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
var subSys string
|
||||
switch idpCfgType {
|
||||
case madmin.OpenidIDPCfg:
|
||||
subSys = madmin.IdentityOpenIDSubSys
|
||||
case madmin.LDAPIDPCfg:
|
||||
subSys = madmin.IdentityLDAPSubSys
|
||||
}
|
||||
|
||||
cfgName := mux.Vars(r)["name"]
|
||||
cfgTarget := madmin.Default
|
||||
if cfgName != "" {
|
||||
cfgTarget = cfgName
|
||||
if idpCfgType == madmin.LDAPIDPCfg && cfgName != madmin.Default {
|
||||
// LDAP does not support multiple configurations. So cfgName must be
|
||||
// empty or `madmin.Default`.
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigLDAPNonDefaultConfigName), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check that this is a valid Create vs Update API call.
|
||||
s := globalServerConfig.Clone()
|
||||
if apiErrCode := handleCreateUpdateValidation(s, subSys, cfgTarget, isUpdate); apiErrCode != ErrNone {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(apiErrCode), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
cfgData := ""
|
||||
{
|
||||
tgtSuffix := ""
|
||||
if cfgTarget != madmin.Default {
|
||||
tgtSuffix = config.SubSystemSeparator + cfgTarget
|
||||
}
|
||||
cfgData = subSys + tgtSuffix + config.KvSpaceSeparator + string(reqBytes)
|
||||
}
|
||||
|
||||
cfg, err := readServerConfig(ctx, objectAPI, nil)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
dynamic, err := cfg.ReadConfig(strings.NewReader(cfgData))
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// IDP config is not dynamic. Sanity check.
|
||||
if dynamic {
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), "", r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err = validateConfig(ctx, cfg, subSys); err != nil {
|
||||
|
||||
var validationErr ldap.Validation
|
||||
if errors.As(err, &validationErr) {
|
||||
// If we got an LDAP validation error, we need to send appropriate
|
||||
// error message back to client (likely mc).
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigLDAPValidation),
|
||||
validationErr.FormatError(), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Update the actual server config on disk.
|
||||
if err = saveServerConfig(ctx, objectAPI, cfg); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Write to the config input KV to history.
|
||||
if err = saveServerConfigHistory(ctx, objectAPI, []byte(cfgData)); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseHeadersOnly(w)
|
||||
}
|
||||
|
||||
func handleCreateUpdateValidation(s config.Config, subSys, cfgTarget string, isUpdate bool) APIErrorCode {
|
||||
if cfgTarget != madmin.Default {
|
||||
// This cannot give an error at this point.
|
||||
subSysTargets, _ := s.GetAvailableTargets(subSys)
|
||||
subSysTargetsSet := set.CreateStringSet(subSysTargets...)
|
||||
if isUpdate && !subSysTargetsSet.Contains(cfgTarget) {
|
||||
return ErrAdminConfigIDPCfgNameDoesNotExist
|
||||
}
|
||||
if !isUpdate && subSysTargetsSet.Contains(cfgTarget) {
|
||||
return ErrAdminConfigIDPCfgNameAlreadyExists
|
||||
}
|
||||
|
||||
return ErrNone
|
||||
}
|
||||
|
||||
// For the default configuration name, since it will always be an available
|
||||
// target, we need to check if a configuration value has been set previously
|
||||
// to figure out if this is a valid create or update API call.
|
||||
|
||||
// This cannot really error (FIXME: improve the type for GetConfigInfo)
|
||||
var cfgInfos []madmin.IDPCfgInfo
|
||||
switch subSys {
|
||||
case madmin.IdentityOpenIDSubSys:
|
||||
cfgInfos, _ = globalIAMSys.OpenIDConfig.GetConfigInfo(s, cfgTarget)
|
||||
case madmin.IdentityLDAPSubSys:
|
||||
cfgInfos, _ = globalIAMSys.LDAPConfig.GetConfigInfo(s, cfgTarget)
|
||||
}
|
||||
|
||||
if len(cfgInfos) > 0 && !isUpdate {
|
||||
return ErrAdminConfigIDPCfgNameAlreadyExists
|
||||
}
|
||||
if len(cfgInfos) == 0 && isUpdate {
|
||||
return ErrAdminConfigIDPCfgNameDoesNotExist
|
||||
}
|
||||
return ErrNone
|
||||
}
|
||||
|
||||
// AddIdentityProviderCfg: adds a new IDP config for openid/ldap.
|
||||
//
|
||||
// PUT <admin-prefix>/idp-cfg/openid/dex1 -> create named config `dex1`
|
||||
//
|
||||
// PUT <admin-prefix>/idp-cfg/openid/_ -> create (default) named config `_`
|
||||
func (a adminAPIHandlers) AddIdentityProviderCfg(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
addOrUpdateIDPHandler(ctx, w, r, false)
|
||||
}
|
||||
|
||||
// UpdateIdentityProviderCfg: updates an existing IDP config for openid/ldap.
|
||||
//
|
||||
// POST <admin-prefix>/idp-cfg/openid/dex1 -> update named config `dex1`
|
||||
//
|
||||
// POST <admin-prefix>/idp-cfg/openid/_ -> update (default) named config `_`
|
||||
func (a adminAPIHandlers) UpdateIdentityProviderCfg(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
addOrUpdateIDPHandler(ctx, w, r, true)
|
||||
}
|
||||
|
||||
// ListIdentityProviderCfg:
|
||||
//
|
||||
// GET <admin-prefix>/idp-cfg/openid -> lists openid provider configs.
|
||||
func (a adminAPIHandlers) ListIdentityProviderCfg(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
password := cred.SecretKey
|
||||
|
||||
idpCfgType := mux.Vars(r)["type"]
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpCfgType) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigInvalidIDPType), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
var cfgList []madmin.IDPListItem
|
||||
var err error
|
||||
switch idpCfgType {
|
||||
case madmin.OpenidIDPCfg:
|
||||
cfg := globalServerConfig.Clone()
|
||||
cfgList, err = globalIAMSys.OpenIDConfig.GetConfigList(cfg)
|
||||
case madmin.LDAPIDPCfg:
|
||||
cfg := globalServerConfig.Clone()
|
||||
cfgList, err = globalIAMSys.LDAPConfig.GetConfigList(cfg)
|
||||
|
||||
default:
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := json.Marshal(cfgList)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
econfigData, err := madmin.EncryptData(password, data)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseJSON(w, econfigData)
|
||||
}
|
||||
|
||||
// GetIdentityProviderCfg:
|
||||
//
|
||||
// GET <admin-prefix>/idp-cfg/openid/dex_test
|
||||
func (a adminAPIHandlers) GetIdentityProviderCfg(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
idpCfgType := mux.Vars(r)["type"]
|
||||
cfgName := mux.Vars(r)["name"]
|
||||
password := cred.SecretKey
|
||||
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpCfgType) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigInvalidIDPType), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := globalServerConfig.Clone()
|
||||
var cfgInfos []madmin.IDPCfgInfo
|
||||
var err error
|
||||
switch idpCfgType {
|
||||
case madmin.OpenidIDPCfg:
|
||||
cfgInfos, err = globalIAMSys.OpenIDConfig.GetConfigInfo(cfg, cfgName)
|
||||
case madmin.LDAPIDPCfg:
|
||||
cfgInfos, err = globalIAMSys.LDAPConfig.GetConfigInfo(cfg, cfgName)
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, openid.ErrProviderConfigNotFound) || errors.Is(err, cfgldap.ErrProviderConfigNotFound) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminNoSuchConfigTarget), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
res := madmin.IDPConfig{
|
||||
Type: idpCfgType,
|
||||
Name: cfgName,
|
||||
Info: cfgInfos,
|
||||
}
|
||||
data, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
econfigData, err := madmin.EncryptData(password, data)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseJSON(w, econfigData)
|
||||
}
|
||||
|
||||
// DeleteIdentityProviderCfg:
|
||||
//
|
||||
// DELETE <admin-prefix>/idp-cfg/openid/dex_test
|
||||
func (a adminAPIHandlers) DeleteIdentityProviderCfg(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.ConfigUpdateAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
idpCfgType := mux.Vars(r)["type"]
|
||||
cfgName := mux.Vars(r)["name"]
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpCfgType) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigInvalidIDPType), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
cfgCopy := globalServerConfig.Clone()
|
||||
var subSys string
|
||||
switch idpCfgType {
|
||||
case madmin.OpenidIDPCfg:
|
||||
subSys = config.IdentityOpenIDSubSys
|
||||
cfgInfos, err := globalIAMSys.OpenIDConfig.GetConfigInfo(cfgCopy, cfgName)
|
||||
if err != nil {
|
||||
if errors.Is(err, openid.ErrProviderConfigNotFound) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminNoSuchConfigTarget), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
hasEnv := false
|
||||
for _, ci := range cfgInfos {
|
||||
if ci.IsCfg && ci.IsEnv {
|
||||
hasEnv = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hasEnv {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigEnvOverridden), r.URL)
|
||||
return
|
||||
}
|
||||
case madmin.LDAPIDPCfg:
|
||||
subSys = config.IdentityLDAPSubSys
|
||||
cfgInfos, err := globalIAMSys.LDAPConfig.GetConfigInfo(cfgCopy, cfgName)
|
||||
if err != nil {
|
||||
if errors.Is(err, openid.ErrProviderConfigNotFound) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminNoSuchConfigTarget), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
hasEnv := false
|
||||
for _, ci := range cfgInfos {
|
||||
if ci.IsCfg && ci.IsEnv {
|
||||
hasEnv = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hasEnv {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigEnvOverridden), r.URL)
|
||||
return
|
||||
}
|
||||
default:
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := readServerConfig(ctx, objectAPI, nil)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
cfgKey := fmt.Sprintf("%s:%s", subSys, cfgName)
|
||||
if cfgName == madmin.Default {
|
||||
cfgKey = subSys
|
||||
}
|
||||
if err = cfg.DelKVS(cfgKey); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
if err = validateConfig(ctx, cfg, subSys); err != nil {
|
||||
|
||||
var validationErr ldap.Validation
|
||||
if errors.As(err, &validationErr) {
|
||||
// If we got an LDAP validation error, we need to send appropriate
|
||||
// error message back to client (likely mc).
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigLDAPValidation),
|
||||
validationErr.FormatError(), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL)
|
||||
return
|
||||
}
|
||||
if err = saveServerConfig(ctx, objectAPI, cfg); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
dynamic := config.SubSystemsDynamic.Contains(subSys)
|
||||
if dynamic {
|
||||
applyDynamic(ctx, objectAPI, cfg, subSys, r, w)
|
||||
}
|
||||
}
|
||||
658
cmd/admin-handlers-idp-ldap.go
Normal file
658
cmd/admin-handlers-idp-ldap.go
Normal file
@@ -0,0 +1,658 @@
|
||||
// Copyright (c) 2015-2022 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/mux"
|
||||
xldap "github.com/minio/pkg/v3/ldap"
|
||||
"github.com/minio/pkg/v3/policy"
|
||||
)
|
||||
|
||||
// ListLDAPPolicyMappingEntities lists users/groups mapped to given/all policies.
|
||||
//
|
||||
// GET <admin-prefix>/idp/ldap/policy-entities?[query-params]
|
||||
//
|
||||
// Query params:
|
||||
//
|
||||
// user=... -> repeatable query parameter, specifying users to query for
|
||||
// policy mapping
|
||||
//
|
||||
// group=... -> repeatable query parameter, specifying groups to query for
|
||||
// policy mapping
|
||||
//
|
||||
// policy=... -> repeatable query parameter, specifying policy to query for
|
||||
// user/group mapping
|
||||
//
|
||||
// When all query parameters are omitted, returns mappings for all policies.
|
||||
func (a adminAPIHandlers) ListLDAPPolicyMappingEntities(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
// Check authorization.
|
||||
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r,
|
||||
policy.ListGroupsAdminAction, policy.ListUsersAdminAction, policy.ListUserPoliciesAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate API arguments.
|
||||
|
||||
q := madmin.PolicyEntitiesQuery{
|
||||
Users: r.Form["user"],
|
||||
Groups: r.Form["group"],
|
||||
Policy: r.Form["policy"],
|
||||
}
|
||||
|
||||
// Query IAM
|
||||
|
||||
res, err := globalIAMSys.QueryLDAPPolicyEntities(r.Context(), q)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Encode result and send response.
|
||||
|
||||
data, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
password := cred.SecretKey
|
||||
econfigData, err := madmin.EncryptData(password, data)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
writeSuccessResponseJSON(w, econfigData)
|
||||
}
|
||||
|
||||
// AttachDetachPolicyLDAP attaches or detaches policies from an LDAP entity
|
||||
// (user or group).
|
||||
//
|
||||
// POST <admin-prefix>/idp/ldap/policy/{operation}
|
||||
func (a adminAPIHandlers) AttachDetachPolicyLDAP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
// Check authorization.
|
||||
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.UpdatePolicyAssociationAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// fail if ldap is not enabled
|
||||
if !globalIAMSys.LDAPConfig.Enabled() {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminLDAPNotEnabled), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 {
|
||||
// More than maxConfigSize bytes were available
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure body content type is opaque to ensure that request body has not
|
||||
// been interpreted as form data.
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
if contentType != "application/octet-stream" {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate operation
|
||||
operation := mux.Vars(r)["operation"]
|
||||
if operation != "attach" && operation != "detach" {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminInvalidArgument), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
isAttach := operation == "attach"
|
||||
|
||||
// Validate API arguments in body.
|
||||
password := cred.SecretKey
|
||||
reqBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength))
|
||||
if err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
var par madmin.PolicyAssociationReq
|
||||
err = json.Unmarshal(reqBytes, &par)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err := par.IsValid(); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Call IAM subsystem
|
||||
updatedAt, addedOrRemoved, _, err := globalIAMSys.PolicyDBUpdateLDAP(ctx, isAttach, par)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
respBody := madmin.PolicyAssociationResp{
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
if isAttach {
|
||||
respBody.PoliciesAttached = addedOrRemoved
|
||||
} else {
|
||||
respBody.PoliciesDetached = addedOrRemoved
|
||||
}
|
||||
|
||||
data, err := json.Marshal(respBody)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
encryptedData, err := madmin.EncryptData(password, data)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseJSON(w, encryptedData)
|
||||
}
|
||||
|
||||
// AddServiceAccountLDAP adds a new service account for provided LDAP username or DN
|
||||
//
|
||||
// PUT /minio/admin/v3/idp/ldap/add-service-account
|
||||
func (a adminAPIHandlers) AddServiceAccountLDAP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cred, opts, createReq, targetUser, APIError := commonAddServiceAccount(r)
|
||||
if APIError.Code != "" {
|
||||
writeErrorResponseJSON(ctx, w, APIError, r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// fail if ldap is not enabled
|
||||
if !globalIAMSys.LDAPConfig.Enabled() {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminLDAPNotEnabled), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Find the user for the request sender (as it may be sent via a service
|
||||
// account or STS account):
|
||||
requestorUser := cred.AccessKey
|
||||
requestorParentUser := cred.AccessKey
|
||||
requestorGroups := cred.Groups
|
||||
requestorIsDerivedCredential := false
|
||||
if cred.IsServiceAccount() || cred.IsTemp() {
|
||||
requestorParentUser = cred.ParentUser
|
||||
requestorIsDerivedCredential = true
|
||||
}
|
||||
|
||||
// Check if we are creating svc account for request sender.
|
||||
isSvcAccForRequestor := false
|
||||
if targetUser == requestorUser || targetUser == requestorParentUser {
|
||||
isSvcAccForRequestor = true
|
||||
}
|
||||
|
||||
var (
|
||||
targetGroups []string
|
||||
err error
|
||||
)
|
||||
|
||||
// If we are creating svc account for request sender, ensure that targetUser
|
||||
// is a real user (i.e. not derived credentials).
|
||||
if isSvcAccForRequestor {
|
||||
if requestorIsDerivedCredential {
|
||||
if requestorParentUser == "" {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx,
|
||||
errors.New("service accounts cannot be generated for temporary credentials without parent")), r.URL)
|
||||
return
|
||||
}
|
||||
targetUser = requestorParentUser
|
||||
}
|
||||
targetGroups = requestorGroups
|
||||
|
||||
// Deny if the target user is not LDAP
|
||||
foundResult, err := globalIAMSys.LDAPConfig.GetValidatedDNForUsername(targetUser)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
if foundResult == nil {
|
||||
err := errors.New("Specified user does not exist on LDAP server")
|
||||
APIErr := errorCodes.ToAPIErrWithErr(ErrAdminNoSuchUser, err)
|
||||
writeErrorResponseJSON(ctx, w, APIErr, r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// In case of LDAP/OIDC we need to set `opts.claims` to ensure
|
||||
// it is associated with the LDAP/OIDC user properly.
|
||||
for k, v := range cred.Claims {
|
||||
if k == expClaim {
|
||||
continue
|
||||
}
|
||||
opts.claims[k] = v
|
||||
}
|
||||
} else {
|
||||
// We still need to ensure that the target user is a valid LDAP user.
|
||||
//
|
||||
// The target user may be supplied as a (short) username or a DN.
|
||||
// However, for now, we only support using the short username.
|
||||
|
||||
isDN := globalIAMSys.LDAPConfig.ParsesAsDN(targetUser)
|
||||
opts.claims[ldapUserN] = targetUser // simple username
|
||||
var lookupResult *xldap.DNSearchResult
|
||||
lookupResult, targetGroups, err = globalIAMSys.LDAPConfig.LookupUserDN(targetUser)
|
||||
if err != nil {
|
||||
// if not found, check if DN
|
||||
if strings.Contains(err.Error(), "User DN not found for:") {
|
||||
if isDN {
|
||||
// warn user that DNs are not allowed
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminLDAPExpectedLoginName, err), r.URL)
|
||||
} else {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminNoSuchUser, err), r.URL)
|
||||
}
|
||||
}
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
targetUser = lookupResult.NormDN
|
||||
opts.claims[ldapUser] = targetUser // DN
|
||||
opts.claims[ldapActualUser] = lookupResult.ActualDN
|
||||
|
||||
// Check if this user or their groups have a policy applied.
|
||||
ldapPolicies, err := globalIAMSys.PolicyDBGet(targetUser, targetGroups...)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
if len(ldapPolicies) == 0 {
|
||||
err = fmt.Errorf("No policy set for user `%s` or any of their groups: `%s`", opts.claims[ldapActualUser], strings.Join(targetGroups, "`,`"))
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminNoSuchUser, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Add LDAP attributes that were looked up into the claims.
|
||||
for attribKey, attribValue := range lookupResult.Attributes {
|
||||
opts.claims[ldapAttribPrefix+attribKey] = attribValue
|
||||
}
|
||||
}
|
||||
|
||||
newCred, updatedAt, err := globalIAMSys.NewServiceAccount(ctx, targetUser, targetGroups, opts)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
createResp := madmin.AddServiceAccountResp{
|
||||
Credentials: madmin.Credentials{
|
||||
AccessKey: newCred.AccessKey,
|
||||
SecretKey: newCred.SecretKey,
|
||||
Expiration: newCred.Expiration,
|
||||
},
|
||||
}
|
||||
|
||||
data, err := json.Marshal(createResp)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
encryptedData, err := madmin.EncryptData(cred.SecretKey, data)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseJSON(w, encryptedData)
|
||||
|
||||
// Call hook for cluster-replication if the service account is not for a
|
||||
// root user.
|
||||
if newCred.ParentUser != globalActiveCred.AccessKey {
|
||||
replLogIf(ctx, globalSiteReplicationSys.IAMChangeHook(ctx, madmin.SRIAMItem{
|
||||
Type: madmin.SRIAMItemSvcAcc,
|
||||
SvcAccChange: &madmin.SRSvcAccChange{
|
||||
Create: &madmin.SRSvcAccCreate{
|
||||
Parent: newCred.ParentUser,
|
||||
AccessKey: newCred.AccessKey,
|
||||
SecretKey: newCred.SecretKey,
|
||||
Groups: newCred.Groups,
|
||||
Name: newCred.Name,
|
||||
Description: newCred.Description,
|
||||
Claims: opts.claims,
|
||||
SessionPolicy: createReq.Policy,
|
||||
Status: auth.AccountOn,
|
||||
Expiration: createReq.Expiration,
|
||||
},
|
||||
},
|
||||
UpdatedAt: updatedAt,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// ListAccessKeysLDAP - GET /minio/admin/v3/idp/ldap/list-access-keys
|
||||
func (a adminAPIHandlers) ListAccessKeysLDAP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
// Get current object layer instance.
|
||||
objectAPI := newObjectLayerFn()
|
||||
if objectAPI == nil || globalNotificationSys == nil {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
cred, owner, s3Err := validateAdminSignature(ctx, r, "")
|
||||
if s3Err != ErrNone {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
userDN := r.Form.Get("userDN")
|
||||
|
||||
// If listing is requested for a specific user (who is not the request
|
||||
// sender), check that the user has permissions.
|
||||
if userDN != "" && userDN != cred.ParentUser {
|
||||
if !globalIAMSys.IsAllowed(policy.Args{
|
||||
AccountName: cred.AccessKey,
|
||||
Groups: cred.Groups,
|
||||
Action: policy.ListServiceAccountsAdminAction,
|
||||
ConditionValues: getConditionValues(r, "", cred),
|
||||
IsOwner: owner,
|
||||
Claims: cred.Claims,
|
||||
}) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if !globalIAMSys.IsAllowed(policy.Args{
|
||||
AccountName: cred.AccessKey,
|
||||
Groups: cred.Groups,
|
||||
Action: policy.ListServiceAccountsAdminAction,
|
||||
ConditionValues: getConditionValues(r, "", cred),
|
||||
IsOwner: owner,
|
||||
Claims: cred.Claims,
|
||||
DenyOnly: true,
|
||||
}) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||
return
|
||||
}
|
||||
userDN = cred.AccessKey
|
||||
if cred.ParentUser != "" {
|
||||
userDN = cred.ParentUser
|
||||
}
|
||||
}
|
||||
|
||||
dnResult, err := globalIAMSys.LDAPConfig.GetValidatedDNForUsername(userDN)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
if dnResult == nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errNoSuchUser), r.URL)
|
||||
return
|
||||
}
|
||||
targetAccount := dnResult.NormDN
|
||||
|
||||
listType := r.Form.Get("listType")
|
||||
if listType != "sts-only" && listType != "svcacc-only" && listType != "" {
|
||||
// default to both
|
||||
listType = ""
|
||||
}
|
||||
|
||||
var serviceAccounts []auth.Credentials
|
||||
var stsKeys []auth.Credentials
|
||||
|
||||
if listType == "" || listType == "sts-only" {
|
||||
stsKeys, err = globalIAMSys.ListSTSAccounts(ctx, targetAccount)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
if listType == "" || listType == "svcacc-only" {
|
||||
serviceAccounts, err = globalIAMSys.ListServiceAccounts(ctx, targetAccount)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var serviceAccountList []madmin.ServiceAccountInfo
|
||||
var stsKeyList []madmin.ServiceAccountInfo
|
||||
|
||||
for _, svc := range serviceAccounts {
|
||||
expiryTime := svc.Expiration
|
||||
serviceAccountList = append(serviceAccountList, madmin.ServiceAccountInfo{
|
||||
AccessKey: svc.AccessKey,
|
||||
Expiration: &expiryTime,
|
||||
})
|
||||
}
|
||||
for _, sts := range stsKeys {
|
||||
expiryTime := sts.Expiration
|
||||
stsKeyList = append(stsKeyList, madmin.ServiceAccountInfo{
|
||||
AccessKey: sts.AccessKey,
|
||||
Expiration: &expiryTime,
|
||||
})
|
||||
}
|
||||
|
||||
listResp := madmin.ListAccessKeysLDAPResp{
|
||||
ServiceAccounts: serviceAccountList,
|
||||
STSKeys: stsKeyList,
|
||||
}
|
||||
|
||||
data, err := json.Marshal(listResp)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
encryptedData, err := madmin.EncryptData(cred.SecretKey, data)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseJSON(w, encryptedData)
|
||||
}
|
||||
|
||||
// ListAccessKeysLDAPBulk - GET /minio/admin/v3/idp/ldap/list-access-keys-bulk
|
||||
func (a adminAPIHandlers) ListAccessKeysLDAPBulk(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
// Get current object layer instance.
|
||||
objectAPI := newObjectLayerFn()
|
||||
if objectAPI == nil || globalNotificationSys == nil {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
cred, owner, s3Err := validateAdminSignature(ctx, r, "")
|
||||
if s3Err != ErrNone {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
dnList := r.Form["userDNs"]
|
||||
isAll := r.Form.Get("all") == "true"
|
||||
onlySelf := !isAll && len(dnList) == 0
|
||||
|
||||
if isAll && len(dnList) > 0 {
|
||||
// This should be checked on client side, so return generic error
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Empty DN list and not self, list access keys for all users
|
||||
if isAll {
|
||||
if !globalIAMSys.IsAllowed(policy.Args{
|
||||
AccountName: cred.AccessKey,
|
||||
Groups: cred.Groups,
|
||||
Action: policy.ListUsersAdminAction,
|
||||
ConditionValues: getConditionValues(r, "", cred),
|
||||
IsOwner: owner,
|
||||
Claims: cred.Claims,
|
||||
}) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||
return
|
||||
}
|
||||
} else if len(dnList) == 1 {
|
||||
var dn string
|
||||
foundResult, err := globalIAMSys.LDAPConfig.GetValidatedDNForUsername(dnList[0])
|
||||
if err == nil {
|
||||
dn = foundResult.NormDN
|
||||
}
|
||||
if dn == cred.ParentUser || dnList[0] == cred.ParentUser {
|
||||
onlySelf = true
|
||||
}
|
||||
}
|
||||
|
||||
if !globalIAMSys.IsAllowed(policy.Args{
|
||||
AccountName: cred.AccessKey,
|
||||
Groups: cred.Groups,
|
||||
Action: policy.ListServiceAccountsAdminAction,
|
||||
ConditionValues: getConditionValues(r, "", cred),
|
||||
IsOwner: owner,
|
||||
Claims: cred.Claims,
|
||||
DenyOnly: onlySelf,
|
||||
}) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if onlySelf && len(dnList) == 0 {
|
||||
selfDN := cred.AccessKey
|
||||
if cred.ParentUser != "" {
|
||||
selfDN = cred.ParentUser
|
||||
}
|
||||
dnList = append(dnList, selfDN)
|
||||
}
|
||||
|
||||
var ldapUserList []string
|
||||
if isAll {
|
||||
ldapUsers, err := globalIAMSys.ListLDAPUsers(ctx)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
for user := range ldapUsers {
|
||||
ldapUserList = append(ldapUserList, user)
|
||||
}
|
||||
} else {
|
||||
for _, userDN := range dnList {
|
||||
// Validate the userDN
|
||||
foundResult, err := globalIAMSys.LDAPConfig.GetValidatedDNForUsername(userDN)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
if foundResult == nil {
|
||||
continue
|
||||
}
|
||||
ldapUserList = append(ldapUserList, foundResult.NormDN)
|
||||
}
|
||||
}
|
||||
|
||||
listType := r.Form.Get("listType")
|
||||
var listSTSKeys, listServiceAccounts bool
|
||||
switch listType {
|
||||
case madmin.AccessKeyListUsersOnly:
|
||||
listSTSKeys = false
|
||||
listServiceAccounts = false
|
||||
case madmin.AccessKeyListSTSOnly:
|
||||
listSTSKeys = true
|
||||
listServiceAccounts = false
|
||||
case madmin.AccessKeyListSvcaccOnly:
|
||||
listSTSKeys = false
|
||||
listServiceAccounts = true
|
||||
case madmin.AccessKeyListAll:
|
||||
listSTSKeys = true
|
||||
listServiceAccounts = true
|
||||
default:
|
||||
err := errors.New("invalid list type")
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrInvalidRequest, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
accessKeyMap := make(map[string]madmin.ListAccessKeysLDAPResp)
|
||||
for _, internalDN := range ldapUserList {
|
||||
externalDN := globalIAMSys.LDAPConfig.DecodeDN(internalDN)
|
||||
accessKeys := madmin.ListAccessKeysLDAPResp{}
|
||||
if listSTSKeys {
|
||||
stsKeys, err := globalIAMSys.ListSTSAccounts(ctx, internalDN)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
for _, sts := range stsKeys {
|
||||
expiryTime := sts.Expiration
|
||||
accessKeys.STSKeys = append(accessKeys.STSKeys, madmin.ServiceAccountInfo{
|
||||
AccessKey: sts.AccessKey,
|
||||
Expiration: &expiryTime,
|
||||
})
|
||||
}
|
||||
// if only STS keys, skip if user has no STS keys
|
||||
if !listServiceAccounts && len(stsKeys) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if listServiceAccounts {
|
||||
serviceAccounts, err := globalIAMSys.ListServiceAccounts(ctx, internalDN)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
for _, svc := range serviceAccounts {
|
||||
expiryTime := svc.Expiration
|
||||
accessKeys.ServiceAccounts = append(accessKeys.ServiceAccounts, madmin.ServiceAccountInfo{
|
||||
AccessKey: svc.AccessKey,
|
||||
Expiration: &expiryTime,
|
||||
})
|
||||
}
|
||||
// if only service accounts, skip if user has no service accounts
|
||||
if !listSTSKeys && len(serviceAccounts) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
accessKeyMap[externalDN] = accessKeys
|
||||
}
|
||||
|
||||
data, err := json.Marshal(accessKeyMap)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
encryptedData, err := madmin.EncryptData(cred.SecretKey, data)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseJSON(w, encryptedData)
|
||||
}
|
||||
393
cmd/admin-handlers-pools.go
Normal file
393
cmd/admin-handlers-pools.go
Normal file
@@ -0,0 +1,393 @@
|
||||
// Copyright (c) 2015-2024 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/mux"
|
||||
"github.com/minio/pkg/v3/env"
|
||||
"github.com/minio/pkg/v3/policy"
|
||||
)
|
||||
|
||||
var (
|
||||
errRebalanceDecommissionAlreadyRunning = errors.New("Rebalance cannot be started, decommission is already in progress")
|
||||
errDecommissionRebalanceAlreadyRunning = errors.New("Decommission cannot be started, rebalance is already in progress")
|
||||
)
|
||||
|
||||
func (a adminAPIHandlers) StartDecommission(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.DecommissionAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Legacy args style such as non-ellipses style is not supported with this API.
|
||||
if globalEndpoints.Legacy() {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
z, ok := objectAPI.(*erasureServerPools)
|
||||
if !ok || len(z.serverPools) == 1 {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if z.IsDecommissionRunning() {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errDecommissionAlreadyRunning), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if z.IsRebalanceStarted() {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminRebalanceAlreadyStarted), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
v := vars["pool"]
|
||||
byID := vars["by-id"] == "true"
|
||||
|
||||
pools := strings.Split(v, ",")
|
||||
poolIndices := make([]int, 0, len(pools))
|
||||
|
||||
for _, pool := range pools {
|
||||
var idx int
|
||||
if byID {
|
||||
var err error
|
||||
idx, err = strconv.Atoi(pool)
|
||||
if err != nil {
|
||||
// We didn't find any matching pools, invalid input
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errInvalidArgument), r.URL)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
idx = globalEndpoints.GetPoolIdx(pool)
|
||||
if idx == -1 {
|
||||
// We didn't find any matching pools, invalid input
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errInvalidArgument), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
var pool *erasureSets
|
||||
for pidx := range z.serverPools {
|
||||
if pidx == idx {
|
||||
pool = z.serverPools[idx]
|
||||
break
|
||||
}
|
||||
}
|
||||
if pool == nil {
|
||||
// We didn't find any matching pools, invalid input
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errInvalidArgument), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
poolIndices = append(poolIndices, idx)
|
||||
}
|
||||
|
||||
if len(poolIndices) == 0 || !proxyDecommissionRequest(ctx, globalEndpoints[poolIndices[0]].Endpoints[0], w, r) {
|
||||
if err := z.Decommission(r.Context(), poolIndices...); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a adminAPIHandlers) CancelDecommission(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.DecommissionAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Legacy args style such as non-ellipses style is not supported with this API.
|
||||
if globalEndpoints.Legacy() {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
pools, ok := objectAPI.(*erasureServerPools)
|
||||
if !ok {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
v := vars["pool"]
|
||||
byID := vars["by-id"] == "true"
|
||||
idx := -1
|
||||
|
||||
if byID {
|
||||
if i, err := strconv.Atoi(v); err == nil && i >= 0 && i < len(globalEndpoints) {
|
||||
idx = i
|
||||
}
|
||||
} else {
|
||||
idx = globalEndpoints.GetPoolIdx(v)
|
||||
}
|
||||
|
||||
if idx == -1 {
|
||||
// We didn't find any matching pools, invalid input
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errInvalidArgument), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if !proxyDecommissionRequest(ctx, globalEndpoints[idx].Endpoints[0], w, r) {
|
||||
if err := pools.DecommissionCancel(ctx, idx); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a adminAPIHandlers) StatusPool(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.ServerInfoAdminAction, policy.DecommissionAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Legacy args style such as non-ellipses style is not supported with this API.
|
||||
if globalEndpoints.Legacy() {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
pools, ok := objectAPI.(*erasureServerPools)
|
||||
if !ok {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
v := vars["pool"]
|
||||
byID := vars["by-id"] == "true"
|
||||
idx := -1
|
||||
|
||||
if byID {
|
||||
if i, err := strconv.Atoi(v); err == nil && i >= 0 && i < len(globalEndpoints) {
|
||||
idx = i
|
||||
}
|
||||
} else {
|
||||
idx = globalEndpoints.GetPoolIdx(v)
|
||||
}
|
||||
|
||||
if idx == -1 {
|
||||
apiErr := toAdminAPIErr(ctx, errInvalidArgument)
|
||||
apiErr.Description = fmt.Sprintf("specified pool '%s' not found, please specify a valid pool", v)
|
||||
// We didn't find any matching pools, invalid input
|
||||
writeErrorResponseJSON(ctx, w, apiErr, r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
status, err := pools.Status(r.Context(), idx)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
adminLogIf(r.Context(), json.NewEncoder(w).Encode(&status))
|
||||
}
|
||||
|
||||
func (a adminAPIHandlers) ListPools(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.ServerInfoAdminAction, policy.DecommissionAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Legacy args style such as non-ellipses style is not supported with this API.
|
||||
if globalEndpoints.Legacy() {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
pools, ok := objectAPI.(*erasureServerPools)
|
||||
if !ok {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
poolsStatus := make([]PoolStatus, len(globalEndpoints))
|
||||
for idx := range globalEndpoints {
|
||||
status, err := pools.Status(r.Context(), idx)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
poolsStatus[idx] = status
|
||||
}
|
||||
|
||||
adminLogIf(r.Context(), json.NewEncoder(w).Encode(poolsStatus))
|
||||
}
|
||||
|
||||
func (a adminAPIHandlers) RebalanceStart(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.RebalanceAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// NB rebalance-start admin API is always coordinated from first pool's
|
||||
// first node. The following is required to serialize (the effects of)
|
||||
// concurrent rebalance-start commands.
|
||||
if ep := globalEndpoints[0].Endpoints[0]; !ep.IsLocal {
|
||||
for nodeIdx, proxyEp := range globalProxyEndpoints {
|
||||
if proxyEp.Endpoint.Host == ep.Host {
|
||||
if proxyRequestByNodeIndex(ctx, w, r, nodeIdx) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pools, ok := objectAPI.(*erasureServerPools)
|
||||
if !ok || len(pools.serverPools) == 1 {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if pools.IsDecommissionRunning() {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errRebalanceDecommissionAlreadyRunning), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if pools.IsRebalanceStarted() {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminRebalanceAlreadyStarted), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
bucketInfos, err := objectAPI.ListBuckets(ctx, BucketOptions{})
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
buckets := make([]string, 0, len(bucketInfos))
|
||||
for _, bInfo := range bucketInfos {
|
||||
buckets = append(buckets, bInfo.Name)
|
||||
}
|
||||
|
||||
var id string
|
||||
if id, err = pools.initRebalanceMeta(ctx, buckets); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Rebalance routine is run on the first node of any pool participating in rebalance.
|
||||
pools.StartRebalance()
|
||||
|
||||
b, err := json.Marshal(struct {
|
||||
ID string `json:"id"`
|
||||
}{ID: id})
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseJSON(w, b)
|
||||
// Notify peers to load rebalance.bin and start rebalance routine if they happen to be
|
||||
// participating pool's leader node
|
||||
globalNotificationSys.LoadRebalanceMeta(ctx, true)
|
||||
}
|
||||
|
||||
func (a adminAPIHandlers) RebalanceStatus(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.RebalanceAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Proxy rebalance-status to first pool first node, so that users see a
|
||||
// consistent view of rebalance progress even though different rebalancing
|
||||
// pools may temporarily have out of date info on the others.
|
||||
if ep := globalEndpoints[0].Endpoints[0]; !ep.IsLocal {
|
||||
for nodeIdx, proxyEp := range globalProxyEndpoints {
|
||||
if proxyEp.Endpoint.Host == ep.Host {
|
||||
if proxyRequestByNodeIndex(ctx, w, r, nodeIdx) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pools, ok := objectAPI.(*erasureServerPools)
|
||||
if !ok {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
rs, err := rebalanceStatus(ctx, pools)
|
||||
if err != nil {
|
||||
if errors.Is(err, errRebalanceNotStarted) || errors.Is(err, errConfigNotFound) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminRebalanceNotStarted), r.URL)
|
||||
return
|
||||
}
|
||||
adminLogIf(ctx, fmt.Errorf("failed to fetch rebalance status: %w", err))
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
adminLogIf(r.Context(), json.NewEncoder(w).Encode(rs))
|
||||
}
|
||||
|
||||
func (a adminAPIHandlers) RebalanceStop(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.RebalanceAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pools, ok := objectAPI.(*erasureServerPools)
|
||||
if !ok {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Cancel any ongoing rebalance operation
|
||||
globalNotificationSys.StopRebalance(r.Context())
|
||||
writeSuccessResponseHeadersOnly(w)
|
||||
adminLogIf(ctx, pools.saveRebalanceStats(GlobalContext, 0, rebalSaveStoppedAt))
|
||||
globalNotificationSys.LoadRebalanceMeta(ctx, false)
|
||||
}
|
||||
|
||||
func proxyDecommissionRequest(ctx context.Context, defaultEndPoint Endpoint, w http.ResponseWriter, r *http.Request) (proxy bool) {
|
||||
host := env.Get("_MINIO_DECOM_ENDPOINT_HOST", defaultEndPoint.Host)
|
||||
if host == "" {
|
||||
return
|
||||
}
|
||||
for nodeIdx, proxyEp := range globalProxyEndpoints {
|
||||
if proxyEp.Endpoint.Host == host && !proxyEp.IsLocal {
|
||||
if proxyRequestByNodeIndex(ctx, w, r, nodeIdx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
623
cmd/admin-handlers-site-replication.go
Normal file
623
cmd/admin-handlers-site-replication.go
Normal file
@@ -0,0 +1,623 @@
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
xioutil "github.com/minio/minio/internal/ioutil"
|
||||
"github.com/minio/mux"
|
||||
"github.com/minio/pkg/v3/policy"
|
||||
)
|
||||
|
||||
// SiteReplicationAdd - PUT /minio/admin/v3/site-replication/add
|
||||
func (a adminAPIHandlers) SiteReplicationAdd(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.SiteReplicationAddAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var sites []madmin.PeerSite
|
||||
if err := parseJSONBody(ctx, r.Body, &sites, cred.SecretKey); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
opts := getSRAddOptions(r)
|
||||
status, err := globalSiteReplicationSys.AddPeerClusters(ctx, sites, opts)
|
||||
if err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := json.Marshal(status)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseJSON(w, body)
|
||||
}
|
||||
|
||||
func getSRAddOptions(r *http.Request) (opts madmin.SRAddOptions) {
|
||||
opts.ReplicateILMExpiry = r.Form.Get("replicateILMExpiry") == "true"
|
||||
return
|
||||
}
|
||||
|
||||
// SRPeerJoin - PUT /minio/admin/v3/site-replication/join
|
||||
//
|
||||
// used internally to tell current cluster to enable SR with
|
||||
// the provided peer clusters and service account.
|
||||
func (a adminAPIHandlers) SRPeerJoin(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.SiteReplicationAddAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var joinArg madmin.SRPeerJoinReq
|
||||
if err := parseJSONBody(ctx, r.Body, &joinArg, cred.SecretKey); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err := globalSiteReplicationSys.PeerJoinReq(ctx, joinArg); err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SRPeerBucketOps - PUT /minio/admin/v3/site-replication/bucket-ops?bucket=x&operation=y
|
||||
func (a adminAPIHandlers) SRPeerBucketOps(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationOperationAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
bucket := vars["bucket"]
|
||||
operation := madmin.BktOp(vars["operation"])
|
||||
|
||||
var err error
|
||||
switch operation {
|
||||
default:
|
||||
err = errSRInvalidRequest(errInvalidArgument)
|
||||
case madmin.MakeWithVersioningBktOp:
|
||||
createdAt, cerr := time.Parse(time.RFC3339Nano, strings.TrimSpace(r.Form.Get("createdAt")))
|
||||
if cerr != nil {
|
||||
createdAt = timeSentinel
|
||||
}
|
||||
|
||||
opts := MakeBucketOptions{
|
||||
LockEnabled: r.Form.Get("lockEnabled") == "true",
|
||||
VersioningEnabled: r.Form.Get("versioningEnabled") == "true",
|
||||
ForceCreate: r.Form.Get("forceCreate") == "true",
|
||||
CreatedAt: createdAt,
|
||||
}
|
||||
err = globalSiteReplicationSys.PeerBucketMakeWithVersioningHandler(ctx, bucket, opts)
|
||||
case madmin.ConfigureReplBktOp:
|
||||
err = globalSiteReplicationSys.PeerBucketConfigureReplHandler(ctx, bucket)
|
||||
case madmin.DeleteBucketBktOp, madmin.ForceDeleteBucketBktOp:
|
||||
err = globalSiteReplicationSys.PeerBucketDeleteHandler(ctx, bucket, DeleteBucketOptions{
|
||||
Force: operation == madmin.ForceDeleteBucketBktOp,
|
||||
SRDeleteOp: getSRBucketDeleteOp(true),
|
||||
})
|
||||
case madmin.PurgeDeletedBucketOp:
|
||||
globalSiteReplicationSys.purgeDeletedBucket(ctx, objectAPI, bucket)
|
||||
}
|
||||
if err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SRPeerReplicateIAMItem - PUT /minio/admin/v3/site-replication/iam-item
|
||||
func (a adminAPIHandlers) SRPeerReplicateIAMItem(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationOperationAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var item madmin.SRIAMItem
|
||||
if err := parseJSONBody(ctx, r.Body, &item, ""); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
switch item.Type {
|
||||
default:
|
||||
err = errSRInvalidRequest(errInvalidArgument)
|
||||
case madmin.SRIAMItemPolicy:
|
||||
if item.Policy == nil {
|
||||
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil, item.UpdatedAt)
|
||||
} else {
|
||||
policy, perr := policy.ParseConfig(bytes.NewReader(item.Policy))
|
||||
if perr != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, perr), r.URL)
|
||||
return
|
||||
}
|
||||
if policy.IsEmpty() {
|
||||
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil, item.UpdatedAt)
|
||||
} else {
|
||||
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, policy, item.UpdatedAt)
|
||||
}
|
||||
}
|
||||
case madmin.SRIAMItemSvcAcc:
|
||||
err = globalSiteReplicationSys.PeerSvcAccChangeHandler(ctx, item.SvcAccChange, item.UpdatedAt)
|
||||
case madmin.SRIAMItemPolicyMapping:
|
||||
err = globalSiteReplicationSys.PeerPolicyMappingHandler(ctx, item.PolicyMapping, item.UpdatedAt)
|
||||
case madmin.SRIAMItemSTSAcc:
|
||||
err = globalSiteReplicationSys.PeerSTSAccHandler(ctx, item.STSCredential, item.UpdatedAt)
|
||||
case madmin.SRIAMItemIAMUser:
|
||||
err = globalSiteReplicationSys.PeerIAMUserChangeHandler(ctx, item.IAMUser, item.UpdatedAt)
|
||||
case madmin.SRIAMItemGroupInfo:
|
||||
err = globalSiteReplicationSys.PeerGroupInfoChangeHandler(ctx, item.GroupInfo, item.UpdatedAt)
|
||||
}
|
||||
if err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SRPeerReplicateBucketItem - PUT /minio/admin/v3/site-replication/peer/bucket-meta
|
||||
func (a adminAPIHandlers) SRPeerReplicateBucketItem(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationOperationAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var item madmin.SRBucketMeta
|
||||
if err := parseJSONBody(ctx, r.Body, &item, ""); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if item.Bucket == "" {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errSRInvalidRequest(errInvalidArgument)), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
switch item.Type {
|
||||
default:
|
||||
err = globalSiteReplicationSys.PeerBucketMetadataUpdateHandler(ctx, item)
|
||||
case madmin.SRBucketMetaTypePolicy:
|
||||
if item.Policy == nil {
|
||||
err = globalSiteReplicationSys.PeerBucketPolicyHandler(ctx, item.Bucket, nil, item.UpdatedAt)
|
||||
} else {
|
||||
bktPolicy, berr := policy.ParseBucketPolicyConfig(bytes.NewReader(item.Policy), item.Bucket)
|
||||
if berr != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, berr), r.URL)
|
||||
return
|
||||
}
|
||||
if bktPolicy.IsEmpty() {
|
||||
err = globalSiteReplicationSys.PeerBucketPolicyHandler(ctx, item.Bucket, nil, item.UpdatedAt)
|
||||
} else {
|
||||
err = globalSiteReplicationSys.PeerBucketPolicyHandler(ctx, item.Bucket, bktPolicy, item.UpdatedAt)
|
||||
}
|
||||
}
|
||||
case madmin.SRBucketMetaTypeQuotaConfig:
|
||||
if item.Quota == nil {
|
||||
err = globalSiteReplicationSys.PeerBucketQuotaConfigHandler(ctx, item.Bucket, nil, item.UpdatedAt)
|
||||
} else {
|
||||
quotaConfig, err := parseBucketQuota(item.Bucket, item.Quota)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
if err = globalSiteReplicationSys.PeerBucketQuotaConfigHandler(ctx, item.Bucket, quotaConfig, item.UpdatedAt); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
case madmin.SRBucketMetaTypeVersionConfig:
|
||||
err = globalSiteReplicationSys.PeerBucketVersioningHandler(ctx, item.Bucket, item.Versioning, item.UpdatedAt)
|
||||
case madmin.SRBucketMetaTypeTags:
|
||||
err = globalSiteReplicationSys.PeerBucketTaggingHandler(ctx, item.Bucket, item.Tags, item.UpdatedAt)
|
||||
case madmin.SRBucketMetaTypeObjectLockConfig:
|
||||
err = globalSiteReplicationSys.PeerBucketObjectLockConfigHandler(ctx, item.Bucket, item.ObjectLockConfig, item.UpdatedAt)
|
||||
case madmin.SRBucketMetaTypeSSEConfig:
|
||||
err = globalSiteReplicationSys.PeerBucketSSEConfigHandler(ctx, item.Bucket, item.SSEConfig, item.UpdatedAt)
|
||||
case madmin.SRBucketMetaLCConfig:
|
||||
err = globalSiteReplicationSys.PeerBucketLCConfigHandler(ctx, item.Bucket, item.ExpiryLCConfig, item.UpdatedAt)
|
||||
}
|
||||
if err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SiteReplicationInfo - GET /minio/admin/v3/site-replication/info
|
||||
func (a adminAPIHandlers) SiteReplicationInfo(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationInfoAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
info, err := globalSiteReplicationSys.GetClusterInfo(ctx)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(w).Encode(info); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (a adminAPIHandlers) SRPeerGetIDPSettings(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationAddAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
idpSettings := globalSiteReplicationSys.GetIDPSettings(ctx)
|
||||
if err := json.NewEncoder(w).Encode(idpSettings); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func parseJSONBody(ctx context.Context, body io.Reader, v interface{}, encryptionKey string) error {
|
||||
data, err := io.ReadAll(body)
|
||||
if err != nil {
|
||||
return SRError{
|
||||
Cause: err,
|
||||
Code: ErrSiteReplicationInvalidRequest,
|
||||
}
|
||||
}
|
||||
if encryptionKey != "" {
|
||||
data, err = madmin.DecryptData(encryptionKey, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return SRError{
|
||||
Cause: err,
|
||||
Code: ErrSiteReplicationInvalidRequest,
|
||||
}
|
||||
}
|
||||
}
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
// SiteReplicationStatus - GET /minio/admin/v3/site-replication/status
|
||||
func (a adminAPIHandlers) SiteReplicationStatus(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationInfoAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
opts := getSRStatusOptions(r)
|
||||
// default options to all if status options are unset for backward compatibility
|
||||
var dfltOpts madmin.SRStatusOptions
|
||||
if opts == dfltOpts {
|
||||
opts.Buckets = true
|
||||
opts.Users = true
|
||||
opts.Policies = true
|
||||
opts.Groups = true
|
||||
opts.ILMExpiryRules = true
|
||||
}
|
||||
info, err := globalSiteReplicationSys.SiteReplicationStatus(ctx, objectAPI, opts)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
// Report the ILMExpiryStats only if at least one site has replication of ILM expiry enabled
|
||||
var replicateILMExpiry bool
|
||||
for _, site := range info.Sites {
|
||||
if site.ReplicateILMExpiry {
|
||||
replicateILMExpiry = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !replicateILMExpiry {
|
||||
// explicitly send nil for ILMExpiryStats
|
||||
info.ILMExpiryStats = nil
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(w).Encode(info); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SiteReplicationMetaInfo - GET /minio/admin/v3/site-replication/metainfo
|
||||
func (a adminAPIHandlers) SiteReplicationMetaInfo(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationInfoAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
opts := getSRStatusOptions(r)
|
||||
info, err := globalSiteReplicationSys.SiteReplicationMetaInfo(ctx, objectAPI, opts)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(w).Encode(info); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SiteReplicationEdit - PUT /minio/admin/v3/site-replication/edit
|
||||
func (a adminAPIHandlers) SiteReplicationEdit(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, cred := validateAdminReq(ctx, w, r, policy.SiteReplicationAddAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
var site madmin.PeerInfo
|
||||
err := parseJSONBody(ctx, r.Body, &site, cred.SecretKey)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
opts := getSREditOptions(r)
|
||||
status, err := globalSiteReplicationSys.EditPeerCluster(ctx, site, opts)
|
||||
if err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
body, err := json.Marshal(status)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseJSON(w, body)
|
||||
}
|
||||
|
||||
func getSREditOptions(r *http.Request) (opts madmin.SREditOptions) {
|
||||
opts.DisableILMExpiryReplication = r.Form.Get("disableILMExpiryReplication") == "true"
|
||||
opts.EnableILMExpiryReplication = r.Form.Get("enableILMExpiryReplication") == "true"
|
||||
return
|
||||
}
|
||||
|
||||
// SRPeerEdit - PUT /minio/admin/v3/site-replication/peer/edit
|
||||
//
|
||||
// used internally to tell current cluster to update endpoint for peer
|
||||
func (a adminAPIHandlers) SRPeerEdit(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationAddAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var pi madmin.PeerInfo
|
||||
if err := parseJSONBody(ctx, r.Body, &pi, ""); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err := globalSiteReplicationSys.PeerEditReq(ctx, pi); err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SRStateEdit - PUT /minio/admin/v3/site-replication/state/edit
|
||||
//
|
||||
// used internally to tell current cluster to update site replication state
|
||||
func (a adminAPIHandlers) SRStateEdit(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationOperationAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var state madmin.SRStateEditReq
|
||||
if err := parseJSONBody(ctx, r.Body, &state, ""); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
if err := globalSiteReplicationSys.PeerStateEditReq(ctx, state); err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getSRStatusOptions(r *http.Request) (opts madmin.SRStatusOptions) {
|
||||
q := r.Form
|
||||
opts.Buckets = q.Get("buckets") == "true"
|
||||
opts.Policies = q.Get("policies") == "true"
|
||||
opts.Groups = q.Get("groups") == "true"
|
||||
opts.Users = q.Get("users") == "true"
|
||||
opts.ILMExpiryRules = q.Get("ilm-expiry-rules") == "true"
|
||||
opts.PeerState = q.Get("peer-state") == "true"
|
||||
opts.Entity = madmin.GetSREntityType(q.Get("entity"))
|
||||
opts.EntityValue = q.Get("entityvalue")
|
||||
opts.ShowDeleted = q.Get("showDeleted") == "true"
|
||||
opts.Metrics = q.Get("metrics") == "true"
|
||||
return
|
||||
}
|
||||
|
||||
// SiteReplicationRemove - PUT /minio/admin/v3/site-replication/remove
|
||||
func (a adminAPIHandlers) SiteReplicationRemove(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationRemoveAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
var rreq madmin.SRRemoveReq
|
||||
err := parseJSONBody(ctx, r.Body, &rreq, "")
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
status, err := globalSiteReplicationSys.RemovePeerCluster(ctx, objectAPI, rreq)
|
||||
if err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := json.Marshal(status)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
writeSuccessResponseJSON(w, body)
|
||||
}
|
||||
|
||||
// SRPeerRemove - PUT /minio/admin/v3/site-replication/peer/remove
|
||||
//
|
||||
// used internally to tell current cluster to update endpoint for peer
|
||||
func (a adminAPIHandlers) SRPeerRemove(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationRemoveAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var req madmin.SRRemoveReq
|
||||
if err := parseJSONBody(ctx, r.Body, &req, ""); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err := globalSiteReplicationSys.InternalRemoveReq(ctx, objectAPI, req); err != nil {
|
||||
adminLogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SiteReplicationResyncOp - PUT /minio/admin/v3/site-replication/resync/op
|
||||
func (a adminAPIHandlers) SiteReplicationResyncOp(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.SiteReplicationResyncAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var peerSite madmin.PeerInfo
|
||||
if err := parseJSONBody(ctx, r.Body, &peerSite, ""); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
op := madmin.SiteResyncOp(vars["operation"])
|
||||
var (
|
||||
status madmin.SRResyncOpStatus
|
||||
err error
|
||||
)
|
||||
switch op {
|
||||
case madmin.SiteResyncStart:
|
||||
status, err = globalSiteReplicationSys.startResync(ctx, objectAPI, peerSite)
|
||||
case madmin.SiteResyncCancel:
|
||||
status, err = globalSiteReplicationSys.cancelResync(ctx, objectAPI, peerSite)
|
||||
default:
|
||||
err = errSRInvalidRequest(errInvalidArgument)
|
||||
}
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
body, err := json.Marshal(status)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
writeSuccessResponseJSON(w, body)
|
||||
}
|
||||
|
||||
// SiteReplicationDevNull - everything goes to io.Discard
|
||||
// [POST] /minio/admin/v3/site-replication/devnull
|
||||
func (a adminAPIHandlers) SiteReplicationDevNull(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
globalSiteNetPerfRX.Connect()
|
||||
defer globalSiteNetPerfRX.Disconnect()
|
||||
|
||||
connectTime := time.Now()
|
||||
for {
|
||||
n, err := io.CopyN(xioutil.Discard, r.Body, 128*humanize.KiByte)
|
||||
atomic.AddUint64(&globalSiteNetPerfRX.RX, uint64(n))
|
||||
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||
// If there is a disconnection before globalNetPerfMinDuration (we give a margin of error of 1 sec)
|
||||
// would mean the network is not stable. Logging here will help in debugging network issues.
|
||||
if time.Since(connectTime) < (globalNetPerfMinDuration - time.Second) {
|
||||
adminLogIf(ctx, err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SiteReplicationNetPerf - everything goes to io.Discard
|
||||
// [POST] /minio/admin/v3/site-replication/netperf
|
||||
func (a adminAPIHandlers) SiteReplicationNetPerf(w http.ResponseWriter, r *http.Request) {
|
||||
durationStr := r.Form.Get(peerRESTDuration)
|
||||
duration, _ := time.ParseDuration(durationStr)
|
||||
if duration < globalNetPerfMinDuration {
|
||||
duration = globalNetPerfMinDuration
|
||||
}
|
||||
result := siteNetperf(r.Context(), duration)
|
||||
adminLogIf(r.Context(), gob.NewEncoder(w).Encode(result))
|
||||
}
|
||||
152
cmd/admin-handlers-users-race_test.go
Normal file
152
cmd/admin-handlers-users-race_test.go
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
// Tests in this file are not run under the `-race` flag as they are too slow
|
||||
// and cause context deadline errors.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
minio "github.com/minio/minio-go/v7"
|
||||
"github.com/minio/pkg/v3/sync/errgroup"
|
||||
)
|
||||
|
||||
func runAllIAMConcurrencyTests(suite *TestSuiteIAM, c *check) {
|
||||
suite.SetUpSuite(c)
|
||||
suite.TestDeleteUserRace(c)
|
||||
suite.TearDownSuite(c)
|
||||
}
|
||||
|
||||
func TestIAMInternalIDPConcurrencyServerSuite(t *testing.T) {
|
||||
if runtime.GOOS == globalWindowsOSName {
|
||||
t.Skip("windows is clunky")
|
||||
}
|
||||
|
||||
baseTestCases := []TestSuiteCommon{
|
||||
// Init and run test on ErasureSD backend with signature v4.
|
||||
{serverType: "ErasureSD", signer: signerV4},
|
||||
// Init and run test on ErasureSD backend, with tls enabled.
|
||||
{serverType: "ErasureSD", signer: signerV4, secure: true},
|
||||
// Init and run test on Erasure backend.
|
||||
{serverType: "Erasure", signer: signerV4},
|
||||
// Init and run test on ErasureSet backend.
|
||||
{serverType: "ErasureSet", signer: signerV4},
|
||||
}
|
||||
testCases := []*TestSuiteIAM{}
|
||||
for _, bt := range baseTestCases {
|
||||
testCases = append(testCases,
|
||||
newTestSuiteIAM(bt, false),
|
||||
newTestSuiteIAM(bt, true),
|
||||
)
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
etcdStr := ""
|
||||
if testCase.withEtcdBackend {
|
||||
etcdStr = " (with etcd backend)"
|
||||
}
|
||||
t.Run(
|
||||
fmt.Sprintf("Test: %d, ServerType: %s%s", i+1, testCase.serverType, etcdStr),
|
||||
func(t *testing.T) {
|
||||
runAllIAMConcurrencyTests(testCase, &check{t, testCase.serverType})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TestSuiteIAM) TestDeleteUserRace(c *check) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
|
||||
defer cancel()
|
||||
|
||||
bucket := getRandomBucketName()
|
||||
err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{})
|
||||
if err != nil {
|
||||
c.Fatalf("bucket creat error: %v", err)
|
||||
}
|
||||
|
||||
// Create a policy policy
|
||||
policy := "mypolicy"
|
||||
policyBytes := []byte(fmt.Sprintf(`{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:PutObject",
|
||||
"s3:GetObject",
|
||||
"s3:ListBucket"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::%s/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`, bucket))
|
||||
err = s.adm.AddCannedPolicy(ctx, policy, policyBytes)
|
||||
if err != nil {
|
||||
c.Fatalf("policy add error: %v", err)
|
||||
}
|
||||
|
||||
userCount := 50
|
||||
accessKeys := make([]string, userCount)
|
||||
secretKeys := make([]string, userCount)
|
||||
for i := 0; i < userCount; i++ {
|
||||
accessKey, secretKey := mustGenerateCredentials(c)
|
||||
err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
|
||||
if err != nil {
|
||||
c.Fatalf("Unable to set user: %v", err)
|
||||
}
|
||||
|
||||
userReq := madmin.PolicyAssociationReq{
|
||||
Policies: []string{policy},
|
||||
User: accessKey,
|
||||
}
|
||||
if _, err := s.adm.AttachPolicy(ctx, userReq); err != nil {
|
||||
c.Fatalf("Unable to attach policy: %v", err)
|
||||
}
|
||||
|
||||
accessKeys[i] = accessKey
|
||||
secretKeys[i] = secretKey
|
||||
}
|
||||
|
||||
g := errgroup.Group{}
|
||||
for i := 0; i < userCount; i++ {
|
||||
g.Go(func(i int) func() error {
|
||||
return func() error {
|
||||
uClient := s.getUserClient(c, accessKeys[i], secretKeys[i], "")
|
||||
err := s.adm.RemoveUser(ctx, accessKeys[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.mustNotListObjects(ctx, uClient, bucket)
|
||||
return nil
|
||||
}
|
||||
}(i), i)
|
||||
}
|
||||
if errs := g.Wait(); len(errs) > 0 {
|
||||
c.Fatalf("unable to remove users: %v", errs)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
1682
cmd/admin-handlers-users_test.go
Normal file
1682
cmd/admin-handlers-users_test.go
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -21,17 +21,19 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/mux"
|
||||
)
|
||||
|
||||
// adminErasureTestBed - encapsulates subsystems that need to be setup for
|
||||
@@ -40,11 +42,13 @@ type adminErasureTestBed struct {
|
||||
erasureDirs []string
|
||||
objLayer ObjectLayer
|
||||
router *mux.Router
|
||||
done context.CancelFunc
|
||||
}
|
||||
|
||||
// prepareAdminErasureTestBed - helper function that setups a single-node
|
||||
// Erasure backend for admin-handler tests.
|
||||
func prepareAdminErasureTestBed(ctx context.Context) (*adminErasureTestBed, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
// reset global variables to start afresh.
|
||||
resetTestGlobals()
|
||||
@@ -56,24 +60,26 @@ func prepareAdminErasureTestBed(ctx context.Context) (*adminErasureTestBed, erro
|
||||
// Initializing objectLayer for HealFormatHandler.
|
||||
objLayer, erasureDirs, xlErr := initTestErasureObjLayer(ctx)
|
||||
if xlErr != nil {
|
||||
cancel()
|
||||
return nil, xlErr
|
||||
}
|
||||
|
||||
// Initialize minio server config.
|
||||
if err := newTestConfig(globalMinioDefaultRegion, objLayer); err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize boot time
|
||||
globalBootTime = UTCNow()
|
||||
|
||||
globalEndpoints = mustGetPoolEndpoints(erasureDirs...)
|
||||
globalEndpoints = mustGetPoolEndpoints(0, erasureDirs...)
|
||||
|
||||
newAllSubsystems()
|
||||
initAllSubsystems(ctx)
|
||||
|
||||
initAllSubsystems(ctx, objLayer)
|
||||
initConfigSubsystem(ctx, objLayer)
|
||||
|
||||
globalIAMSys.InitStore(objLayer)
|
||||
globalIAMSys.Init(ctx, objLayer, globalEtcdClient, 2*time.Second)
|
||||
|
||||
// Setup admin mgmt REST API handlers.
|
||||
adminRouter := mux.NewRouter()
|
||||
@@ -83,12 +89,14 @@ func prepareAdminErasureTestBed(ctx context.Context) (*adminErasureTestBed, erro
|
||||
erasureDirs: erasureDirs,
|
||||
objLayer: objLayer,
|
||||
router: adminRouter,
|
||||
done: cancel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TearDown - method that resets the test bed for subsequent unit
|
||||
// tests to start afresh.
|
||||
func (atb *adminErasureTestBed) TearDown() {
|
||||
atb.done()
|
||||
removeRoots(atb.erasureDirs)
|
||||
resetTestGlobals()
|
||||
}
|
||||
@@ -100,7 +108,7 @@ func initTestErasureObjLayer(ctx context.Context) (ObjectLayer, []string, error)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
endpoints := mustGetPoolEndpoints(erasureDirs...)
|
||||
endpoints := mustGetPoolEndpoints(0, erasureDirs...)
|
||||
globalPolicySys = NewPolicySys()
|
||||
objLayer, err := newErasureServerPools(ctx, endpoints)
|
||||
if err != nil {
|
||||
@@ -160,6 +168,7 @@ func testServiceSignalReceiver(cmd cmdType, t *testing.T) {
|
||||
func getServiceCmdRequest(cmd cmdType, cred auth.Credentials) (*http.Request, error) {
|
||||
queryVal := url.Values{}
|
||||
queryVal.Set("action", string(cmd.toServiceAction()))
|
||||
queryVal.Set("type", "2")
|
||||
resource := adminPathPrefix + adminAPIVersionPrefix + "/service?" + queryVal.Encode()
|
||||
req, err := newTestRequest(http.MethodPost, resource, 0, nil)
|
||||
if err != nil {
|
||||
@@ -212,12 +221,18 @@ func testServicesCmdHandler(cmd cmdType, t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
adminTestBed.router.ServeHTTP(rec, req)
|
||||
|
||||
resp, _ := io.ReadAll(rec.Body)
|
||||
if rec.Code != http.StatusOK {
|
||||
resp, _ := ioutil.ReadAll(rec.Body)
|
||||
t.Errorf("Expected to receive %d status code but received %d. Body (%s)",
|
||||
http.StatusOK, rec.Code, string(resp))
|
||||
}
|
||||
|
||||
result := &serviceResult{}
|
||||
if err := json.Unmarshal(resp, result); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_ = result
|
||||
|
||||
// Wait until testServiceSignalReceiver() called in a goroutine quits.
|
||||
wg.Wait()
|
||||
}
|
||||
@@ -229,8 +244,8 @@ func TestServiceRestartHandler(t *testing.T) {
|
||||
|
||||
// buildAdminRequest - helper function to build an admin API request.
|
||||
func buildAdminRequest(queryVal url.Values, method, path string,
|
||||
contentLength int64, bodySeeker io.ReadSeeker) (*http.Request, error) {
|
||||
|
||||
contentLength int64, bodySeeker io.ReadSeeker) (*http.Request, error,
|
||||
) {
|
||||
req, err := newTestRequest(method,
|
||||
adminPathPrefix+adminAPIVersionPrefix+path+"?"+queryVal.Encode(),
|
||||
contentLength, bodySeeker)
|
||||
@@ -333,7 +348,7 @@ func TestExtractHealInitParams(t *testing.T) {
|
||||
}
|
||||
return v
|
||||
}
|
||||
qParmsArr := []url.Values{
|
||||
qParamsArr := []url.Values{
|
||||
// Invalid cases
|
||||
mkParams("", true, true),
|
||||
mkParams("111", true, true),
|
||||
@@ -358,9 +373,9 @@ func TestExtractHealInitParams(t *testing.T) {
|
||||
body := `{"recursive": false, "dryRun": true, "remove": false, "scanMode": 0}`
|
||||
|
||||
// Test all combinations!
|
||||
for pIdx, parms := range qParmsArr {
|
||||
for pIdx, params := range qParamsArr {
|
||||
for vIdx, vars := range varsArr {
|
||||
_, err := extractHealInitParams(vars, parms, bytes.NewReader([]byte(body)))
|
||||
_, err := extractHealInitParams(vars, params, bytes.NewReader([]byte(body)))
|
||||
isErrCase := false
|
||||
if pIdx < 4 || vIdx < 1 {
|
||||
isErrCase = true
|
||||
@@ -373,5 +388,148 @@ func TestExtractHealInitParams(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type byResourceUID struct{ madmin.LockEntries }
|
||||
|
||||
func (b byResourceUID) Less(i, j int) bool {
|
||||
toUniqLock := func(entry madmin.LockEntry) string {
|
||||
return fmt.Sprintf("%s/%s", entry.Resource, entry.ID)
|
||||
}
|
||||
return toUniqLock(b.LockEntries[i]) < toUniqLock(b.LockEntries[j])
|
||||
}
|
||||
|
||||
func TestTopLockEntries(t *testing.T) {
|
||||
locksHeld := make(map[string][]lockRequesterInfo)
|
||||
var owners []string
|
||||
for i := 0; i < 4; i++ {
|
||||
owners = append(owners, fmt.Sprintf("node-%d", i))
|
||||
}
|
||||
|
||||
// Simulate DeleteObjects of 10 objects in a single request. i.e same lock
|
||||
// request UID, but 10 different resource names associated with it.
|
||||
var lris []lockRequesterInfo
|
||||
uuid := mustGetUUID()
|
||||
for i := 0; i < 10; i++ {
|
||||
resource := fmt.Sprintf("bucket/delete-object-%d", i)
|
||||
lri := lockRequesterInfo{
|
||||
Name: resource,
|
||||
Writer: true,
|
||||
UID: uuid,
|
||||
Owner: owners[i%len(owners)],
|
||||
Group: true,
|
||||
Quorum: 3,
|
||||
}
|
||||
lris = append(lris, lri)
|
||||
locksHeld[resource] = []lockRequesterInfo{lri}
|
||||
}
|
||||
|
||||
// Add a few concurrent read locks to the mix
|
||||
for i := 0; i < 50; i++ {
|
||||
resource := fmt.Sprintf("bucket/get-object-%d", i)
|
||||
lri := lockRequesterInfo{
|
||||
Name: resource,
|
||||
UID: mustGetUUID(),
|
||||
Owner: owners[i%len(owners)],
|
||||
Quorum: 2,
|
||||
}
|
||||
lris = append(lris, lri)
|
||||
locksHeld[resource] = append(locksHeld[resource], lri)
|
||||
// concurrent read lock, same resource different uid
|
||||
lri.UID = mustGetUUID()
|
||||
lris = append(lris, lri)
|
||||
locksHeld[resource] = append(locksHeld[resource], lri)
|
||||
}
|
||||
|
||||
var peerLocks []*PeerLocks
|
||||
for _, owner := range owners {
|
||||
peerLocks = append(peerLocks, &PeerLocks{
|
||||
Addr: owner,
|
||||
Locks: locksHeld,
|
||||
})
|
||||
}
|
||||
var exp madmin.LockEntries
|
||||
for _, lri := range lris {
|
||||
lockType := func(lri lockRequesterInfo) string {
|
||||
if lri.Writer {
|
||||
return "WRITE"
|
||||
}
|
||||
return "READ"
|
||||
}
|
||||
exp = append(exp, madmin.LockEntry{
|
||||
Resource: lri.Name,
|
||||
Type: lockType(lri),
|
||||
ServerList: owners,
|
||||
Owner: lri.Owner,
|
||||
ID: lri.UID,
|
||||
Quorum: lri.Quorum,
|
||||
Timestamp: time.Unix(0, lri.Timestamp),
|
||||
})
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
peerLocks []*PeerLocks
|
||||
expected madmin.LockEntries
|
||||
}{
|
||||
{
|
||||
peerLocks: peerLocks,
|
||||
expected: exp,
|
||||
},
|
||||
}
|
||||
|
||||
// printEntries := func(entries madmin.LockEntries) {
|
||||
// for i, entry := range entries {
|
||||
// fmt.Printf("%d: %s %s %s %s %v %d\n", i, entry.Resource, entry.ID, entry.Owner, entry.Type, entry.ServerList, entry.Elapsed)
|
||||
// }
|
||||
// }
|
||||
|
||||
check := func(exp, got madmin.LockEntries) (int, bool) {
|
||||
if len(exp) != len(got) {
|
||||
return 0, false
|
||||
}
|
||||
sort.Slice(exp, byResourceUID{exp}.Less)
|
||||
sort.Slice(got, byResourceUID{got}.Less)
|
||||
// printEntries(exp)
|
||||
// printEntries(got)
|
||||
for i, e := range exp {
|
||||
if !e.Timestamp.Equal(got[i].Timestamp) {
|
||||
return i, false
|
||||
}
|
||||
// Skip checking elapsed since it's time sensitive.
|
||||
// if e.Elapsed != got[i].Elapsed {
|
||||
// return false
|
||||
// }
|
||||
if e.Resource != got[i].Resource {
|
||||
return i, false
|
||||
}
|
||||
if e.Type != got[i].Type {
|
||||
return i, false
|
||||
}
|
||||
if e.Source != got[i].Source {
|
||||
return i, false
|
||||
}
|
||||
if e.Owner != got[i].Owner {
|
||||
return i, false
|
||||
}
|
||||
if e.ID != got[i].ID {
|
||||
return i, false
|
||||
}
|
||||
if len(e.ServerList) != len(got[i].ServerList) {
|
||||
return i, false
|
||||
}
|
||||
for j := range e.ServerList {
|
||||
if e.ServerList[j] != got[i].ServerList[j] {
|
||||
return i, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, true
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
got := topLockEntries(tc.peerLocks, false)
|
||||
if idx, ok := check(tc.expected, got); !ok {
|
||||
t.Fatalf("%d: mismatch at %d \n expected %#v but got %#v", i, idx, tc.expected[idx], got[idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user