mirror of
https://github.com/rustfs/rustfs.git
synced 2026-01-17 09:40:32 +00:00
Compare commits
1562 Commits
dev_object
...
1.0.0-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
874d486b1e | ||
|
|
21516251b0 | ||
|
|
a2f83b0d2d | ||
|
|
aa65766312 | ||
|
|
660f004cfd | ||
|
|
6d2c420f54 | ||
|
|
5f0b9a5fa8 | ||
|
|
8378e308e0 | ||
|
|
b9f54519fd | ||
|
|
4108a9649f | ||
|
|
6244e23451 | ||
|
|
713b322f99 | ||
|
|
e1a5a195c3 | ||
|
|
bc37417d6c | ||
|
|
3dbcaaa221 | ||
|
|
49f480d346 | ||
|
|
055a99ba25 | ||
|
|
2bd11d476e | ||
|
|
297004c259 | ||
|
|
4e2c4d8dba | ||
|
|
0626099c3b | ||
|
|
107ddcf394 | ||
|
|
8893ffc10f | ||
|
|
f23e855d23 | ||
|
|
8366413970 | ||
|
|
9862677fcf | ||
|
|
e50bc4c60c | ||
|
|
5f6104731d | ||
|
|
6a6866c337 | ||
|
|
ce2ce4b16e | ||
|
|
1ecd5a87d9 | ||
|
|
72aead5466 | ||
|
|
abd5dff9b5 | ||
|
|
040b05c318 | ||
|
|
ce470c95c4 | ||
|
|
32e531bc61 | ||
|
|
dcf25e46af | ||
|
|
2b079ae065 | ||
|
|
d41ccc1551 | ||
|
|
fa17f7b1e3 | ||
|
|
c41299a29f | ||
|
|
79156d2d82 | ||
|
|
26542b741e | ||
|
|
8b2b4a0146 | ||
|
|
5cf9087113 | ||
|
|
dd12250987 | ||
|
|
e172b277f2 | ||
|
|
086331b8e7 | ||
|
|
96d22c3276 | ||
|
|
caa3564439 | ||
|
|
18933fdb58 | ||
|
|
65a731a243 | ||
|
|
89035d3b3b | ||
|
|
c6527643a3 | ||
|
|
b9157d5e9d | ||
|
|
20be2d9859 | ||
|
|
855541678e | ||
|
|
73d3d8ab5c | ||
|
|
6983a3ffce | ||
|
|
d6653f1258 | ||
|
|
7ab53a6d7d | ||
|
|
85ee9811d8 | ||
|
|
61bd76f77e | ||
|
|
8cf611426b | ||
|
|
b0ac977a3d | ||
|
|
6a5fbe7ef1 | ||
|
|
e20e84fedb | ||
|
|
5826396cd0 | ||
|
|
0be4264eb1 | ||
|
|
70ae0cd1a8 | ||
|
|
bf690ad353 | ||
|
|
2e14b32ccd | ||
|
|
557e85c0c1 | ||
|
|
8d30e68284 | ||
|
|
830d2b8df5 | ||
|
|
cb19bbef22 | ||
|
|
291de5bb96 | ||
|
|
86f9e6c49f | ||
|
|
97e29a27a0 | ||
|
|
d3575dccce | ||
|
|
aeffacb53e | ||
|
|
b9ff63d064 | ||
|
|
68bc59c6eb | ||
|
|
cc63e56b8a | ||
|
|
7c79d4c1ca | ||
|
|
a61e9dd27d | ||
|
|
3c9ed62bbb | ||
|
|
cf7a930d39 | ||
|
|
e641036cda | ||
|
|
d736c6224d | ||
|
|
c8868a47d7 | ||
|
|
9d74f56f57 | ||
|
|
cc440f23a3 | ||
|
|
ddf19b3444 | ||
|
|
18a3902d2c | ||
|
|
024f9512be | ||
|
|
f62db65a39 | ||
|
|
4fa389c047 | ||
|
|
f069790a72 | ||
|
|
2b1241de96 | ||
|
|
fff2bc6523 | ||
|
|
a2828a8015 | ||
|
|
3fd70de4a6 | ||
|
|
37861d9af6 | ||
|
|
38220697e0 | ||
|
|
0ea5351a1b | ||
|
|
68090d1c35 | ||
|
|
57c8c76e58 | ||
|
|
c5358ba62c | ||
|
|
c63c6187c5 | ||
|
|
880d22a4bb | ||
|
|
2fe0c8cd32 | ||
|
|
e9610f1fb7 | ||
|
|
8218070ebc | ||
|
|
e4971b6d83 | ||
|
|
24738dc635 | ||
|
|
0606d3fc29 | ||
|
|
bfeb746c5f | ||
|
|
7b76fc0efe | ||
|
|
dab493009e | ||
|
|
8e75ea07e7 | ||
|
|
b21f701a81 | ||
|
|
62e68d4e51 | ||
|
|
a96f352fa7 | ||
|
|
a231f05d4f | ||
|
|
6ea075bd46 | ||
|
|
6a24ad1ecd | ||
|
|
9a5a5f0cde | ||
|
|
111f994a9d | ||
|
|
cc1b0eff1a | ||
|
|
20054d0a56 | ||
|
|
54c45739f1 | ||
|
|
3fbbde4897 | ||
|
|
b69a4ed691 | ||
|
|
f46ffd8885 | ||
|
|
c7095d913f | ||
|
|
f30345aaa8 | ||
|
|
4d86866d61 | ||
|
|
a770b17e0c | ||
|
|
f38108d20d | ||
|
|
4ed84a6bc4 | ||
|
|
e4690c48b4 | ||
|
|
c358e6203f | ||
|
|
7b891750e8 | ||
|
|
1efcce0001 | ||
|
|
fafba030c2 | ||
|
|
7bd8431006 | ||
|
|
8a4cb0319a | ||
|
|
749537664f | ||
|
|
35489ea352 | ||
|
|
bd5d27ffbd | ||
|
|
b2610df635 | ||
|
|
f4014a56b6 | ||
|
|
33e5d5a14c | ||
|
|
dbe27dc9cd | ||
|
|
ca30b78a1b | ||
|
|
ce7cfedfcb | ||
|
|
2698d3aa10 | ||
|
|
26f84a9696 | ||
|
|
831cb0b6d9 | ||
|
|
61e600b9ba | ||
|
|
280d14997a | ||
|
|
7a7ca6e154 | ||
|
|
37b4ab935d | ||
|
|
39a290e061 | ||
|
|
a6cfb04453 | ||
|
|
a99e8543db | ||
|
|
d72da751c8 | ||
|
|
3579bd1bb5 | ||
|
|
746b29a641 | ||
|
|
ca18177e59 | ||
|
|
65fb1b5f8d | ||
|
|
7e17d7d729 | ||
|
|
364c3098b2 | ||
|
|
e8a5b5d12c | ||
|
|
3d5bf5a6d6 | ||
|
|
46c50b1bb1 | ||
|
|
833f9c908f | ||
|
|
caf8d80318 | ||
|
|
a0f8b2f2be | ||
|
|
317cadaed8 | ||
|
|
8690e4a3ef | ||
|
|
005881f838 | ||
|
|
81f0c9763f | ||
|
|
811d1a598b | ||
|
|
17076a56b3 | ||
|
|
34ec13e532 | ||
|
|
dc93398768 | ||
|
|
4654ba0937 | ||
|
|
de3773fbbc | ||
|
|
3817d2d682 | ||
|
|
7b2e5aa0ae | ||
|
|
270047f47a | ||
|
|
744bcfc4a9 | ||
|
|
7bb7f9e309 | ||
|
|
2c176dd864 | ||
|
|
0178ec5026 | ||
|
|
5a5712fde0 | ||
|
|
080af2b39f | ||
|
|
4559baaeeb | ||
|
|
1722780560 | ||
|
|
5155a3d544 | ||
|
|
928453db62 | ||
|
|
cc71f40a6d | ||
|
|
c7af6587f5 | ||
|
|
e0f65e5e24 | ||
|
|
48d530cb13 | ||
|
|
d3cc36f6e0 | ||
|
|
3721c93a55 | ||
|
|
e145586b65 | ||
|
|
c658d88d25 | ||
|
|
e6b019c29d | ||
|
|
09e8bc8f02 | ||
|
|
450db14305 | ||
|
|
147bfe3de5 | ||
|
|
a48d800426 | ||
|
|
e7135b8f4d | ||
|
|
3a68060e58 | ||
|
|
58faf141bd | ||
|
|
039108ee5e | ||
|
|
5b1b527851 | ||
|
|
85421657ad | ||
|
|
d019f3d5bd | ||
|
|
bbd13a5aac | ||
|
|
a28d9c814f | ||
|
|
81a790f13f | ||
|
|
0f7a98a91f | ||
|
|
e81a57ce24 | ||
|
|
efae4f5203 | ||
|
|
65ff5faada | ||
|
|
04cfe837a3 | ||
|
|
4a786618d4 | ||
|
|
4cadc4c12d | ||
|
|
086eab8c70 | ||
|
|
da4a4e7cbe | ||
|
|
39e988537c | ||
|
|
e520299c4b | ||
|
|
fa8ac29e76 | ||
|
|
c48ebd5149 | ||
|
|
e254ddc947 | ||
|
|
ca298b460c | ||
|
|
52342f2f8e | ||
|
|
2f3f86a9f2 | ||
|
|
ac4f1400fc | ||
|
|
d29bf4809d | ||
|
|
1606276223 | ||
|
|
57cda74fbd | ||
|
|
82cc1402c4 | ||
|
|
2f3dbac59b | ||
|
|
3c5e20b633 | ||
|
|
8309d2f8be | ||
|
|
3a567768c1 | ||
|
|
87423bfb8c | ||
|
|
af948f9e88 | ||
|
|
048727f183 | ||
|
|
b14cd7508b | ||
|
|
47d72baf88 | ||
|
|
09095f2abd | ||
|
|
bb282bcd5d | ||
|
|
2c5b01eb6f | ||
|
|
c413465645 | ||
|
|
e82a69c9ca | ||
|
|
8452c11e9a | ||
|
|
5cc56784a7 | ||
|
|
0ca03465e3 | ||
|
|
d529b9abbd | ||
|
|
8d636dba7f | ||
|
|
3135c8017d | ||
|
|
2cdd6ad192 | ||
|
|
bdb7e8d321 | ||
|
|
e8a59d7c07 | ||
|
|
2eeb9dbcbc | ||
|
|
7c9046c2cd | ||
|
|
cc0caa751c | ||
|
|
4fa8cb7ef0 | ||
|
|
b4210b1c3f | ||
|
|
e40562b03d | ||
|
|
98ce64aaae | ||
|
|
7ca9f44fca | ||
|
|
682ad8742d | ||
|
|
4252377249 | ||
|
|
6afb26b377 | ||
|
|
e6b931f71e | ||
|
|
7b890108ee | ||
|
|
8a81c3d95d | ||
|
|
40d99a5377 | ||
|
|
7527162bec | ||
|
|
3338f3236f | ||
|
|
0e575ef2be | ||
|
|
5c610e26c9 | ||
|
|
754ffd0ff2 | ||
|
|
3cf265611a | ||
|
|
9339093638 | ||
|
|
3321439951 | ||
|
|
6ea0185519 | ||
|
|
e62947f7b2 | ||
|
|
4bbf1c33b8 | ||
|
|
711cab777f | ||
|
|
f85ef06783 | ||
|
|
cecde068e1 | ||
|
|
28c71cc351 | ||
|
|
a64bd436c8 | ||
|
|
91c099e35f | ||
|
|
152ad57c1d | ||
|
|
3edfdbea76 | ||
|
|
2e4218706b | ||
|
|
11712414bb | ||
|
|
f798bb0fce | ||
|
|
1d2aeb288a | ||
|
|
27ab2350c9 | ||
|
|
96de65ebab | ||
|
|
ec5be0a2c3 | ||
|
|
9495df6d5e | ||
|
|
690ce7fae4 | ||
|
|
bf48d88a47 | ||
|
|
158462ca0b | ||
|
|
d66525a22f | ||
|
|
62a7824d0d | ||
|
|
1432ddb119 | ||
|
|
b570b6aa36 | ||
|
|
17aaf2cbc2 | ||
|
|
3076ba3253 | ||
|
|
df9347b34a | ||
|
|
b51ee48699 | ||
|
|
32d1db184a | ||
|
|
15a3012d05 | ||
|
|
7032318858 | ||
|
|
3862e255f0 | ||
|
|
f199718e3b | ||
|
|
b003d08d55 | ||
|
|
c589972fa7 | ||
|
|
2755d4125d | ||
|
|
77ddd1cfe0 | ||
|
|
1f62c35846 | ||
|
|
3eaa3d68c7 | ||
|
|
2fec6d161c | ||
|
|
3f8d56501c | ||
|
|
29fba447c3 | ||
|
|
6302764512 | ||
|
|
c03458202a | ||
|
|
a394a3163f | ||
|
|
6ea770b6c8 | ||
|
|
8c15835414 | ||
|
|
3dacde092c | ||
|
|
998f4578b1 | ||
|
|
a5bbce4920 | ||
|
|
db355bb26b | ||
|
|
4199bf2ba4 | ||
|
|
726ec9a8b5 | ||
|
|
9384b831ec | ||
|
|
3c7b66e039 | ||
|
|
8555d48ca2 | ||
|
|
6b0de4254f | ||
|
|
906cc5ffd7 | ||
|
|
7fe0cc74d2 | ||
|
|
703c465bd0 | ||
|
|
7f983e72e7 | ||
|
|
004b6945f2 | ||
|
|
541b812bb4 | ||
|
|
15bd320231 | ||
|
|
d3e2c9cb86 | ||
|
|
7cff7f0bd1 | ||
|
|
18096353c0 | ||
|
|
632e064ea5 | ||
|
|
4a9b842aa3 | ||
|
|
2edc2e243c | ||
|
|
545e5b2efb | ||
|
|
eefb2f8127 | ||
|
|
2f487be832 | ||
|
|
eec086c1ec | ||
|
|
644698e7ca | ||
|
|
2bad907cc6 | ||
|
|
3ceec7b5f2 | ||
|
|
ce672917cd | ||
|
|
1a921df86b | ||
|
|
ace04bcd97 | ||
|
|
a6a5aabf01 | ||
|
|
f9310f0b97 | ||
|
|
155cb546ac | ||
|
|
059a546604 | ||
|
|
581eb6641c | ||
|
|
1dddb4bb84 | ||
|
|
7dde4083db | ||
|
|
fe15655f1e | ||
|
|
d27ddcb8f7 | ||
|
|
8660c2af12 | ||
|
|
7154b8f871 | ||
|
|
f13e921c14 | ||
|
|
2a802acfe0 | ||
|
|
8747a91df8 | ||
|
|
4aaeff8e4c | ||
|
|
c74410858e | ||
|
|
f05a0b8e83 | ||
|
|
fb42ba1a14 | ||
|
|
2967deaed8 | ||
|
|
7069f9e7a2 | ||
|
|
cad2aa436b | ||
|
|
50e17d81fd | ||
|
|
de35928496 | ||
|
|
cf8acd38d0 | ||
|
|
b5c846132e | ||
|
|
b2e2c0fecd | ||
|
|
6350398c31 | ||
|
|
462e75b227 | ||
|
|
ecbd1e0bc3 | ||
|
|
181e08cb8e | ||
|
|
87a4ed2107 | ||
|
|
15befb705f | ||
|
|
af9bcde89f | ||
|
|
a1f4abf6c3 | ||
|
|
96d0155bcc | ||
|
|
84791e4877 | ||
|
|
8e48e4f490 | ||
|
|
a9b7b95606 | ||
|
|
c65d0caa1b | ||
|
|
f669838a2f | ||
|
|
0a7b0485b1 | ||
|
|
7b5f1d5835 | ||
|
|
0d9f13740d | ||
|
|
dbe573513e | ||
|
|
08a6cacdc2 | ||
|
|
adb3ea171a | ||
|
|
b5dcafefbb | ||
|
|
c29841a5e7 | ||
|
|
1620b0f0d8 | ||
|
|
ce16ad868b | ||
|
|
f03b945e4f | ||
|
|
1240b5320f | ||
|
|
63b0cb2c7a | ||
|
|
9d594cbda6 | ||
|
|
2b641b7ef3 | ||
|
|
a95138868e | ||
|
|
ade4d33eb1 | ||
|
|
ca8f399832 | ||
|
|
e4ac7d7a3e | ||
|
|
faf195cc4b | ||
|
|
a9c6bb8f0d | ||
|
|
50e52206cc | ||
|
|
732f59d10a | ||
|
|
25418e1372 | ||
|
|
864fcce07b | ||
|
|
319ff77b07 | ||
|
|
cea6ddbdf1 | ||
|
|
394fb36362 | ||
|
|
8c0d3fa227 | ||
|
|
7abcfe31e8 | ||
|
|
ea7a178ca7 | ||
|
|
61af17a4ec | ||
|
|
08a64bffac | ||
|
|
d858cd8d19 | ||
|
|
ebbf3a7bc3 | ||
|
|
54972a57b1 | ||
|
|
13317322de | ||
|
|
7adc1ba09d | ||
|
|
e4cc8ed5b9 | ||
|
|
2cadbba6ad | ||
|
|
c14bf4b479 | ||
|
|
dee19fdc35 | ||
|
|
864071d641 | ||
|
|
1e48f8d74e | ||
|
|
fb9f8319b2 | ||
|
|
7dfe5f92cc | ||
|
|
1f577b14b0 | ||
|
|
4d726273aa | ||
|
|
fc194f3a4a | ||
|
|
7eb722b444 | ||
|
|
9d90913697 | ||
|
|
d1a2f2939c | ||
|
|
1a9bfd1b86 | ||
|
|
7ebcfebecb | ||
|
|
0f1e9d0c63 | ||
|
|
17928cf9c8 | ||
|
|
cae578d6b5 | ||
|
|
42b38336ad | ||
|
|
ad71db6367 | ||
|
|
bc398ccf1b | ||
|
|
e838f4292c | ||
|
|
df71cea9af | ||
|
|
077fb4ed69 | ||
|
|
3eb0fd7ddd | ||
|
|
d7b3e20233 | ||
|
|
c970ed1587 | ||
|
|
d9d889cb4f | ||
|
|
9604efd5cc | ||
|
|
5fff195295 | ||
|
|
1a91371306 | ||
|
|
530c73916e | ||
|
|
c6b3051c67 | ||
|
|
a6c3b122bd | ||
|
|
0f22b21c8d | ||
|
|
1c088546b3 | ||
|
|
2d9394a8e0 | ||
|
|
72f5a24144 | ||
|
|
c6de1ae994 | ||
|
|
791780dd68 | ||
|
|
be8a615cd7 | ||
|
|
dff7476143 | ||
|
|
fd1bf30de8 | ||
|
|
704043ef02 | ||
|
|
6659b77f9a | ||
|
|
60635aeb65 | ||
|
|
5304b73588 | ||
|
|
44958797e5 | ||
|
|
136118ed21 | ||
|
|
9e9f721c08 | ||
|
|
41194b3f6b | ||
|
|
0477e8d3ed | ||
|
|
cdd285dec8 | ||
|
|
c69a2f83a6 | ||
|
|
76e38526c6 | ||
|
|
571cedf4ce | ||
|
|
dd7da015e3 | ||
|
|
0c351965a2 | ||
|
|
07c3cb3f0a | ||
|
|
33cd4c546a | ||
|
|
f6f1f3b329 | ||
|
|
780ff5f5df | ||
|
|
f8f58a8355 | ||
|
|
43f2963d14 | ||
|
|
a7115ba699 | ||
|
|
f64458018b | ||
|
|
b1358d4711 | ||
|
|
2a9a60197b | ||
|
|
db100a6db9 | ||
|
|
fd85b55d37 | ||
|
|
fd03ba54f3 | ||
|
|
e6e0821512 | ||
|
|
dc03f53655 | ||
|
|
57cbd35de1 | ||
|
|
d7cac5c727 | ||
|
|
e90b531567 | ||
|
|
5cb040f863 | ||
|
|
7a94363b38 | ||
|
|
76fdefeca4 | ||
|
|
29ddf4dbc8 | ||
|
|
de275b0510 | ||
|
|
0ac1095c70 | ||
|
|
fc47ca9dd2 | ||
|
|
01d5383ce3 | ||
|
|
08f7de2e33 | ||
|
|
05fad0aca0 | ||
|
|
b411a38813 | ||
|
|
a45411edbe | ||
|
|
8f917e4a19 | ||
|
|
7e1135df8f | ||
|
|
e5cd061e18 | ||
|
|
bd9509e030 | ||
|
|
1dde7015de | ||
|
|
e346d20228 | ||
|
|
79d58a98f4 | ||
|
|
6aecd72acc | ||
|
|
cdcf5d0917 | ||
|
|
5a363c5523 | ||
|
|
19cdf9660b | ||
|
|
66f5bf1bbc | ||
|
|
95ababe7a8 | ||
|
|
c1590a054c | ||
|
|
738a81e50e | ||
|
|
26d69cdc7f | ||
|
|
82c8b7f308 | ||
|
|
a6e3561f83 | ||
|
|
43df8b6927 | ||
|
|
42bec96db0 | ||
|
|
569099af9e | ||
|
|
750194e4dc | ||
|
|
68a89d59e5 | ||
|
|
c25c94ec30 | ||
|
|
e9d6e2ca95 | ||
|
|
e8f8a0872d | ||
|
|
87bec2e6a6 | ||
|
|
d71b0958db | ||
|
|
9590d99e7c | ||
|
|
82ddc2c76a | ||
|
|
05f1412323 | ||
|
|
ed4a9db9a0 | ||
|
|
8e3c22b595 | ||
|
|
9fc4bb919e | ||
|
|
7dae5f8ab7 | ||
|
|
7926ac015a | ||
|
|
86353d98d5 | ||
|
|
57b136d854 | ||
|
|
e650c117af | ||
|
|
a96907dda8 | ||
|
|
ec69f9376e | ||
|
|
ef597fff31 | ||
|
|
a095a607c0 | ||
|
|
4613b36697 | ||
|
|
e67c7d977f | ||
|
|
e38dd25bf6 | ||
|
|
35dac63c36 | ||
|
|
b2da8148bd | ||
|
|
56575d58f4 | ||
|
|
a745b91ded | ||
|
|
08d0021cd6 | ||
|
|
707c773dd9 | ||
|
|
92319f190c | ||
|
|
ef3f86ccf5 | ||
|
|
e58eaacb20 | ||
|
|
45a9b6313e | ||
|
|
380a451bc1 | ||
|
|
827818b493 | ||
|
|
cffd4fc28d | ||
|
|
cf2ed47fe8 | ||
|
|
ad7ff448d6 | ||
|
|
8981dfae9f | ||
|
|
fff7e5f827 | ||
|
|
873a04aed0 | ||
|
|
b2e1a7ac33 | ||
|
|
ea13098beb | ||
|
|
52a1f9b8a7 | ||
|
|
6d1a4ab824 | ||
|
|
c8ab89292e | ||
|
|
4f347a92c1 | ||
|
|
e4453adf82 | ||
|
|
15b6a426fb | ||
|
|
0ed92b3b6f | ||
|
|
18b50c1752 | ||
|
|
59c008b0ee | ||
|
|
27400394e4 | ||
|
|
536b2e2ed9 | ||
|
|
6c70c03985 | ||
|
|
5af648230a | ||
|
|
770f28d205 | ||
|
|
177dec627c | ||
|
|
70e94fd018 | ||
|
|
3b6397012b | ||
|
|
ad4dec1aa2 | ||
|
|
29fbfc3d6e | ||
|
|
cbb41c8fa3 | ||
|
|
b1d8e60802 | ||
|
|
bfc165abe0 | ||
|
|
21a829e7cf | ||
|
|
8ea6f7e627 | ||
|
|
cf70e12e14 | ||
|
|
9dfb8f8dcf | ||
|
|
f624ec5ba6 | ||
|
|
779a2d0a1a | ||
|
|
800ced24b7 | ||
|
|
bbd2bc2dde | ||
|
|
69598b48e3 | ||
|
|
fffa1ec671 | ||
|
|
7fc280c22b | ||
|
|
f1d84c7abe | ||
|
|
04874f5986 | ||
|
|
e5b8abcfcd | ||
|
|
85c8ea5ba6 | ||
|
|
38b7bbbc49 | ||
|
|
b9fb5e7e84 | ||
|
|
08fe5192fb | ||
|
|
cefdf4d6f9 | ||
|
|
c40099a636 | ||
|
|
3940ae2d69 | ||
|
|
c651bea903 | ||
|
|
d76d60a295 | ||
|
|
eb58ca0d8d | ||
|
|
26f128df02 | ||
|
|
b3ee5c8d4f | ||
|
|
e90bae35b9 | ||
|
|
37109fc618 | ||
|
|
33a0b9669c | ||
|
|
445e7df835 | ||
|
|
c3a17caa80 | ||
|
|
24d6c555f7 | ||
|
|
42c8890d4a | ||
|
|
ab8b19eb5d | ||
|
|
1b24fbdb00 | ||
|
|
9baede4bc4 | ||
|
|
9c6580c24b | ||
|
|
b88b95d885 | ||
|
|
f97c262a1b | ||
|
|
0c435c6a05 | ||
|
|
6a4fffaae7 | ||
|
|
f5a97b63b9 | ||
|
|
6d31834799 | ||
|
|
10b787b852 | ||
|
|
dc61d07206 | ||
|
|
fdd7b14825 | ||
|
|
f4d8033138 | ||
|
|
f48d8fc65e | ||
|
|
f73bc6a82b | ||
|
|
952f04149f | ||
|
|
c872901269 | ||
|
|
3201ad9315 | ||
|
|
befea90c25 | ||
|
|
d3ce2e04fa | ||
|
|
a07ca8ae81 | ||
|
|
dd5d84cc42 | ||
|
|
c900faba81 | ||
|
|
d44d8bedc1 | ||
|
|
99a5866680 | ||
|
|
ebf1a9d9c4 | ||
|
|
5fd9b17b53 | ||
|
|
80ce54cc1d | ||
|
|
71d6d9ec48 | ||
|
|
fce968f893 | ||
|
|
b2f5e28b95 | ||
|
|
2f5c386628 | ||
|
|
b128cc9db4 | ||
|
|
f6b734ca69 | ||
|
|
9964457d09 | ||
|
|
2ae7661810 | ||
|
|
5188d00a09 | ||
|
|
d29bfcca9a | ||
|
|
885944802a | ||
|
|
3dbdd38889 | ||
|
|
69e27bfece | ||
|
|
00a83c56d5 | ||
|
|
0ed8a8dd19 | ||
|
|
99b473e229 | ||
|
|
96625b42ff | ||
|
|
212ec9ca69 | ||
|
|
8de029bd7a | ||
|
|
aa6e4e416b | ||
|
|
5204cb67d8 | ||
|
|
b740e53110 | ||
|
|
4136dd5393 | ||
|
|
5de28e6e7e | ||
|
|
56dd13981c | ||
|
|
3a0ea8992f | ||
|
|
0b552b1697 | ||
|
|
b2194758dc | ||
|
|
c57ee8e471 | ||
|
|
0c7748658c | ||
|
|
cb82b0aac8 | ||
|
|
8d4c3dfa0e | ||
|
|
2d750bc030 | ||
|
|
2a90b3bb70 | ||
|
|
f47a417319 | ||
|
|
b365aab902 | ||
|
|
15efeb572f | ||
|
|
d017409f5b | ||
|
|
de0e9bee20 | ||
|
|
28edca1b63 | ||
|
|
1994302574 | ||
|
|
9b3561fb15 | ||
|
|
9bdc96de8c | ||
|
|
f2692b78dd | ||
|
|
96cc708b5b | ||
|
|
dad94b5c74 | ||
|
|
e79a12fc2b | ||
|
|
0598183f4f | ||
|
|
b950f61b70 | ||
|
|
9d9bc150f6 | ||
|
|
83e2c8f69f | ||
|
|
0b270bf0cc | ||
|
|
63ef986bac | ||
|
|
c87e50b002 | ||
|
|
31697a55b6 | ||
|
|
4b8fcc4b31 | ||
|
|
f2ebffd0ca | ||
|
|
d516eec200 | ||
|
|
4d88af731c | ||
|
|
2c8b9a8323 | ||
|
|
d12817a772 | ||
|
|
c4c6d439bc | ||
|
|
6f706c102e | ||
|
|
66cca1f7bc | ||
|
|
3e1be45739 | ||
|
|
64d87faaf3 | ||
|
|
35f4cdd297 | ||
|
|
147df8ab0b | ||
|
|
9c666d31f4 | ||
|
|
23ead0ea99 | ||
|
|
530b2d9d74 | ||
|
|
a208f876f8 | ||
|
|
7ea163fbe1 | ||
|
|
ab31be8d46 | ||
|
|
d91429c5cf | ||
|
|
c8e13b8ab5 | ||
|
|
2b57af75ea | ||
|
|
545ae79e44 | ||
|
|
c3b47adf39 | ||
|
|
cd89be47b9 | ||
|
|
aeb696687b | ||
|
|
a12d48595e | ||
|
|
28a4a917d4 | ||
|
|
197ae72e93 | ||
|
|
f1c6f276e6 | ||
|
|
b3339c258f | ||
|
|
4795638763 | ||
|
|
c3ecfeae6c | ||
|
|
bcdd204fa0 | ||
|
|
ef162396cf | ||
|
|
ff4769ca1e | ||
|
|
01cf4c663d | ||
|
|
17d7c869ac | ||
|
|
c3ca7960e9 | ||
|
|
eaa506add5 | ||
|
|
70031effa7 | ||
|
|
7a7aee2049 | ||
|
|
4b4dbab1ae | ||
|
|
bcab86fc38 | ||
|
|
937a0c7dee | ||
|
|
b821533b19 | ||
|
|
4ecb53c526 | ||
|
|
a0fef99f8a | ||
|
|
bbd9fcbc84 | ||
|
|
1689cef204 | ||
|
|
9b2e36afc4 | ||
|
|
a635fc8f88 | ||
|
|
1e3421e973 | ||
|
|
efdff73bbf | ||
|
|
6fcdde0617 | ||
|
|
951c08ec4f | ||
|
|
6690396b65 | ||
|
|
f1f5967117 | ||
|
|
a3f0313288 | ||
|
|
bd38457adc | ||
|
|
9b77084ffe | ||
|
|
220ac24bce | ||
|
|
ffe13d1e21 | ||
|
|
fb8953206f | ||
|
|
f9dc9ef5f8 | ||
|
|
ad30f0db89 | ||
|
|
4ff452ffd2 | ||
|
|
c8fbb54fac | ||
|
|
5e226f6581 | ||
|
|
7be453420d | ||
|
|
aa4bff263b | ||
|
|
c10d723c6d | ||
|
|
cdd7a6d753 | ||
|
|
f2929fd2ee | ||
|
|
6660b048a1 | ||
|
|
69b6c0f660 | ||
|
|
145bb6ac7c | ||
|
|
7e59bfa877 | ||
|
|
79eabe4e4c | ||
|
|
03c3d4abfe | ||
|
|
09b855a3a9 | ||
|
|
be910f65be | ||
|
|
6dbf7d8d65 | ||
|
|
34dc49c2f5 | ||
|
|
f113f8afea | ||
|
|
cff10f8c55 | ||
|
|
7ffe6a9063 | ||
|
|
637db138fd | ||
|
|
4fb9913c93 | ||
|
|
e9ba185362 | ||
|
|
44b02afdfd | ||
|
|
d136b09fc2 | ||
|
|
94798f70d3 | ||
|
|
35aa6ba454 | ||
|
|
68a9936566 | ||
|
|
442c618013 | ||
|
|
bc513c2604 | ||
|
|
5988606410 | ||
|
|
de3dd17417 | ||
|
|
dab937d241 | ||
|
|
5ca101b81c | ||
|
|
dc3ab0ee55 | ||
|
|
bf3cc5c13c | ||
|
|
3b56bb69e6 | ||
|
|
b5ebc90667 | ||
|
|
0a01113c83 | ||
|
|
7f89b2a0ea | ||
|
|
f9c7d6e26f | ||
|
|
9f57ea9bb6 | ||
|
|
dae6616720 | ||
|
|
15a246bf75 | ||
|
|
a9404aa8e2 | ||
|
|
65971bb657 | ||
|
|
8a2e41cf47 | ||
|
|
48a6c770fa | ||
|
|
f3e04374e9 | ||
|
|
c07d165ceb | ||
|
|
b117281c58 | ||
|
|
19d897a40b | ||
|
|
b11665b1eb | ||
|
|
e87cc87cbf | ||
|
|
ff41d4f964 | ||
|
|
2030ad946e | ||
|
|
0a756e48b8 | ||
|
|
c8a1013685 | ||
|
|
82a0d80ee7 | ||
|
|
3f5e6e9983 | ||
|
|
bd1ceae375 | ||
|
|
704c41b116 | ||
|
|
c04536b132 | ||
|
|
03589201fb | ||
|
|
8b28e0869c | ||
|
|
391eb5b6f9 | ||
|
|
279f8397f7 | ||
|
|
08bca5a129 | ||
|
|
836956ad87 | ||
|
|
624889bae5 | ||
|
|
f8cc6f45b8 | ||
|
|
0be26498d9 | ||
|
|
5f5ddc32a7 | ||
|
|
d00bfd6243 | ||
|
|
4a60b31ae3 | ||
|
|
1c15eac66a | ||
|
|
9861361aee | ||
|
|
ea78c416d0 | ||
|
|
7ba0df6dfc | ||
|
|
6a7a447df3 | ||
|
|
375834ea46 | ||
|
|
5c339f0757 | ||
|
|
e13ffc8137 | ||
|
|
e268845322 | ||
|
|
67dc3bc658 | ||
|
|
05af5d7ddf | ||
|
|
9535a9a7ad | ||
|
|
4f1cbf72c6 | ||
|
|
296791b56b | ||
|
|
b29b15f3b5 | ||
|
|
821ff036be | ||
|
|
c62ceef1d3 | ||
|
|
f382c799ad | ||
|
|
56f8b1fb81 | ||
|
|
bbf2da962e | ||
|
|
babe201e64 | ||
|
|
cfb9cfa7c7 | ||
|
|
133a294024 | ||
|
|
403e278af2 | ||
|
|
4d9eca1667 | ||
|
|
4a449d85e6 | ||
|
|
bfe8c5e9bf | ||
|
|
121be5eeda | ||
|
|
0aaef2d9a8 | ||
|
|
1c44e2d099 | ||
|
|
9831d14d5e | ||
|
|
4545a08297 | ||
|
|
1dd4544680 | ||
|
|
9e7b5991eb | ||
|
|
6d6f936ba0 | ||
|
|
30ef259c89 | ||
|
|
a19dccf52f | ||
|
|
d25d233cdc | ||
|
|
3a799a6cca | ||
|
|
b8e4515714 | ||
|
|
59343a7263 | ||
|
|
105fd5e9d5 | ||
|
|
47f365d385 | ||
|
|
4cc787b0ec | ||
|
|
db5b247d58 | ||
|
|
dd0a744a3e | ||
|
|
2d0f286a66 | ||
|
|
82799c3ac5 | ||
|
|
535ab137f4 | ||
|
|
c749cda90d | ||
|
|
3fb689ebfa | ||
|
|
390664f2ed | ||
|
|
450ff132e0 | ||
|
|
4d7848fa3b | ||
|
|
f52882ede6 | ||
|
|
a55b45c0ee | ||
|
|
2f5f352f9a | ||
|
|
dc3154b883 | ||
|
|
a0e38f5d51 | ||
|
|
5e44901b45 | ||
|
|
874679e65d | ||
|
|
57f0370ab8 | ||
|
|
dfc633dcee | ||
|
|
2126c36a73 | ||
|
|
83ac391603 | ||
|
|
7e4112a912 | ||
|
|
5e131c9b49 | ||
|
|
b4da1006e9 | ||
|
|
70c3ac27eb | ||
|
|
25fb79776b | ||
|
|
e484002d4a | ||
|
|
d84c0e03d9 | ||
|
|
d2c71a5a47 | ||
|
|
1af62dafa4 | ||
|
|
34b16293ba | ||
|
|
ff23706b10 | ||
|
|
037ea961f2 | ||
|
|
9c7bcd4c2f | ||
|
|
27cde3ec20 | ||
|
|
f08ec77a73 | ||
|
|
2e0ee441cf | ||
|
|
fe6dd0c6eb | ||
|
|
938df62167 | ||
|
|
bfd58e5749 | ||
|
|
e725c97536 | ||
|
|
9dec7b1f66 | ||
|
|
d4c58cb41f | ||
|
|
c078a4e04c | ||
|
|
ab26b7c1ca | ||
|
|
b46a78fb63 | ||
|
|
a40c2e2978 | ||
|
|
259e0f95da | ||
|
|
67ea60a0fa | ||
|
|
5158ca14c9 | ||
|
|
34448c9e89 | ||
|
|
13302c3586 | ||
|
|
efed0a4884 | ||
|
|
e95318092c | ||
|
|
f472c16e8a | ||
|
|
fac0feeeaa | ||
|
|
f902d4b1f6 | ||
|
|
8467f299a0 | ||
|
|
84953bf15b | ||
|
|
2951b7ef28 | ||
|
|
03b84cb0f1 | ||
|
|
582980fa5d | ||
|
|
2406e14e04 | ||
|
|
4195371a1e | ||
|
|
dc0d415b15 | ||
|
|
803ef21305 | ||
|
|
64e631fcdb | ||
|
|
9dd75b4a3b | ||
|
|
8ab8201403 | ||
|
|
99c229524d | ||
|
|
adf4cfd76a | ||
|
|
a46aaf3f11 | ||
|
|
5ef03665bd | ||
|
|
b92d68e4aa | ||
|
|
92cafd766e | ||
|
|
78d00d60f3 | ||
|
|
707311f3d8 | ||
|
|
b7fdd5f70a | ||
|
|
5501649d79 | ||
|
|
43bc4db87c | ||
|
|
25e3807428 | ||
|
|
0cf7c19b96 | ||
|
|
2786174ffb | ||
|
|
4ccd69c782 | ||
|
|
f5e63f42a4 | ||
|
|
245e57de63 | ||
|
|
e7b6a17e42 | ||
|
|
caae9a92a1 | ||
|
|
1c4e41598c | ||
|
|
dd089200ab | ||
|
|
5341da3021 | ||
|
|
da4daee645 | ||
|
|
f6e3575a50 | ||
|
|
87b985ad36 | ||
|
|
8172c4f06a | ||
|
|
637d614618 | ||
|
|
f32e5edcef | ||
|
|
76c0de5f38 | ||
|
|
9608ef917b | ||
|
|
b286689025 | ||
|
|
a4588521a1 | ||
|
|
f1f310ad86 | ||
|
|
edd9bb5da6 | ||
|
|
1b26715cc8 | ||
|
|
e960e6255d | ||
|
|
a362c0788d | ||
|
|
d40581f9d4 | ||
|
|
e6dafd2402 | ||
|
|
1bfd370606 | ||
|
|
087cb64b8c | ||
|
|
2042f6aa67 | ||
|
|
b6708c511b | ||
|
|
6232b62e3e | ||
|
|
03adb9f57e | ||
|
|
a413b34c3e | ||
|
|
0552254c23 | ||
|
|
7d2c9b06de | ||
|
|
e80ca05a2e | ||
|
|
47231c559c | ||
|
|
0e9d552712 | ||
|
|
fbcd8407ce | ||
|
|
df0fb73ea4 | ||
|
|
52711a38a5 | ||
|
|
e7f62c2f20 | ||
|
|
1362225754 | ||
|
|
008919cdfa | ||
|
|
8f8a645568 | ||
|
|
d66c22c4a6 | ||
|
|
0fefa5f2a9 | ||
|
|
810d555abd | ||
|
|
26a5dd6401 | ||
|
|
950c04894e | ||
|
|
8444d18eac | ||
|
|
16d813e7bb | ||
|
|
97d7f0343a | ||
|
|
9424361fc9 | ||
|
|
13e77a781f | ||
|
|
ba5e3e4590 | ||
|
|
badd1ef940 | ||
|
|
b154ed4202 | ||
|
|
af6deab60d | ||
|
|
ed0966cca3 | ||
|
|
b38c181992 | ||
|
|
a2fb252330 | ||
|
|
c83d5e1e59 | ||
|
|
c189503def | ||
|
|
1dae53d9b2 | ||
|
|
d99086c5aa | ||
|
|
84d4a08128 | ||
|
|
3e56641b0a | ||
|
|
25e0151d0f | ||
|
|
e39775bb34 | ||
|
|
9533e312b1 | ||
|
|
a7a2ae2781 | ||
|
|
3de66a37d1 | ||
|
|
eb12c9b005 | ||
|
|
c6c48cb803 | ||
|
|
14627a5c2b | ||
|
|
7148c5d5cd | ||
|
|
c98e849621 | ||
|
|
4a173bbd98 | ||
|
|
123c517f11 | ||
|
|
7ef16996e9 | ||
|
|
cef786bfe2 | ||
|
|
05057a56d9 | ||
|
|
78b7551081 | ||
|
|
fc2c11d095 | ||
|
|
357a334b0b | ||
|
|
44d2ed12e2 | ||
|
|
d01e913354 | ||
|
|
ee8875fd18 | ||
|
|
fdf5555f11 | ||
|
|
bedc81e973 | ||
|
|
f707639392 | ||
|
|
f87b2bee95 | ||
|
|
8c632986a0 | ||
|
|
190a674581 | ||
|
|
cb3b739969 | ||
|
|
43a0a35f22 | ||
|
|
9f73339e58 | ||
|
|
c673adef70 | ||
|
|
a4d5a17f4e | ||
|
|
bb2f765e5d | ||
|
|
8519a596ec | ||
|
|
6dc9e6d3ee | ||
|
|
bfa0e77f78 | ||
|
|
33dc6c53a4 | ||
|
|
048da8c972 | ||
|
|
48d1e6ccb2 | ||
|
|
1a418c74b4 | ||
|
|
df60aa50fe | ||
|
|
26c5ebea53 | ||
|
|
e95bb4c4d7 | ||
|
|
6ddfc80943 | ||
|
|
a421b5be2b | ||
|
|
98ed93133b | ||
|
|
bbc76302a2 | ||
|
|
82a6d529c1 | ||
|
|
9d95d14129 | ||
|
|
73efd4f493 | ||
|
|
9e6ec15f32 | ||
|
|
9f9be9d1a2 | ||
|
|
f3b627e91b | ||
|
|
bbe3d9231e | ||
|
|
13b53efb2a | ||
|
|
1b9ae3ccb3 | ||
|
|
5cc138e23d | ||
|
|
7029524504 | ||
|
|
b79b967036 | ||
|
|
b1510d1107 | ||
|
|
bb5d41147a | ||
|
|
f56a0d1b6d | ||
|
|
a5d8d1990c | ||
|
|
11abe53ef6 | ||
|
|
0606de818d | ||
|
|
237690b876 | ||
|
|
1bfba1e913 | ||
|
|
f88126008b | ||
|
|
597a3dc5b7 | ||
|
|
821bb6fe3f | ||
|
|
e9583c6a47 | ||
|
|
8439b375ed | ||
|
|
1d81a9e40f | ||
|
|
e6d5fe328d | ||
|
|
ec8b73bd9c | ||
|
|
e302ac8059 | ||
|
|
e94d462652 | ||
|
|
2ea2fd4308 | ||
|
|
706fce49b9 | ||
|
|
a79f684e25 | ||
|
|
6291087ecb | ||
|
|
01e0e4b673 | ||
|
|
7eed1a6db3 | ||
|
|
2defaa801b | ||
|
|
caad7a38a4 | ||
|
|
be7d1ab0cc | ||
|
|
fe50ccd39f | ||
|
|
4ca8a7ffcf | ||
|
|
33ad10de29 | ||
|
|
26b6d3a1ec | ||
|
|
b400860538 | ||
|
|
47ade0f135 | ||
|
|
e50530bc44 | ||
|
|
ff3f8f5cc7 | ||
|
|
81dc9f0e8f | ||
|
|
c07c500026 | ||
|
|
1100490c97 | ||
|
|
164a7c71cb | ||
|
|
2ed6d48a1b | ||
|
|
bf71bc27b9 | ||
|
|
3edaefe168 | ||
|
|
70374bda43 | ||
|
|
bce600ba10 | ||
|
|
1300f3922e | ||
|
|
49160f03c0 | ||
|
|
91c2592213 | ||
|
|
8b401a4f6c | ||
|
|
476d93d076 | ||
|
|
2e9ea680c1 | ||
|
|
e202c3bb42 | ||
|
|
2f1265e8cb | ||
|
|
b416c45bbb | ||
|
|
c410784cc9 | ||
|
|
0ae379fd7a | ||
|
|
e922c70c99 | ||
|
|
692048d537 | ||
|
|
69e19fe8ab | ||
|
|
38781d439e | ||
|
|
2aba454318 | ||
|
|
d4a220bddc | ||
|
|
6f75546d15 | ||
|
|
c3ef8bf5cd | ||
|
|
4a9d44ec28 | ||
|
|
16a81047e3 | ||
|
|
71d4f1568f | ||
|
|
2c26ea534f | ||
|
|
6ab538050d | ||
|
|
4a5443996f | ||
|
|
52a81f8fd5 | ||
|
|
09ea11c13d | ||
|
|
28dc7379a6 | ||
|
|
3d9757e5bf | ||
|
|
60ab942882 | ||
|
|
1e28b9efa9 | ||
|
|
a8d36fba8f | ||
|
|
640cf1caf3 | ||
|
|
c0e6751893 | ||
|
|
e6cd184cd6 | ||
|
|
57c6a08b29 | ||
|
|
edb1e692f1 | ||
|
|
49593dd77d | ||
|
|
9ff243a7a9 | ||
|
|
76fb6775d1 | ||
|
|
3f2772c74c | ||
|
|
e4f5ca7ea8 | ||
|
|
7723419142 | ||
|
|
16d66a4206 | ||
|
|
dd9abc1db0 | ||
|
|
420648536a | ||
|
|
02300b65ce | ||
|
|
15abf7bb0f | ||
|
|
505381e760 | ||
|
|
c365bf79a6 | ||
|
|
707bcd9183 | ||
|
|
f9b6b88a96 | ||
|
|
762b436e46 | ||
|
|
8de9b72c49 | ||
|
|
f520fda6fb | ||
|
|
a96ac8c024 | ||
|
|
db3bd12b1e | ||
|
|
12114dce16 | ||
|
|
0f39887cdb | ||
|
|
472b67e930 | ||
|
|
90d67846e6 | ||
|
|
a0e37098bb | ||
|
|
adef6476ec | ||
|
|
8f14e0d024 | ||
|
|
e18d2d2f28 | ||
|
|
40ddf0df0d | ||
|
|
563ec523a3 | ||
|
|
05d4339427 | ||
|
|
81dc3d4404 | ||
|
|
386e2e0736 | ||
|
|
eae5b68f52 | ||
|
|
07675f3039 | ||
|
|
fd3d742d71 | ||
|
|
c9f07fb8ab | ||
|
|
3adbadd808 | ||
|
|
7b3721aa44 | ||
|
|
2ec64ce5a4 | ||
|
|
f88a8cf8fb | ||
|
|
3c72a90f02 | ||
|
|
22463c9eb1 | ||
|
|
1bbd5168c9 | ||
|
|
000866556e | ||
|
|
c9423038da | ||
|
|
1b61aa135e | ||
|
|
7cc8a5a405 | ||
|
|
4c22dd2c82 | ||
|
|
f62d81314c | ||
|
|
b95d0238e2 | ||
|
|
9aaab22f1a | ||
|
|
540878d2a9 | ||
|
|
ae2a901725 | ||
|
|
47afe69cf0 | ||
|
|
38db5a1d49 | ||
|
|
4871cc3243 | ||
|
|
9cf7dc128e | ||
|
|
46f7c010ca | ||
|
|
10c63effd4 | ||
|
|
cc7b27325e | ||
|
|
3a608e5554 | ||
|
|
1092b2696a | ||
|
|
c29199515c | ||
|
|
2f046b00f7 | ||
|
|
973cfff6e2 | ||
|
|
9931245b94 | ||
|
|
25bfd1bbc4 | ||
|
|
63191be183 | ||
|
|
66c6e532f6 | ||
|
|
32baa7f146 | ||
|
|
87a332ee81 | ||
|
|
80248981dd | ||
|
|
9421da7b22 | ||
|
|
3537fad8db | ||
|
|
8edd667ff4 | ||
|
|
cbaea166c4 | ||
|
|
91e65867bb | ||
|
|
b4a95d20e2 | ||
|
|
dfb4c9f726 | ||
|
|
bd02676dbc | ||
|
|
b4f8088f0f | ||
|
|
04ab9d75a9 | ||
|
|
a65b074e9f | ||
|
|
cef6a583b7 | ||
|
|
8e3bc8b8a7 | ||
|
|
3d6172c544 | ||
|
|
96c69ef224 | ||
|
|
03e3c8fb70 | ||
|
|
99055e0cd8 | ||
|
|
071e913a08 | ||
|
|
053cbc1b35 | ||
|
|
98f48ba067 | ||
|
|
67c05c9c37 | ||
|
|
5d46bba216 | ||
|
|
4a11fa61ed | ||
|
|
b0d3641b29 | ||
|
|
ca3dac9a25 | ||
|
|
e29ef738f2 | ||
|
|
e2b89e5ae2 | ||
|
|
d759eb8b7c | ||
|
|
1509f37f62 | ||
|
|
0ce707538a | ||
|
|
be2504ce82 | ||
|
|
3faa233056 | ||
|
|
72201049f0 | ||
|
|
d91856d824 | ||
|
|
f46c53b77e | ||
|
|
4f1e31999d | ||
|
|
ad4a32762d | ||
|
|
2b50f44ab2 | ||
|
|
43c94c7be2 | ||
|
|
6329eee92a | ||
|
|
6bd0da059f | ||
|
|
84b1ebd2c8 | ||
|
|
0fa13379e4 | ||
|
|
b54458c433 | ||
|
|
2252ac9592 | ||
|
|
180905ecba | ||
|
|
ed85e4e24c | ||
|
|
d0f52a2b78 | ||
|
|
1f40e97dd8 | ||
|
|
33c3ceb361 | ||
|
|
8d1fb577e2 | ||
|
|
4f8526dacb | ||
|
|
76d8be8818 | ||
|
|
dbb6980e96 | ||
|
|
29100d5250 | ||
|
|
8cce210d64 | ||
|
|
54101378e3 | ||
|
|
ed6fde5e4d | ||
|
|
6a3e01b3cc | ||
|
|
5417965e38 | ||
|
|
8381eb96d5 | ||
|
|
c95cca5efc | ||
|
|
e1071574e6 | ||
|
|
f6be1cecfe | ||
|
|
ef8995006d | ||
|
|
20ba112bd8 | ||
|
|
3bc98eec16 | ||
|
|
184d593d27 | ||
|
|
629f2f000f | ||
|
|
48d2285d36 | ||
|
|
91dd4703e2 | ||
|
|
a920204f3e | ||
|
|
f1004a1324 | ||
|
|
42abe26bd7 | ||
|
|
83590cb742 | ||
|
|
3e34718bfe | ||
|
|
1a4498a58c | ||
|
|
b88c9a5ae1 | ||
|
|
bfa73f630d | ||
|
|
5b0342526d | ||
|
|
ef870a6ac2 | ||
|
|
bc9868f926 | ||
|
|
c71e86b8d1 | ||
|
|
f9ab3e24b1 | ||
|
|
1f1e73ce08 | ||
|
|
60d02f3afd | ||
|
|
2b3f03eae9 | ||
|
|
175482371c | ||
|
|
b715eb530a | ||
|
|
bac129069b | ||
|
|
5e70d8b841 | ||
|
|
61cf686527 | ||
|
|
c25cf508ff | ||
|
|
f4fea92cea | ||
|
|
210d7a4d59 | ||
|
|
266d5618ae | ||
|
|
2dd46203ee | ||
|
|
7f35685b35 | ||
|
|
ff52b376fe | ||
|
|
dc6f485aca | ||
|
|
ff053c36e5 | ||
|
|
2339bdcbdf | ||
|
|
a2b7c4b245 | ||
|
|
f593cce6c9 | ||
|
|
4eec956905 | ||
|
|
aa8135a1ad | ||
|
|
35f0d7a82a | ||
|
|
1aa9af8081 | ||
|
|
7e108f167d | ||
|
|
c494bd56ed | ||
|
|
8d1447e08c | ||
|
|
fc533c7b2b | ||
|
|
c930fb45f1 | ||
|
|
f7e7f6e257 | ||
|
|
32b64e74d0 | ||
|
|
0e7d3811c1 | ||
|
|
72b754881e | ||
|
|
2935945585 | ||
|
|
ced17f6deb | ||
|
|
da627f24a3 | ||
|
|
df8cdfeaef | ||
|
|
21c414a5e9 | ||
|
|
8c24e38ee1 | ||
|
|
39c3a72cc0 | ||
|
|
29f4eaf94f | ||
|
|
a8e546c9af | ||
|
|
38fafed013 | ||
|
|
78f3944b5d | ||
|
|
84b7b86ed5 | ||
|
|
d93bc39d8d | ||
|
|
9370cdc88b | ||
|
|
cfd6e2b32f | ||
|
|
0a373bc260 | ||
|
|
939d37822d | ||
|
|
2058f765b0 | ||
|
|
7f8854d7f5 | ||
|
|
0080383c40 | ||
|
|
d183dd6e91 | ||
|
|
b9280ab62d | ||
|
|
a8d9970500 | ||
|
|
f289e75651 | ||
|
|
05056f6b8d | ||
|
|
df4504eb06 | ||
|
|
edf56c122f | ||
|
|
25e98cd1e2 | ||
|
|
077076a547 | ||
|
|
de09015154 | ||
|
|
11c3c093c8 | ||
|
|
adae1143cd | ||
|
|
022fed41a4 | ||
|
|
9137ff4a15 | ||
|
|
1b64b5fa80 | ||
|
|
6a7b719725 | ||
|
|
5677839d48 | ||
|
|
10642afabf | ||
|
|
d2bc9e0784 | ||
|
|
278aae8fff | ||
|
|
d1b1e896f6 | ||
|
|
49d3d77fd6 | ||
|
|
299e8f7a2c | ||
|
|
f5263cf8d7 | ||
|
|
9f4c49c07d | ||
|
|
81757e7182 | ||
|
|
71ff5aa253 | ||
|
|
5411691fb2 | ||
|
|
82bca87418 | ||
|
|
b007903616 | ||
|
|
24b3afbe3d | ||
|
|
7dacc47f16 | ||
|
|
669808069d | ||
|
|
d73499ac79 | ||
|
|
c36c145b34 | ||
|
|
4735cedba2 | ||
|
|
7b8c11ecf3 | ||
|
|
a5f9b392ed | ||
|
|
4263772246 | ||
|
|
c6f804bebf | ||
|
|
07c11b2097 | ||
|
|
6165ac334b | ||
|
|
bb81b7b8d9 | ||
|
|
5d3cc2146b | ||
|
|
d378753f7c | ||
|
|
1c1e51e082 | ||
|
|
c130037574 | ||
|
|
4b289122c1 | ||
|
|
449f8633cf | ||
|
|
b53056fe09 | ||
|
|
7bae23ce85 | ||
|
|
07c6905146 | ||
|
|
ac4fe6cd1d | ||
|
|
1b60d05434 | ||
|
|
22c85612c7 | ||
|
|
815bc126a7 | ||
|
|
111cd16827 | ||
|
|
89cc7641cb | ||
|
|
1a6fa86fc2 | ||
|
|
fc04e08873 | ||
|
|
73ee65e3a7 | ||
|
|
e8685c977f | ||
|
|
a487dcdfa5 | ||
|
|
d84f85e636 | ||
|
|
f991e7b5f3 | ||
|
|
3e77ca6497 | ||
|
|
98b4422956 | ||
|
|
59f63b3bf9 | ||
|
|
c4775920f4 | ||
|
|
e3fe26ae72 | ||
|
|
87930d0f7d | ||
|
|
13b9c0ef34 | ||
|
|
365aa7e368 | ||
|
|
5880f9fd30 | ||
|
|
278f2c3239 | ||
|
|
d69439a11a | ||
|
|
6812c94cc6 | ||
|
|
563f4d79a3 | ||
|
|
1db958ec53 | ||
|
|
38216648d4 | ||
|
|
a0649be28d | ||
|
|
62b1f34472 | ||
|
|
c71e62e810 | ||
|
|
72565fa493 | ||
|
|
98df9b91ba | ||
|
|
edb2016b28 | ||
|
|
91c3fc2fe1 | ||
|
|
9db3a04275 | ||
|
|
e9f9af0e7a | ||
|
|
c6aa724060 | ||
|
|
0cc9970184 | ||
|
|
7d0d95b298 | ||
|
|
18b07128fc | ||
|
|
735c08caf4 | ||
|
|
2b7005ad49 | ||
|
|
f9462162a5 | ||
|
|
b33a788c20 | ||
|
|
bbc8986917 | ||
|
|
38b62d9d6c | ||
|
|
1234db75b1 | ||
|
|
3c70c3c6e8 | ||
|
|
65b883ace9 | ||
|
|
fb949bec56 | ||
|
|
a225663049 | ||
|
|
d1a62f6697 | ||
|
|
5eb6d52daf | ||
|
|
ace3fbdfa0 | ||
|
|
e013d33691 | ||
|
|
9db300035a | ||
|
|
de438cc66f | ||
|
|
e2f3721459 | ||
|
|
064a180b3a | ||
|
|
4c7c3d4c96 | ||
|
|
a9e515e5c0 | ||
|
|
3463fbec81 | ||
|
|
8d820c3096 | ||
|
|
fd626ec94d | ||
|
|
b0804ac7f0 | ||
|
|
732a9d0712 | ||
|
|
03572ebcab | ||
|
|
2681bbd08f | ||
|
|
918a263fd0 | ||
|
|
57a10e87b6 | ||
|
|
faeb477880 | ||
|
|
d3714de65b | ||
|
|
0e4e574bd4 | ||
|
|
92d6b07578 | ||
|
|
0a9f06096b |
423
.cursorrules
423
.cursorrules
@@ -3,43 +3,66 @@
|
|||||||
## ⚠️ CRITICAL DEVELOPMENT RULES ⚠️
|
## ⚠️ CRITICAL DEVELOPMENT RULES ⚠️
|
||||||
|
|
||||||
### 🚨 NEVER COMMIT DIRECTLY TO MASTER/MAIN BRANCH 🚨
|
### 🚨 NEVER COMMIT DIRECTLY TO MASTER/MAIN BRANCH 🚨
|
||||||
|
|
||||||
- **This is the most important rule - NEVER modify code directly on main or master branch**
|
- **This is the most important rule - NEVER modify code directly on main or master branch**
|
||||||
|
- **ALL CHANGES MUST GO THROUGH PULL REQUESTS - NO EXCEPTIONS**
|
||||||
- **Always work on feature branches and use pull requests for all changes**
|
- **Always work on feature branches and use pull requests for all changes**
|
||||||
- **Any direct commits to master/main branch are strictly forbidden**
|
- **Any direct commits to master/main branch are strictly forbidden**
|
||||||
|
- **Pull requests are the ONLY way to merge code to main branch**
|
||||||
- Before starting any development, always:
|
- Before starting any development, always:
|
||||||
1. `git checkout main` (switch to main branch)
|
1. `git checkout main` (switch to main branch)
|
||||||
2. `git pull` (get latest changes)
|
2. `git pull` (get latest changes)
|
||||||
3. `git checkout -b feat/your-feature-name` (create and switch to feature branch)
|
3. `git checkout -b feat/your-feature-name` (create and switch to feature branch)
|
||||||
4. Make your changes on the feature branch
|
4. Make your changes on the feature branch
|
||||||
5. Commit and push to the feature branch
|
5. Commit and push to the feature branch
|
||||||
6. Create a pull request for review
|
6. **Create a pull request for review - THIS IS MANDATORY**
|
||||||
|
7. **Wait for PR approval and merge through GitHub interface only**
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
RustFS is a high-performance distributed object storage system written in Rust, compatible with S3 API. The project adopts a modular architecture, supporting erasure coding storage, multi-tenant management, observability, and other enterprise-level features.
|
RustFS is a high-performance distributed object storage system written in Rust, compatible with S3 API. The project adopts a modular architecture, supporting erasure coding storage, multi-tenant management, observability, and other enterprise-level features.
|
||||||
|
|
||||||
## Core Architecture Principles
|
## Core Architecture Principles
|
||||||
|
|
||||||
### 1. Modular Design
|
### 1. Modular Design
|
||||||
|
|
||||||
- Project uses Cargo workspace structure, containing multiple independent crates
|
- Project uses Cargo workspace structure, containing multiple independent crates
|
||||||
- Core modules: `rustfs` (main service), `ecstore` (erasure coding storage), `common` (shared components)
|
- Core modules: `rustfs` (main service), `ecstore` (erasure coding storage), `common` (shared components)
|
||||||
- Functional modules: `iam` (identity management), `madmin` (management interface), `crypto` (encryption), etc.
|
- Functional modules: `iam` (identity management), `madmin` (management interface), `crypto` (encryption), etc.
|
||||||
- Tool modules: `cli` (command line tool), `crates/*` (utility libraries)
|
- Tool modules: `cli` (command line tool), `crates/*` (utility libraries)
|
||||||
|
|
||||||
### 2. Asynchronous Programming Pattern
|
### 2. Asynchronous Programming Pattern
|
||||||
|
|
||||||
- Comprehensive use of `tokio` async runtime
|
- Comprehensive use of `tokio` async runtime
|
||||||
- Prioritize `async/await` syntax
|
- Prioritize `async/await` syntax
|
||||||
- Use `async-trait` for async methods in traits
|
- Use `async-trait` for async methods in traits
|
||||||
- Avoid blocking operations, use `spawn_blocking` when necessary
|
- Avoid blocking operations, use `spawn_blocking` when necessary
|
||||||
|
|
||||||
### 3. Error Handling Strategy
|
### 3. Error Handling Strategy
|
||||||
- Use unified error type `common::error::Error`
|
|
||||||
- Support error chains and context information
|
- **Use modular, type-safe error handling with `thiserror`**
|
||||||
- Use `thiserror` to define specific error types
|
- Each module should define its own error type using `thiserror::Error` derive macro
|
||||||
- Error conversion uses `downcast_ref` for type checking
|
- Support error chains and context information through `#[from]` and `#[source]` attributes
|
||||||
|
- Use `Result<T>` type aliases for consistency within each module
|
||||||
|
- Error conversion between modules should use explicit `From` implementations
|
||||||
|
- Follow the pattern: `pub type Result<T> = core::result::Result<T, Error>`
|
||||||
|
- Use `#[error("description")]` attributes for clear error messages
|
||||||
|
- Support error downcasting when needed through `other()` helper methods
|
||||||
|
- Implement `Clone` for errors when required by the domain logic
|
||||||
|
- **Current module error types:**
|
||||||
|
- `ecstore::error::StorageError` - Storage layer errors
|
||||||
|
- `ecstore::disk::error::DiskError` - Disk operation errors
|
||||||
|
- `iam::error::Error` - Identity and access management errors
|
||||||
|
- `policy::error::Error` - Policy-related errors
|
||||||
|
- `crypto::error::Error` - Cryptographic operation errors
|
||||||
|
- `filemeta::error::Error` - File metadata errors
|
||||||
|
- `rustfs::error::ApiError` - API layer errors
|
||||||
|
- Module-specific error types for specialized functionality
|
||||||
|
|
||||||
## Code Style Guidelines
|
## Code Style Guidelines
|
||||||
|
|
||||||
### 1. Formatting Configuration
|
### 1. Formatting Configuration
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
max_width = 130
|
max_width = 130
|
||||||
fn_call_width = 90
|
fn_call_width = 90
|
||||||
@@ -55,21 +78,25 @@ single_line_let_else_max_width = 100
|
|||||||
Before every commit, you **MUST**:
|
Before every commit, you **MUST**:
|
||||||
|
|
||||||
1. **Format your code**:
|
1. **Format your code**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo fmt --all
|
cargo fmt --all
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Verify formatting**:
|
2. **Verify formatting**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo fmt --all --check
|
cargo fmt --all --check
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Pass clippy checks**:
|
3. **Pass clippy checks**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo clippy --all-targets --all-features -- -D warnings
|
cargo clippy --all-targets --all-features -- -D warnings
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Ensure compilation**:
|
4. **Ensure compilation**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo check --all-targets
|
cargo check --all-targets
|
||||||
```
|
```
|
||||||
@@ -144,6 +171,7 @@ Example output when formatting fails:
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 3. Naming Conventions
|
### 3. Naming Conventions
|
||||||
|
|
||||||
- Use `snake_case` for functions, variables, modules
|
- Use `snake_case` for functions, variables, modules
|
||||||
- Use `PascalCase` for types, traits, enums
|
- Use `PascalCase` for types, traits, enums
|
||||||
- Constants use `SCREAMING_SNAKE_CASE`
|
- Constants use `SCREAMING_SNAKE_CASE`
|
||||||
@@ -153,6 +181,7 @@ Example output when formatting fails:
|
|||||||
- Choose names that clearly express the purpose and intent
|
- Choose names that clearly express the purpose and intent
|
||||||
|
|
||||||
### 4. Type Declaration Guidelines
|
### 4. Type Declaration Guidelines
|
||||||
|
|
||||||
- **Prefer type inference over explicit type declarations** when the type is obvious from context
|
- **Prefer type inference over explicit type declarations** when the type is obvious from context
|
||||||
- Let the Rust compiler infer types whenever possible to reduce verbosity and improve maintainability
|
- Let the Rust compiler infer types whenever possible to reduce verbosity and improve maintainability
|
||||||
- Only specify types explicitly when:
|
- Only specify types explicitly when:
|
||||||
@@ -162,6 +191,7 @@ Example output when formatting fails:
|
|||||||
- Needed to resolve ambiguity between multiple possible types
|
- Needed to resolve ambiguity between multiple possible types
|
||||||
|
|
||||||
**Good examples (prefer these):**
|
**Good examples (prefer these):**
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Compiler can infer the type
|
// Compiler can infer the type
|
||||||
let items = vec![1, 2, 3, 4];
|
let items = vec![1, 2, 3, 4];
|
||||||
@@ -173,6 +203,7 @@ let filtered: Vec<_> = items.iter().filter(|&&x| x > 2).collect();
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Avoid unnecessary explicit types:**
|
**Avoid unnecessary explicit types:**
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Unnecessary - type is obvious
|
// Unnecessary - type is obvious
|
||||||
let items: Vec<i32> = vec![1, 2, 3, 4];
|
let items: Vec<i32> = vec![1, 2, 3, 4];
|
||||||
@@ -181,6 +212,7 @@ let result: ProcessResult = process_data(&input);
|
|||||||
```
|
```
|
||||||
|
|
||||||
**When explicit types are beneficial:**
|
**When explicit types are beneficial:**
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// API boundaries - always specify types
|
// API boundaries - always specify types
|
||||||
pub fn process_data(input: &[u8]) -> Result<ProcessResult, Error> { ... }
|
pub fn process_data(input: &[u8]) -> Result<ProcessResult, Error> { ... }
|
||||||
@@ -193,6 +225,7 @@ let cache: HashMap<String, Arc<Mutex<CacheEntry>>> = HashMap::new();
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 5. Documentation Comments
|
### 5. Documentation Comments
|
||||||
|
|
||||||
- Public APIs must have documentation comments
|
- Public APIs must have documentation comments
|
||||||
- Use `///` for documentation comments
|
- Use `///` for documentation comments
|
||||||
- Complex functions add `# Examples` and `# Parameters` descriptions
|
- Complex functions add `# Examples` and `# Parameters` descriptions
|
||||||
@@ -201,6 +234,7 @@ let cache: HashMap<String, Arc<Mutex<CacheEntry>>> = HashMap::new();
|
|||||||
- Avoid meaningless comments like "debug 111" or placeholder text
|
- Avoid meaningless comments like "debug 111" or placeholder text
|
||||||
|
|
||||||
### 6. Import Guidelines
|
### 6. Import Guidelines
|
||||||
|
|
||||||
- Standard library imports first
|
- Standard library imports first
|
||||||
- Third-party crate imports in the middle
|
- Third-party crate imports in the middle
|
||||||
- Project internal imports last
|
- Project internal imports last
|
||||||
@@ -209,6 +243,7 @@ let cache: HashMap<String, Arc<Mutex<CacheEntry>>> = HashMap::new();
|
|||||||
## Asynchronous Programming Guidelines
|
## Asynchronous Programming Guidelines
|
||||||
|
|
||||||
### 1. Trait Definition
|
### 1. Trait Definition
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait StorageAPI: Send + Sync {
|
pub trait StorageAPI: Send + Sync {
|
||||||
@@ -217,6 +252,7 @@ pub trait StorageAPI: Send + Sync {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 2. Error Handling
|
### 2. Error Handling
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Use ? operator to propagate errors
|
// Use ? operator to propagate errors
|
||||||
async fn example_function() -> Result<()> {
|
async fn example_function() -> Result<()> {
|
||||||
@@ -227,6 +263,7 @@ async fn example_function() -> Result<()> {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 3. Concurrency Control
|
### 3. Concurrency Control
|
||||||
|
|
||||||
- Use `Arc` and `Mutex`/`RwLock` for shared state management
|
- Use `Arc` and `Mutex`/`RwLock` for shared state management
|
||||||
- Prioritize async locks from `tokio::sync`
|
- Prioritize async locks from `tokio::sync`
|
||||||
- Avoid holding locks for long periods
|
- Avoid holding locks for long periods
|
||||||
@@ -234,6 +271,7 @@ async fn example_function() -> Result<()> {
|
|||||||
## Logging and Tracing Guidelines
|
## Logging and Tracing Guidelines
|
||||||
|
|
||||||
### 1. Tracing Usage
|
### 1. Tracing Usage
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[tracing::instrument(skip(self, data))]
|
#[tracing::instrument(skip(self, data))]
|
||||||
async fn process_data(&self, data: &[u8]) -> Result<()> {
|
async fn process_data(&self, data: &[u8]) -> Result<()> {
|
||||||
@@ -243,6 +281,7 @@ async fn process_data(&self, data: &[u8]) -> Result<()> {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 2. Log Levels
|
### 2. Log Levels
|
||||||
|
|
||||||
- `error!`: System errors requiring immediate attention
|
- `error!`: System errors requiring immediate attention
|
||||||
- `warn!`: Warning information that may affect functionality
|
- `warn!`: Warning information that may affect functionality
|
||||||
- `info!`: Important business information
|
- `info!`: Important business information
|
||||||
@@ -250,6 +289,7 @@ async fn process_data(&self, data: &[u8]) -> Result<()> {
|
|||||||
- `trace!`: Detailed execution paths
|
- `trace!`: Detailed execution paths
|
||||||
|
|
||||||
### 3. Structured Logging
|
### 3. Structured Logging
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
info!(
|
info!(
|
||||||
counter.rustfs_api_requests_total = 1_u64,
|
counter.rustfs_api_requests_total = 1_u64,
|
||||||
@@ -262,45 +302,213 @@ info!(
|
|||||||
## Error Handling Guidelines
|
## Error Handling Guidelines
|
||||||
|
|
||||||
### 1. Error Type Definition
|
### 1. Error Type Definition
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[derive(Debug, thiserror::Error)]
|
// Use thiserror for module-specific error types
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum MyError {
|
pub enum MyError {
|
||||||
#[error("IO error: {0}")]
|
#[error("IO error: {0}")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("Storage error: {0}")]
|
||||||
|
Storage(#[from] ecstore::error::StorageError),
|
||||||
|
|
||||||
#[error("Custom error: {message}")]
|
#[error("Custom error: {message}")]
|
||||||
Custom { message: String },
|
Custom { message: String },
|
||||||
|
|
||||||
|
#[error("File not found: {path}")]
|
||||||
|
FileNotFound { path: String },
|
||||||
|
|
||||||
|
#[error("Invalid configuration: {0}")]
|
||||||
|
InvalidConfig(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide Result type alias for the module
|
||||||
|
pub type Result<T> = core::result::Result<T, MyError>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Error Helper Methods
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl MyError {
|
||||||
|
/// Create error from any compatible error type
|
||||||
|
pub fn other<E>(error: E) -> Self
|
||||||
|
where
|
||||||
|
E: Into<Box<dyn std::error::Error + Send + Sync>>,
|
||||||
|
{
|
||||||
|
MyError::Io(std::io::Error::other(error))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Error Conversion
|
### 3. Error Conversion Between Modules
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub fn to_s3_error(err: Error) -> S3Error {
|
// Convert between different module error types
|
||||||
if let Some(storage_err) = err.downcast_ref::<StorageError>() {
|
impl From<ecstore::error::StorageError> for MyError {
|
||||||
match storage_err {
|
fn from(e: ecstore::error::StorageError) -> Self {
|
||||||
StorageError::ObjectNotFound(bucket, object) => {
|
match e {
|
||||||
s3_error!(NoSuchKey, "{}/{}", bucket, object)
|
ecstore::error::StorageError::FileNotFound => {
|
||||||
|
MyError::FileNotFound { path: "unknown".to_string() }
|
||||||
}
|
}
|
||||||
// Other error types...
|
_ => MyError::Storage(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide reverse conversion when needed
|
||||||
|
impl From<MyError> for ecstore::error::StorageError {
|
||||||
|
fn from(e: MyError) -> Self {
|
||||||
|
match e {
|
||||||
|
MyError::FileNotFound { .. } => ecstore::error::StorageError::FileNotFound,
|
||||||
|
MyError::Storage(e) => e,
|
||||||
|
_ => ecstore::error::StorageError::other(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Default error handling
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Error Context
|
### 4. Error Context and Propagation
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Add error context
|
// Use ? operator for clean error propagation
|
||||||
.map_err(|e| Error::from_string(format!("Failed to process {}: {}", path, e)))?
|
async fn example_function() -> Result<()> {
|
||||||
|
let data = read_file("path").await?;
|
||||||
|
process_data(data).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add context to errors
|
||||||
|
fn process_with_context(path: &str) -> Result<()> {
|
||||||
|
std::fs::read(path)
|
||||||
|
.map_err(|e| MyError::Custom {
|
||||||
|
message: format!("Failed to read {}: {}", path, e)
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. API Error Conversion (S3 Example)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Convert storage errors to API-specific errors
|
||||||
|
use s3s::{S3Error, S3ErrorCode};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ApiError {
|
||||||
|
pub code: S3ErrorCode,
|
||||||
|
pub message: String,
|
||||||
|
pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ecstore::error::StorageError> for ApiError {
|
||||||
|
fn from(err: ecstore::error::StorageError) -> Self {
|
||||||
|
let code = match &err {
|
||||||
|
ecstore::error::StorageError::BucketNotFound(_) => S3ErrorCode::NoSuchBucket,
|
||||||
|
ecstore::error::StorageError::ObjectNotFound(_, _) => S3ErrorCode::NoSuchKey,
|
||||||
|
ecstore::error::StorageError::BucketExists(_) => S3ErrorCode::BucketAlreadyExists,
|
||||||
|
ecstore::error::StorageError::InvalidArgument(_, _, _) => S3ErrorCode::InvalidArgument,
|
||||||
|
ecstore::error::StorageError::MethodNotAllowed => S3ErrorCode::MethodNotAllowed,
|
||||||
|
ecstore::error::StorageError::StorageFull => S3ErrorCode::ServiceUnavailable,
|
||||||
|
_ => S3ErrorCode::InternalError,
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiError {
|
||||||
|
code,
|
||||||
|
message: err.to_string(),
|
||||||
|
source: Some(Box::new(err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ApiError> for S3Error {
|
||||||
|
fn from(err: ApiError) -> Self {
|
||||||
|
let mut s3e = S3Error::with_message(err.code, err.message);
|
||||||
|
if let Some(source) = err.source {
|
||||||
|
s3e.set_source(source);
|
||||||
|
}
|
||||||
|
s3e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Error Handling Best Practices
|
||||||
|
|
||||||
|
#### Pattern Matching and Error Classification
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Use pattern matching for specific error handling
|
||||||
|
async fn handle_storage_operation() -> Result<()> {
|
||||||
|
match storage.get_object("bucket", "key").await {
|
||||||
|
Ok(object) => process_object(object),
|
||||||
|
Err(ecstore::error::StorageError::ObjectNotFound(bucket, key)) => {
|
||||||
|
warn!("Object not found: {}/{}", bucket, key);
|
||||||
|
create_default_object(bucket, key).await
|
||||||
|
}
|
||||||
|
Err(ecstore::error::StorageError::BucketNotFound(bucket)) => {
|
||||||
|
error!("Bucket not found: {}", bucket);
|
||||||
|
Err(MyError::Custom {
|
||||||
|
message: format!("Bucket {} does not exist", bucket)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Storage operation failed: {}", e);
|
||||||
|
Err(MyError::Storage(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Error Aggregation and Reporting
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Collect and report multiple errors
|
||||||
|
pub fn validate_configuration(config: &Config) -> Result<()> {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
if config.bucket_name.is_empty() {
|
||||||
|
errors.push("Bucket name cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.region.is_empty() {
|
||||||
|
errors.push("Region must be specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return Err(MyError::Custom {
|
||||||
|
message: format!("Configuration validation failed: {}", errors.join(", "))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Contextual Error Information
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Add operation context to errors
|
||||||
|
#[tracing::instrument(skip(self))]
|
||||||
|
async fn upload_file(&self, bucket: &str, key: &str, data: Vec<u8>) -> Result<()> {
|
||||||
|
self.storage
|
||||||
|
.put_object(bucket, key, data)
|
||||||
|
.await
|
||||||
|
.map_err(|e| MyError::Custom {
|
||||||
|
message: format!("Failed to upload {}/{}: {}", bucket, key, e)
|
||||||
|
})
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Performance Optimization Guidelines
|
## Performance Optimization Guidelines
|
||||||
|
|
||||||
### 1. Memory Management
|
### 1. Memory Management
|
||||||
|
|
||||||
- Use `Bytes` instead of `Vec<u8>` for zero-copy operations
|
- Use `Bytes` instead of `Vec<u8>` for zero-copy operations
|
||||||
- Avoid unnecessary cloning, use reference passing
|
- Avoid unnecessary cloning, use reference passing
|
||||||
- Use `Arc` for sharing large objects
|
- Use `Arc` for sharing large objects
|
||||||
|
|
||||||
### 2. Concurrency Optimization
|
### 2. Concurrency Optimization
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Use join_all for concurrent operations
|
// Use join_all for concurrent operations
|
||||||
let futures = disks.iter().map(|disk| disk.operation());
|
let futures = disks.iter().map(|disk| disk.operation());
|
||||||
@@ -308,12 +516,14 @@ let results = join_all(futures).await;
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 3. Caching Strategy
|
### 3. Caching Strategy
|
||||||
|
|
||||||
- Use `lazy_static` or `OnceCell` for global caching
|
- Use `lazy_static` or `OnceCell` for global caching
|
||||||
- Implement LRU cache to avoid memory leaks
|
- Implement LRU cache to avoid memory leaks
|
||||||
|
|
||||||
## Testing Guidelines
|
## Testing Guidelines
|
||||||
|
|
||||||
### 1. Unit Tests
|
### 1. Unit Tests
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@@ -331,14 +541,55 @@ mod tests {
|
|||||||
fn test_with_cases(input: &str, expected: &str) {
|
fn test_with_cases(input: &str, expected: &str) {
|
||||||
assert_eq!(function(input), expected);
|
assert_eq!(function(input), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_conversion() {
|
||||||
|
use ecstore::error::StorageError;
|
||||||
|
|
||||||
|
let storage_err = StorageError::BucketNotFound("test-bucket".to_string());
|
||||||
|
let api_err: ApiError = storage_err.into();
|
||||||
|
|
||||||
|
assert_eq!(api_err.code, S3ErrorCode::NoSuchBucket);
|
||||||
|
assert!(api_err.message.contains("test-bucket"));
|
||||||
|
assert!(api_err.source.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_types() {
|
||||||
|
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
|
||||||
|
let my_err = MyError::Io(io_err);
|
||||||
|
|
||||||
|
// Test error matching
|
||||||
|
match my_err {
|
||||||
|
MyError::Io(_) => {}, // Expected
|
||||||
|
_ => panic!("Unexpected error type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_context() {
|
||||||
|
let result = process_with_context("nonexistent_file.txt");
|
||||||
|
assert!(result.is_err());
|
||||||
|
|
||||||
|
let err = result.unwrap_err();
|
||||||
|
match err {
|
||||||
|
MyError::Custom { message } => {
|
||||||
|
assert!(message.contains("Failed to read"));
|
||||||
|
assert!(message.contains("nonexistent_file.txt"));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Custom error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Integration Tests
|
### 2. Integration Tests
|
||||||
|
|
||||||
- Use `e2e_test` module for end-to-end testing
|
- Use `e2e_test` module for end-to-end testing
|
||||||
- Simulate real storage environments
|
- Simulate real storage environments
|
||||||
|
|
||||||
### 3. Test Quality Standards
|
### 3. Test Quality Standards
|
||||||
|
|
||||||
- Write meaningful test cases that verify actual functionality
|
- Write meaningful test cases that verify actual functionality
|
||||||
- Avoid placeholder or debug content like "debug 111", "test test", etc.
|
- Avoid placeholder or debug content like "debug 111", "test test", etc.
|
||||||
- Use descriptive test names that clearly indicate what is being tested
|
- Use descriptive test names that clearly indicate what is being tested
|
||||||
@@ -348,9 +599,11 @@ mod tests {
|
|||||||
## Cross-Platform Compatibility Guidelines
|
## Cross-Platform Compatibility Guidelines
|
||||||
|
|
||||||
### 1. CPU Architecture Compatibility
|
### 1. CPU Architecture Compatibility
|
||||||
|
|
||||||
- **Always consider multi-platform and different CPU architecture compatibility** when writing code
|
- **Always consider multi-platform and different CPU architecture compatibility** when writing code
|
||||||
- Support major architectures: x86_64, aarch64 (ARM64), and other target platforms
|
- Support major architectures: x86_64, aarch64 (ARM64), and other target platforms
|
||||||
- Use conditional compilation for architecture-specific code:
|
- Use conditional compilation for architecture-specific code:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn optimized_x86_64_function() { /* x86_64 specific implementation */ }
|
fn optimized_x86_64_function() { /* x86_64 specific implementation */ }
|
||||||
@@ -363,16 +616,19 @@ fn generic_function() { /* Generic fallback implementation */ }
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 2. Platform-Specific Dependencies
|
### 2. Platform-Specific Dependencies
|
||||||
|
|
||||||
- Use feature flags for platform-specific dependencies
|
- Use feature flags for platform-specific dependencies
|
||||||
- Provide fallback implementations for unsupported platforms
|
- Provide fallback implementations for unsupported platforms
|
||||||
- Test on multiple architectures in CI/CD pipeline
|
- Test on multiple architectures in CI/CD pipeline
|
||||||
|
|
||||||
### 3. Endianness Considerations
|
### 3. Endianness Considerations
|
||||||
|
|
||||||
- Use explicit byte order conversion when dealing with binary data
|
- Use explicit byte order conversion when dealing with binary data
|
||||||
- Prefer `to_le_bytes()`, `from_le_bytes()` for consistent little-endian format
|
- Prefer `to_le_bytes()`, `from_le_bytes()` for consistent little-endian format
|
||||||
- Use `byteorder` crate for complex binary format handling
|
- Use `byteorder` crate for complex binary format handling
|
||||||
|
|
||||||
### 4. SIMD and Performance Optimizations
|
### 4. SIMD and Performance Optimizations
|
||||||
|
|
||||||
- Use portable SIMD libraries like `wide` or `packed_simd`
|
- Use portable SIMD libraries like `wide` or `packed_simd`
|
||||||
- Provide fallback implementations for non-SIMD architectures
|
- Provide fallback implementations for non-SIMD architectures
|
||||||
- Use runtime feature detection when appropriate
|
- Use runtime feature detection when appropriate
|
||||||
@@ -380,10 +636,12 @@ fn generic_function() { /* Generic fallback implementation */ }
|
|||||||
## Security Guidelines
|
## Security Guidelines
|
||||||
|
|
||||||
### 1. Memory Safety
|
### 1. Memory Safety
|
||||||
|
|
||||||
- Disable `unsafe` code (workspace.lints.rust.unsafe_code = "deny")
|
- Disable `unsafe` code (workspace.lints.rust.unsafe_code = "deny")
|
||||||
- Use `rustls` instead of `openssl`
|
- Use `rustls` instead of `openssl`
|
||||||
|
|
||||||
### 2. Authentication and Authorization
|
### 2. Authentication and Authorization
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Use IAM system for permission checks
|
// Use IAM system for permission checks
|
||||||
let identity = iam.authenticate(&access_key, &secret_key).await?;
|
let identity = iam.authenticate(&access_key, &secret_key).await?;
|
||||||
@@ -393,11 +651,13 @@ iam.authorize(&identity, &action, &resource).await?;
|
|||||||
## Configuration Management Guidelines
|
## Configuration Management Guidelines
|
||||||
|
|
||||||
### 1. Environment Variables
|
### 1. Environment Variables
|
||||||
|
|
||||||
- Use `RUSTFS_` prefix
|
- Use `RUSTFS_` prefix
|
||||||
- Support both configuration files and environment variables
|
- Support both configuration files and environment variables
|
||||||
- Provide reasonable default values
|
- Provide reasonable default values
|
||||||
|
|
||||||
### 2. Configuration Structure
|
### 2. Configuration Structure
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@@ -411,10 +671,12 @@ pub struct Config {
|
|||||||
## Dependency Management Guidelines
|
## Dependency Management Guidelines
|
||||||
|
|
||||||
### 1. Workspace Dependencies
|
### 1. Workspace Dependencies
|
||||||
|
|
||||||
- Manage versions uniformly at workspace level
|
- Manage versions uniformly at workspace level
|
||||||
- Use `workspace = true` to inherit configuration
|
- Use `workspace = true` to inherit configuration
|
||||||
|
|
||||||
### 2. Feature Flags
|
### 2. Feature Flags
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
[features]
|
[features]
|
||||||
default = ["file"]
|
default = ["file"]
|
||||||
@@ -425,15 +687,18 @@ kafka = ["dep:rdkafka"]
|
|||||||
## Deployment and Operations Guidelines
|
## Deployment and Operations Guidelines
|
||||||
|
|
||||||
### 1. Containerization
|
### 1. Containerization
|
||||||
|
|
||||||
- Provide Dockerfile and docker-compose configuration
|
- Provide Dockerfile and docker-compose configuration
|
||||||
- Support multi-stage builds to optimize image size
|
- Support multi-stage builds to optimize image size
|
||||||
|
|
||||||
### 2. Observability
|
### 2. Observability
|
||||||
|
|
||||||
- Integrate OpenTelemetry for distributed tracing
|
- Integrate OpenTelemetry for distributed tracing
|
||||||
- Support Prometheus metrics collection
|
- Support Prometheus metrics collection
|
||||||
- Provide Grafana dashboards
|
- Provide Grafana dashboards
|
||||||
|
|
||||||
### 3. Health Checks
|
### 3. Health Checks
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Implement health check endpoint
|
// Implement health check endpoint
|
||||||
async fn health_check() -> Result<HealthStatus> {
|
async fn health_check() -> Result<HealthStatus> {
|
||||||
@@ -444,6 +709,7 @@ async fn health_check() -> Result<HealthStatus> {
|
|||||||
## Code Review Checklist
|
## Code Review Checklist
|
||||||
|
|
||||||
### 1. **Code Formatting and Quality (MANDATORY)**
|
### 1. **Code Formatting and Quality (MANDATORY)**
|
||||||
|
|
||||||
- [ ] **Code is properly formatted** (`cargo fmt --all --check` passes)
|
- [ ] **Code is properly formatted** (`cargo fmt --all --check` passes)
|
||||||
- [ ] **All clippy warnings are resolved** (`cargo clippy --all-targets --all-features -- -D warnings` passes)
|
- [ ] **All clippy warnings are resolved** (`cargo clippy --all-targets --all-features -- -D warnings` passes)
|
||||||
- [ ] **Code compiles successfully** (`cargo check --all-targets` passes)
|
- [ ] **Code compiles successfully** (`cargo check --all-targets` passes)
|
||||||
@@ -451,27 +717,32 @@ async fn health_check() -> Result<HealthStatus> {
|
|||||||
- [ ] **No formatting-related changes** mixed with functional changes (separate commits)
|
- [ ] **No formatting-related changes** mixed with functional changes (separate commits)
|
||||||
|
|
||||||
### 2. Functionality
|
### 2. Functionality
|
||||||
|
|
||||||
- [ ] Are all error cases properly handled?
|
- [ ] Are all error cases properly handled?
|
||||||
- [ ] Is there appropriate logging?
|
- [ ] Is there appropriate logging?
|
||||||
- [ ] Is there necessary test coverage?
|
- [ ] Is there necessary test coverage?
|
||||||
|
|
||||||
### 3. Performance
|
### 3. Performance
|
||||||
|
|
||||||
- [ ] Are unnecessary memory allocations avoided?
|
- [ ] Are unnecessary memory allocations avoided?
|
||||||
- [ ] Are async operations used correctly?
|
- [ ] Are async operations used correctly?
|
||||||
- [ ] Are there potential deadlock risks?
|
- [ ] Are there potential deadlock risks?
|
||||||
|
|
||||||
### 4. Security
|
### 4. Security
|
||||||
|
|
||||||
- [ ] Are input parameters properly validated?
|
- [ ] Are input parameters properly validated?
|
||||||
- [ ] Are there appropriate permission checks?
|
- [ ] Are there appropriate permission checks?
|
||||||
- [ ] Is information leakage avoided?
|
- [ ] Is information leakage avoided?
|
||||||
|
|
||||||
### 5. Cross-Platform Compatibility
|
### 5. Cross-Platform Compatibility
|
||||||
|
|
||||||
- [ ] Does the code work on different CPU architectures (x86_64, aarch64)?
|
- [ ] Does the code work on different CPU architectures (x86_64, aarch64)?
|
||||||
- [ ] Are platform-specific features properly gated with conditional compilation?
|
- [ ] Are platform-specific features properly gated with conditional compilation?
|
||||||
- [ ] Is byte order handling correct for binary data?
|
- [ ] Is byte order handling correct for binary data?
|
||||||
- [ ] Are there appropriate fallback implementations for unsupported platforms?
|
- [ ] Are there appropriate fallback implementations for unsupported platforms?
|
||||||
|
|
||||||
### 6. Code Commits and Documentation
|
### 6. Code Commits and Documentation
|
||||||
|
|
||||||
- [ ] Does it comply with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)?
|
- [ ] Does it comply with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)?
|
||||||
- [ ] Are commit messages concise and under 72 characters for the title line?
|
- [ ] Are commit messages concise and under 72 characters for the title line?
|
||||||
- [ ] Commit titles should be concise and in English, avoid Chinese
|
- [ ] Commit titles should be concise and in English, avoid Chinese
|
||||||
@@ -480,6 +751,7 @@ async fn health_check() -> Result<HealthStatus> {
|
|||||||
## Common Patterns and Best Practices
|
## Common Patterns and Best Practices
|
||||||
|
|
||||||
### 1. Resource Management
|
### 1. Resource Management
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Use RAII pattern for resource management
|
// Use RAII pattern for resource management
|
||||||
pub struct ResourceGuard {
|
pub struct ResourceGuard {
|
||||||
@@ -494,6 +766,7 @@ impl Drop for ResourceGuard {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 2. Dependency Injection
|
### 2. Dependency Injection
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Use dependency injection pattern
|
// Use dependency injection pattern
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
@@ -503,6 +776,7 @@ pub struct Service {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 3. Graceful Shutdown
|
### 3. Graceful Shutdown
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Implement graceful shutdown
|
// Implement graceful shutdown
|
||||||
async fn shutdown_gracefully(shutdown_rx: &mut Receiver<()>) {
|
async fn shutdown_gracefully(shutdown_rx: &mut Receiver<()>) {
|
||||||
@@ -521,16 +795,19 @@ async fn shutdown_gracefully(shutdown_rx: &mut Receiver<()>) {
|
|||||||
## Domain-Specific Guidelines
|
## Domain-Specific Guidelines
|
||||||
|
|
||||||
### 1. Storage Operations
|
### 1. Storage Operations
|
||||||
|
|
||||||
- All storage operations must support erasure coding
|
- All storage operations must support erasure coding
|
||||||
- Implement read/write quorum mechanisms
|
- Implement read/write quorum mechanisms
|
||||||
- Support data integrity verification
|
- Support data integrity verification
|
||||||
|
|
||||||
### 2. Network Communication
|
### 2. Network Communication
|
||||||
|
|
||||||
- Use gRPC for internal service communication
|
- Use gRPC for internal service communication
|
||||||
- HTTP/HTTPS support for S3-compatible API
|
- HTTP/HTTPS support for S3-compatible API
|
||||||
- Implement connection pooling and retry mechanisms
|
- Implement connection pooling and retry mechanisms
|
||||||
|
|
||||||
### 3. Metadata Management
|
### 3. Metadata Management
|
||||||
|
|
||||||
- Use FlatBuffers for serialization
|
- Use FlatBuffers for serialization
|
||||||
- Support version control and migration
|
- Support version control and migration
|
||||||
- Implement metadata caching
|
- Implement metadata caching
|
||||||
@@ -540,40 +817,94 @@ These rules should serve as guiding principles when developing the RustFS projec
|
|||||||
### 4. Code Operations
|
### 4. Code Operations
|
||||||
|
|
||||||
#### Branch Management
|
#### Branch Management
|
||||||
- **🚨 CRITICAL: NEVER modify code directly on main or master branch - THIS IS ABSOLUTELY FORBIDDEN 🚨**
|
|
||||||
- **⚠️ ANY DIRECT COMMITS TO MASTER/MAIN WILL BE REJECTED AND MUST BE REVERTED IMMEDIATELY ⚠️**
|
- **🚨 CRITICAL: NEVER modify code directly on main or master branch - THIS IS ABSOLUTELY FORBIDDEN 🚨**
|
||||||
- **Always work on feature branches - NO EXCEPTIONS**
|
- **⚠️ ANY DIRECT COMMITS TO MASTER/MAIN WILL BE REJECTED AND MUST BE REVERTED IMMEDIATELY ⚠️**
|
||||||
- Always check the .cursorrules file before starting to ensure you understand the project guidelines
|
- **🔒 ALL CHANGES MUST GO THROUGH PULL REQUESTS - NO DIRECT COMMITS TO MAIN UNDER ANY CIRCUMSTANCES 🔒**
|
||||||
- **MANDATORY workflow for ALL changes:**
|
- **Always work on feature branches - NO EXCEPTIONS**
|
||||||
|
- Always check the .cursorrules file before starting to ensure you understand the project guidelines
|
||||||
|
- **MANDATORY workflow for ALL changes:**
|
||||||
1. `git checkout main` (switch to main branch)
|
1. `git checkout main` (switch to main branch)
|
||||||
2. `git pull` (get latest changes)
|
2. `git pull` (get latest changes)
|
||||||
3. `git checkout -b feat/your-feature-name` (create and switch to feature branch)
|
3. `git checkout -b feat/your-feature-name` (create and switch to feature branch)
|
||||||
4. Make your changes ONLY on the feature branch
|
4. Make your changes ONLY on the feature branch
|
||||||
5. Test thoroughly before committing
|
5. Test thoroughly before committing
|
||||||
6. Commit and push to the feature branch
|
6. Commit and push to the feature branch
|
||||||
7. Create a pull request for code review
|
7. **Create a pull request for code review - THIS IS THE ONLY WAY TO MERGE TO MAIN**
|
||||||
- Use descriptive branch names following the pattern: `feat/feature-name`, `fix/issue-name`, `refactor/component-name`, etc.
|
8. **Wait for PR approval before merging - NEVER merge your own PRs without review**
|
||||||
- **Double-check current branch before ANY commit: `git branch` to ensure you're NOT on main/master**
|
- Use descriptive branch names following the pattern: `feat/feature-name`, `fix/issue-name`, `refactor/component-name`, etc.
|
||||||
- Ensure all changes are made on feature branches and merged through pull requests
|
- **Double-check current branch before ANY commit: `git branch` to ensure you're NOT on main/master**
|
||||||
|
- **Pull Request Requirements:**
|
||||||
|
- All changes must be submitted via PR regardless of size or urgency
|
||||||
|
- PRs must include comprehensive description and testing information
|
||||||
|
- PRs must pass all CI/CD checks before merging
|
||||||
|
- PRs require at least one approval from code reviewers
|
||||||
|
- Even hotfixes and emergency changes must go through PR process
|
||||||
|
- **Enforcement:**
|
||||||
|
- Main branch should be protected with branch protection rules
|
||||||
|
- Direct pushes to main should be blocked by repository settings
|
||||||
|
- Any accidental direct commits to main must be immediately reverted via PR
|
||||||
|
|
||||||
#### Development Workflow
|
#### Development Workflow
|
||||||
- Use English for all code comments, documentation, and variable names
|
|
||||||
- Write meaningful and descriptive names for variables, functions, and methods
|
## 🎯 **Core Development Principles**
|
||||||
- Avoid meaningless test content like "debug 111" or placeholder values
|
|
||||||
- Before each change, carefully read the existing code to ensure you understand the code structure and implementation, do not break existing logic implementation, do not introduce new issues
|
- **🔴 Every change must be precise - don't modify unless you're confident**
|
||||||
- Ensure each change provides sufficient test cases to guarantee code correctness
|
- Carefully analyze code logic and ensure complete understanding before making changes
|
||||||
- Do not arbitrarily modify numbers and constants in test cases, carefully analyze their meaning to ensure test case correctness
|
- When uncertain, prefer asking users or consulting documentation over blind modifications
|
||||||
- When writing or modifying tests, check existing test cases to ensure they have scientific naming and rigorous logic testing, if not compliant, modify test cases to ensure scientific and rigorous testing
|
- Use small iterative steps, modify only necessary parts at a time
|
||||||
- **Before committing any changes, run `cargo clippy --all-targets --all-features -- -D warnings` to ensure all code passes Clippy checks**
|
- Evaluate impact scope before changes to ensure no new issues are introduced
|
||||||
- After each development completion, first git add . then git commit -m "feat: feature description" or "fix: issue description", ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
|
||||||
- **Keep commit messages concise and under 72 characters** for the title line, use body for detailed explanations if needed
|
- **🚀 GitHub PR creation prioritizes gh command usage**
|
||||||
- After each development completion, first git push to remote repository
|
- Prefer using `gh pr create` command to create Pull Requests
|
||||||
- After each change completion, summarize the changes, do not create summary files, provide a brief change description, ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
- Avoid having users manually create PRs through web interface
|
||||||
- Provide change descriptions needed for PR in the conversation, ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
- Provide clear and professional PR titles and descriptions
|
||||||
- **Always provide PR descriptions in English** after completing any changes, including:
|
- Using `gh` commands ensures better integration and automation
|
||||||
- Clear and concise title following Conventional Commits format
|
|
||||||
- Detailed description of what was changed and why
|
## 📝 **Code Quality Requirements**
|
||||||
- List of key changes and improvements
|
|
||||||
- Any breaking changes or migration notes if applicable
|
- Use English for all code comments, documentation, and variable names
|
||||||
- Testing information and verification steps
|
- Write meaningful and descriptive names for variables, functions, and methods
|
||||||
- **Provide PR descriptions in copyable markdown format** enclosed in code blocks for easy one-click copying
|
- Avoid meaningless test content like "debug 111" or placeholder values
|
||||||
|
- Before each change, carefully read the existing code to ensure you understand the code structure and implementation, do not break existing logic implementation, do not introduce new issues
|
||||||
|
- Ensure each change provides sufficient test cases to guarantee code correctness
|
||||||
|
- Do not arbitrarily modify numbers and constants in test cases, carefully analyze their meaning to ensure test case correctness
|
||||||
|
- When writing or modifying tests, check existing test cases to ensure they have scientific naming and rigorous logic testing, if not compliant, modify test cases to ensure scientific and rigorous testing
|
||||||
|
- **Before committing any changes, run `cargo clippy --all-targets --all-features -- -D warnings` to ensure all code passes Clippy checks**
|
||||||
|
- After each development completion, first git add . then git commit -m "feat: feature description" or "fix: issue description", ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||||
|
- **Keep commit messages concise and under 72 characters** for the title line, use body for detailed explanations if needed
|
||||||
|
- After each development completion, first git push to remote repository
|
||||||
|
- After each change completion, summarize the changes, do not create summary files, provide a brief change description, ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||||
|
- Provide change descriptions needed for PR in the conversation, ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||||
|
- **Always provide PR descriptions in English** after completing any changes, including:
|
||||||
|
- Clear and concise title following Conventional Commits format
|
||||||
|
- Detailed description of what was changed and why
|
||||||
|
- List of key changes and improvements
|
||||||
|
- Any breaking changes or migration notes if applicable
|
||||||
|
- Testing information and verification steps
|
||||||
|
- **Provide PR descriptions in copyable markdown format** enclosed in code blocks for easy one-click copying
|
||||||
|
|
||||||
|
## 🚫 AI 文档生成限制
|
||||||
|
|
||||||
|
### 禁止生成总结文档
|
||||||
|
|
||||||
|
- **严格禁止创建任何形式的AI生成总结文档**
|
||||||
|
- **不得创建包含大量表情符号、详细格式化表格和典型AI风格的文档**
|
||||||
|
- **不得在项目中生成以下类型的文档:**
|
||||||
|
- 基准测试总结文档(BENCHMARK*.md)
|
||||||
|
- 实现对比分析文档(IMPLEMENTATION_COMPARISON*.md)
|
||||||
|
- 性能分析报告文档
|
||||||
|
- 架构总结文档
|
||||||
|
- 功能对比文档
|
||||||
|
- 任何带有大量表情符号和格式化内容的文档
|
||||||
|
- **如果需要文档,请只在用户明确要求时创建,并保持简洁实用的风格**
|
||||||
|
- **文档应当专注于实际需要的信息,避免过度格式化和装饰性内容**
|
||||||
|
- **任何发现的AI生成总结文档都应该立即删除**
|
||||||
|
|
||||||
|
### 允许的文档类型
|
||||||
|
|
||||||
|
- README.md(项目介绍,保持简洁)
|
||||||
|
- 技术文档(仅在明确需要时创建)
|
||||||
|
- 用户手册(仅在明确需要时创建)
|
||||||
|
- API文档(从代码生成)
|
||||||
|
- 变更日志(CHANGELOG.md)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM m.daocloud.io/docker.io/library/ubuntu:22.04
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
ENV LANG C.UTF-8
|
ENV LANG C.UTF-8
|
||||||
|
|
||||||
@@ -7,9 +7,10 @@ RUN sed -i s@http://.*archive.ubuntu.com@http://repo.huaweicloud.com@g /etc/apt/
|
|||||||
RUN apt-get clean && apt-get update && apt-get install wget git curl unzip gcc pkg-config libssl-dev lld libdbus-1-dev libwayland-dev libwebkit2gtk-4.1-dev libxdo-dev -y
|
RUN apt-get clean && apt-get update && apt-get install wget git curl unzip gcc pkg-config libssl-dev lld libdbus-1-dev libwayland-dev libwebkit2gtk-4.1-dev libxdo-dev -y
|
||||||
|
|
||||||
# install protoc
|
# install protoc
|
||||||
RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protoc-30.2-linux-x86_64.zip \
|
RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v31.1/protoc-31.1-linux-x86_64.zip \
|
||||||
&& unzip protoc-30.2-linux-x86_64.zip -d protoc3 \
|
&& unzip protoc-31.1-linux-x86_64.zip -d protoc3 \
|
||||||
&& mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc && mv protoc3/include/* /usr/local/include/ && rm -rf protoc-30.2-linux-x86_64.zip protoc3
|
&& mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc \
|
||||||
|
&& mv protoc3/include/* /usr/local/include/ && rm -rf protoc-31.1-linux-x86_64.zip protoc3
|
||||||
|
|
||||||
# install flatc
|
# install flatc
|
||||||
RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \
|
RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \
|
||||||
@@ -17,10 +18,7 @@ RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.
|
|||||||
&& mv flatc /usr/local/bin/ && chmod +x /usr/local/bin/flatc && rm -rf Linux.flatc.binary.g++-13.zip
|
&& mv flatc /usr/local/bin/ && chmod +x /usr/local/bin/flatc && rm -rf Linux.flatc.binary.g++-13.zip
|
||||||
|
|
||||||
# install rust
|
# install rust
|
||||||
ENV RUSTUP_DIST_SERVER="https://rsproxy.cn"
|
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
ENV RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup"
|
|
||||||
RUN curl -o rustup-init.sh --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh \
|
|
||||||
&& sh rustup-init.sh -y && rm -rf rustup-init.sh
|
|
||||||
|
|
||||||
COPY .docker/cargo.config.toml /root/.cargo/config.toml
|
COPY .docker/cargo.config.toml /root/.cargo/config.toml
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM m.daocloud.io/docker.io/library/rockylinux:9.3 AS builder
|
FROM rockylinux:9.3 AS builder
|
||||||
|
|
||||||
ENV LANG C.UTF-8
|
ENV LANG C.UTF-8
|
||||||
|
|
||||||
@@ -13,10 +13,10 @@ RUN dnf makecache
|
|||||||
RUN yum install wget git unzip gcc openssl-devel pkgconf-pkg-config -y
|
RUN yum install wget git unzip gcc openssl-devel pkgconf-pkg-config -y
|
||||||
|
|
||||||
# install protoc
|
# install protoc
|
||||||
RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protoc-30.2-linux-x86_64.zip \
|
RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v31.1/protoc-31.1-linux-x86_64.zip \
|
||||||
&& unzip protoc-30.2-linux-x86_64.zip -d protoc3 \
|
&& unzip protoc-31.1-linux-x86_64.zip -d protoc3 \
|
||||||
&& mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc \
|
&& mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc \
|
||||||
&& rm -rf protoc-30.2-linux-x86_64.zip protoc3
|
&& mv protoc3/include/* /usr/local/include/ && rm -rf protoc-31.1-linux-x86_64.zip protoc3
|
||||||
|
|
||||||
# install flatc
|
# install flatc
|
||||||
RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \
|
RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \
|
||||||
@@ -25,10 +25,7 @@ RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.
|
|||||||
&& rm -rf Linux.flatc.binary.g++-13.zip
|
&& rm -rf Linux.flatc.binary.g++-13.zip
|
||||||
|
|
||||||
# install rust
|
# install rust
|
||||||
ENV RUSTUP_DIST_SERVER="https://rsproxy.cn"
|
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
ENV RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup"
|
|
||||||
RUN curl -o rustup-init.sh --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh \
|
|
||||||
&& sh rustup-init.sh -y && rm -rf rustup-init.sh
|
|
||||||
|
|
||||||
COPY .docker/cargo.config.toml /root/.cargo/config.toml
|
COPY .docker/cargo.config.toml /root/.cargo/config.toml
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
FROM vault:1.13
|
|
||||||
|
|
||||||
# Configure Vault for dev mode
|
|
||||||
ENV VAULT_DEV_ROOT_TOKEN_ID=rustfs-root-token
|
|
||||||
ENV VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200
|
|
||||||
|
|
||||||
# Install curl for health checks
|
|
||||||
USER root
|
|
||||||
RUN apk add --no-cache curl jq
|
|
||||||
|
|
||||||
# Copy the Vault initialization script
|
|
||||||
COPY vault-init.sh /usr/local/bin/vault-init.sh
|
|
||||||
RUN chmod +x /usr/local/bin/vault-init.sh
|
|
||||||
|
|
||||||
# Switch back to vault user
|
|
||||||
USER vault
|
|
||||||
|
|
||||||
# Expose Vault port
|
|
||||||
EXPOSE 8200
|
|
||||||
|
|
||||||
# Start Vault in dev mode and run the initialization script
|
|
||||||
ENTRYPOINT ["sh", "-c", "vault server -dev & sleep 5 && vault-init.sh"]
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM m.daocloud.io/docker.io/library/ubuntu:22.04
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
ENV LANG C.UTF-8
|
ENV LANG C.UTF-8
|
||||||
|
|
||||||
@@ -7,9 +7,10 @@ RUN sed -i s@http://.*archive.ubuntu.com@http://repo.huaweicloud.com@g /etc/apt/
|
|||||||
RUN apt-get clean && apt-get update && apt-get install wget git curl unzip gcc pkg-config libssl-dev lld libdbus-1-dev libwayland-dev libwebkit2gtk-4.1-dev libxdo-dev -y
|
RUN apt-get clean && apt-get update && apt-get install wget git curl unzip gcc pkg-config libssl-dev lld libdbus-1-dev libwayland-dev libwebkit2gtk-4.1-dev libxdo-dev -y
|
||||||
|
|
||||||
# install protoc
|
# install protoc
|
||||||
RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protoc-30.2-linux-x86_64.zip \
|
RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v31.1/protoc-31.1-linux-x86_64.zip \
|
||||||
&& unzip protoc-30.2-linux-x86_64.zip -d protoc3 \
|
&& unzip protoc-31.1-linux-x86_64.zip -d protoc3 \
|
||||||
&& mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc && mv protoc3/include/* /usr/local/include/ && rm -rf protoc-30.2-linux-x86_64.zip protoc3
|
&& mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc \
|
||||||
|
&& mv protoc3/include/* /usr/local/include/ && rm -rf protoc-31.1-linux-x86_64.zip protoc3
|
||||||
|
|
||||||
# install flatc
|
# install flatc
|
||||||
RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \
|
RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \
|
||||||
@@ -17,10 +18,7 @@ RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.
|
|||||||
&& mv flatc /usr/local/bin/ && chmod +x /usr/local/bin/flatc && rm -rf Linux.flatc.binary.g++-13.zip
|
&& mv flatc /usr/local/bin/ && chmod +x /usr/local/bin/flatc && rm -rf Linux.flatc.binary.g++-13.zip
|
||||||
|
|
||||||
# install rust
|
# install rust
|
||||||
ENV RUSTUP_DIST_SERVER="https://rsproxy.cn"
|
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
ENV RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup"
|
|
||||||
RUN curl -o rustup-init.sh --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh \
|
|
||||||
&& sh rustup-init.sh -y && rm -rf rustup-init.sh
|
|
||||||
|
|
||||||
COPY .docker/cargo.config.toml /root/.cargo/config.toml
|
COPY .docker/cargo.config.toml /root/.cargo/config.toml
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
[source.crates-io]
|
[source.crates-io]
|
||||||
registry = "https://github.com/rust-lang/crates.io-index"
|
registry = "https://github.com/rust-lang/crates.io-index"
|
||||||
replace-with = 'rsproxy-sparse'
|
|
||||||
|
|
||||||
[source.rsproxy]
|
|
||||||
registry = "https://rsproxy.cn/crates.io-index"
|
|
||||||
[registries.rsproxy]
|
|
||||||
index = "https://rsproxy.cn/crates.io-index"
|
|
||||||
[source.rsproxy-sparse]
|
|
||||||
registry = "sparse+https://rsproxy.cn/index/"
|
|
||||||
|
|
||||||
[net]
|
[net]
|
||||||
git-fetch-with-cli = true
|
git-fetch-with-cli = true
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
services:
|
|
||||||
rustyvault:
|
|
||||||
build:
|
|
||||||
context: ./.docker
|
|
||||||
dockerfile: Dockerfile.rustyvault
|
|
||||||
container_name: rustyvault
|
|
||||||
hostname: rustyvault
|
|
||||||
ports:
|
|
||||||
- "8200:8200" # Vault API port
|
|
||||||
volumes:
|
|
||||||
- vault-data:/vault/data
|
|
||||||
- vault-config:/vault/config
|
|
||||||
cap_add:
|
|
||||||
- IPC_LOCK # Allow the vault to lock sensitive data in memory
|
|
||||||
environment:
|
|
||||||
- VAULT_DEV_ROOT_TOKEN_ID=rustfs-root-token
|
|
||||||
- VAULT_ADDR=http://0.0.0.0:8200
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "curl", "-s", "http://127.0.0.1:8200/v1/sys/health"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 3
|
|
||||||
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
driver: bridge
|
|
||||||
37
.docker/mqtt/config/emqx.conf
Normal file
37
.docker/mqtt/config/emqx.conf
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# 节点配置
|
||||||
|
node.name = "emqx@127.0.0.1"
|
||||||
|
node.cookie = "aBcDeFgHiJkLmNoPqRsTuVwXyZ012345"
|
||||||
|
node.data_dir = "/opt/emqx/data"
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
log.console = {level = info, enable = true}
|
||||||
|
log.file = {path = "/opt/emqx/log/emqx.log", enable = true, level = info}
|
||||||
|
|
||||||
|
# MQTT TCP 监听器
|
||||||
|
listeners.tcp.default = {bind = "0.0.0.0:1883", max_connections = 1000000, enable = true}
|
||||||
|
|
||||||
|
# MQTT SSL 监听器
|
||||||
|
listeners.ssl.default = {bind = "0.0.0.0:8883", enable = false}
|
||||||
|
|
||||||
|
# MQTT WebSocket 监听器
|
||||||
|
listeners.ws.default = {bind = "0.0.0.0:8083", enable = true}
|
||||||
|
|
||||||
|
# MQTT WebSocket SSL 监听器
|
||||||
|
listeners.wss.default = {bind = "0.0.0.0:8084", enable = false}
|
||||||
|
|
||||||
|
# 管理控制台
|
||||||
|
dashboard.listeners.http = {bind = "0.0.0.0:18083", enable = true}
|
||||||
|
|
||||||
|
# HTTP API
|
||||||
|
management.listeners.http = {bind = "0.0.0.0:8081", enable = true}
|
||||||
|
|
||||||
|
# 认证配置
|
||||||
|
authentication = [
|
||||||
|
{enable = true, mechanism = password_based, backend = built_in_database, user_id_type = username}
|
||||||
|
]
|
||||||
|
|
||||||
|
# 授权配置
|
||||||
|
authorization.sources = [{type = built_in_database, enable = true}]
|
||||||
|
|
||||||
|
# 持久化消息存储
|
||||||
|
message.storage.backend = built_in_database
|
||||||
9
.docker/mqtt/config/vm.args
Normal file
9
.docker/mqtt/config/vm.args
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-name emqx@127.0.0.1
|
||||||
|
-setcookie aBcDeFgHiJkLmNoPqRsTuVwXyZ012345
|
||||||
|
+P 2097152
|
||||||
|
+t 1048576
|
||||||
|
+zdbbl 32768
|
||||||
|
-kernel inet_dist_listen_min 6000
|
||||||
|
-kernel inet_dist_listen_max 6100
|
||||||
|
-smp enable
|
||||||
|
-mnesia dir "/opt/emqx/data/mnesia"
|
||||||
74
.docker/mqtt/docker-compose-more.yml
Normal file
74
.docker/mqtt/docker-compose-more.yml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
services:
|
||||||
|
emqx:
|
||||||
|
image: emqx/emqx:latest
|
||||||
|
container_name: emqx
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- EMQX_NODE__NAME=emqx@127.0.0.1
|
||||||
|
- EMQX_NODE__COOKIE=aBcDeFgHiJkLmNoPqRsTuVwXyZ012345
|
||||||
|
- EMQX_NODE__DATA_DIR=/opt/emqx/data
|
||||||
|
- EMQX_LOG__CONSOLE__LEVEL=info
|
||||||
|
- EMQX_LOG__CONSOLE__ENABLE=true
|
||||||
|
- EMQX_LOG__FILE__PATH=/opt/emqx/log/emqx.log
|
||||||
|
- EMQX_LOG__FILE__LEVEL=info
|
||||||
|
- EMQX_LOG__FILE__ENABLE=true
|
||||||
|
- EMQX_LISTENERS__TCP__DEFAULT__BIND=0.0.0.0:1883
|
||||||
|
- EMQX_LISTENERS__TCP__DEFAULT__MAX_CONNECTIONS=1000000
|
||||||
|
- EMQX_LISTENERS__TCP__DEFAULT__ENABLE=true
|
||||||
|
- EMQX_LISTENERS__SSL__DEFAULT__BIND=0.0.0.0:8883
|
||||||
|
- EMQX_LISTENERS__SSL__DEFAULT__ENABLE=false
|
||||||
|
- EMQX_LISTENERS__WS__DEFAULT__BIND=0.0.0.0:8083
|
||||||
|
- EMQX_LISTENERS__WS__DEFAULT__ENABLE=true
|
||||||
|
- EMQX_LISTENERS__WSS__DEFAULT__BIND=0.0.0.0:8084
|
||||||
|
- EMQX_LISTENERS__WSS__DEFAULT__ENABLE=false
|
||||||
|
- EMQX_DASHBOARD__LISTENERS__HTTP__BIND=0.0.0.0:18083
|
||||||
|
- EMQX_DASHBOARD__LISTENERS__HTTP__ENABLE=true
|
||||||
|
- EMQX_MANAGEMENT__LISTENERS__HTTP__BIND=0.0.0.0:8081
|
||||||
|
- EMQX_MANAGEMENT__LISTENERS__HTTP__ENABLE=true
|
||||||
|
- EMQX_AUTHENTICATION__1__ENABLE=true
|
||||||
|
- EMQX_AUTHENTICATION__1__MECHANISM=password_based
|
||||||
|
- EMQX_AUTHENTICATION__1__BACKEND=built_in_database
|
||||||
|
- EMQX_AUTHENTICATION__1__USER_ID_TYPE=username
|
||||||
|
- EMQX_AUTHORIZATION__SOURCES__1__TYPE=built_in_database
|
||||||
|
- EMQX_AUTHORIZATION__SOURCES__1__ENABLE=true
|
||||||
|
ports:
|
||||||
|
- "1883:1883" # MQTT TCP
|
||||||
|
- "8883:8883" # MQTT SSL
|
||||||
|
- "8083:8083" # MQTT WebSocket
|
||||||
|
- "8084:8084" # MQTT WebSocket SSL
|
||||||
|
- "18083:18083" # Web 管理控制台
|
||||||
|
- "8081:8081" # HTTP API
|
||||||
|
volumes:
|
||||||
|
- ./data:/opt/emqx/data
|
||||||
|
- ./log:/opt/emqx/log
|
||||||
|
- ./config:/opt/emqx/etc
|
||||||
|
networks:
|
||||||
|
- mqtt-net
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "/opt/emqx/bin/emqx_ctl", "status" ]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "100m"
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
mqtt-net:
|
||||||
|
driver: bridge
|
||||||
29
.docker/mqtt/docker-compose.yml
Normal file
29
.docker/mqtt/docker-compose.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
services:
|
||||||
|
emqx:
|
||||||
|
image: emqx/emqx:latest
|
||||||
|
container_name: emqx
|
||||||
|
ports:
|
||||||
|
- "1883:1883"
|
||||||
|
- "8083:8083"
|
||||||
|
- "8084:8084"
|
||||||
|
- "8883:8883"
|
||||||
|
- "18083:18083"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
driver: bridge
|
||||||
@@ -22,21 +22,6 @@ docker compose -f docker-compose.yml up -d
|
|||||||
|
|
||||||
## 配置可观测性
|
## 配置可观测性
|
||||||
|
|
||||||
### 创建配置文件
|
```shell
|
||||||
|
export RUSTFS_OBS_ENDPOINT="http://localhost:4317" # OpenTelemetry Collector 地址
|
||||||
1. 进入 `deploy/config` 目录
|
```
|
||||||
2. 复制示例配置:`cp obs.toml.example obs.toml`
|
|
||||||
3. 编辑 `obs.toml` 配置文件,修改以下关键参数:
|
|
||||||
|
|
||||||
| 配置项 | 说明 | 示例值 |
|
|
||||||
|-----------------|----------------------------|-----------------------|
|
|
||||||
| endpoint | OpenTelemetry Collector 地址 | http://localhost:4317 |
|
|
||||||
| service_name | 服务名称 | rustfs |
|
|
||||||
| service_version | 服务版本 | 1.0.0 |
|
|
||||||
| environment | 运行环境 | production |
|
|
||||||
| meter_interval | 指标导出间隔 (秒) | 30 |
|
|
||||||
| sample_ratio | 采样率 | 1.0 |
|
|
||||||
| use_stdout | 是否输出到控制台 | true/false |
|
|
||||||
| logger_level | 日志级别 | info |
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
[observability]
|
|
||||||
endpoint = "http://otel-collector:4317" # Default is "http://localhost:4317" if not specified
|
|
||||||
use_stdout = false # Output with stdout, true output, false no output
|
|
||||||
sample_ratio = 2.0
|
|
||||||
meter_interval = 30
|
|
||||||
service_name = "rustfs"
|
|
||||||
service_version = "0.1.0"
|
|
||||||
environments = "production"
|
|
||||||
logger_level = "debug"
|
|
||||||
local_logging_enabled = true
|
|
||||||
|
|
||||||
#[[sinks]]
|
|
||||||
#type = "Kafka"
|
|
||||||
#brokers = "localhost:9092"
|
|
||||||
#topic = "logs"
|
|
||||||
#batch_size = 100 # Default is 100 if not specified
|
|
||||||
#batch_timeout_ms = 1000 # Default is 1000ms if not specified
|
|
||||||
#
|
|
||||||
#[[sinks]]
|
|
||||||
#type = "Webhook"
|
|
||||||
#endpoint = "http://localhost:8080/webhook"
|
|
||||||
#auth_token = ""
|
|
||||||
#batch_size = 100 # Default is 3 if not specified
|
|
||||||
#batch_timeout_ms = 1000 # Default is 100ms if not specified
|
|
||||||
|
|
||||||
[[sinks]]
|
|
||||||
type = "File"
|
|
||||||
path = "/root/data/logs/rustfs.log"
|
|
||||||
buffer_size = 100 # Default is 8192 bytes if not specified
|
|
||||||
flush_interval_ms = 1000
|
|
||||||
flush_threshold = 100
|
|
||||||
|
|
||||||
[logger]
|
|
||||||
queue_capacity = 10
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
[observability]
|
|
||||||
endpoint = "http://localhost:4317" # Default is "http://localhost:4317" if not specified
|
|
||||||
use_stdout = false # Output with stdout, true output, false no output
|
|
||||||
sample_ratio = 2.0
|
|
||||||
meter_interval = 30
|
|
||||||
service_name = "rustfs"
|
|
||||||
service_version = "0.1.0"
|
|
||||||
environments = "production"
|
|
||||||
logger_level = "debug"
|
|
||||||
local_logging_enabled = true
|
|
||||||
|
|
||||||
#[[sinks]]
|
|
||||||
#type = "Kafka"
|
|
||||||
#brokers = "localhost:9092"
|
|
||||||
#topic = "logs"
|
|
||||||
#batch_size = 100 # Default is 100 if not specified
|
|
||||||
#batch_timeout_ms = 1000 # Default is 1000ms if not specified
|
|
||||||
#
|
|
||||||
#[[sinks]]
|
|
||||||
#type = "Webhook"
|
|
||||||
#endpoint = "http://localhost:8080/webhook"
|
|
||||||
#auth_token = ""
|
|
||||||
#batch_size = 100 # Default is 3 if not specified
|
|
||||||
#batch_timeout_ms = 1000 # Default is 100ms if not specified
|
|
||||||
|
|
||||||
[[sinks]]
|
|
||||||
type = "File"
|
|
||||||
path = "/root/data/logs/rustfs.log"
|
|
||||||
buffer_size = 100 # Default is 8192 bytes if not specified
|
|
||||||
flush_interval_ms = 1000
|
|
||||||
flush_threshold = 100
|
|
||||||
|
|
||||||
[logger]
|
|
||||||
queue_capacity = 10
|
|
||||||
@@ -1,3 +1,17 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
otel-collector:
|
otel-collector:
|
||||||
image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib:0.127.0
|
image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib:0.127.0
|
||||||
@@ -16,7 +30,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- otel-network
|
- otel-network
|
||||||
jaeger:
|
jaeger:
|
||||||
image: jaegertracing/jaeger:2.6.0
|
image: jaegertracing/jaeger:2.7.0
|
||||||
environment:
|
environment:
|
||||||
- TZ=Asia/Shanghai
|
- TZ=Asia/Shanghai
|
||||||
ports:
|
ports:
|
||||||
@@ -47,7 +61,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- otel-network
|
- otel-network
|
||||||
grafana:
|
grafana:
|
||||||
image: grafana/grafana:12.0.1
|
image: grafana/grafana:12.0.2
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000" # Web UI
|
- "3000:3000" # Web UI
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
service:
|
service:
|
||||||
extensions: [ jaeger_storage, jaeger_query, remote_sampling, healthcheckv2 ]
|
extensions: [ jaeger_storage, jaeger_query, remote_sampling, healthcheckv2 ]
|
||||||
pipelines:
|
pipelines:
|
||||||
@@ -50,10 +64,10 @@ extensions:
|
|||||||
backends:
|
backends:
|
||||||
some_store:
|
some_store:
|
||||||
memory:
|
memory:
|
||||||
max_traces: 100000
|
max_traces: 1000000
|
||||||
another_store:
|
another_store:
|
||||||
memory:
|
memory:
|
||||||
max_traces: 100000
|
max_traces: 1000000
|
||||||
metric_backends:
|
metric_backends:
|
||||||
some_metrics_storage:
|
some_metrics_storage:
|
||||||
prometheus:
|
prometheus:
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
auth_enabled: false
|
auth_enabled: false
|
||||||
|
|
||||||
server:
|
server:
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
receivers:
|
receivers:
|
||||||
otlp:
|
otlp:
|
||||||
protocols:
|
protocols:
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
global:
|
global:
|
||||||
scrape_interval: 5s # 刮取间隔
|
scrape_interval: 5s # 刮取间隔
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
openobserve:
|
openobserve:
|
||||||
image: public.ecr.aws/zinclabs/openobserve:latest
|
image: public.ecr.aws/zinclabs/openobserve:latest
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
receivers:
|
receivers:
|
||||||
otlp:
|
otlp:
|
||||||
protocols:
|
protocols:
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# vault-init.sh - Initialize Vault for RustFS SSE-KMS
|
|
||||||
|
|
||||||
# Wait for Vault to start
|
|
||||||
until curl -s http://127.0.0.1:8200/v1/sys/health | grep "initialized" > /dev/null; do
|
|
||||||
echo "Waiting for Vault to start..."
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
# Set the Vault token
|
|
||||||
export VAULT_TOKEN="$VAULT_DEV_ROOT_TOKEN_ID"
|
|
||||||
export VAULT_ADDR="http://127.0.0.1:8200"
|
|
||||||
|
|
||||||
echo "Vault is running and initialized"
|
|
||||||
|
|
||||||
# Enable the Transit secrets engine (for encryption operations)
|
|
||||||
vault secrets enable transit
|
|
||||||
echo "Transit secrets engine enabled"
|
|
||||||
|
|
||||||
# Create a key for RustFS encryption
|
|
||||||
vault write -f transit/keys/rustfs-encryption-key
|
|
||||||
echo "Created rustfs-encryption-key"
|
|
||||||
|
|
||||||
# Create another key for RustFS with rotation capability
|
|
||||||
vault write -f transit/keys/rustfs-rotating-key
|
|
||||||
echo "Created rustfs-rotating-key"
|
|
||||||
|
|
||||||
# Set up key rotation policy
|
|
||||||
vault write transit/keys/rustfs-rotating-key/config auto_rotate_period="30d"
|
|
||||||
echo "Set up auto rotation for rustfs-rotating-key"
|
|
||||||
|
|
||||||
# Create a policy for RustFS to access these keys
|
|
||||||
cat > /tmp/rustfs-policy.hcl << EOF
|
|
||||||
# Policy for RustFS encryption operations
|
|
||||||
path "transit/encrypt/rustfs-encryption-key" {
|
|
||||||
capabilities = ["create", "update"]
|
|
||||||
}
|
|
||||||
|
|
||||||
path "transit/decrypt/rustfs-encryption-key" {
|
|
||||||
capabilities = ["create", "update"]
|
|
||||||
}
|
|
||||||
|
|
||||||
path "transit/encrypt/rustfs-rotating-key" {
|
|
||||||
capabilities = ["create", "update"]
|
|
||||||
}
|
|
||||||
|
|
||||||
path "transit/decrypt/rustfs-rotating-key" {
|
|
||||||
capabilities = ["create", "update"]
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Create the policy
|
|
||||||
vault policy write rustfs-encryption-policy /tmp/rustfs-policy.hcl
|
|
||||||
echo "Created rustfs-encryption-policy"
|
|
||||||
|
|
||||||
# Create a token for RustFS to use
|
|
||||||
RUSTFS_TOKEN=$(vault token create -policy=rustfs-encryption-policy -field=token)
|
|
||||||
echo "Created token for RustFS: $RUSTFS_TOKEN"
|
|
||||||
|
|
||||||
# Store the token for RustFS to use
|
|
||||||
echo "RUSTFS_KMS_VAULT_TOKEN=$RUSTFS_TOKEN" > /vault/config/rustfs-kms.env
|
|
||||||
echo "RUSTFS_KMS_VAULT_ENDPOINT=http://rustyvault:8200" >> /vault/config/rustfs-kms.env
|
|
||||||
echo "RUSTFS_KMS_VAULT_KEY_NAME=rustfs-encryption-key" >> /vault/config/rustfs-kms.env
|
|
||||||
|
|
||||||
echo "RustFS KMS configuration has been created"
|
|
||||||
echo "============================================"
|
|
||||||
echo "Vault is ready for use with RustFS SSE-KMS"
|
|
||||||
echo "============================================"
|
|
||||||
|
|
||||||
# Keep the container running
|
|
||||||
tail -f /dev/null
|
|
||||||
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
- OS: [e.g. iOS]
|
||||||
|
- Browser [e.g. chrome, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Smartphone (please complete the following information):**
|
||||||
|
- Device: [e.g. iPhone6]
|
||||||
|
- OS: [e.g. iOS8.1]
|
||||||
|
- Browser [e.g. stock browser, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
109
.github/actions/setup/action.yml
vendored
109
.github/actions/setup/action.yml
vendored
@@ -1,54 +1,107 @@
|
|||||||
name: "setup"
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
description: "setup environment for rustfs"
|
name: "Setup Rust Environment"
|
||||||
|
description: "Setup Rust development environment with caching for RustFS"
|
||||||
|
|
||||||
inputs:
|
inputs:
|
||||||
rust-version:
|
rust-version:
|
||||||
required: true
|
description: "Rust version to install"
|
||||||
|
required: false
|
||||||
default: "stable"
|
default: "stable"
|
||||||
description: "Rust version to use"
|
|
||||||
cache-shared-key:
|
cache-shared-key:
|
||||||
required: true
|
description: "Shared cache key for Rust dependencies"
|
||||||
default: ""
|
required: false
|
||||||
description: "Cache key for shared cache"
|
default: "rustfs-deps"
|
||||||
cache-save-if:
|
cache-save-if:
|
||||||
required: true
|
description: "Condition for saving cache"
|
||||||
default: true
|
required: false
|
||||||
description: "Cache save condition"
|
default: "true"
|
||||||
run-os:
|
install-cross-tools:
|
||||||
required: true
|
description: "Install cross-compilation tools"
|
||||||
default: "ubuntu-latest"
|
required: false
|
||||||
description: "Running system"
|
default: "false"
|
||||||
|
target:
|
||||||
|
description: "Target architecture to add"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
github-token:
|
||||||
|
description: "GitHub token for API access"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- name: Install system dependencies
|
- name: Install system dependencies (Ubuntu)
|
||||||
if: inputs.run-os == 'ubuntu-latest'
|
if: runner.os == 'Linux'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt update
|
sudo apt-get update
|
||||||
sudo apt install -y musl-tools build-essential lld libdbus-1-dev libwayland-dev libwebkit2gtk-4.1-dev libxdo-dev
|
sudo apt-get install -y \
|
||||||
|
musl-tools \
|
||||||
|
build-essential \
|
||||||
|
lld \
|
||||||
|
libdbus-1-dev \
|
||||||
|
libwayland-dev \
|
||||||
|
libwebkit2gtk-4.1-dev \
|
||||||
|
libxdo-dev \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev
|
||||||
|
|
||||||
- uses: arduino/setup-protoc@v3
|
- name: Cache protoc binary
|
||||||
|
id: cache-protoc
|
||||||
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
version: "30.2"
|
path: ~/.local/bin/protoc
|
||||||
|
key: protoc-31.1-${{ runner.os }}-${{ runner.arch }}
|
||||||
|
|
||||||
- uses: Nugine/setup-flatc@v1
|
- name: Install protoc
|
||||||
|
if: steps.cache-protoc.outputs.cache-hit != 'true'
|
||||||
|
uses: arduino/setup-protoc@v3
|
||||||
with:
|
with:
|
||||||
version: "24.3.25"
|
version: "31.1"
|
||||||
|
repo-token: ${{ inputs.github-token }}
|
||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- name: Install flatc
|
||||||
|
uses: Nugine/setup-flatc@v1
|
||||||
|
with:
|
||||||
|
version: "25.2.10"
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ inputs.rust-version }}
|
toolchain: ${{ inputs.rust-version }}
|
||||||
|
targets: ${{ inputs.target }}
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- name: Install Zig
|
||||||
|
if: inputs.install-cross-tools == 'true'
|
||||||
|
uses: mlugg/setup-zig@v2
|
||||||
|
|
||||||
|
- name: Install cargo-zigbuild
|
||||||
|
if: inputs.install-cross-tools == 'true'
|
||||||
|
uses: taiki-e/install-action@cargo-zigbuild
|
||||||
|
|
||||||
|
- name: Setup Rust cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
cache-on-failure: true
|
|
||||||
cache-all-crates: true
|
cache-all-crates: true
|
||||||
|
cache-on-failure: true
|
||||||
shared-key: ${{ inputs.cache-shared-key }}
|
shared-key: ${{ inputs.cache-shared-key }}
|
||||||
save-if: ${{ inputs.cache-save-if }}
|
save-if: ${{ inputs.cache-save-if }}
|
||||||
|
# Cache workspace dependencies
|
||||||
- uses: mlugg/setup-zig@v2
|
workspaces: |
|
||||||
- uses: taiki-e/install-action@cargo-zigbuild
|
. -> target
|
||||||
|
cli/rustfs-gui -> cli/rustfs-gui/target
|
||||||
|
|||||||
14
.github/dependabot.yml
vendored
14
.github/dependabot.yml
vendored
@@ -1,3 +1,17 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
# To get started with Dependabot version updates, you'll need to specify which
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
# package ecosystems to update and where the package manifests are located.
|
# package ecosystems to update and where the package manifests are located.
|
||||||
# Please see the documentation for all configuration options:
|
# Please see the documentation for all configuration options:
|
||||||
|
|||||||
39
.github/pull_request_template.md
vendored
Normal file
39
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<!--
|
||||||
|
Pull Request Template for RustFS
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Type of Change
|
||||||
|
- [ ] New Feature
|
||||||
|
- [ ] Bug Fix
|
||||||
|
- [ ] Documentation
|
||||||
|
- [ ] Performance Improvement
|
||||||
|
- [ ] Test/CI
|
||||||
|
- [ ] Refactor
|
||||||
|
- [ ] Other:
|
||||||
|
|
||||||
|
## Related Issues
|
||||||
|
<!-- List related Issue numbers, e.g. #123 -->
|
||||||
|
|
||||||
|
## Summary of Changes
|
||||||
|
<!-- Briefly describe the main changes and motivation for this PR -->
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
- [ ] I have read and followed the [CONTRIBUTING.md](CONTRIBUTING.md) guidelines
|
||||||
|
- [ ] Code is formatted with `cargo fmt --all`
|
||||||
|
- [ ] Passed `cargo clippy --all-targets --all-features -- -D warnings`
|
||||||
|
- [ ] Passed `cargo check --all-targets`
|
||||||
|
- [ ] Added/updated necessary tests
|
||||||
|
- [ ] Documentation updated (if needed)
|
||||||
|
- [ ] CI/CD passed (if applicable)
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
- [ ] Breaking change (compatibility)
|
||||||
|
- [ ] Requires doc/config/deployment update
|
||||||
|
- [ ] Other impact:
|
||||||
|
|
||||||
|
## Additional Notes
|
||||||
|
<!-- Any extra information for reviewers -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Thank you for your contribution! Please ensure your PR follows the community standards ([CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)) and sign the CLA if this is your first contribution.
|
||||||
77
.github/workflows/audit.yml
vendored
77
.github/workflows/audit.yml
vendored
@@ -1,25 +1,78 @@
|
|||||||
name: Audit
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
name: Security Audit
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [main]
|
||||||
- main
|
paths:
|
||||||
paths:
|
|
||||||
- '**/Cargo.toml'
|
- '**/Cargo.toml'
|
||||||
- '**/Cargo.lock'
|
- '**/Cargo.lock'
|
||||||
|
- '.github/workflows/audit.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches: [main]
|
||||||
- main
|
paths:
|
||||||
paths:
|
|
||||||
- '**/Cargo.toml'
|
- '**/Cargo.toml'
|
||||||
- '**/Cargo.lock'
|
- '**/Cargo.lock'
|
||||||
|
- '.github/workflows/audit.yml'
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 0' # at midnight of each sunday
|
- cron: '0 0 * * 0' # Weekly on Sunday at midnight UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
audit:
|
security-audit:
|
||||||
|
name: Security Audit
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repository
|
||||||
- uses: taiki-e/install-action@cargo-audit
|
uses: actions/checkout@v4
|
||||||
- run: cargo audit -D warnings
|
|
||||||
|
- name: Install cargo-audit
|
||||||
|
uses: taiki-e/install-action@v2
|
||||||
|
with:
|
||||||
|
tool: cargo-audit
|
||||||
|
|
||||||
|
- name: Run security audit
|
||||||
|
run: |
|
||||||
|
cargo audit -D warnings --json | tee audit-results.json
|
||||||
|
|
||||||
|
- name: Upload audit results
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: security-audit-results-${{ github.run_number }}
|
||||||
|
path: audit-results.json
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
dependency-review:
|
||||||
|
name: Dependency Review
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Dependency Review
|
||||||
|
uses: actions/dependency-review-action@v4
|
||||||
|
with:
|
||||||
|
fail-on-severity: moderate
|
||||||
|
comment-summary-in-pr: true
|
||||||
|
|||||||
916
.github/workflows/build.yml
vendored
916
.github/workflows/build.yml
vendored
@@ -1,416 +1,610 @@
|
|||||||
name: Build RustFS And GUI
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
name: Build and Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 0 * * 0" # at midnight of each sunday
|
|
||||||
push:
|
push:
|
||||||
branches:
|
tags: ["*"]
|
||||||
- main
|
branches: [main]
|
||||||
tags: [ "v*", "*" ]
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**.txt"
|
||||||
|
- ".github/**"
|
||||||
|
- "docs/**"
|
||||||
|
- "deploy/**"
|
||||||
|
- "scripts/dev_*.sh"
|
||||||
|
- "LICENSE*"
|
||||||
|
- "README*"
|
||||||
|
- "**/*.png"
|
||||||
|
- "**/*.jpg"
|
||||||
|
- "**/*.svg"
|
||||||
|
- ".gitignore"
|
||||||
|
- ".dockerignore"
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**.txt"
|
||||||
|
- ".github/**"
|
||||||
|
- "docs/**"
|
||||||
|
- "deploy/**"
|
||||||
|
- "scripts/dev_*.sh"
|
||||||
|
- "LICENSE*"
|
||||||
|
- "README*"
|
||||||
|
- "**/*.png"
|
||||||
|
- "**/*.jpg"
|
||||||
|
- "**/*.svg"
|
||||||
|
- ".gitignore"
|
||||||
|
- ".dockerignore"
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * 0" # Weekly on Sunday at midnight UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
force_build:
|
||||||
|
description: "Force build even without changes"
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
RUST_BACKTRACE: 1
|
||||||
|
# Optimize build performance
|
||||||
|
CARGO_INCREMENTAL: 0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
# Second layer: Business logic level checks (handling build strategy)
|
||||||
|
build-check:
|
||||||
|
name: Build Strategy Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
should_build: ${{ steps.check.outputs.should_build }}
|
||||||
|
build_type: ${{ steps.check.outputs.build_type }}
|
||||||
|
steps:
|
||||||
|
- name: Determine build strategy
|
||||||
|
id: check
|
||||||
|
run: |
|
||||||
|
should_build=false
|
||||||
|
build_type="none"
|
||||||
|
|
||||||
|
# Business logic: when we need to build
|
||||||
|
if [[ "${{ github.event_name }}" == "schedule" ]] || \
|
||||||
|
[[ "${{ github.event_name }}" == "workflow_dispatch" ]] || \
|
||||||
|
[[ "${{ github.event.inputs.force_build }}" == "true" ]] || \
|
||||||
|
[[ "${{ contains(github.event.head_commit.message, '--build') }}" == "true" ]]; then
|
||||||
|
should_build=true
|
||||||
|
build_type="development"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Always build for tag pushes (version releases)
|
||||||
|
if [[ "${{ startsWith(github.ref, 'refs/tags/') }}" == "true" ]]; then
|
||||||
|
should_build=true
|
||||||
|
build_type="release"
|
||||||
|
echo "🏷️ Tag detected: forcing release build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "should_build=$should_build" >> $GITHUB_OUTPUT
|
||||||
|
echo "build_type=$build_type" >> $GITHUB_OUTPUT
|
||||||
|
echo "Build needed: $should_build (type: $build_type)"
|
||||||
|
|
||||||
|
# Build RustFS binaries
|
||||||
build-rustfs:
|
build-rustfs:
|
||||||
|
name: Build RustFS
|
||||||
|
needs: [build-check]
|
||||||
|
if: needs.build-check.outputs.should_build == 'true'
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
timeout-minutes: 60
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: ${{ matrix.cross == 'false' && '-C target-cpu=native' || '' }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-latest, macos-latest, windows-latest ]
|
include:
|
||||||
variant:
|
# Linux builds
|
||||||
- { profile: release, target: x86_64-unknown-linux-musl, glibc: "default" }
|
|
||||||
- { profile: release, target: x86_64-unknown-linux-gnu, glibc: "default" }
|
|
||||||
- { profile: release, target: aarch64-apple-darwin, glibc: "default" }
|
|
||||||
#- { profile: release, target: aarch64-unknown-linux-gnu, glibc: "default" }
|
|
||||||
- { profile: release, target: aarch64-unknown-linux-musl, glibc: "default" }
|
|
||||||
#- { profile: release, target: x86_64-pc-windows-msvc, glibc: "default" }
|
|
||||||
exclude:
|
|
||||||
# Linux targets on non-Linux systems
|
|
||||||
- os: macos-latest
|
|
||||||
variant: { profile: release, target: x86_64-unknown-linux-gnu, glibc: "default" }
|
|
||||||
- os: macos-latest
|
|
||||||
variant: { profile: release, target: x86_64-unknown-linux-musl, glibc: "default" }
|
|
||||||
- os: macos-latest
|
|
||||||
variant: { profile: release, target: aarch64-unknown-linux-gnu, glibc: "default" }
|
|
||||||
- os: macos-latest
|
|
||||||
variant: { profile: release, target: aarch64-unknown-linux-musl, glibc: "default" }
|
|
||||||
- os: windows-latest
|
|
||||||
variant: { profile: release, target: x86_64-unknown-linux-gnu, glibc: "default" }
|
|
||||||
- os: windows-latest
|
|
||||||
variant: { profile: release, target: x86_64-unknown-linux-musl, glibc: "default" }
|
|
||||||
- os: windows-latest
|
|
||||||
variant: { profile: release, target: aarch64-unknown-linux-gnu, glibc: "default" }
|
|
||||||
- os: windows-latest
|
|
||||||
variant: { profile: release, target: aarch64-unknown-linux-musl, glibc: "default" }
|
|
||||||
|
|
||||||
# Apple targets on non-macOS systems
|
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
variant: { profile: release, target: aarch64-apple-darwin, glibc: "default" }
|
target: x86_64-unknown-linux-musl
|
||||||
- os: windows-latest
|
cross: false
|
||||||
variant: { profile: release, target: aarch64-apple-darwin, glibc: "default" }
|
platform: linux
|
||||||
|
|
||||||
# Windows targets on non-Windows systems
|
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
variant: { profile: release, target: x86_64-pc-windows-msvc, glibc: "default" }
|
target: aarch64-unknown-linux-musl
|
||||||
|
cross: true
|
||||||
|
platform: linux
|
||||||
|
# macOS builds
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
variant: { profile: release, target: x86_64-pc-windows-msvc, glibc: "default" }
|
target: aarch64-apple-darwin
|
||||||
|
cross: false
|
||||||
|
platform: macos
|
||||||
|
- os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
cross: false
|
||||||
|
platform: macos
|
||||||
|
# # Windows builds
|
||||||
|
# - os: windows-latest
|
||||||
|
# target: x86_64-pc-windows-msvc
|
||||||
|
# cross: false
|
||||||
|
# platform: windows
|
||||||
|
# - os: windows-latest
|
||||||
|
# target: aarch64-pc-windows-msvc
|
||||||
|
# cross: true
|
||||||
|
# platform: windows
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
# Installation system dependencies
|
- name: Setup Rust environment
|
||||||
- name: Install system dependencies (Ubuntu)
|
uses: ./.github/actions/setup
|
||||||
if: runner.os == 'Linux'
|
with:
|
||||||
|
rust-version: stable
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
cache-shared-key: build-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
cache-save-if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
install-cross-tools: ${{ matrix.cross }}
|
||||||
|
|
||||||
|
- name: Download static console assets
|
||||||
run: |
|
run: |
|
||||||
sudo apt update
|
|
||||||
sudo apt install -y musl-tools build-essential lld libdbus-1-dev libwayland-dev libwebkit2gtk-4.1-dev libxdo-dev
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
#Install Rust using dtolnay/rust-toolchain
|
|
||||||
- uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
targets: ${{ matrix.variant.target }}
|
|
||||||
components: rustfmt, clippy
|
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
- name: Cache Protoc
|
|
||||||
id: cache-protoc
|
|
||||||
uses: actions/cache@v4.2.3
|
|
||||||
with:
|
|
||||||
path: /Users/runner/hostedtoolcache/protoc
|
|
||||||
key: protoc-${{ runner.os }}-30.2
|
|
||||||
restore-keys: |
|
|
||||||
protoc-${{ runner.os }}-
|
|
||||||
|
|
||||||
- name: Install Protoc
|
|
||||||
if: steps.cache-protoc.outputs.cache-hit != 'true'
|
|
||||||
uses: arduino/setup-protoc@v3
|
|
||||||
with:
|
|
||||||
version: '30.2'
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Setup Flatc
|
|
||||||
uses: Nugine/setup-flatc@v1
|
|
||||||
with:
|
|
||||||
version: "25.2.10"
|
|
||||||
|
|
||||||
# Cache Cargo dependencies
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
cache-on-failure: true
|
|
||||||
cache-all-crates: true
|
|
||||||
shared-key: rustfs-${{ matrix.os }}-${{ matrix.variant.profile }}-${{ matrix.variant.target }}-${{ matrix.variant.glibc }}-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
save-if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
|
|
||||||
# Set up Zig for cross-compilation
|
|
||||||
- uses: mlugg/setup-zig@v2
|
|
||||||
if: matrix.variant.glibc != 'default' || contains(matrix.variant.target, 'linux')
|
|
||||||
|
|
||||||
- uses: taiki-e/install-action@cargo-zigbuild
|
|
||||||
if: matrix.variant.glibc != 'default' || contains(matrix.variant.target, 'linux')
|
|
||||||
|
|
||||||
# Download static resources
|
|
||||||
- name: Download and Extract Static Assets
|
|
||||||
run: |
|
|
||||||
url="https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip"
|
|
||||||
|
|
||||||
# Create a static resource directory
|
|
||||||
mkdir -p ./rustfs/static
|
mkdir -p ./rustfs/static
|
||||||
|
if [[ "${{ matrix.platform }}" == "windows" ]]; then
|
||||||
# Download static resources
|
curl.exe -L "https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip" -o console.zip --retry 3 --retry-delay 5 --max-time 300
|
||||||
echo "::group::Downloading static assets"
|
if [[ $? -eq 0 ]]; then
|
||||||
curl -L -o static_assets.zip "$url" --retry 3
|
unzip -o console.zip -d ./rustfs/static
|
||||||
|
rm console.zip
|
||||||
# Unzip static resources
|
else
|
||||||
echo "::group::Extracting static assets"
|
echo "Warning: Failed to download console assets, continuing without them"
|
||||||
if [ "${{ runner.os }}" = "Windows" ]; then
|
echo "// Static assets not available" > ./rustfs/static/empty.txt
|
||||||
7z x static_assets.zip -o./rustfs/static
|
fi
|
||||||
del static_assets.zip
|
|
||||||
else
|
else
|
||||||
unzip -o static_assets.zip -d ./rustfs/static
|
curl -L "https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip" \
|
||||||
rm static_assets.zip
|
-o console.zip --retry 3 --retry-delay 5 --max-time 300
|
||||||
|
if [[ $? -eq 0 ]]; then
|
||||||
|
unzip -o console.zip -d ./rustfs/static
|
||||||
|
rm console.zip
|
||||||
|
else
|
||||||
|
echo "Warning: Failed to download console assets, continuing without them"
|
||||||
|
echo "// Static assets not available" > ./rustfs/static/empty.txt
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "::group::Static assets content"
|
- name: Build RustFS
|
||||||
ls -la ./rustfs/static
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
# Build rustfs
|
|
||||||
- name: Build rustfs
|
|
||||||
id: build
|
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
echo "::group::Setting up build parameters"
|
# Force rebuild by touching build.rs
|
||||||
PROFILE="${{ matrix.variant.profile }}"
|
|
||||||
TARGET="${{ matrix.variant.target }}"
|
|
||||||
GLIBC="${{ matrix.variant.glibc }}"
|
|
||||||
|
|
||||||
# Determine whether to use zigbuild
|
|
||||||
USE_ZIGBUILD=false
|
|
||||||
if [[ "$GLIBC" != "default" || "$TARGET" == *"linux"* ]]; then
|
|
||||||
USE_ZIGBUILD=true
|
|
||||||
echo "Using zigbuild for cross-compilation"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Determine the target parameters
|
|
||||||
TARGET_ARG="$TARGET"
|
|
||||||
if [[ "$GLIBC" != "default" ]]; then
|
|
||||||
TARGET_ARG="${TARGET}.${GLIBC}"
|
|
||||||
echo "Using custom glibc target: $TARGET_ARG"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Confirm the profile directory name
|
|
||||||
if [[ "$PROFILE" == "dev" ]]; then
|
|
||||||
PROFILE_DIR="debug"
|
|
||||||
else
|
|
||||||
PROFILE_DIR="$PROFILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Determine the binary suffix
|
|
||||||
BIN_SUFFIX=""
|
|
||||||
if [[ "${{ matrix.variant.target }}" == *"windows"* ]]; then
|
|
||||||
BIN_SUFFIX=".exe"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Determine the binary name - Use the appropriate extension for Windows
|
|
||||||
BIN_NAME="rustfs.${PROFILE}.${TARGET}"
|
|
||||||
if [[ "$GLIBC" != "default" ]]; then
|
|
||||||
BIN_NAME="${BIN_NAME}.glibc${GLIBC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Windows systems use exe suffix, and other systems do not have suffix
|
|
||||||
if [[ "${{ matrix.variant.target }}" == *"windows"* ]]; then
|
|
||||||
BIN_NAME="${BIN_NAME}.exe"
|
|
||||||
else
|
|
||||||
BIN_NAME="${BIN_NAME}.bin"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Binary name will be: $BIN_NAME"
|
|
||||||
|
|
||||||
echo "::group::Building rustfs"
|
|
||||||
# Refresh build information
|
|
||||||
touch rustfs/build.rs
|
touch rustfs/build.rs
|
||||||
|
|
||||||
# Identify the build command and execute it
|
if [[ "${{ matrix.cross }}" == "true" ]]; then
|
||||||
if [[ "$USE_ZIGBUILD" == "true" ]]; then
|
if [[ "${{ matrix.platform }}" == "windows" ]]; then
|
||||||
echo "Build command: cargo zigbuild --profile $PROFILE --target $TARGET_ARG -p rustfs --bins"
|
# Use cross for Windows ARM64
|
||||||
cargo zigbuild --profile $PROFILE --target $TARGET_ARG -p rustfs --bins
|
cargo install cross --git https://github.com/cross-rs/cross
|
||||||
|
cross build --release --target ${{ matrix.target }} -p rustfs --bins
|
||||||
|
else
|
||||||
|
# Use zigbuild for Linux ARM64
|
||||||
|
cargo zigbuild --release --target ${{ matrix.target }} -p rustfs --bins
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "Build command: cargo build --profile $PROFILE --target $TARGET_ARG -p rustfs --bins"
|
cargo build --release --target ${{ matrix.target }} -p rustfs --bins
|
||||||
cargo build --profile $PROFILE --target $TARGET_ARG -p rustfs --bins
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Determine the binary path and output path
|
- name: Create release package
|
||||||
BIN_PATH="target/${TARGET_ARG}/${PROFILE_DIR}/rustfs${BIN_SUFFIX}"
|
|
||||||
OUT_PATH="target/artifacts/${BIN_NAME}"
|
|
||||||
|
|
||||||
# Create a target directory
|
|
||||||
mkdir -p target/artifacts
|
|
||||||
|
|
||||||
echo "Copying binary from ${BIN_PATH} to ${OUT_PATH}"
|
|
||||||
cp "${BIN_PATH}" "${OUT_PATH}"
|
|
||||||
|
|
||||||
# Record the output path for use in the next steps
|
|
||||||
echo "bin_path=${OUT_PATH}" >> $GITHUB_OUTPUT
|
|
||||||
echo "bin_name=${BIN_NAME}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Package Binary and Static Assets
|
|
||||||
id: package
|
id: package
|
||||||
run: |
|
|
||||||
# Create component file name
|
|
||||||
ARTIFACT_NAME="rustfs-${{ matrix.variant.profile }}-${{ matrix.variant.target }}"
|
|
||||||
if [ "${{ matrix.variant.glibc }}" != "default" ]; then
|
|
||||||
ARTIFACT_NAME="${ARTIFACT_NAME}-glibc${{ matrix.variant.glibc }}"
|
|
||||||
fi
|
|
||||||
echo "artifact_name=${ARTIFACT_NAME}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# Get the binary path
|
|
||||||
BIN_PATH="${{ steps.build.outputs.bin_path }}"
|
|
||||||
|
|
||||||
# Create a packaged directory structure - only contains bin and docs directories
|
|
||||||
mkdir -p ${ARTIFACT_NAME}/{bin,docs}
|
|
||||||
|
|
||||||
# Copy binary files (note the difference between Windows and other systems)
|
|
||||||
if [[ "${{ matrix.variant.target }}" == *"windows"* ]]; then
|
|
||||||
cp "${BIN_PATH}" ${ARTIFACT_NAME}/bin/rustfs.exe
|
|
||||||
else
|
|
||||||
cp "${BIN_PATH}" ${ARTIFACT_NAME}/bin/rustfs
|
|
||||||
fi
|
|
||||||
|
|
||||||
# copy documents and licenses
|
|
||||||
if [ -f "LICENSE" ]; then
|
|
||||||
cp LICENSE ${ARTIFACT_NAME}/docs/
|
|
||||||
fi
|
|
||||||
if [ -f "README.md" ]; then
|
|
||||||
cp README.md ${ARTIFACT_NAME}/docs/
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Packaged as zip
|
|
||||||
if [ "${{ runner.os }}" = "Windows" ]; then
|
|
||||||
7z a ${ARTIFACT_NAME}.zip ${ARTIFACT_NAME}
|
|
||||||
else
|
|
||||||
zip -r ${ARTIFACT_NAME}.zip ${ARTIFACT_NAME}
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Created artifact: ${ARTIFACT_NAME}.zip"
|
|
||||||
ls -la ${ARTIFACT_NAME}.zip
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ${{ steps.package.outputs.artifact_name }}
|
|
||||||
path: ${{ steps.package.outputs.artifact_name }}.zip
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
- name: Upload to Aliyun OSS
|
|
||||||
if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/main'
|
|
||||||
uses: JohnGuan/oss-upload-action@main
|
|
||||||
with:
|
|
||||||
key-id: ${{ secrets.ALICLOUDOSS_KEY_ID }}
|
|
||||||
key-secret: ${{ secrets.ALICLOUDOSS_KEY_SECRET }}
|
|
||||||
region: oss-cn-beijing
|
|
||||||
bucket: rustfs-artifacts
|
|
||||||
assets: |
|
|
||||||
${{ steps.package.outputs.artifact_name }}.zip:/artifacts/rustfs/${{ steps.package.outputs.artifact_name }}.zip
|
|
||||||
${{ steps.package.outputs.artifact_name }}.zip:/artifacts/rustfs/${{ steps.package.outputs.artifact_name }}.latest.zip
|
|
||||||
|
|
||||||
# Determine whether to perform GUI construction based on conditions
|
|
||||||
- name: Prepare for GUI build
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
id: prepare_gui
|
|
||||||
run: |
|
run: |
|
||||||
# Create a target directory
|
PACKAGE_NAME="rustfs-${{ matrix.target }}"
|
||||||
|
mkdir -p "${PACKAGE_NAME}"/{bin,docs}
|
||||||
|
|
||||||
|
# Copy binary with platform-specific handling
|
||||||
|
if [[ "${{ matrix.target }}" == *"windows"* ]]; then
|
||||||
|
cp target/${{ matrix.target }}/release/rustfs.exe "${PACKAGE_NAME}/bin/"
|
||||||
|
# Add Windows-specific files
|
||||||
|
echo "RustFS for Windows" > "${PACKAGE_NAME}/README.txt"
|
||||||
|
else
|
||||||
|
cp target/${{ matrix.target }}/release/rustfs "${PACKAGE_NAME}/bin/"
|
||||||
|
chmod +x "${PACKAGE_NAME}/bin/rustfs"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy documentation
|
||||||
|
[ -f "LICENSE" ] && cp LICENSE "${PACKAGE_NAME}/docs/"
|
||||||
|
[ -f "README.md" ] && cp README.md "${PACKAGE_NAME}/docs/"
|
||||||
|
|
||||||
|
# Add platform-specific information
|
||||||
|
cat > "${PACKAGE_NAME}/docs/PLATFORM_INFO.txt" << EOF
|
||||||
|
Platform: ${{ matrix.platform }}
|
||||||
|
Target: ${{ matrix.target }}
|
||||||
|
Build Date: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
Git Commit: ${GITHUB_SHA::8}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create appropriate archive format for platform
|
||||||
|
if [[ "${{ matrix.platform }}" == "windows" ]]; then
|
||||||
|
# Use PowerShell to create zip on Windows
|
||||||
|
powershell -Command "Compress-Archive -Path '${PACKAGE_NAME}' -DestinationPath '${PACKAGE_NAME}.zip'"
|
||||||
|
echo "package_name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT
|
||||||
|
echo "package_file=${PACKAGE_NAME}.zip" >> $GITHUB_OUTPUT
|
||||||
|
echo "Package created: ${PACKAGE_NAME}.zip"
|
||||||
|
else
|
||||||
|
tar -czf "${PACKAGE_NAME}.tar.gz" "${PACKAGE_NAME}"
|
||||||
|
echo "package_name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT
|
||||||
|
echo "package_file=${PACKAGE_NAME}.tar.gz" >> $GITHUB_OUTPUT
|
||||||
|
echo "Package created: ${PACKAGE_NAME}.tar.gz"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ steps.package.outputs.package_name }}
|
||||||
|
path: ${{ steps.package.outputs.package_file }}
|
||||||
|
retention-days: ${{ startsWith(github.ref, 'refs/tags/') && 30 || 7 }}
|
||||||
|
|
||||||
|
# Build GUI (only for releases)
|
||||||
|
build-gui:
|
||||||
|
name: Build GUI
|
||||||
|
needs: [build-check, build-rustfs]
|
||||||
|
if: needs.build-check.outputs.build_type == 'release'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
timeout-minutes: 45
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: x86_64-unknown-linux-musl
|
||||||
|
platform: linux
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
platform: macos
|
||||||
|
- os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
platform: windows
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Rust environment
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
with:
|
||||||
|
rust-version: stable
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
cache-shared-key: gui-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Download RustFS binary
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rustfs-${{ matrix.target }}
|
||||||
|
path: ./artifacts
|
||||||
|
|
||||||
|
- name: Prepare embedded binary
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
mkdir -p ./cli/rustfs-gui/embedded-rustfs/
|
mkdir -p ./cli/rustfs-gui/embedded-rustfs/
|
||||||
|
|
||||||
# Copy the currently built binary to the embedded-rustfs directory
|
if [[ "${{ matrix.platform }}" == "windows" ]]; then
|
||||||
if [[ "${{ matrix.variant.target }}" == *"windows"* ]]; then
|
# Extract from zip file
|
||||||
cp "${{ steps.build.outputs.bin_path }}" ./cli/rustfs-gui/embedded-rustfs/rustfs.exe
|
unzip ./artifacts/rustfs-${{ matrix.target }}.zip -d ./artifacts/
|
||||||
|
cp ./artifacts/rustfs-${{ matrix.target }}/bin/rustfs.exe ./cli/rustfs-gui/embedded-rustfs/
|
||||||
else
|
else
|
||||||
cp "${{ steps.build.outputs.bin_path }}" ./cli/rustfs-gui/embedded-rustfs/rustfs
|
# Extract from tar.gz file
|
||||||
|
tar -xzf ./artifacts/rustfs-${{ matrix.target }}.tar.gz -C ./artifacts/
|
||||||
|
cp ./artifacts/rustfs-${{ matrix.target }}/bin/rustfs ./cli/rustfs-gui/embedded-rustfs/
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Copied binary to embedded-rustfs directory"
|
- name: Install Dioxus CLI
|
||||||
ls -la ./cli/rustfs-gui/embedded-rustfs/
|
uses: taiki-e/cache-cargo-install-action@v2
|
||||||
shell: bash
|
|
||||||
|
|
||||||
#Install the dioxus-cli tool
|
|
||||||
- uses: taiki-e/cache-cargo-install-action@v2
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
with:
|
with:
|
||||||
tool: dioxus-cli
|
tool: dioxus-cli
|
||||||
|
|
||||||
# Build and package GUI applications
|
- name: Build GUI
|
||||||
- name: Build and Bundle rustfs-gui
|
working-directory: ./cli/rustfs-gui
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
id: build_gui
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
echo "::group::Setting up build parameters for GUI"
|
case "${{ matrix.platform }}" in
|
||||||
PROFILE="${{ matrix.variant.profile }}"
|
"linux")
|
||||||
TARGET="${{ matrix.variant.target }}"
|
dx bundle --platform linux --package-types deb --package-types appimage --release
|
||||||
GLIBC="${{ matrix.variant.glibc }}"
|
;;
|
||||||
RELEASE_PATH="target/artifacts/$TARGET"
|
"macos")
|
||||||
|
dx bundle --platform macos --package-types dmg --release
|
||||||
# Make sure the output directory exists
|
;;
|
||||||
mkdir -p ${RELEASE_PATH}
|
"windows")
|
||||||
|
dx bundle --platform windows --package-types msi --release
|
||||||
# Configure the target platform linker
|
;;
|
||||||
echo "::group::Configuring linker for $TARGET"
|
|
||||||
case "$TARGET" in
|
|
||||||
"x86_64-unknown-linux-gnu")
|
|
||||||
export CC_x86_64_unknown_linux_gnu=gcc
|
|
||||||
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=gcc
|
|
||||||
;;
|
|
||||||
"x86_64-unknown-linux-musl")
|
|
||||||
export CC_x86_64_unknown_linux_musl=musl-gcc
|
|
||||||
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc
|
|
||||||
;;
|
|
||||||
"aarch64-unknown-linux-gnu")
|
|
||||||
export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc
|
|
||||||
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
|
|
||||||
;;
|
|
||||||
"aarch64-unknown-linux-musl")
|
|
||||||
export CC_aarch64_unknown_linux_musl=aarch64-linux-musl-gcc
|
|
||||||
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc
|
|
||||||
;;
|
|
||||||
"aarch64-apple-darwin")
|
|
||||||
export CC_aarch64_apple_darwin=clang
|
|
||||||
export CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER=clang
|
|
||||||
;;
|
|
||||||
"x86_64-pc-windows-msvc")
|
|
||||||
export CC_x86_64_pc_windows_msvc=cl
|
|
||||||
export CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER=link
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
echo "::group::Building GUI application"
|
- name: Package GUI
|
||||||
cd cli/rustfs-gui
|
id: gui_package
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
GUI_PACKAGE="rustfs-gui-${{ matrix.target }}"
|
||||||
|
mkdir -p "${GUI_PACKAGE}"
|
||||||
|
|
||||||
# Building according to the target platform
|
# Copy GUI bundles
|
||||||
if [[ "$TARGET" == *"apple-darwin"* ]]; then
|
if [[ -d "cli/rustfs-gui/dist/bundle" ]]; then
|
||||||
echo "Building for macOS"
|
cp -r cli/rustfs-gui/dist/bundle/* "${GUI_PACKAGE}/"
|
||||||
dx bundle --platform macos --package-types "macos" --package-types "dmg" --release --profile ${PROFILE} --out-dir ../../${RELEASE_PATH}
|
|
||||||
elif [[ "$TARGET" == *"windows-msvc"* ]]; then
|
|
||||||
echo "Building for Windows"
|
|
||||||
dx bundle --platform windows --package-types "msi" --release --profile ${PROFILE} --out-dir ../../${RELEASE_PATH}
|
|
||||||
elif [[ "$TARGET" == *"linux"* ]]; then
|
|
||||||
echo "Building for Linux"
|
|
||||||
dx bundle --platform linux --package-types "deb" --package-types "rpm" --package-types "appimage" --release --profile ${PROFILE} --out-dir ../../${RELEASE_PATH}
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd ../..
|
# Create appropriate archive format
|
||||||
|
if [[ "${{ matrix.platform }}" == "windows" ]]; then
|
||||||
# Create component name
|
powershell -Command "Compress-Archive -Path '${GUI_PACKAGE}' -DestinationPath '${GUI_PACKAGE}.zip'"
|
||||||
GUI_ARTIFACT_NAME="rustfs-gui-${PROFILE}-${TARGET}"
|
echo "gui_package=${GUI_PACKAGE}" >> $GITHUB_OUTPUT
|
||||||
|
echo "gui_file=${GUI_PACKAGE}.zip" >> $GITHUB_OUTPUT
|
||||||
if [ "$GLIBC" != "default" ]; then
|
|
||||||
GUI_ARTIFACT_NAME="${GUI_ARTIFACT_NAME}-glibc${GLIBC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "::group::Packaging GUI application"
|
|
||||||
# Select packaging method according to the operating system
|
|
||||||
if [ "${{ runner.os }}" = "Windows" ]; then
|
|
||||||
7z a ${GUI_ARTIFACT_NAME}.zip ${RELEASE_PATH}/*
|
|
||||||
else
|
else
|
||||||
zip -r ${GUI_ARTIFACT_NAME}.zip ${RELEASE_PATH}/*
|
tar -czf "${GUI_PACKAGE}.tar.gz" "${GUI_PACKAGE}"
|
||||||
|
echo "gui_package=${GUI_PACKAGE}" >> $GITHUB_OUTPUT
|
||||||
|
echo "gui_file=${GUI_PACKAGE}.tar.gz" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "gui_artifact_name=${GUI_ARTIFACT_NAME}" >> $GITHUB_OUTPUT
|
- name: Upload GUI artifacts
|
||||||
echo "Created GUI artifact: ${GUI_ARTIFACT_NAME}.zip"
|
uses: actions/upload-artifact@v4
|
||||||
ls -la ${GUI_ARTIFACT_NAME}.zip
|
|
||||||
|
|
||||||
# Upload GUI components
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.build_gui.outputs.gui_artifact_name }}
|
name: ${{ steps.gui_package.outputs.gui_package }}
|
||||||
path: ${{ steps.build_gui.outputs.gui_artifact_name }}.zip
|
path: ${{ steps.gui_package.outputs.gui_file }}
|
||||||
retention-days: 7
|
retention-days: 30
|
||||||
|
|
||||||
# Upload GUI to Alibaba Cloud OSS
|
# Release management
|
||||||
- name: Upload GUI to Aliyun OSS
|
release:
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
name: GitHub Release
|
||||||
uses: JohnGuan/oss-upload-action@main
|
needs: [build-check, build-rustfs, build-gui]
|
||||||
with:
|
if: always() && needs.build-check.outputs.build_type == 'release'
|
||||||
key-id: ${{ secrets.ALICLOUDOSS_KEY_ID }}
|
|
||||||
key-secret: ${{ secrets.ALICLOUDOSS_KEY_SECRET }}
|
|
||||||
region: oss-cn-beijing
|
|
||||||
bucket: rustfs-artifacts
|
|
||||||
assets: |
|
|
||||||
${{ steps.build_gui.outputs.gui_artifact_name }}.zip:/artifacts/rustfs/${{ steps.build_gui.outputs.gui_artifact_name }}.zip
|
|
||||||
${{ steps.build_gui.outputs.gui_artifact_name }}.zip:/artifacts/rustfs/${{ steps.build_gui.outputs.gui_artifact_name }}.latest.zip
|
|
||||||
|
|
||||||
|
|
||||||
merge:
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [ build-rustfs ]
|
permissions:
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/upload-artifact/merge@v4
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
name: rustfs-packages
|
fetch-depth: 0
|
||||||
pattern: "rustfs-*"
|
|
||||||
delete-merged: true
|
- name: Download all artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: ./release-artifacts
|
||||||
|
|
||||||
|
- name: Prepare release assets
|
||||||
|
id: release_prep
|
||||||
|
run: |
|
||||||
|
VERSION="${GITHUB_REF#refs/tags/}"
|
||||||
|
VERSION_CLEAN="${VERSION#v}"
|
||||||
|
|
||||||
|
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||||
|
echo "version_clean=${VERSION_CLEAN}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Organize artifacts
|
||||||
|
mkdir -p ./release-files
|
||||||
|
|
||||||
|
# Copy all artifacts (both .tar.gz and .zip files)
|
||||||
|
find ./release-artifacts -name "*.tar.gz" -exec cp {} ./release-files/ \;
|
||||||
|
find ./release-artifacts -name "*.zip" -exec cp {} ./release-files/ \;
|
||||||
|
|
||||||
|
# Generate checksums for all files
|
||||||
|
cd ./release-files
|
||||||
|
if ls *.tar.gz >/dev/null 2>&1; then
|
||||||
|
sha256sum *.tar.gz >> SHA256SUMS
|
||||||
|
sha512sum *.tar.gz >> SHA512SUMS
|
||||||
|
fi
|
||||||
|
if ls *.zip >/dev/null 2>&1; then
|
||||||
|
sha256sum *.zip >> SHA256SUMS
|
||||||
|
sha512sum *.zip >> SHA512SUMS
|
||||||
|
fi
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Display what we're releasing
|
||||||
|
echo "=== Release Files ==="
|
||||||
|
ls -la ./release-files/
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.release_prep.outputs.version }}"
|
||||||
|
VERSION_CLEAN="${{ steps.release_prep.outputs.version_clean }}"
|
||||||
|
|
||||||
|
# Check if release already exists
|
||||||
|
if gh release view "$VERSION" >/dev/null 2>&1; then
|
||||||
|
echo "Release $VERSION already exists, skipping creation"
|
||||||
|
else
|
||||||
|
# Get release notes from tag message
|
||||||
|
RELEASE_NOTES=$(git tag -l --format='%(contents)' "${VERSION}")
|
||||||
|
if [[ -z "$RELEASE_NOTES" || "$RELEASE_NOTES" =~ ^[[:space:]]*$ ]]; then
|
||||||
|
RELEASE_NOTES="Release ${VERSION_CLEAN}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine if this is a prerelease
|
||||||
|
PRERELEASE_FLAG=""
|
||||||
|
if [[ "$VERSION" == *"alpha"* ]] || [[ "$VERSION" == *"beta"* ]] || [[ "$VERSION" == *"rc"* ]]; then
|
||||||
|
PRERELEASE_FLAG="--prerelease"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create the release only if it doesn't exist
|
||||||
|
gh release create "$VERSION" \
|
||||||
|
--title "RustFS $VERSION_CLEAN" \
|
||||||
|
--notes "$RELEASE_NOTES" \
|
||||||
|
$PRERELEASE_FLAG
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload release assets
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.release_prep.outputs.version }}"
|
||||||
|
|
||||||
|
cd ./release-files
|
||||||
|
|
||||||
|
# Upload all binary archives
|
||||||
|
for file in *.tar.gz *.zip; do
|
||||||
|
if [[ -f "$file" ]]; then
|
||||||
|
echo "Uploading $file..."
|
||||||
|
gh release upload "$VERSION" "$file" --clobber
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Upload checksum files
|
||||||
|
if [[ -f "SHA256SUMS" ]]; then
|
||||||
|
echo "Uploading SHA256SUMS..."
|
||||||
|
gh release upload "$VERSION" "SHA256SUMS" --clobber
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "SHA512SUMS" ]]; then
|
||||||
|
echo "Uploading SHA512SUMS..."
|
||||||
|
gh release upload "$VERSION" "SHA512SUMS" --clobber
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Update release notes
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.release_prep.outputs.version }}"
|
||||||
|
VERSION_CLEAN="${{ steps.release_prep.outputs.version_clean }}"
|
||||||
|
|
||||||
|
# Check if release already has custom notes (not auto-generated)
|
||||||
|
EXISTING_NOTES=$(gh release view "$VERSION" --json body --jq '.body' 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
# Only update if release notes are empty or auto-generated
|
||||||
|
if [[ -z "$EXISTING_NOTES" ]] || [[ "$EXISTING_NOTES" == *"Release ${VERSION_CLEAN}"* ]]; then
|
||||||
|
echo "Updating release notes for $VERSION"
|
||||||
|
|
||||||
|
# Get original release notes from tag
|
||||||
|
ORIGINAL_NOTES=$(git tag -l --format='%(contents)' "${VERSION}")
|
||||||
|
if [[ -z "$ORIGINAL_NOTES" || "$ORIGINAL_NOTES" =~ ^[[:space:]]*$ ]]; then
|
||||||
|
ORIGINAL_NOTES="Release ${VERSION_CLEAN}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create comprehensive release notes
|
||||||
|
cat > enhanced_notes.md << EOF
|
||||||
|
## RustFS ${VERSION_CLEAN}
|
||||||
|
|
||||||
|
${ORIGINAL_NOTES}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🚀 Quick Download
|
||||||
|
|
||||||
|
**Linux (Recommended - Static Binaries):**
|
||||||
|
\`\`\`bash
|
||||||
|
# x86_64 (Intel/AMD)
|
||||||
|
curl -LO https://github.com/rustfs/rustfs/releases/download/${VERSION}/rustfs-x86_64-unknown-linux-musl.tar.gz
|
||||||
|
tar -xzf rustfs-x86_64-unknown-linux-musl.tar.gz
|
||||||
|
sudo cp rustfs-x86_64-unknown-linux-musl/bin/rustfs /usr/local/bin/
|
||||||
|
|
||||||
|
# ARM64 (Graviton, Apple Silicon VMs)
|
||||||
|
curl -LO https://github.com/rustfs/rustfs/releases/download/${VERSION}/rustfs-aarch64-unknown-linux-musl.tar.gz
|
||||||
|
tar -xzf rustfs-aarch64-unknown-linux-musl.tar.gz
|
||||||
|
sudo cp rustfs-aarch64-unknown-linux-musl/bin/rustfs /usr/local/bin/
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**macOS:**
|
||||||
|
\`\`\`bash
|
||||||
|
# Apple Silicon (M1/M2/M3)
|
||||||
|
curl -LO https://github.com/rustfs/rustfs/releases/download/${VERSION}/rustfs-aarch64-apple-darwin.tar.gz
|
||||||
|
tar -xzf rustfs-aarch64-apple-darwin.tar.gz
|
||||||
|
sudo cp rustfs-aarch64-apple-darwin/bin/rustfs /usr/local/bin/
|
||||||
|
|
||||||
|
# Intel
|
||||||
|
curl -LO https://github.com/rustfs/rustfs/releases/download/${VERSION}/rustfs-x86_64-apple-darwin.tar.gz
|
||||||
|
tar -xzf rustfs-x86_64-apple-darwin.tar.gz
|
||||||
|
sudo cp rustfs-x86_64-apple-darwin/bin/rustfs /usr/local/bin/
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Windows:**
|
||||||
|
\`\`\`powershell
|
||||||
|
# Download and extract
|
||||||
|
Invoke-WebRequest https://github.com/rustfs/rustfs/releases/download/${VERSION}/rustfs-x86_64-pc-windows-msvc.zip -OutFile rustfs.zip
|
||||||
|
Expand-Archive rustfs.zip
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 📁 Available Downloads
|
||||||
|
|
||||||
|
| Platform | Architecture | File | Size |
|
||||||
|
|----------|-------------|------|------|
|
||||||
|
| Linux | x86_64 | \`rustfs-x86_64-unknown-linux-musl.tar.gz\` | Static binary |
|
||||||
|
| Linux | ARM64 | \`rustfs-aarch64-unknown-linux-musl.tar.gz\` | Static binary |
|
||||||
|
| macOS | Apple Silicon | \`rustfs-aarch64-apple-darwin.tar.gz\` | Native binary |
|
||||||
|
| macOS | Intel | \`rustfs-x86_64-apple-darwin.tar.gz\` | Native binary |
|
||||||
|
| Windows | x86_64 | \`rustfs-x86_64-pc-windows-msvc.zip\` | MSVC binary |
|
||||||
|
| Windows | ARM64 | \`rustfs-aarch64-pc-windows-msvc.zip\` | Experimental |
|
||||||
|
|
||||||
|
### 🔐 Verification
|
||||||
|
|
||||||
|
Download checksums and verify your download:
|
||||||
|
\`\`\`bash
|
||||||
|
# Download checksums
|
||||||
|
curl -LO https://github.com/rustfs/rustfs/releases/download/${VERSION}/SHA256SUMS
|
||||||
|
|
||||||
|
# Verify (Linux/macOS)
|
||||||
|
sha256sum -c SHA256SUMS --ignore-missing
|
||||||
|
|
||||||
|
# Verify (Windows PowerShell)
|
||||||
|
\$expectedHash = (Get-Content SHA256SUMS | Select-String "rustfs-x86_64-pc-windows-msvc.zip").Line.Split()[0]
|
||||||
|
\$actualHash = (Get-FileHash rustfs-x86_64-pc-windows-msvc.zip -Algorithm SHA256).Hash.ToLower()
|
||||||
|
if (\$expectedHash -eq \$actualHash) { "✅ Checksum verified" } else { "❌ Checksum mismatch" }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 🛠️ System Requirements
|
||||||
|
|
||||||
|
- **Linux**: Any distribution with glibc 2.17+ (CentOS 7+, Ubuntu 16.04+)
|
||||||
|
- **macOS**: 10.15+ (Catalina or later)
|
||||||
|
- **Windows**: Windows 10 version 1809 or later
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
- [Installation Guide](https://github.com/rustfs/rustfs#installation)
|
||||||
|
- [Quick Start](https://github.com/rustfs/rustfs#quick-start)
|
||||||
|
- [Configuration](https://github.com/rustfs/rustfs/blob/main/docs/)
|
||||||
|
- [API Documentation](https://docs.rs/rustfs)
|
||||||
|
|
||||||
|
### 🆘 Support
|
||||||
|
|
||||||
|
- 🐛 [Report Issues](https://github.com/rustfs/rustfs/issues)
|
||||||
|
- 💬 [Community Discussions](https://github.com/rustfs/rustfs/discussions)
|
||||||
|
- 📖 [Documentation](https://github.com/rustfs/rustfs/tree/main/docs)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Update the release with enhanced notes
|
||||||
|
gh release edit "$VERSION" --notes-file enhanced_notes.md
|
||||||
|
else
|
||||||
|
echo "Release $VERSION already has custom notes, skipping update to preserve manual edits"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Upload to OSS (optional)
|
||||||
|
upload-oss:
|
||||||
|
name: Upload to OSS
|
||||||
|
needs: [build-check, build-rustfs]
|
||||||
|
if: always() && needs.build-check.outputs.build_type == 'release' && needs.build-rustfs.result == 'success'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
OSS_ACCESS_KEY_ID: ${{ secrets.ALICLOUDOSS_KEY_ID }}
|
||||||
|
OSS_ACCESS_KEY_SECRET: ${{ secrets.ALICLOUDOSS_KEY_SECRET }}
|
||||||
|
OSS_REGION: cn-beijing
|
||||||
|
OSS_ENDPOINT: https://oss-cn-beijing.aliyuncs.com
|
||||||
|
steps:
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: ./artifacts
|
||||||
|
|
||||||
|
- name: Upload to Aliyun OSS
|
||||||
|
if: ${{ env.OSS_ACCESS_KEY_ID != '' }}
|
||||||
|
run: |
|
||||||
|
# Install ossutil
|
||||||
|
curl -o ossutil.zip https://gosspublic.alicdn.com/ossutil/v2/2.1.1/ossutil-2.1.1-linux-amd64.zip
|
||||||
|
unzip ossutil.zip
|
||||||
|
sudo mv ossutil-*/ossutil /usr/local/bin/
|
||||||
|
|
||||||
|
# Upload files (both .tar.gz and .zip)
|
||||||
|
find ./artifacts -name "*.tar.gz" -exec ossutil cp {} oss://rustfs-artifacts/artifacts/rustfs/ --force \;
|
||||||
|
find ./artifacts -name "*.zip" -exec ossutil cp {} oss://rustfs-artifacts/artifacts/rustfs/ --force \;
|
||||||
|
|
||||||
|
# Create latest.json
|
||||||
|
VERSION="${GITHUB_REF#refs/tags/v}"
|
||||||
|
echo "{\"version\":\"${VERSION}\",\"release_date\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" > latest.json
|
||||||
|
ossutil cp latest.json oss://rustfs-version/latest.json --force
|
||||||
|
|||||||
198
.github/workflows/ci.yml
vendored
198
.github/workflows/ci.yml
vendored
@@ -1,21 +1,71 @@
|
|||||||
name: CI
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
name: Continuous Integration
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [main]
|
||||||
- main
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**.txt"
|
||||||
|
- "docs/**"
|
||||||
|
- "deploy/**"
|
||||||
|
- "scripts/dev_*.sh"
|
||||||
|
- "scripts/probe.sh"
|
||||||
|
- "LICENSE*"
|
||||||
|
- ".gitignore"
|
||||||
|
- ".dockerignore"
|
||||||
|
- "README*"
|
||||||
|
- "**/*.png"
|
||||||
|
- "**/*.jpg"
|
||||||
|
- "**/*.svg"
|
||||||
|
- ".github/workflows/build.yml"
|
||||||
|
- ".github/workflows/docker.yml"
|
||||||
|
- ".github/workflows/audit.yml"
|
||||||
|
- ".github/workflows/performance.yml"
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches: [main]
|
||||||
- main
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**.txt"
|
||||||
|
- "docs/**"
|
||||||
|
- "deploy/**"
|
||||||
|
- "scripts/dev_*.sh"
|
||||||
|
- "scripts/probe.sh"
|
||||||
|
- "LICENSE*"
|
||||||
|
- ".gitignore"
|
||||||
|
- ".dockerignore"
|
||||||
|
- "README*"
|
||||||
|
- "**/*.png"
|
||||||
|
- "**/*.jpg"
|
||||||
|
- "**/*.svg"
|
||||||
|
- ".github/workflows/build.yml"
|
||||||
|
- ".github/workflows/docker.yml"
|
||||||
|
- ".github/workflows/audit.yml"
|
||||||
|
- ".github/workflows/performance.yml"
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 0' # at midnight of each sunday
|
- cron: "0 0 * * 0" # Weekly on Sunday at midnight UTC
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
RUST_BACKTRACE: 1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
skip-check:
|
skip-check:
|
||||||
|
name: Skip Duplicate Actions
|
||||||
permissions:
|
permissions:
|
||||||
actions: write
|
actions: write
|
||||||
contents: read
|
contents: read
|
||||||
@@ -23,100 +73,82 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||||
steps:
|
steps:
|
||||||
- id: skip_check
|
- name: Skip duplicate actions
|
||||||
|
id: skip_check
|
||||||
uses: fkirc/skip-duplicate-actions@v5
|
uses: fkirc/skip-duplicate-actions@v5
|
||||||
with:
|
with:
|
||||||
concurrent_skipping: 'same_content_newer'
|
concurrent_skipping: "same_content_newer"
|
||||||
cancel_others: true
|
cancel_others: true
|
||||||
paths_ignore: '["*.md"]'
|
paths_ignore: '["*.md", "docs/**", "deploy/**"]'
|
||||||
|
# Never skip release events and tag pushes
|
||||||
|
do_not_skip: '["release", "push"]'
|
||||||
|
|
||||||
# Quality checks for pull requests
|
test-and-lint:
|
||||||
pr-checks:
|
name: Test and Lint
|
||||||
name: Pull Request Quality Checks
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
runs-on: self-hosted
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/actions/setup
|
|
||||||
|
|
||||||
- name: Format Check
|
|
||||||
run: cargo fmt --all --check
|
|
||||||
|
|
||||||
- name: Lint Check
|
|
||||||
run: cargo check --all-targets
|
|
||||||
|
|
||||||
- name: Clippy Check
|
|
||||||
run: cargo clippy --all-targets --all-features -- -D warnings
|
|
||||||
|
|
||||||
- name: Unit Tests
|
|
||||||
run: cargo test --all --exclude e2e_test
|
|
||||||
|
|
||||||
develop:
|
|
||||||
needs: skip-check
|
needs: skip-check
|
||||||
if: needs.skip-check.outputs.should_skip != 'true'
|
if: needs.skip-check.outputs.should_skip != 'true'
|
||||||
runs-on: self-hosted
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 60
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repository
|
||||||
- uses: ./.github/actions/setup
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Format
|
- name: Setup Rust environment
|
||||||
run: cargo fmt --all --check
|
uses: ./.github/actions/setup
|
||||||
|
with:
|
||||||
|
rust-version: stable
|
||||||
|
cache-shared-key: ci-test-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
|
||||||
- name: Lint
|
- name: Run tests
|
||||||
run: cargo check --all-targets
|
|
||||||
|
|
||||||
- name: Clippy
|
|
||||||
run: cargo clippy --all-targets --all-features -- -D warnings
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: cargo test --all --exclude e2e_test
|
run: cargo test --all --exclude e2e_test
|
||||||
|
|
||||||
- name: Build debug
|
- name: Check code formatting
|
||||||
|
run: cargo fmt --all --check
|
||||||
|
|
||||||
|
- name: Run clippy lints
|
||||||
|
run: cargo clippy --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
|
e2e-tests:
|
||||||
|
name: End-to-End Tests
|
||||||
|
needs: skip-check
|
||||||
|
if: needs.skip-check.outputs.should_skip != 'true'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Rust environment
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
with:
|
||||||
|
rust-version: stable
|
||||||
|
cache-shared-key: ci-e2e-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install s3s-e2e test tool
|
||||||
|
uses: taiki-e/cache-cargo-install-action@v2
|
||||||
|
with:
|
||||||
|
tool: s3s-e2e
|
||||||
|
git: https://github.com/Nugine/s3s.git
|
||||||
|
rev: b7714bfaa17ddfa9b23ea01774a1e7bbdbfc2ca3
|
||||||
|
|
||||||
|
- name: Build debug binary
|
||||||
run: |
|
run: |
|
||||||
touch rustfs/build.rs
|
touch rustfs/build.rs
|
||||||
cargo build -p rustfs --bins
|
cargo build -p rustfs --bins
|
||||||
|
|
||||||
- name: Pack artifacts
|
- name: Run end-to-end tests
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ./target/artifacts
|
|
||||||
cp target/debug/rustfs ./target/artifacts/rustfs-debug
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: rustfs
|
|
||||||
path: ./target/artifacts/*
|
|
||||||
|
|
||||||
s3s-e2e:
|
|
||||||
name: E2E (s3s-e2e)
|
|
||||||
needs:
|
|
||||||
- skip-check
|
|
||||||
- develop
|
|
||||||
if: needs.skip-check.outputs.should_skip != 'true'
|
|
||||||
runs-on: self-hosted
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
cache-on-failure: true
|
|
||||||
cache-all-crates: true
|
|
||||||
|
|
||||||
- name: Install s3s-e2e
|
|
||||||
run: |
|
|
||||||
cargo install s3s-e2e --git https://github.com/Nugine/s3s.git
|
|
||||||
s3s-e2e --version
|
s3s-e2e --version
|
||||||
|
./scripts/e2e-run.sh ./target/debug/rustfs /tmp/rustfs
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
- name: Upload test logs
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: rustfs
|
name: e2e-test-logs-${{ github.run_number }}
|
||||||
path: ./target/artifacts
|
|
||||||
|
|
||||||
- name: Run s3s-e2e
|
|
||||||
timeout-minutes: 10
|
|
||||||
run: |
|
|
||||||
./scripts/e2e-run.sh ./target/artifacts/rustfs-debug /tmp/rustfs
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: s3s-e2e.logs
|
|
||||||
path: /tmp/rustfs.log
|
path: /tmp/rustfs.log
|
||||||
|
retention-days: 3
|
||||||
|
|||||||
210
.github/workflows/docker.yml
vendored
Normal file
210
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
name: Docker Images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: ["*"]
|
||||||
|
branches: [main]
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**.txt"
|
||||||
|
- ".github/**"
|
||||||
|
- "docs/**"
|
||||||
|
- "deploy/**"
|
||||||
|
- "scripts/dev_*.sh"
|
||||||
|
- "LICENSE*"
|
||||||
|
- "README*"
|
||||||
|
- "**/*.png"
|
||||||
|
- "**/*.jpg"
|
||||||
|
- "**/*.svg"
|
||||||
|
- ".gitignore"
|
||||||
|
- ".dockerignore"
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
- "**.txt"
|
||||||
|
- ".github/**"
|
||||||
|
- "docs/**"
|
||||||
|
- "deploy/**"
|
||||||
|
- "scripts/dev_*.sh"
|
||||||
|
- "LICENSE*"
|
||||||
|
- "README*"
|
||||||
|
- "**/*.png"
|
||||||
|
- "**/*.jpg"
|
||||||
|
- "**/*.svg"
|
||||||
|
- ".gitignore"
|
||||||
|
- ".dockerignore"
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
push_images:
|
||||||
|
description: "Push images to registries"
|
||||||
|
required: false
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
REGISTRY_DOCKERHUB: rustfs/rustfs
|
||||||
|
REGISTRY_GHCR: ghcr.io/${{ github.repository }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Check if we should build
|
||||||
|
build-check:
|
||||||
|
name: Build Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
should_build: ${{ steps.check.outputs.should_build }}
|
||||||
|
should_push: ${{ steps.check.outputs.should_push }}
|
||||||
|
steps:
|
||||||
|
- name: Check build conditions
|
||||||
|
id: check
|
||||||
|
run: |
|
||||||
|
should_build=false
|
||||||
|
should_push=false
|
||||||
|
|
||||||
|
# Always build on workflow_dispatch or when changes detected
|
||||||
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]] || \
|
||||||
|
[[ "${{ github.event_name }}" == "push" ]] || \
|
||||||
|
[[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||||
|
should_build=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Push only on main branch, tags, or manual trigger
|
||||||
|
if [[ "${{ github.ref }}" == "refs/heads/main" ]] || \
|
||||||
|
[[ "${{ startsWith(github.ref, 'refs/tags/') }}" == "true" ]] || \
|
||||||
|
[[ "${{ github.event.inputs.push_images }}" == "true" ]]; then
|
||||||
|
should_push=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "should_build=$should_build" >> $GITHUB_OUTPUT
|
||||||
|
echo "should_push=$should_push" >> $GITHUB_OUTPUT
|
||||||
|
echo "Build: $should_build, Push: $should_push"
|
||||||
|
|
||||||
|
# Build multi-arch Docker images
|
||||||
|
build-docker:
|
||||||
|
name: Build Docker Images
|
||||||
|
needs: build-check
|
||||||
|
if: needs.build-check.outputs.should_build == 'true'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 60
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
variant:
|
||||||
|
- name: production
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
- name: ubuntu
|
||||||
|
dockerfile: .docker/Dockerfile.ubuntu22.04
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
- name: alpine
|
||||||
|
dockerfile: .docker/Dockerfile.alpine
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
if: needs.build-check.outputs.should_push == 'true' && secrets.DOCKERHUB_USERNAME != ''
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
if: needs.build-check.outputs.should_push == 'true'
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ env.REGISTRY_DOCKERHUB }}
|
||||||
|
${{ env.REGISTRY_GHCR }}
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch,suffix=-${{ matrix.variant.name }}
|
||||||
|
type=ref,event=pr,suffix=-${{ matrix.variant.name }}
|
||||||
|
type=semver,pattern={{version}},suffix=-${{ matrix.variant.name }}
|
||||||
|
type=semver,pattern={{major}}.{{minor}},suffix=-${{ matrix.variant.name }}
|
||||||
|
type=raw,value=latest,suffix=-${{ matrix.variant.name }},enable={{is_default_branch}}
|
||||||
|
flavor: |
|
||||||
|
latest=false
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ${{ matrix.variant.dockerfile }}
|
||||||
|
platforms: ${{ matrix.variant.platforms }}
|
||||||
|
push: ${{ needs.build-check.outputs.should_push == 'true' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha,scope=docker-${{ matrix.variant.name }}
|
||||||
|
cache-to: type=gha,mode=max,scope=docker-${{ matrix.variant.name }}
|
||||||
|
build-args: |
|
||||||
|
BUILDTIME=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
|
||||||
|
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
||||||
|
REVISION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
|
||||||
|
|
||||||
|
# Create manifest for main production image
|
||||||
|
create-manifest:
|
||||||
|
name: Create Manifest
|
||||||
|
needs: [build-check, build-docker]
|
||||||
|
if: needs.build-check.outputs.should_push == 'true' && startsWith(github.ref, 'refs/tags/')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
if: secrets.DOCKERHUB_USERNAME != ''
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create and push manifest
|
||||||
|
run: |
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/}
|
||||||
|
|
||||||
|
# Create main image tag (without variant suffix)
|
||||||
|
if [[ -n "${{ secrets.DOCKERHUB_USERNAME }}" ]]; then
|
||||||
|
docker buildx imagetools create \
|
||||||
|
-t ${{ env.REGISTRY_DOCKERHUB }}:${VERSION} \
|
||||||
|
-t ${{ env.REGISTRY_DOCKERHUB }}:latest \
|
||||||
|
${{ env.REGISTRY_DOCKERHUB }}:${VERSION}-production
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker buildx imagetools create \
|
||||||
|
-t ${{ env.REGISTRY_GHCR }}:${VERSION} \
|
||||||
|
-t ${{ env.REGISTRY_GHCR }}:latest \
|
||||||
|
${{ env.REGISTRY_GHCR }}:${VERSION}-production
|
||||||
18
.github/workflows/issue-translator.yml
vendored
Normal file
18
.github/workflows/issue-translator.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
name: 'issue-translator'
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: usthe/issues-translate-action@v2.7
|
||||||
|
with:
|
||||||
|
IS_MODIFY_TITLE: false
|
||||||
|
# not require, default false, . Decide whether to modify the issue title
|
||||||
|
# if true, the robot account @Issues-translate-bot must have modification permissions, invite @Issues-translate-bot to your project or use your custom bot.
|
||||||
|
CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically.
|
||||||
|
# not require. Customize the translation robot prefix message.
|
||||||
140
.github/workflows/performance.yml
vendored
Normal file
140
.github/workflows/performance.yml
vendored
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
name: Performance Testing
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- '**/*.rs'
|
||||||
|
- '**/Cargo.toml'
|
||||||
|
- '**/Cargo.lock'
|
||||||
|
- '.github/workflows/performance.yml'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
profile_duration:
|
||||||
|
description: "Profiling duration in seconds"
|
||||||
|
required: false
|
||||||
|
default: "120"
|
||||||
|
type: string
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
RUST_BACKTRACE: 1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
performance-profile:
|
||||||
|
name: Performance Profiling
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Rust environment
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
with:
|
||||||
|
rust-version: nightly
|
||||||
|
cache-shared-key: perf-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install additional nightly components
|
||||||
|
run: rustup component add llvm-tools-preview
|
||||||
|
|
||||||
|
- name: Install samply profiler
|
||||||
|
uses: taiki-e/cache-cargo-install-action@v2
|
||||||
|
with:
|
||||||
|
tool: samply
|
||||||
|
|
||||||
|
- name: Configure kernel for profiling
|
||||||
|
run: echo '1' | sudo tee /proc/sys/kernel/perf_event_paranoid
|
||||||
|
|
||||||
|
- name: Prepare test environment
|
||||||
|
run: |
|
||||||
|
# Create test volumes
|
||||||
|
for i in {0..4}; do
|
||||||
|
mkdir -p ./target/volume/test$i
|
||||||
|
done
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
echo "RUSTFS_VOLUMES=./target/volume/test{0...4}" >> $GITHUB_ENV
|
||||||
|
echo "RUST_LOG=rustfs=info,ecstore=info,s3s=info,iam=info,rustfs-obs=info" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Download static files
|
||||||
|
run: |
|
||||||
|
curl -L "https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip" \
|
||||||
|
-o tempfile.zip --retry 3 --retry-delay 5
|
||||||
|
unzip -o tempfile.zip -d ./rustfs/static
|
||||||
|
rm tempfile.zip
|
||||||
|
|
||||||
|
- name: Build with profiling optimizations
|
||||||
|
run: |
|
||||||
|
RUSTFLAGS="-C force-frame-pointers=yes -C debug-assertions=off" \
|
||||||
|
cargo +nightly build --profile profiling -p rustfs --bins
|
||||||
|
|
||||||
|
- name: Run performance profiling
|
||||||
|
id: profiling
|
||||||
|
run: |
|
||||||
|
DURATION="${{ github.event.inputs.profile_duration || '120' }}"
|
||||||
|
echo "Running profiling for ${DURATION} seconds..."
|
||||||
|
|
||||||
|
timeout "${DURATION}s" samply record \
|
||||||
|
--output samply-profile.json \
|
||||||
|
./target/profiling/rustfs ${RUSTFS_VOLUMES} || true
|
||||||
|
|
||||||
|
if [ -f "samply-profile.json" ]; then
|
||||||
|
echo "profile_generated=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "Profile generated successfully"
|
||||||
|
else
|
||||||
|
echo "profile_generated=false" >> $GITHUB_OUTPUT
|
||||||
|
echo "::warning::Profile data not generated"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload profile data
|
||||||
|
if: steps.profiling.outputs.profile_generated == 'true'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: performance-profile-${{ github.run_number }}
|
||||||
|
path: samply-profile.json
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
benchmark:
|
||||||
|
name: Benchmark Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 45
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Rust environment
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
with:
|
||||||
|
rust-version: stable
|
||||||
|
cache-shared-key: bench-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
|
||||||
|
- name: Run benchmarks
|
||||||
|
run: |
|
||||||
|
cargo bench --package ecstore --bench comparison_benchmark -- --output-format json | \
|
||||||
|
tee benchmark-results.json
|
||||||
|
|
||||||
|
- name: Upload benchmark results
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: benchmark-results-${{ github.run_number }}
|
||||||
|
path: benchmark-results.json
|
||||||
|
retention-days: 7
|
||||||
68
.github/workflows/samply.yml
vendored
68
.github/workflows/samply.yml
vendored
@@ -1,68 +0,0 @@
|
|||||||
name: Profile with Samply
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
workflow_dispatch:
|
|
||||||
jobs:
|
|
||||||
profile:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
|
||||||
with:
|
|
||||||
components: llvm-tools-preview
|
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry
|
|
||||||
~/.cargo/git
|
|
||||||
target
|
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Install samply
|
|
||||||
uses: taiki-e/cache-cargo-install-action@v2
|
|
||||||
with:
|
|
||||||
tool: samply
|
|
||||||
|
|
||||||
- name: Configure kernel for profiling
|
|
||||||
run: echo '1' | sudo tee /proc/sys/kernel/perf_event_paranoid
|
|
||||||
|
|
||||||
- name: Create test volumes
|
|
||||||
run: |
|
|
||||||
for i in {0..4}; do
|
|
||||||
mkdir -p ./target/volume/test$i
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Set environment variables
|
|
||||||
run: |
|
|
||||||
echo "RUSTFS_VOLUMES=./target/volume/test{0...4}" >> $GITHUB_ENV
|
|
||||||
echo "RUST_LOG=rustfs=info,ecstore=info,s3s=info,iam=info,rustfs-obs=info" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Download static files
|
|
||||||
run: |
|
|
||||||
curl -L "https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip" -o tempfile.zip && unzip -o tempfile.zip -d ./rustfs/static && rm tempfile.zip
|
|
||||||
|
|
||||||
- name: Build with profiling
|
|
||||||
run: |
|
|
||||||
RUSTFLAGS="-C force-frame-pointers=yes" cargo +nightly build --profile profiling -p rustfs --bins
|
|
||||||
|
|
||||||
- name: Run samply with timeout
|
|
||||||
id: samply_record
|
|
||||||
run: |
|
|
||||||
timeout 120s samply record --output samply.json ./target/profiling/rustfs ${RUSTFS_VOLUMES}
|
|
||||||
if [ -f "samply.json" ]; then
|
|
||||||
echo "profile_generated=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "profile_generated=false" >> $GITHUB_OUTPUT
|
|
||||||
echo "::error::Failed to generate profile data"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Upload profile data
|
|
||||||
if: steps.samply_record.outputs.profile_generated == 'true'
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: samply-profile-${{ github.run_number }}
|
|
||||||
path: samply.json
|
|
||||||
retention-days: 7
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -10,7 +10,6 @@ rustfs/static/*
|
|||||||
!rustfs/static/.gitkeep
|
!rustfs/static/.gitkeep
|
||||||
vendor
|
vendor
|
||||||
cli/rustfs-gui/embedded-rustfs/rustfs
|
cli/rustfs-gui/embedded-rustfs/rustfs
|
||||||
deploy/config/obs.toml
|
|
||||||
*.log
|
*.log
|
||||||
deploy/certs/*
|
deploy/certs/*
|
||||||
*jsonl
|
*jsonl
|
||||||
@@ -19,3 +18,4 @@ deploy/certs/*
|
|||||||
.cargo
|
.cargo
|
||||||
profile.json
|
profile.json
|
||||||
.docker/openobserve-otel/data
|
.docker/openobserve-otel/data
|
||||||
|
*.zst
|
||||||
|
|||||||
51
.vscode/launch.json
vendored
51
.vscode/launch.json
vendored
@@ -20,33 +20,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"RUST_LOG": "rustfs=debug,ecstore=info,s3s=debug",
|
"RUST_LOG": "rustfs=debug,ecstore=info,s3s=debug"
|
||||||
"RUSTFS_VOLUMES": "./target/volume/test{0...3}",
|
|
||||||
"RUSTFS_ADDRESS": "[::]:9000",
|
|
||||||
"RUSTFS_CONSOLE_ENABLE": "true",
|
|
||||||
"RUSTFS_CONSOLE_ADDRESS": "[::]:9002",
|
|
||||||
"RUSTFS_SERVER_DOMAINS": "localhost:9000",
|
|
||||||
"RUSTFS_TLS_PATH": "./deploy/certs",
|
|
||||||
"RUSTFS_OBS_CONFIG": "./deploy/config/obs.example.toml",
|
|
||||||
"RUSTFS__OBSERVABILITY__ENDPOINT": "http://localhost:4317",
|
|
||||||
"RUSTFS__OBSERVABILITY__USE_STDOUT": "true",
|
|
||||||
"RUSTFS__OBSERVABILITY__SAMPLE_RATIO": "2.0",
|
|
||||||
"RUSTFS__OBSERVABILITY__METER_INTERVAL": "30",
|
|
||||||
"RUSTFS__OBSERVABILITY__SERVICE_NAME": "rustfs",
|
|
||||||
"RUSTFS__OBSERVABILITY__SERVICE_VERSION": "0.1.0",
|
|
||||||
"RUSTFS__OBSERVABILITY__ENVIRONMENT": "develop",
|
|
||||||
"RUSTFS__OBSERVABILITY__LOGGER_LEVEL": "info",
|
|
||||||
"RUSTFS__SINKS__FILE__ENABLED": "true",
|
|
||||||
"RUSTFS__SINKS__FILE__PATH": "./deploy/logs/rustfs.log",
|
|
||||||
"RUSTFS__SINKS__WEBHOOK__ENABLED": "false",
|
|
||||||
"RUSTFS__SINKS__WEBHOOK__ENDPOINT": "",
|
|
||||||
"RUSTFS__SINKS__WEBHOOK__AUTH_TOKEN": "",
|
|
||||||
"RUSTFS__SINKS__KAFKA__ENABLED": "false",
|
|
||||||
"RUSTFS__SINKS__KAFKA__BOOTSTRAP_SERVERS": "",
|
|
||||||
"RUSTFS__SINKS__KAFKA__TOPIC": "",
|
|
||||||
"RUSTFS__LOGGER__QUEUE_CAPACITY": "10"
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
"args": [
|
||||||
|
"--access-key",
|
||||||
|
"AKEXAMPLERUSTFS",
|
||||||
|
"--secret-key",
|
||||||
|
"SKEXAMPLERUSTFS",
|
||||||
|
"--address",
|
||||||
|
"0.0.0.0:9010",
|
||||||
|
"--domain-name",
|
||||||
|
"127.0.0.1:9010",
|
||||||
|
"./target/volume/test{0...4}"
|
||||||
|
],
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -86,6 +72,19 @@
|
|||||||
},
|
},
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug executable target/debug/rustfs",
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/target/debug/rustfs",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
//"stopAtEntry": false,
|
||||||
|
//"preLaunchTask": "cargo build",
|
||||||
|
"sourceLanguages": [
|
||||||
|
"rust"
|
||||||
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
39
CLA.md
Normal file
39
CLA.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
RustFS Individual Contributor License Agreement
|
||||||
|
|
||||||
|
Thank you for your interest in contributing documentation and related software code to a project hosted or managed by RustFS. In order to clarify the intellectual property license granted with Contributions from any person or entity, RustFS must have a Contributor License Agreement (“CLA”) on file that has been signed by each Contributor, indicating agreement to the license terms below. This version of the Contributor License Agreement allows an individual to submit Contributions to the applicable project. If you are making a submission on behalf of a legal entity, then you should sign the separate Corporate Contributor License Agreement.
|
||||||
|
|
||||||
|
You accept and agree to the following terms and conditions for Your present and future Contributions submitted to RustFS. You hereby irrevocably assign and transfer to RustFS all right, title, and interest in and to Your Contributions, including all copyrights and other intellectual property rights therein.
|
||||||
|
|
||||||
|
Definitions
|
||||||
|
|
||||||
|
“You” (or “Your”) shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with RustFS. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
“Contribution” shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to RustFS for inclusion in, or documentation of, any of the products or projects owned or managed by RustFS (the “Work”), including without limitation any Work described in Schedule A. For the purposes of this definition, “submitted” means any form of electronic or written communication sent to RustFS or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, RustFS for the purpose of discussing and improving the Work.
|
||||||
|
|
||||||
|
Assignment of Copyright
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this Agreement, You hereby irrevocably assign and transfer to RustFS all right, title, and interest in and to Your Contributions, including all copyrights and other intellectual property rights therein, for the entire term of such rights, including all renewals and extensions. You agree to execute all documents and take all actions as may be reasonably necessary to vest in RustFS the ownership of Your Contributions and to assist RustFS in perfecting, maintaining, and enforcing its rights in Your Contributions.
|
||||||
|
|
||||||
|
Grant of Patent License
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this Agreement, You hereby grant to RustFS and to recipients of documentation and software distributed by RustFS a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
|
||||||
|
|
||||||
|
You represent that you are legally entitled to grant the above assignment and license.
|
||||||
|
|
||||||
|
You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions.
|
||||||
|
|
||||||
|
You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
Should You wish to submit work that is not Your original creation, You may submit it to RustFS separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as “Submitted on behalf of a third-party: [named here]”.
|
||||||
|
|
||||||
|
You agree to notify RustFS of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.
|
||||||
|
|
||||||
|
Modification of CLA
|
||||||
|
|
||||||
|
RustFS reserves the right to update or modify this CLA in the future. Any updates or modifications to this CLA shall apply only to Contributions made after the effective date of the revised CLA. Contributions made prior to the update shall remain governed by the version of the CLA that was in effect at the time of submission. It is not necessary for all Contributors to re-sign the CLA when the CLA is updated or modified.
|
||||||
|
|
||||||
|
Governing Law and Dispute Resolution
|
||||||
|
|
||||||
|
This Agreement will be governed by and construed in accordance with the laws of the People’s Republic of China excluding that body of laws known as conflict of laws. The parties expressly agree that the United Nations Convention on Contracts for the International Sale of Goods will not apply. Any legal action or proceeding arising under this Agreement will be brought exclusively in the courts located in Beijing, China, and the parties hereby irrevocably consent to the personal jurisdiction and venue therein.
|
||||||
|
|
||||||
|
For your reading convenience, this Agreement is written in parallel English and Chinese sections. To the extent there is a conflict between the English and Chinese sections, the English sections shall govern.
|
||||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
hello@rustfs.com.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
||||||
@@ -11,21 +11,25 @@
|
|||||||
Before every commit, you **MUST**:
|
Before every commit, you **MUST**:
|
||||||
|
|
||||||
1. **Format your code**:
|
1. **Format your code**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo fmt --all
|
cargo fmt --all
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Verify formatting**:
|
2. **Verify formatting**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo fmt --all --check
|
cargo fmt --all --check
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Pass clippy checks**:
|
3. **Pass clippy checks**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo clippy --all-targets --all-features -- -D warnings
|
cargo clippy --all-targets --all-features -- -D warnings
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Ensure compilation**:
|
4. **Ensure compilation**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo check --all-targets
|
cargo check --all-targets
|
||||||
```
|
```
|
||||||
@@ -136,6 +140,7 @@ Install the `rust-analyzer` extension and add to your `settings.json`:
|
|||||||
#### Other IDEs
|
#### Other IDEs
|
||||||
|
|
||||||
Configure your IDE to:
|
Configure your IDE to:
|
||||||
|
|
||||||
- Use the project's `rustfmt.toml` configuration
|
- Use the project's `rustfmt.toml` configuration
|
||||||
- Format on save
|
- Format on save
|
||||||
- Run clippy checks
|
- Run clippy checks
|
||||||
1706
Cargo.lock
generated
1706
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
180
Cargo.toml
180
Cargo.toml
@@ -1,33 +1,50 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"appauth", # Application authentication and authorization
|
|
||||||
"cli/rustfs-gui", # Graphical user interface client
|
|
||||||
"common/common", # Shared utilities and data structures
|
|
||||||
"common/lock", # Distributed locking implementation
|
|
||||||
"common/protos", # Protocol buffer definitions
|
|
||||||
"common/workers", # Worker thread pools and task scheduling
|
|
||||||
"crates/config", # Configuration management
|
|
||||||
"crates/event-notifier", # Event notification system
|
|
||||||
"crates/obs", # Observability utilities
|
|
||||||
"crates/utils", # Utility functions and helpers
|
|
||||||
"crypto", # Cryptography and security features
|
|
||||||
"ecstore", # Erasure coding storage implementation
|
|
||||||
"e2e_test", # End-to-end test suite
|
|
||||||
"iam", # Identity and Access Management
|
|
||||||
"madmin", # Management dashboard and admin API interface
|
|
||||||
"rustfs", # Core file system implementation
|
"rustfs", # Core file system implementation
|
||||||
"s3select/api", # S3 Select API interface
|
"cli/rustfs-gui", # Graphical user interface client
|
||||||
"s3select/query", # S3 Select query engine
|
"crates/appauth", # Application authentication and authorization
|
||||||
"crates/zip",
|
"crates/common", # Shared utilities and data structures
|
||||||
|
"crates/config", # Configuration management
|
||||||
|
"crates/crypto", # Cryptography and security features
|
||||||
|
"crates/ecstore", # Erasure coding storage implementation
|
||||||
|
"crates/e2e_test", # End-to-end test suite
|
||||||
|
"crates/filemeta", # File metadata management
|
||||||
|
"crates/iam", # Identity and Access Management
|
||||||
|
"crates/lock", # Distributed locking implementation
|
||||||
|
"crates/madmin", # Management dashboard and admin API interface
|
||||||
|
"crates/notify", # Notification system for events
|
||||||
|
"crates/obs", # Observability utilities
|
||||||
|
"crates/protos", # Protocol buffer definitions
|
||||||
|
"crates/rio", # Rust I/O utilities and abstractions
|
||||||
|
"crates/s3select-api", # S3 Select API interface
|
||||||
|
"crates/s3select-query", # S3 Select query engine
|
||||||
|
"crates/signer", # client signer
|
||||||
|
"crates/utils", # Utility functions and helpers
|
||||||
|
"crates/workers", # Worker thread pools and task scheduling
|
||||||
|
"crates/zip", # ZIP file handling and compression
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
repository = "https://github.com/rustfs/rustfs"
|
repository = "https://github.com/rustfs/rustfs"
|
||||||
rust-version = "1.75"
|
rust-version = "1.85"
|
||||||
version = "0.0.1"
|
version = "0.0.3"
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
unsafe_code = "deny"
|
unsafe_code = "deny"
|
||||||
@@ -36,64 +53,82 @@ unsafe_code = "deny"
|
|||||||
all = "warn"
|
all = "warn"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
api = { path = "./s3select/api", version = "0.0.1" }
|
rustfs-s3select-api = { path = "crates/s3select-api", version = "0.0.3" }
|
||||||
appauth = { path = "./appauth", version = "0.0.1" }
|
rustfs-appauth = { path = "crates/appauth", version = "0.0.3" }
|
||||||
common = { path = "./common/common", version = "0.0.1" }
|
rustfs-common = { path = "crates/common", version = "0.0.3" }
|
||||||
crypto = { path = "./crypto", version = "0.0.1" }
|
rustfs-crypto = { path = "crates/crypto", version = "0.0.3" }
|
||||||
ecstore = { path = "./ecstore", version = "0.0.1" }
|
rustfs-ecstore = { path = "crates/ecstore", version = "0.0.3" }
|
||||||
iam = { path = "./iam", version = "0.0.1" }
|
rustfs-iam = { path = "crates/iam", version = "0.0.3" }
|
||||||
lock = { path = "./common/lock", version = "0.0.1" }
|
rustfs-lock = { path = "crates/lock", version = "0.0.3" }
|
||||||
madmin = { path = "./madmin", version = "0.0.1" }
|
rustfs-madmin = { path = "crates/madmin", version = "0.0.3" }
|
||||||
policy = { path = "./policy", version = "0.0.1" }
|
rustfs-policy = { path = "crates/policy", version = "0.0.3" }
|
||||||
protos = { path = "./common/protos", version = "0.0.1" }
|
rustfs-protos = { path = "crates/protos", version = "0.0.3" }
|
||||||
query = { path = "./s3select/query", version = "0.0.1" }
|
rustfs-s3select-query = { path = "crates/s3select-query", version = "0.0.3" }
|
||||||
rustfs = { path = "./rustfs", version = "0.0.1" }
|
rustfs = { path = "./rustfs", version = "0.0.3" }
|
||||||
rustfs-zip = { path = "./crates/zip", version = "0.0.1" }
|
rustfs-zip = { path = "./crates/zip", version = "0.0.3" }
|
||||||
rustfs-config = { path = "./crates/config", version = "0.0.1" }
|
rustfs-config = { path = "./crates/config", version = "0.0.3" }
|
||||||
rustfs-obs = { path = "crates/obs", version = "0.0.1" }
|
rustfs-obs = { path = "crates/obs", version = "0.0.3" }
|
||||||
rustfs-event-notifier = { path = "crates/event-notifier", version = "0.0.1" }
|
rustfs-notify = { path = "crates/notify", version = "0.0.3" }
|
||||||
rustfs-utils = { path = "crates/utils", version = "0.0.1" }
|
rustfs-utils = { path = "crates/utils", version = "0.0.3" }
|
||||||
workers = { path = "./common/workers", version = "0.0.1" }
|
rustfs-rio = { path = "crates/rio", version = "0.0.3" }
|
||||||
tokio-tar = "0.3.1"
|
rustfs-filemeta = { path = "crates/filemeta", version = "0.0.3" }
|
||||||
|
rustfs-signer = { path = "crates/signer", version = "0.0.3" }
|
||||||
|
rustfs-workers = { path = "crates/workers", version = "0.0.3" }
|
||||||
|
aes-gcm = { version = "0.10.3", features = ["std"] }
|
||||||
|
arc-swap = "1.7.1"
|
||||||
|
argon2 = { version = "0.5.3", features = ["std"] }
|
||||||
atoi = "2.0.0"
|
atoi = "2.0.0"
|
||||||
|
async-channel = "2.4.0"
|
||||||
async-recursion = "1.1.1"
|
async-recursion = "1.1.1"
|
||||||
async-trait = "0.1.88"
|
async-trait = "0.1.88"
|
||||||
|
async-compression = { version = "0.4.0" }
|
||||||
atomic_enum = "0.3.0"
|
atomic_enum = "0.3.0"
|
||||||
aws-sdk-s3 = "1.29.0"
|
aws-sdk-s3 = "1.96.0"
|
||||||
axum = "0.8.4"
|
axum = "0.8.4"
|
||||||
axum-extra = "0.10.1"
|
axum-extra = "0.10.1"
|
||||||
axum-server = { version = "0.7.2", features = ["tls-rustls"] }
|
axum-server = { version = "0.7.2", features = ["tls-rustls"] }
|
||||||
backon = "1.5.1"
|
base64-simd = "0.8.0"
|
||||||
blake2 = "0.10.6"
|
base64 = "0.22.1"
|
||||||
bytes = "1.10.1"
|
brotli = "8.0.1"
|
||||||
|
bytes = { version = "1.10.1", features = ["serde"] }
|
||||||
bytesize = "2.0.1"
|
bytesize = "2.0.1"
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
|
cfg-if = "1.0.1"
|
||||||
|
chacha20poly1305 = { version = "0.10.1" }
|
||||||
chrono = { version = "0.4.41", features = ["serde"] }
|
chrono = { version = "0.4.41", features = ["serde"] }
|
||||||
clap = { version = "4.5.39", features = ["derive", "env"] }
|
clap = { version = "4.5.40", features = ["derive", "env"] }
|
||||||
config = "0.15.11"
|
|
||||||
const-str = { version = "0.6.2", features = ["std", "proc"] }
|
const-str = { version = "0.6.2", features = ["std", "proc"] }
|
||||||
|
crc32fast = "1.4.2"
|
||||||
|
criterion = { version = "0.5", features = ["html_reports"] }
|
||||||
|
dashmap = "6.1.0"
|
||||||
datafusion = "46.0.1"
|
datafusion = "46.0.1"
|
||||||
derive_builder = "0.20.2"
|
derive_builder = "0.20.2"
|
||||||
dioxus = { version = "0.6.3", features = ["router"] }
|
dioxus = { version = "0.6.3", features = ["router"] }
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
|
enumset = "1.1.6"
|
||||||
flatbuffers = "25.2.10"
|
flatbuffers = "25.2.10"
|
||||||
flexi_logger = { version = "0.30.2", features = ["trc"] }
|
flate2 = "1.1.2"
|
||||||
|
flexi_logger = { version = "0.31.2", features = ["trc", "dont_minimize_extra_stacks"] }
|
||||||
|
form_urlencoded = "1.2.1"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
futures-core = "0.3.31"
|
futures-core = "0.3.31"
|
||||||
futures-util = "0.3.31"
|
futures-util = "0.3.31"
|
||||||
glob = "0.3.2"
|
glob = "0.3.2"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
|
hex-simd = "0.8.0"
|
||||||
highway = { version = "1.3.0" }
|
highway = { version = "1.3.0" }
|
||||||
|
hmac = "0.12.1"
|
||||||
hyper = "1.6.0"
|
hyper = "1.6.0"
|
||||||
hyper-util = { version = "0.1.14", features = [
|
hyper-util = { version = "0.1.14", features = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"server-auto",
|
"server-auto",
|
||||||
"server-graceful",
|
"server-graceful",
|
||||||
] }
|
] }
|
||||||
|
hyper-rustls = "0.27.7"
|
||||||
http = "1.3.1"
|
http = "1.3.1"
|
||||||
http-body = "1.0.1"
|
http-body = "1.0.1"
|
||||||
humantime = "2.2.0"
|
humantime = "2.2.0"
|
||||||
include_dir = "0.7.4"
|
ipnetwork = { version = "0.21.1", features = ["serde"] }
|
||||||
jsonwebtoken = "9.3.1"
|
jsonwebtoken = "9.3.1"
|
||||||
keyring = { version = "3.6.2", features = [
|
keyring = { version = "3.6.2", features = [
|
||||||
"apple-native",
|
"apple-native",
|
||||||
@@ -103,9 +138,9 @@ keyring = { version = "3.6.2", features = [
|
|||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
libsystemd = { version = "0.7.2" }
|
libsystemd = { version = "0.7.2" }
|
||||||
local-ip-address = "0.6.5"
|
local-ip-address = "0.6.5"
|
||||||
|
lz4 = "1.28.1"
|
||||||
matchit = "0.8.4"
|
matchit = "0.8.4"
|
||||||
md-5 = "0.10.6"
|
md-5 = "0.10.6"
|
||||||
mime = "0.3.17"
|
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
netif = "0.1.6"
|
netif = "0.1.6"
|
||||||
nix = { version = "0.30.1", features = ["fs"] }
|
nix = { version = "0.30.1", features = ["fs"] }
|
||||||
@@ -129,17 +164,20 @@ opentelemetry-semantic-conventions = { version = "0.30.0", features = [
|
|||||||
"semconv_experimental",
|
"semconv_experimental",
|
||||||
] }
|
] }
|
||||||
parking_lot = "0.12.4"
|
parking_lot = "0.12.4"
|
||||||
|
path-absolutize = "3.1.1"
|
||||||
|
path-clean = "1.0.1"
|
||||||
|
blake3 = { version = "1.8.2" }
|
||||||
|
pbkdf2 = "0.12.2"
|
||||||
percent-encoding = "2.3.1"
|
percent-encoding = "2.3.1"
|
||||||
pin-project-lite = "0.2.16"
|
pin-project-lite = "0.2.16"
|
||||||
# pin-utils = "0.1.0"
|
|
||||||
prost = "0.13.5"
|
prost = "0.13.5"
|
||||||
prost-build = "0.13.5"
|
prost-build = "0.13.5"
|
||||||
protobuf = "3.7"
|
quick-xml = "0.37.5"
|
||||||
rand = "0.8.5"
|
rand = "0.9.1"
|
||||||
rdkafka = { version = "0.37.0", features = ["tokio"] }
|
rdkafka = { version = "0.37.0", features = ["tokio"] }
|
||||||
reed-solomon-erasure = { version = "6.0.0", features = ["simd-accel"] }
|
reed-solomon-simd = { version = "3.0.1" }
|
||||||
regex = { version = "1.11.1" }
|
regex = { version = "1.11.1" }
|
||||||
reqwest = { version = "0.12.19", default-features = false, features = [
|
reqwest = { version = "0.12.22", default-features = false, features = [
|
||||||
"rustls-tls",
|
"rustls-tls",
|
||||||
"charset",
|
"charset",
|
||||||
"http2",
|
"http2",
|
||||||
@@ -154,22 +192,26 @@ rfd = { version = "0.15.3", default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
rmp = "0.8.14"
|
rmp = "0.8.14"
|
||||||
rmp-serde = "1.3.0"
|
rmp-serde = "1.3.0"
|
||||||
|
rsa = "0.9.8"
|
||||||
rumqttc = { version = "0.24" }
|
rumqttc = { version = "0.24" }
|
||||||
rust-embed = { version = "8.7.2" }
|
rust-embed = { version = "8.7.2" }
|
||||||
|
rust-i18n = { version = "3.1.5" }
|
||||||
rustfs-rsc = "2025.506.1"
|
rustfs-rsc = "2025.506.1"
|
||||||
rustls = { version = "0.23.27" }
|
rustls = { version = "0.23.28" }
|
||||||
rustls-pki-types = "1.12.0"
|
rustls-pki-types = "1.12.0"
|
||||||
rustls-pemfile = "2.2.0"
|
rustls-pemfile = "2.2.0"
|
||||||
s3s = { git = "https://github.com/Nugine/s3s.git", rev = "4733cdfb27b2713e832967232cbff413bb768c10" }
|
s3s = { version = "0.12.0-minio-preview.1" }
|
||||||
s3s-policy = { git = "https://github.com/Nugine/s3s.git", rev = "4733cdfb27b2713e832967232cbff413bb768c10" }
|
shadow-rs = { version = "1.2.0", default-features = false }
|
||||||
shadow-rs = { version = "1.1.1", default-features = false }
|
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = { version = "1.0.140", features = ["raw_value"] }
|
||||||
|
serde-xml-rs = "0.8.1"
|
||||||
serde_urlencoded = "0.7.1"
|
serde_urlencoded = "0.7.1"
|
||||||
serde_with = "3.12.0"
|
sha1 = "0.10.6"
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
smallvec = { version = "1.15.0", features = ["serde"] }
|
siphasher = "1.0.1"
|
||||||
|
smallvec = { version = "1.15.1", features = ["serde"] }
|
||||||
snafu = "0.8.6"
|
snafu = "0.8.6"
|
||||||
|
snap = "1.1.1"
|
||||||
socket2 = "0.5.10"
|
socket2 = "0.5.10"
|
||||||
strum = { version = "0.27.1", features = ["derive"] }
|
strum = { version = "0.27.1", features = ["derive"] }
|
||||||
sysinfo = "0.35.2"
|
sysinfo = "0.35.2"
|
||||||
@@ -183,12 +225,13 @@ time = { version = "0.3.41", features = [
|
|||||||
"macros",
|
"macros",
|
||||||
"serde",
|
"serde",
|
||||||
] }
|
] }
|
||||||
tokio = { version = "1.45.1", features = ["fs", "rt-multi-thread"] }
|
tokio = { version = "1.46.0", features = ["fs", "rt-multi-thread"] }
|
||||||
tonic = { version = "0.13.1", features = ["gzip"] }
|
|
||||||
tonic-build = { version = "0.13.1" }
|
|
||||||
tokio-rustls = { version = "0.26.2", default-features = false }
|
tokio-rustls = { version = "0.26.2", default-features = false }
|
||||||
tokio-stream = { version = "0.1.17" }
|
tokio-stream = { version = "0.1.17" }
|
||||||
|
tokio-tar = "0.3.1"
|
||||||
tokio-util = { version = "0.7.15", features = ["io", "compat"] }
|
tokio-util = { version = "0.7.15", features = ["io", "compat"] }
|
||||||
|
tonic = { version = "0.13.1", features = ["gzip"] }
|
||||||
|
tonic-build = { version = "0.13.1" }
|
||||||
tower = { version = "0.5.2", features = ["timeout"] }
|
tower = { version = "0.5.2", features = ["timeout"] }
|
||||||
tower-http = { version = "0.6.6", features = ["cors"] }
|
tower-http = { version = "0.6.6", features = ["cors"] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
@@ -205,8 +248,11 @@ uuid = { version = "1.17.0", features = [
|
|||||||
"fast-rng",
|
"fast-rng",
|
||||||
"macro-diagnostics",
|
"macro-diagnostics",
|
||||||
] }
|
] }
|
||||||
|
wildmatch = { version = "2.4.0", features = ["serde"] }
|
||||||
winapi = { version = "0.3.9" }
|
winapi = { version = "0.3.9" }
|
||||||
|
xxhash-rust = { version = "0.8.15", features = ["xxh64", "xxh3"] }
|
||||||
|
zip = "2.4.2"
|
||||||
|
zstd = "0.13.3"
|
||||||
|
|
||||||
[profile.wasm-dev]
|
[profile.wasm-dev]
|
||||||
inherits = "dev"
|
inherits = "dev"
|
||||||
@@ -220,10 +266,6 @@ inherits = "dev"
|
|||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
lto = "thin"
|
|
||||||
codegen-units = 1
|
|
||||||
panic = "abort" # Optional, remove the panic expansion code
|
|
||||||
strip = true # strip symbol information to reduce binary size
|
|
||||||
|
|
||||||
[profile.production]
|
[profile.production]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
|||||||
60
Dockerfile
60
Dockerfile
@@ -1,17 +1,51 @@
|
|||||||
FROM alpine:latest
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
# RUN apk add --no-cache <package-name>
|
FROM alpine:3.18 AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
RUN apk add -U --no-cache \
|
||||||
|
ca-certificates \
|
||||||
RUN mkdir -p /data/rustfs0 /data/rustfs1 /data/rustfs2 /data/rustfs3
|
curl \
|
||||||
|
bash \
|
||||||
COPY ./target/x86_64-unknown-linux-musl/release/rustfs /app/rustfs
|
unzip
|
||||||
|
|
||||||
RUN chmod +x /app/rustfs
|
|
||||||
|
|
||||||
EXPOSE 9000
|
|
||||||
EXPOSE 9001
|
|
||||||
|
|
||||||
|
|
||||||
CMD ["/app/rustfs"]
|
RUN curl -Lo /tmp/rustfs.zip https://dl.rustfs.com/artifacts/rustfs/rustfs-release-x86_64-unknown-linux-musl.latest.zip && \
|
||||||
|
unzip -o /tmp/rustfs.zip -d /tmp && \
|
||||||
|
tar -xzf /tmp/rustfs-x86_64-unknown-linux-musl.tar.gz -C /tmp && \
|
||||||
|
mv /tmp/rustfs-x86_64-unknown-linux-musl/bin/rustfs /rustfs && \
|
||||||
|
chmod +x /rustfs && \
|
||||||
|
rm -rf /tmp/*
|
||||||
|
|
||||||
|
FROM alpine:3.18
|
||||||
|
|
||||||
|
RUN apk add -U --no-cache \
|
||||||
|
ca-certificates \
|
||||||
|
bash
|
||||||
|
|
||||||
|
COPY --from=builder /rustfs /usr/local/bin/rustfs
|
||||||
|
|
||||||
|
ENV RUSTFS_ACCESS_KEY=rustfsadmin \
|
||||||
|
RUSTFS_SECRET_KEY=rustfsadmin \
|
||||||
|
RUSTFS_ADDRESS=":9000" \
|
||||||
|
RUSTFS_CONSOLE_ADDRESS=":9001" \
|
||||||
|
RUSTFS_CONSOLE_ENABLE=true \
|
||||||
|
RUST_LOG=warn
|
||||||
|
|
||||||
|
EXPOSE 9000 9001
|
||||||
|
|
||||||
|
RUN mkdir -p /data
|
||||||
|
VOLUME /data
|
||||||
|
|
||||||
|
CMD ["rustfs", "/data"]
|
||||||
|
|||||||
121
Dockerfile.multi-stage
Normal file
121
Dockerfile.multi-stage
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# Multi-stage Dockerfile for RustFS
|
||||||
|
# Supports cross-compilation for amd64 and arm64 architectures
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
ARG BUILDPLATFORM
|
||||||
|
|
||||||
|
# Build stage
|
||||||
|
FROM --platform=$BUILDPLATFORM rust:1.85-bookworm AS builder
|
||||||
|
|
||||||
|
# Install required build dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
wget \
|
||||||
|
git \
|
||||||
|
curl \
|
||||||
|
unzip \
|
||||||
|
gcc \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
lld \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install cross-compilation tools for ARM64
|
||||||
|
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y gcc-aarch64-linux-gnu && \
|
||||||
|
rm -rf /var/lib/apt/lists/*; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install protoc
|
||||||
|
RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v31.1/protoc-31.1-linux-x86_64.zip \
|
||||||
|
&& unzip protoc-31.1-linux-x86_64.zip -d protoc3 \
|
||||||
|
&& mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc \
|
||||||
|
&& mv protoc3/include/* /usr/local/include/ && rm -rf protoc-31.1-linux-x86_64.zip protoc3
|
||||||
|
|
||||||
|
# Install flatc
|
||||||
|
RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \
|
||||||
|
&& unzip Linux.flatc.binary.g++-13.zip \
|
||||||
|
&& mv flatc /usr/local/bin/ && chmod +x /usr/local/bin/flatc && rm -rf Linux.flatc.binary.g++-13.zip
|
||||||
|
|
||||||
|
# Set up Rust targets based on platform
|
||||||
|
RUN case "$TARGETPLATFORM" in \
|
||||||
|
"linux/amd64") rustup target add x86_64-unknown-linux-gnu ;; \
|
||||||
|
"linux/arm64") rustup target add aarch64-unknown-linux-gnu ;; \
|
||||||
|
*) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Set up environment for cross-compilation
|
||||||
|
ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
|
||||||
|
ENV CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc
|
||||||
|
ENV CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++
|
||||||
|
|
||||||
|
WORKDIR /usr/src/rustfs
|
||||||
|
|
||||||
|
# Copy Cargo files for dependency caching
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY */Cargo.toml ./*/
|
||||||
|
|
||||||
|
# Create dummy main.rs files for dependency compilation
|
||||||
|
RUN find . -name "Cargo.toml" -not -path "./Cargo.toml" | \
|
||||||
|
xargs -I {} dirname {} | \
|
||||||
|
xargs -I {} sh -c 'mkdir -p {}/src && echo "fn main() {}" > {}/src/main.rs'
|
||||||
|
|
||||||
|
# Build dependencies only (cache layer)
|
||||||
|
RUN case "$TARGETPLATFORM" in \
|
||||||
|
"linux/amd64") cargo build --release --target x86_64-unknown-linux-gnu ;; \
|
||||||
|
"linux/arm64") cargo build --release --target aarch64-unknown-linux-gnu ;; \
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Generate protobuf code
|
||||||
|
RUN cargo run --bin gproto
|
||||||
|
|
||||||
|
# Build the actual application
|
||||||
|
RUN case "$TARGETPLATFORM" in \
|
||||||
|
"linux/amd64") \
|
||||||
|
cargo build --release --target x86_64-unknown-linux-gnu --bin rustfs && \
|
||||||
|
cp target/x86_64-unknown-linux-gnu/release/rustfs /usr/local/bin/rustfs \
|
||||||
|
;; \
|
||||||
|
"linux/arm64") \
|
||||||
|
cargo build --release --target aarch64-unknown-linux-gnu --bin rustfs && \
|
||||||
|
cp target/aarch64-unknown-linux-gnu/release/rustfs /usr/local/bin/rustfs \
|
||||||
|
;; \
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Runtime stage - Ubuntu minimal for better compatibility
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
|
# Install runtime dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
ca-certificates \
|
||||||
|
tzdata \
|
||||||
|
wget \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create rustfs user and group
|
||||||
|
RUN groupadd -g 1000 rustfs && \
|
||||||
|
useradd -d /app -g rustfs -u 1000 -s /bin/bash rustfs
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Create data directories
|
||||||
|
RUN mkdir -p /data/rustfs{0,1,2,3} && \
|
||||||
|
chown -R rustfs:rustfs /data /app
|
||||||
|
|
||||||
|
# Copy binary from builder stage
|
||||||
|
COPY --from=builder /usr/local/bin/rustfs /app/rustfs
|
||||||
|
RUN chmod +x /app/rustfs && chown rustfs:rustfs /app/rustfs
|
||||||
|
|
||||||
|
# Switch to non-root user
|
||||||
|
USER rustfs
|
||||||
|
|
||||||
|
# Expose ports
|
||||||
|
EXPOSE 9000 9001
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||||
|
CMD wget --no-verbose --tries=1 --spider http://localhost:9000/health || exit 1
|
||||||
|
|
||||||
|
# Set default command
|
||||||
|
CMD ["/app/rustfs"]
|
||||||
201
LICENSE
201
LICENSE
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2024 Beijing Henghesha Technology Co., Ltd.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|||||||
118
Makefile
118
Makefile
@@ -79,3 +79,121 @@ build: BUILD_CMD = /root/.cargo/bin/cargo build --release --bin rustfs --target-
|
|||||||
build:
|
build:
|
||||||
$(DOCKER_CLI) build -t $(ROCKYLINUX_BUILD_IMAGE_NAME) -f $(DOCKERFILE_PATH)/Dockerfile.$(BUILD_OS) .
|
$(DOCKER_CLI) build -t $(ROCKYLINUX_BUILD_IMAGE_NAME) -f $(DOCKERFILE_PATH)/Dockerfile.$(BUILD_OS) .
|
||||||
$(DOCKER_CLI) run --rm --name $(ROCKYLINUX_BUILD_CONTAINER_NAME) -v $(shell pwd):/root/s3-rustfs -it $(ROCKYLINUX_BUILD_IMAGE_NAME) $(BUILD_CMD)
|
$(DOCKER_CLI) run --rm --name $(ROCKYLINUX_BUILD_CONTAINER_NAME) -v $(shell pwd):/root/s3-rustfs -it $(ROCKYLINUX_BUILD_IMAGE_NAME) $(BUILD_CMD)
|
||||||
|
|
||||||
|
.PHONY: build-musl
|
||||||
|
build-musl:
|
||||||
|
@echo "🔨 Building rustfs for x86_64-unknown-linux-musl..."
|
||||||
|
cargo build --target x86_64-unknown-linux-musl --bin rustfs -r
|
||||||
|
|
||||||
|
.PHONY: build-gnu
|
||||||
|
build-gnu:
|
||||||
|
@echo "🔨 Building rustfs for x86_64-unknown-linux-gnu..."
|
||||||
|
cargo build --target x86_64-unknown-linux-gnu --bin rustfs -r
|
||||||
|
|
||||||
|
.PHONY: deploy-dev
|
||||||
|
deploy-dev: build-musl
|
||||||
|
@echo "🚀 Deploying to dev server: $${IP}"
|
||||||
|
./scripts/dev_deploy.sh $${IP}
|
||||||
|
|
||||||
|
# Multi-architecture Docker build targets
|
||||||
|
.PHONY: docker-build-multiarch
|
||||||
|
docker-build-multiarch:
|
||||||
|
@echo "🏗️ Building multi-architecture Docker images..."
|
||||||
|
./scripts/build-docker-multiarch.sh
|
||||||
|
|
||||||
|
.PHONY: docker-build-multiarch-push
|
||||||
|
docker-build-multiarch-push:
|
||||||
|
@echo "🚀 Building and pushing multi-architecture Docker images..."
|
||||||
|
./scripts/build-docker-multiarch.sh --push
|
||||||
|
|
||||||
|
.PHONY: docker-build-multiarch-version
|
||||||
|
docker-build-multiarch-version:
|
||||||
|
@if [ -z "$(VERSION)" ]; then \
|
||||||
|
echo "❌ 错误: 请指定版本, 例如: make docker-build-multiarch-version VERSION=v1.0.0"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@echo "🏗️ Building multi-architecture Docker images (version: $(VERSION))..."
|
||||||
|
./scripts/build-docker-multiarch.sh --version $(VERSION)
|
||||||
|
|
||||||
|
.PHONY: docker-push-multiarch-version
|
||||||
|
docker-push-multiarch-version:
|
||||||
|
@if [ -z "$(VERSION)" ]; then \
|
||||||
|
echo "❌ 错误: 请指定版本, 例如: make docker-push-multiarch-version VERSION=v1.0.0"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@echo "🚀 Building and pushing multi-architecture Docker images (version: $(VERSION))..."
|
||||||
|
./scripts/build-docker-multiarch.sh --version $(VERSION) --push
|
||||||
|
|
||||||
|
.PHONY: docker-build-ubuntu
|
||||||
|
docker-build-ubuntu:
|
||||||
|
@echo "🏗️ Building multi-architecture Ubuntu Docker images..."
|
||||||
|
./scripts/build-docker-multiarch.sh --type ubuntu
|
||||||
|
|
||||||
|
.PHONY: docker-build-rockylinux
|
||||||
|
docker-build-rockylinux:
|
||||||
|
@echo "🏗️ Building multi-architecture RockyLinux Docker images..."
|
||||||
|
./scripts/build-docker-multiarch.sh --type rockylinux
|
||||||
|
|
||||||
|
.PHONY: docker-build-devenv
|
||||||
|
docker-build-devenv:
|
||||||
|
@echo "🏗️ Building multi-architecture development environment Docker images..."
|
||||||
|
./scripts/build-docker-multiarch.sh --type devenv
|
||||||
|
|
||||||
|
.PHONY: docker-build-all-types
|
||||||
|
docker-build-all-types:
|
||||||
|
@echo "🏗️ Building all multi-architecture Docker image types..."
|
||||||
|
./scripts/build-docker-multiarch.sh --type production
|
||||||
|
./scripts/build-docker-multiarch.sh --type ubuntu
|
||||||
|
./scripts/build-docker-multiarch.sh --type rockylinux
|
||||||
|
./scripts/build-docker-multiarch.sh --type devenv
|
||||||
|
|
||||||
|
.PHONY: docker-inspect-multiarch
|
||||||
|
docker-inspect-multiarch:
|
||||||
|
@if [ -z "$(IMAGE)" ]; then \
|
||||||
|
echo "❌ 错误: 请指定镜像, 例如: make docker-inspect-multiarch IMAGE=rustfs/rustfs:latest"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@echo "🔍 Inspecting multi-architecture image: $(IMAGE)"
|
||||||
|
docker buildx imagetools inspect $(IMAGE)
|
||||||
|
|
||||||
|
.PHONY: build-cross-all
|
||||||
|
build-cross-all:
|
||||||
|
@echo "🔧 Building all target architectures..."
|
||||||
|
@if ! command -v cross &> /dev/null; then \
|
||||||
|
echo "📦 Installing cross..."; \
|
||||||
|
cargo install cross; \
|
||||||
|
fi
|
||||||
|
@echo "🔨 Generating protobuf code..."
|
||||||
|
cargo run --bin gproto || true
|
||||||
|
@echo "🔨 Building x86_64-unknown-linux-musl..."
|
||||||
|
cargo build --release --target x86_64-unknown-linux-musl --bin rustfs
|
||||||
|
@echo "🔨 Building aarch64-unknown-linux-gnu..."
|
||||||
|
cross build --release --target aarch64-unknown-linux-gnu --bin rustfs
|
||||||
|
@echo "✅ All architectures built successfully!"
|
||||||
|
|
||||||
|
.PHONY: help-docker
|
||||||
|
help-docker:
|
||||||
|
@echo "🐳 Docker 多架构构建帮助:"
|
||||||
|
@echo ""
|
||||||
|
@echo "基本构建:"
|
||||||
|
@echo " make docker-build-multiarch # 构建多架构镜像(不推送)"
|
||||||
|
@echo " make docker-build-multiarch-push # 构建并推送多架构镜像"
|
||||||
|
@echo ""
|
||||||
|
@echo "版本构建:"
|
||||||
|
@echo " make docker-build-multiarch-version VERSION=v1.0.0 # 构建指定版本"
|
||||||
|
@echo " make docker-push-multiarch-version VERSION=v1.0.0 # 构建并推送指定版本"
|
||||||
|
@echo ""
|
||||||
|
@echo "镜像类型:"
|
||||||
|
@echo " make docker-build-ubuntu # 构建 Ubuntu 镜像"
|
||||||
|
@echo " make docker-build-rockylinux # 构建 RockyLinux 镜像"
|
||||||
|
@echo " make docker-build-devenv # 构建开发环境镜像"
|
||||||
|
@echo " make docker-build-all-types # 构建所有类型镜像"
|
||||||
|
@echo ""
|
||||||
|
@echo "辅助工具:"
|
||||||
|
@echo " make build-cross-all # 构建所有架构的二进制文件"
|
||||||
|
@echo " make docker-inspect-multiarch IMAGE=xxx # 检查镜像的架构支持"
|
||||||
|
@echo ""
|
||||||
|
@echo "环境变量 (在推送时需要设置):"
|
||||||
|
@echo " DOCKERHUB_USERNAME Docker Hub 用户名"
|
||||||
|
@echo " DOCKERHUB_TOKEN Docker Hub 访问令牌"
|
||||||
|
@echo " GITHUB_TOKEN GitHub 访问令牌"
|
||||||
|
|||||||
183
README.md
183
README.md
@@ -1,94 +1,127 @@
|
|||||||
# RustFS
|
[](https://rustfs.com)
|
||||||
|
|
||||||
## English Documentation |[中文文档](README_ZH.md)
|
<p align="center">RustFS is a high-performance distributed object storage software built using Rust</p>
|
||||||
|
|
||||||
### Prerequisites
|
<p align="center">
|
||||||
|
<a href="https://github.com/rustfs/rustfs/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/rustfs/rustfs/actions/workflows/ci.yml/badge.svg" /></a>
|
||||||
|
<a href="https://github.com/rustfs/rustfs/actions/workflows/docker.yml"><img alt="Build and Push Docker Images" src="https://github.com/rustfs/rustfs/actions/workflows/docker.yml/badge.svg" /></a>
|
||||||
|
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/rustfs/rustfs"/>
|
||||||
|
<img alt="Github Last Commit" src="https://img.shields.io/github/last-commit/rustfs/rustfs"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
| Package | Version | Download Link |
|
<p align="center">
|
||||||
|---------|---------|----------------------------------------------------------------------------------------------------------------------------------|
|
<a href="https://docs.rustfs.com/en/introduction.html">Getting Started</a>
|
||||||
| Rust | 1.8.5+ | [rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) |
|
· <a href="https://docs.rustfs.com/en/">Docs</a>
|
||||||
| protoc | 30.2+ | [protoc-30.2-linux-x86_64.zip](https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protoc-30.2-linux-x86_64.zip) |
|
· <a href="https://github.com/rustfs/rustfs/issues">Bug reports</a>
|
||||||
| flatc | 24.0+ | [Linux.flatc.binary.g++-13.zip](https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip) |
|
· <a href="https://github.com/rustfs/rustfs/discussions">Discussions</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
### Building RustFS
|
<p align="center">
|
||||||
|
English | <a href="https://github.com/rustfs/rustfs/blob/main/README_ZH.md">简体中文</a> |
|
||||||
|
<!-- Keep these links. Translations will automatically update with the README. -->
|
||||||
|
<a href="https://readme-i18n.com/rustfs/rustfs?lang=de">Deutsch</a> |
|
||||||
|
<a href="https://readme-i18n.com/rustfs/rustfs?lang=es">Español</a> |
|
||||||
|
<a href="https://readme-i18n.com/rustfs/rustfs?lang=fr">français</a> |
|
||||||
|
<a href="https://readme-i18n.com/rustfs/rustfs?lang=ja">日本語</a> |
|
||||||
|
<a href="https://readme-i18n.com/rustfs/rustfs?lang=ko">한국어</a> |
|
||||||
|
<a href="https://readme-i18n.com/rustfs/rustfs?lang=pt">Português</a> |
|
||||||
|
<a href="https://readme-i18n.com/rustfs/rustfs?lang=ru">Русский</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
#### Generate Protobuf Code
|
RustFS is a high-performance distributed object storage software built using Rust, one of the most popular languages worldwide. Along with MinIO, it shares a range of advantages such as simplicity, S3 compatibility, open-source nature, support for data lakes, AI, and big data. Furthermore, it has a better and more user-friendly open-source license in comparison to other storage systems, being constructed under the Apache license. As Rust serves as its foundation, RustFS provides faster speed and safer distributed features for high-performance object storage.
|
||||||
|
|
||||||
```bash
|
## Features
|
||||||
cargo run --bin gproto
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Using Docker for Prerequisites
|
- **High Performance**: Built with Rust, ensuring speed and efficiency.
|
||||||
|
- **Distributed Architecture**: Scalable and fault-tolerant design for large-scale deployments.
|
||||||
|
- **S3 Compatibility**: Seamless integration with existing S3-compatible applications.
|
||||||
|
- **Data Lake Support**: Optimized for big data and AI workloads.
|
||||||
|
- **Open Source**: Licensed under Apache 2.0, encouraging community contributions and transparency.
|
||||||
|
- **User-Friendly**: Designed with simplicity in mind, making it easy to deploy and manage.
|
||||||
|
|
||||||
```yaml
|
## RustFS vs MinIO
|
||||||
- uses: arduino/setup-protoc@v3
|
|
||||||
with:
|
|
||||||
version: "30.2"
|
|
||||||
|
|
||||||
- uses: Nugine/setup-flatc@v1
|
Stress test server parameters
|
||||||
with:
|
|
||||||
version: "25.2.10"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Adding Console Web UI
|
| Type | parameter | Remark |
|
||||||
|
| - | - | - |
|
||||||
|
|CPU | 2 Core | Intel Xeon(Sapphire Rapids) Platinum 8475B , 2.7/3.2 GHz| |
|
||||||
|
|Memory| 4GB | |
|
||||||
|
|Network | 15Gbp | |
|
||||||
|
|Driver | 40GB x 4 | IOPS 3800 / Driver |
|
||||||
|
|
||||||
|
<https://github.com/user-attachments/assets/2e4979b5-260c-4f2c-ac12-c87fd558072a>
|
||||||
|
|
||||||
|
### RustFS vs Other object storage
|
||||||
|
|
||||||
|
| RustFS | Other object storage|
|
||||||
|
| - | - |
|
||||||
|
| Powerful Console | Simple and useless Console |
|
||||||
|
| Developed based on Rust language, memory is safer | Developed in Go or C, with potential issues like memory GC/leaks |
|
||||||
|
| Does not report logs to third-party countries | Reporting logs to other third countries may violate national security laws |
|
||||||
|
| Licensed under Apache, more business-friendly | AGPL V3 License and other License, polluted open source and License traps, infringement of intellectual property rights |
|
||||||
|
| Comprehensive S3 support, works with domestic and international cloud providers | Full support for S3, but no local cloud vendor support |
|
||||||
|
| Rust-based development, strong support for secure and innovative devices | Poor support for edge gateways and secure innovative devices|
|
||||||
|
| Stable commercial prices, free community support | High pricing, with costs up to $250,000 for 1PiB |
|
||||||
|
| No risk | Intellectual property risks and risks of prohibited uses |
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
To get started with RustFS, follow these steps:
|
||||||
|
|
||||||
|
1. **One-click installation script (Option 1)**
|
||||||
|
|
||||||
1. Download the latest console UI:
|
|
||||||
```bash
|
```bash
|
||||||
wget https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip
|
curl -O https://rustfs.com/install_rustfs.sh && bash install_rustfs.sh
|
||||||
```
|
|
||||||
2. Create the static directory:
|
|
||||||
```bash
|
|
||||||
mkdir -p ./rustfs/static
|
|
||||||
```
|
|
||||||
3. Extract and compile RustFS:
|
|
||||||
```bash
|
|
||||||
unzip rustfs-console-latest.zip -d ./rustfs/static
|
|
||||||
cargo build
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running RustFS
|
2. **Docker Quick Start (Option 2)**
|
||||||
|
|
||||||
#### Configuration
|
```bash
|
||||||
|
podman run -d -p 9000:9000 -p 9001:9001 -v /data:/data quay.io/rustfs/rustfs
|
||||||
Set the required environment variables:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Basic config
|
|
||||||
export RUSTFS_VOLUMES="./target/volume/test"
|
|
||||||
export RUSTFS_ADDRESS="0.0.0.0:9000"
|
|
||||||
export RUSTFS_CONSOLE_ENABLE=true
|
|
||||||
export RUSTFS_CONSOLE_ADDRESS="0.0.0.0:9001"
|
|
||||||
|
|
||||||
# Observability config
|
|
||||||
export RUSTFS_OBS_ENDPOINT="http://localhost:4317"
|
|
||||||
|
|
||||||
# Event message configuration
|
|
||||||
#export RUSTFS_EVENT_CONFIG="./deploy/config/event.toml"
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Start the service
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./rustfs /data/rustfs
|
|
||||||
```
|
|
||||||
|
|
||||||
### Observability Stack
|
|
||||||
|
|
||||||
#### Deployment
|
|
||||||
|
|
||||||
1. Navigate to the observability directory:
|
|
||||||
```bash
|
|
||||||
cd .docker/observability
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Start the observability stack:
|
|
||||||
```bash
|
|
||||||
docker compose -f docker-compose.yml up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Access Monitoring Dashboards
|
3. **Access the Console**: Open your web browser and navigate to `http://localhost:9001` to access the RustFS console, default username and password is `rustfsadmin` .
|
||||||
|
4. **Create a Bucket**: Use the console to create a new bucket for your objects.
|
||||||
|
5. **Upload Objects**: You can upload files directly through the console or use S3-compatible APIs to interact with your RustFS instance.
|
||||||
|
|
||||||
- Grafana: `http://localhost:3000` (credentials: `admin`/`admin`)
|
## Documentation
|
||||||
- Jaeger: `http://localhost:16686`
|
|
||||||
- Prometheus: `http://localhost:9090`
|
For detailed documentation, including configuration options, API references, and advanced usage, please visit our [Documentation](https://docs.rustfs.com).
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
If you have any questions or need assistance, you can:
|
||||||
|
|
||||||
|
- Check the [FAQ](https://github.com/rustfs/rustfs/discussions/categories/q-a) for common issues and solutions.
|
||||||
|
- Join our [GitHub Discussions](https://github.com/rustfs/rustfs/discussions) to ask questions and share your experiences.
|
||||||
|
- Open an issue on our [GitHub Issues](https://github.com/rustfs/rustfs/issues) page for bug reports or feature requests.
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- [Documentation](https://docs.rustfs.com) - The manual you should read
|
||||||
|
- [Changelog](https://github.com/rustfs/rustfs/releases) - What we broke and fixed
|
||||||
|
- [GitHub Discussions](https://github.com/rustfs/rustfs/discussions) - Where the community lives
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
- **Bugs**: [GitHub Issues](https://github.com/rustfs/rustfs/issues)
|
||||||
|
- **Business**: <hello@rustfs.com>
|
||||||
|
- **Jobs**: <jobs@rustfs.com>
|
||||||
|
- **General Discussion**: [GitHub Discussions](https://github.com/rustfs/rustfs/discussions)
|
||||||
|
- **Contributing**: [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
RustFS is a community-driven project, and we appreciate all contributions. Check out the [Contributors](https://github.com/rustfs/rustfs/graphs/contributors) page to see the amazing people who have helped make RustFS better.
|
||||||
|
|
||||||
|
<a href="https://github.com/rustfs/rustfs/graphs/contributors">
|
||||||
|
<img src="https://contrib.rocks/image?repo=rustfs/rustfs" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[Apache 2.0](https://opensource.org/licenses/Apache-2.0)
|
||||||
|
|
||||||
|
**RustFS** is a trademark of RustFS, Inc. All other trademarks are the property of their respective owners.
|
||||||
|
|||||||
176
README_ZH.md
176
README_ZH.md
@@ -1,99 +1,119 @@
|
|||||||
# RustFS
|
[](https://rustfs.com)
|
||||||
|
|
||||||
## [English Documentation](README.md) |中文文档
|
<p align="center">RustFS 是一个使用 Rust 构建的高性能分布式对象存储软件</p >
|
||||||
|
|
||||||
### 前置要求
|
<p align="center">
|
||||||
|
<a href="https://github.com/rustfs/rustfs/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/rustfs/rustfs/actions/workflows/ci.yml/badge.svg" /></a>
|
||||||
|
<a href="https://github.com/rustfs/rustfs/actions/workflows/docker.yml"><img alt="Build and Push Docker Images" src="https://github.com/rustfs/rustfs/actions/workflows/docker.yml/badge.svg" /></a>
|
||||||
|
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/rustfs/rustfs"/>
|
||||||
|
<img alt="Github Last Commit" src="https://img.shields.io/github/last-commit/rustfs/rustfs"/>
|
||||||
|
</p >
|
||||||
|
|
||||||
| 软件包 | 版本 | 下载链接 |
|
<p align="center">
|
||||||
|--------|--------|----------------------------------------------------------------------------------------------------------------------------------|
|
<a href="https://docs.rustfs.com/zh/introduction.html">快速开始</a >
|
||||||
| Rust | 1.8.5+ | [rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) |
|
· <a href="https://docs.rustfs.com/zh/">文档</a >
|
||||||
| protoc | 30.2+ | [protoc-30.2-linux-x86_64.zip](https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protoc-30.2-linux-x86_64.zip) |
|
· <a href="https://github.com/rustfs/rustfs/issues">问题报告</a >
|
||||||
| flatc | 24.0+ | [Linux.flatc.binary.g++-13.zip](https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip) |
|
· <a href="https://github.com/rustfs/rustfs/discussions">讨论</a >
|
||||||
|
</p >
|
||||||
|
|
||||||
### 构建 RustFS
|
<p align="center">
|
||||||
|
<a href="https://github.com/rustfs/rustfs/blob/main/README.md">English</a > | 简体中文
|
||||||
|
</p >
|
||||||
|
|
||||||
#### 生成 Protobuf 代码
|
RustFS 是一个使用 Rust(全球最受欢迎的编程语言之一)构建的高性能分布式对象存储软件。与 MinIO 一样,它具有简单性、S3 兼容性、开源特性以及对数据湖、AI 和大数据的支持等一系列优势。此外,与其他存储系统相比,它采用 Apache 许可证构建,拥有更好、更用户友好的开源许可证。由于以 Rust 为基础,RustFS 为高性能对象存储提供了更快的速度和更安全的分布式功能。
|
||||||
|
|
||||||
```bash
|
## 特性
|
||||||
cargo run --bin gproto
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 使用 Docker 安装依赖
|
- **高性能**:使用 Rust 构建,确保速度和效率。
|
||||||
|
- **分布式架构**:可扩展且容错的设计,适用于大规模部署。
|
||||||
|
- **S3 兼容性**:与现有 S3 兼容应用程序无缝集成。
|
||||||
|
- **数据湖支持**:针对大数据和 AI 工作负载进行了优化。
|
||||||
|
- **开源**:采用 Apache 2.0 许可证,鼓励社区贡献和透明度。
|
||||||
|
- **用户友好**:设计简单,易于部署和管理。
|
||||||
|
|
||||||
```yaml
|
## RustFS vs MinIO
|
||||||
- uses: arduino/setup-protoc@v3
|
|
||||||
with:
|
|
||||||
version: "30.2"
|
|
||||||
|
|
||||||
- uses: Nugine/setup-flatc@v1
|
压力测试服务器参数
|
||||||
with:
|
|
||||||
version: "25.2.10"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 添加控制台 Web UI
|
| 类型 | 参数 | 备注 |
|
||||||
|
| - | - | - |
|
||||||
|
|CPU | 2 核心 | Intel Xeon(Sapphire Rapids) Platinum 8475B , 2.7/3.2 GHz| |
|
||||||
|
|内存| 4GB | |
|
||||||
|
|网络 | 15Gbp | |
|
||||||
|
|驱动器 | 40GB x 4 | IOPS 3800 / 驱动器 |
|
||||||
|
|
||||||
|
<https://github.com/user-attachments/assets/2e4979b5-260c-4f2c-ac12-c87fd558072a>
|
||||||
|
|
||||||
|
### RustFS vs 其他对象存储
|
||||||
|
|
||||||
|
| RustFS | 其他对象存储|
|
||||||
|
| - | - |
|
||||||
|
| 强大的控制台 | 简单且无用的控制台 |
|
||||||
|
| 基于 Rust 语言开发,内存更安全 | 使用 Go 或 C 开发,存在内存 GC/泄漏等潜在问题 |
|
||||||
|
| 不向第三方国家报告日志 | 向其他第三方国家报告日志可能违反国家安全法律 |
|
||||||
|
| 采用 Apache 许可证,对商业更友好 | AGPL V3 许可证等其他许可证,污染开源和许可证陷阱,侵犯知识产权 |
|
||||||
|
| 全面的 S3 支持,适用于国内外云提供商 | 完全支持 S3,但不支持本地云厂商 |
|
||||||
|
| 基于 Rust 开发,对安全和创新设备有强大支持 | 对边缘网关和安全创新设备支持较差|
|
||||||
|
| 稳定的商业价格,免费社区支持 | 高昂的定价,1PiB 成本高达 $250,000 |
|
||||||
|
| 无风险 | 知识产权风险和禁止使用的风险 |
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
要开始使用 RustFS,请按照以下步骤操作:
|
||||||
|
|
||||||
|
1. **一键脚本快速启动 (方案一)**
|
||||||
|
|
||||||
1. 下载最新的控制台 UI:
|
|
||||||
```bash
|
```bash
|
||||||
wget https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip
|
curl -O https://rustfs.com/install_rustfs.sh && bash install_rustfs.sh
|
||||||
```
|
|
||||||
2. 创建静态资源目录:
|
|
||||||
```bash
|
|
||||||
mkdir -p ./rustfs/static
|
|
||||||
```
|
|
||||||
3. 解压并编译 RustFS:
|
|
||||||
```bash
|
|
||||||
unzip rustfs-console-latest.zip -d ./rustfs/static
|
|
||||||
cargo build
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 运行 RustFS
|
2. **Docker快速启动(方案二)**
|
||||||
|
|
||||||
#### 配置
|
```bash
|
||||||
|
podman run -d -p 9000:9000 -p 9001:9001 -v /data:/data quay.io/rustfs/rustfs
|
||||||
设置必要的环境变量:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 基础配置
|
|
||||||
export RUSTFS_VOLUMES="./target/volume/test"
|
|
||||||
export RUSTFS_ADDRESS="0.0.0.0:9000"
|
|
||||||
export RUSTFS_CONSOLE_ENABLE=true
|
|
||||||
export RUSTFS_CONSOLE_ADDRESS="0.0.0.0:9001"
|
|
||||||
|
|
||||||
# 可观测性配置
|
|
||||||
export RUSTFS_OBS_ENDPOINT="http://localhost:4317"
|
|
||||||
|
|
||||||
# 事件消息配置
|
|
||||||
#export RUSTFS_EVENT_CONFIG="./deploy/config/event.toml"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 启动服务
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./rustfs /data/rustfs
|
|
||||||
```
|
|
||||||
|
|
||||||
### 可观测性系统
|
|
||||||
|
|
||||||
#### 部署
|
|
||||||
|
|
||||||
1. 进入可观测性目录:
|
|
||||||
```bash
|
|
||||||
cd .docker/observability
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 启动可观测性系统:
|
|
||||||
```bash
|
|
||||||
docker compose -f docker-compose.yml up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 访问监控面板
|
3. **访问控制台**:打开 Web 浏览器并导航到 `http://localhost:9001` 以访问 RustFS 控制台,默认的用户名和密码是 `rustfsadmin` 。
|
||||||
|
4. **创建存储桶**:使用控制台为您的对象创建新的存储桶。
|
||||||
|
5. **上传对象**:您可以直接通过控制台上传文件,或使用 S3 兼容的 API 与您的 RustFS 实例交互。
|
||||||
|
|
||||||
- Grafana: `http://localhost:3000` (默认账号/密码:`admin`/`admin`)
|
## 文档
|
||||||
- Jaeger: `http://localhost:16686`
|
|
||||||
- Prometheus: `http://localhost:9090`
|
|
||||||
|
|
||||||
#### 配置可观测性
|
有关详细文档,包括配置选项、API 参考和高级用法,请访问我们的[文档](https://docs.rustfs.com)。
|
||||||
|
|
||||||
```
|
## 获取帮助
|
||||||
OpenTelemetry Collector 地址(endpoint): http://localhost:4317
|
|
||||||
```
|
如果您有任何问题或需要帮助,您可以:
|
||||||
|
|
||||||
|
- 查看[常见问题解答](https://github.com/rustfs/rustfs/discussions/categories/q-a)以获取常见问题和解决方案。
|
||||||
|
- 加入我们的 [GitHub 讨论](https://github.com/rustfs/rustfs/discussions)来提问和分享您的经验。
|
||||||
|
- 在我们的 [GitHub Issues](https://github.com/rustfs/rustfs/issues) 页面上开启问题,报告错误或功能请求。
|
||||||
|
|
||||||
|
## 链接
|
||||||
|
|
||||||
|
- [文档](https://docs.rustfs.com) - 您应该阅读的手册
|
||||||
|
- [更新日志](https://docs.rustfs.com/changelog) - 我们破坏和修复的内容
|
||||||
|
- [GitHub 讨论](https://github.com/rustfs/rustfs/discussions) - 社区所在地
|
||||||
|
|
||||||
|
## 联系
|
||||||
|
|
||||||
|
- **错误报告**:[GitHub Issues](https://github.com/rustfs/rustfs/issues)
|
||||||
|
- **商务合作**:<hello@rustfs.com>
|
||||||
|
- **招聘**:<jobs@rustfs.com>
|
||||||
|
- **一般讨论**:[GitHub 讨论](https://github.com/rustfs/rustfs/discussions)
|
||||||
|
- **贡献**:[CONTRIBUTING.md](CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## 贡献者
|
||||||
|
|
||||||
|
RustFS 是一个社区驱动的项目,我们感谢所有的贡献。查看[贡献者](https://github.com/rustfs/rustfs/graphs/contributors)页面,了解帮助 RustFS 变得更好的杰出人员。
|
||||||
|
|
||||||
|
<a href="https://github.com/rustfs/rustfs/graphs/contributors">
|
||||||
|
<img src="https://contrib.rocks/image?repo=rustfs/rustfs" />
|
||||||
|
</a >
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
[Apache 2.0](https://opensource.org/licenses/Apache-2.0)
|
||||||
|
|
||||||
|
**RustFS** 是 RustFS, Inc. 的商标。所有其他商标均为其各自所有者的财产。
|
||||||
|
|||||||
18
SECURITY.md
Normal file
18
SECURITY.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Use this section to tell people about which versions of your project are
|
||||||
|
currently being supported with security updates.
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 1.x.x | :white_check_mark: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Use this section to tell people how to report a vulnerability.
|
||||||
|
|
||||||
|
Tell them where to go, how often they can expect to get an update on a
|
||||||
|
reported vulnerability, what to expect if the vulnerability is accepted or
|
||||||
|
declined, etc.
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
# RustFS SSE-KMS 改进实现总结
|
|
||||||
|
|
||||||
本次改进针对 RustFS 的 SSE-KMS 系统进行了四个主要增强,使其更符合 MinIO 标准并支持动态配置管理。
|
|
||||||
|
|
||||||
## 实现的改进
|
|
||||||
|
|
||||||
### 1. 创建 KMS 配置子系统 ✅
|
|
||||||
|
|
||||||
**实现位置**: `crates/config/src/lib.rs`
|
|
||||||
|
|
||||||
- 创建了统一的配置管理器 `ConfigManager`
|
|
||||||
- 支持动态 KMS 配置的读取、设置和持久化
|
|
||||||
- 提供线程安全的全局配置访问
|
|
||||||
- 支持配置验证和错误处理
|
|
||||||
|
|
||||||
**主要功能**:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// 全局配置管理器
|
|
||||||
ConfigManager::global().get_kms_config("vault").await
|
|
||||||
ConfigManager::global().set_kms_config("vault", config).await
|
|
||||||
ConfigManager::global().validate_all_configs().await
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. KMS 配置查找和验证 ✅
|
|
||||||
|
|
||||||
**实现位置**: `ecstore/src/config/kms.rs`
|
|
||||||
|
|
||||||
- 实现了完整的 KMS 配置结构 `Config`
|
|
||||||
- 支持环境变量和配置文件双重配置源
|
|
||||||
- 提供配置验证和连接测试功能
|
|
||||||
- 兼容 MinIO 的配置参数命名
|
|
||||||
|
|
||||||
**主要特性**:
|
|
||||||
|
|
||||||
- 支持 Vault 端点、密钥名称、认证 token 等配置
|
|
||||||
- 自动验证配置完整性和有效性
|
|
||||||
- 支持 TLS 配置和证书验证
|
|
||||||
- 提供连接测试功能
|
|
||||||
|
|
||||||
### 3. S3 标准元数据格式支持 ✅
|
|
||||||
|
|
||||||
**实现位置**: `crypto/src/sse_kms.rs`
|
|
||||||
|
|
||||||
- 实现了 MinIO 兼容的元数据格式
|
|
||||||
- 支持标准 S3 SSE-KMS HTTP 头部
|
|
||||||
- 提供元数据与 HTTP 头部的双向转换
|
|
||||||
- 支持分片加密的元数据管理
|
|
||||||
|
|
||||||
**标准头部支持**:
|
|
||||||
|
|
||||||
```
|
|
||||||
x-amz-server-side-encryption: aws:kms
|
|
||||||
x-amz-server-side-encryption-aws-kms-key-id: key-id
|
|
||||||
x-amz-server-side-encryption-context: context
|
|
||||||
x-amz-meta-sse-kms-encrypted-key: encrypted-data-key
|
|
||||||
x-amz-meta-sse-kms-iv: initialization-vector
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 管理 API 支持动态配置 ✅
|
|
||||||
|
|
||||||
**实现位置**: `rustfs/src/admin/handlers.rs` 和 `rustfs/src/admin/mod.rs`
|
|
||||||
|
|
||||||
- 添加了 KMS 配置管理的 REST API 端点
|
|
||||||
- 支持 MinIO 兼容的配置管理路径
|
|
||||||
- 提供获取和设置配置的 HTTP 接口
|
|
||||||
- 支持实时配置更新和验证
|
|
||||||
|
|
||||||
**API 端点**:
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /minio/admin/v3/config # 获取所有配置
|
|
||||||
POST /minio/admin/v3/config/kms_vault/{target} # 设置KMS配置
|
|
||||||
GET /rustfs/admin/v3/config # RustFS原生配置API
|
|
||||||
POST /rustfs/admin/v3/config/kms_vault/{target} # RustFS原生设置API
|
|
||||||
```
|
|
||||||
|
|
||||||
## 技术特性
|
|
||||||
|
|
||||||
### 兼容性
|
|
||||||
|
|
||||||
- ✅ 完全兼容 MinIO 的 SSE-KMS 配置格式
|
|
||||||
- ✅ 支持标准 S3 SSE-KMS HTTP 头部
|
|
||||||
- ✅ 兼容 MinIO Admin API 配置管理接口
|
|
||||||
|
|
||||||
### 安全性
|
|
||||||
|
|
||||||
- ✅ 支持 RustyVault KMS 集成
|
|
||||||
- ✅ 数据密钥的安全生成和加密存储
|
|
||||||
- ✅ 支持 TLS 连接和证书验证
|
|
||||||
- ✅ 敏感配置信息的安全处理
|
|
||||||
|
|
||||||
### 性能
|
|
||||||
|
|
||||||
- ✅ 异步配置操作
|
|
||||||
- ✅ 线程安全的全局配置缓存
|
|
||||||
- ✅ 高效的元数据序列化/反序列化
|
|
||||||
- ✅ 支持分片并行加密
|
|
||||||
|
|
||||||
### 可维护性
|
|
||||||
|
|
||||||
- ✅ 模块化设计,职责分离
|
|
||||||
- ✅ 完整的错误处理和日志记录
|
|
||||||
- ✅ 丰富的单元测试覆盖
|
|
||||||
- ✅ 详细的文档和注释
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 配置 KMS
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 通过环境变量配置
|
|
||||||
export RUSTFS_KMS_ENABLED=true
|
|
||||||
export RUSTFS_KMS_VAULT_ENDPOINT=http://vault:8200
|
|
||||||
export RUSTFS_KMS_VAULT_KEY_NAME=rustfs-key
|
|
||||||
export RUSTFS_KMS_VAULT_TOKEN=vault-token
|
|
||||||
|
|
||||||
# 通过API配置
|
|
||||||
curl -X POST "http://rustfs:9000/minio/admin/v3/config/kms_vault/default" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"endpoint": "http://vault:8200",
|
|
||||||
"key_name": "rustfs-encryption-key",
|
|
||||||
"token": "vault-token",
|
|
||||||
"enabled": true
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### 使用 SSE-KMS 上传对象
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 使用aws-cli上传加密对象
|
|
||||||
aws s3 cp file.txt s3://bucket/file.txt \
|
|
||||||
--server-side-encryption aws:kms \
|
|
||||||
--ssekms-key-id rustfs-encryption-key
|
|
||||||
```
|
|
||||||
|
|
||||||
## 部署注意事项
|
|
||||||
|
|
||||||
1. **RustyVault 集成**: 确保 RustyVault 服务可访问且已正确配置 transit 引擎
|
|
||||||
2. **网络安全**: 建议在生产环境中使用 TLS 连接到 Vault
|
|
||||||
3. **权限管理**: 确保 RustFS 具有访问 Vault 密钥的适当权限
|
|
||||||
4. **监控**: 建议监控 KMS 连接状态和加密操作性能
|
|
||||||
|
|
||||||
## 后续发展
|
|
||||||
|
|
||||||
这次实现为 RustFS 的企业级加密功能奠定了坚实基础。未来可以考虑:
|
|
||||||
|
|
||||||
- 支持多个 KMS 提供商(AWS KMS, Azure Key Vault 等)
|
|
||||||
- 实现密钥轮换功能
|
|
||||||
- 添加加密性能监控和优化
|
|
||||||
- 支持更复杂的访问控制策略
|
|
||||||
|
|
||||||
通过这些改进,RustFS 现在具备了与 MinIO 相当的 SSE-KMS 功能,可以满足企业级数据加密需求。
|
|
||||||
68
TODO.md
68
TODO.md
@@ -1,68 +0,0 @@
|
|||||||
# TODO LIST
|
|
||||||
|
|
||||||
## 基础存储
|
|
||||||
|
|
||||||
- [x] EC 可用读写数量判断 Read/WriteQuorum
|
|
||||||
- [ ] 优化后台并发执行,可中断,传引用?
|
|
||||||
- [x] 小文件存储到 metafile, inlinedata
|
|
||||||
- [x] 完善 bucketmeta
|
|
||||||
- [x] 对象锁
|
|
||||||
- [x] 边读写边 hash,实现 reader 嵌套
|
|
||||||
- [x] 远程 rpc
|
|
||||||
- [x] 错误类型判断,程序中判断错误类型,如何统一错误
|
|
||||||
- [x] 优化 xlmeta, 自定义 msg 数据结构
|
|
||||||
- [ ] 优化 io.reader 参考 GetObjectNInfo 方便 io copy 如果 异步写,再平衡
|
|
||||||
- [ ] 代码优化 使用范型?
|
|
||||||
- [ ] 抽象出 metafile 存储
|
|
||||||
|
|
||||||
## 基础功能
|
|
||||||
|
|
||||||
- [ ] 桶操作
|
|
||||||
- [x] 创建 CreateBucket
|
|
||||||
- [x] 列表 ListBuckets
|
|
||||||
- [ ] 桶下面的文件列表 ListObjects
|
|
||||||
- [x] 简单实现功能
|
|
||||||
- [ ] 优化并发读取
|
|
||||||
- [ ] 删除
|
|
||||||
- [x] 详情 HeadBucket
|
|
||||||
- [ ] 文件操作
|
|
||||||
- [x] 上传 PutObject
|
|
||||||
- [x] 大文件上传
|
|
||||||
- [x] 创建分片上传 CreateMultipartUpload
|
|
||||||
- [x] 上传分片 PubObjectPart
|
|
||||||
- [x] 提交完成 CompleteMultipartUpload
|
|
||||||
- [x] 取消上传 AbortMultipartUpload
|
|
||||||
- [x] 下载 GetObject
|
|
||||||
- [x] 删除 DeleteObjects
|
|
||||||
- [ ] 版本控制
|
|
||||||
- [ ] 对象锁
|
|
||||||
- [ ] 复制 CopyObject
|
|
||||||
- [ ] 详情 HeadObject
|
|
||||||
- [ ] 对象预先签名(get、put、head、post)
|
|
||||||
|
|
||||||
## 扩展功能
|
|
||||||
|
|
||||||
- [ ] 用户管理
|
|
||||||
- [ ] Policy 管理
|
|
||||||
- [ ] AK/SK分配管理
|
|
||||||
- [ ] data scanner 统计和对象修复
|
|
||||||
- [ ] 桶配额
|
|
||||||
- [ ] 桶只读
|
|
||||||
- [ ] 桶复制
|
|
||||||
- [ ] 桶事件通知
|
|
||||||
- [ ] 桶公开、桶私有
|
|
||||||
- [ ] 对象生命周期管理
|
|
||||||
- [ ] prometheus 对接
|
|
||||||
- [ ] 日志收集和日志外发
|
|
||||||
- [ ] 对象压缩
|
|
||||||
- [ ] STS
|
|
||||||
- [ ] 分层(阿里云、腾讯云、S3 远程对接)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 性能优化
|
|
||||||
- [ ] bitrot impl AsyncRead/AsyncWrite
|
|
||||||
- [ ] erasure 并发读写
|
|
||||||
- [x] 完善删除逻辑,并发处理,先移动到回收站,
|
|
||||||
- [ ] 空间不足时清空回收站
|
|
||||||
- [ ] list_object 使用 reader 传输
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "appauth"
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
rust-version.workspace = true
|
|
||||||
version.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
base64-simd = "0.8.0"
|
|
||||||
common.workspace = true
|
|
||||||
hex-simd = "0.8.0"
|
|
||||||
rand.workspace = true
|
|
||||||
rsa = "0.9.8"
|
|
||||||
serde.workspace = true
|
|
||||||
serde_json.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
pub mod token;
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
启动两个rustfs
|
|
||||||
rustfs --address 0.0.0.0:9000 /rustfs-data9000
|
|
||||||
rustfs --address 0.0.0.0:9001 /rustfs-data9001
|
|
||||||
|
|
||||||
|
|
||||||
### 使用 minio mc 设置 alias 分别为 rustfs 和 rustfs2
|
|
||||||
|
|
||||||
|
|
||||||
### 创建 bucket
|
|
||||||
mc mb rustfs/srcbucket
|
|
||||||
|
|
||||||
### 创建 desc bucket
|
|
||||||
|
|
||||||
mc mb rustfs2/destbucket
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 开启版本控制
|
|
||||||
|
|
||||||
mc version enable rustfs/srcbucket
|
|
||||||
mc version enable rustfs2/destbucket
|
|
||||||
|
|
||||||
#### 使用修改过的 mc 才能 add bucket replication
|
|
||||||
|
|
||||||
./mc replication add rustfs/srcbucket --remote-bucket rustfs2/destbucket
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###### 复制一个小文件;
|
|
||||||
mc cp ./1.txt rustfs/srcbucket
|
|
||||||
|
|
||||||
###### 查看是否成功
|
|
||||||
mc ls --versions rustfs/srcbucket/1.txt
|
|
||||||
mc ls --versions rustfs/destbucket/1.txt
|
|
||||||
|
|
||||||
|
|
||||||
##### 复制一个大文件
|
|
||||||
1 创建一个大文件
|
|
||||||
dd if=/dev/zero of=./dd.out bs=4096000 count=1000
|
|
||||||
|
|
||||||
mc cp ./dd.out rustfs/srcbucket/
|
|
||||||
|
|
||||||
##### 查看是否成功
|
|
||||||
mc ls --versions rustfs/srcbucket/dd.out
|
|
||||||
mc ls --versions rustfs2/destbucket/dd.out
|
|
||||||
@@ -1,10 +1,24 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
clear
|
clear
|
||||||
|
|
||||||
# 获取当前平台架构
|
# Get the current platform architecture
|
||||||
ARCH=$(uname -m)
|
ARCH=$(uname -m)
|
||||||
|
|
||||||
# 根据架构设置 target 目录
|
# Set the target directory according to the schema
|
||||||
if [ "$ARCH" == "x86_64" ]; then
|
if [ "$ARCH" == "x86_64" ]; then
|
||||||
TARGET_DIR="target/x86_64"
|
TARGET_DIR="target/x86_64"
|
||||||
elif [ "$ARCH" == "aarch64" ]; then
|
elif [ "$ARCH" == "aarch64" ]; then
|
||||||
@@ -13,8 +27,8 @@ else
|
|||||||
TARGET_DIR="target/unknown"
|
TARGET_DIR="target/unknown"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 设置 CARGO_TARGET_DIR 并构建项目
|
# Set CARGO_TARGET_DIR and build the project
|
||||||
CARGO_TARGET_DIR=$TARGET_DIR RUSTFLAGS="-C link-arg=-fuse-ld=mold" cargo build --package rustfs
|
CARGO_TARGET_DIR=$TARGET_DIR RUSTFLAGS="-C link-arg=-fuse-ld=mold" cargo build --release --package rustfs
|
||||||
|
|
||||||
echo -e "\a"
|
echo -e "\a"
|
||||||
echo -e "\a"
|
echo -e "\a"
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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]
|
[package]
|
||||||
name = "rustfs-gui"
|
name = "rustfs-gui"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
@@ -15,6 +29,7 @@ keyring = { workspace = true }
|
|||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
rfd = { workspace = true }
|
rfd = { workspace = true }
|
||||||
rust-embed = { workspace = true, features = ["interpolate-folder-path"] }
|
rust-embed = { workspace = true, features = ["interpolate-folder-path"] }
|
||||||
|
rust-i18n = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
sha2 = { workspace = true }
|
sha2 = { workspace = true }
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2024 RustFS Team
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
window.switchTab = function (tabId) {
|
window.switchTab = function (tabId) {
|
||||||
// Hide everything
|
// Hide everything
|
||||||
document.querySelectorAll('.tab-content').forEach(content => {
|
document.querySelectorAll('.tab-content').forEach(content => {
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2024 RustFS Team
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
#navbar {
|
#navbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2024 RustFS Team
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
*, ::before, ::after {
|
*, ::before, ::after {
|
||||||
--tw-border-spacing-x: 0;
|
--tw-border-spacing-x: 0;
|
||||||
--tw-border-spacing-y: 0;
|
--tw-border-spacing-y: 0;
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2024 RustFS Team
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use crate::components::navbar::LoadingSpinner;
|
use crate::components::navbar::LoadingSpinner;
|
||||||
use crate::route::Route;
|
use crate::route::Route;
|
||||||
use crate::utils::{RustFSConfig, ServiceManager};
|
use crate::utils::{RustFSConfig, ServiceManager};
|
||||||
@@ -30,11 +44,11 @@ pub fn Home() -> Element {
|
|||||||
#[allow(clippy::redundant_closure)]
|
#[allow(clippy::redundant_closure)]
|
||||||
let service = use_signal(|| ServiceManager::new());
|
let service = use_signal(|| ServiceManager::new());
|
||||||
let conf = RustFSConfig::load().unwrap_or_else(|e| {
|
let conf = RustFSConfig::load().unwrap_or_else(|e| {
|
||||||
ServiceManager::show_error(&format!("加载配置失败:{}", e));
|
ServiceManager::show_error(&format!("load config failed: {e}"));
|
||||||
RustFSConfig::default()
|
RustFSConfig::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
debug!("loaded configurations:{:?}", conf);
|
debug!("loaded configurations: {:?}", conf);
|
||||||
let config = use_signal(|| conf.clone());
|
let config = use_signal(|| conf.clone());
|
||||||
|
|
||||||
use dioxus_router::prelude::Link;
|
use dioxus_router::prelude::Link;
|
||||||
@@ -78,7 +92,7 @@ pub fn Home() -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
ServiceManager::show_error(&format!("服务启动失败:{}", e));
|
ServiceManager::show_error(&format!("start service failed: {e}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Only set loading to false when it's actually done
|
// Only set loading to false when it's actually done
|
||||||
@@ -104,7 +118,7 @@ pub fn Home() -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
ServiceManager::show_error(&format!("服务停止失败:{}", e));
|
ServiceManager::show_error(&format!("stop service failed: {e}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!("service_state: {:?}", service_state.read());
|
debug!("service_state: {:?}", service_state.read());
|
||||||
@@ -143,6 +157,7 @@ pub fn Home() -> Element {
|
|||||||
Title { "RustFS APP" }
|
Title { "RustFS APP" }
|
||||||
Meta {
|
Meta {
|
||||||
name: "description",
|
name: "description",
|
||||||
|
// TODO: translate to english
|
||||||
content: "RustFS RustFS 用热门安全的 Rust 语言开发,兼容 S3 协议。适用于 AI/ML 及海量数据存储、大数据、互联网、工业和保密存储等全部场景。近乎免费使用。遵循 Apache 2 协议,支持国产保密设备和系统。",
|
content: "RustFS RustFS 用热门安全的 Rust 语言开发,兼容 S3 协议。适用于 AI/ML 及海量数据存储、大数据、互联网、工业和保密存储等全部场景。近乎免费使用。遵循 Apache 2 协议,支持国产保密设备和系统。",
|
||||||
}
|
}
|
||||||
div { class: "min-h-screen flex flex-col items-center bg-white",
|
div { class: "min-h-screen flex flex-col items-center bg-white",
|
||||||
@@ -166,7 +181,7 @@ pub fn Home() -> Element {
|
|||||||
}
|
}
|
||||||
LoadingSpinner {
|
LoadingSpinner {
|
||||||
loading: loading.read().to_owned(),
|
loading: loading.read().to_owned(),
|
||||||
text: "服务处理中...",
|
text: "processing...",
|
||||||
}
|
}
|
||||||
button { class: button_class, onclick: toggle_service,
|
button { class: button_class, onclick: toggle_service,
|
||||||
svg {
|
svg {
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
mod home;
|
mod home;
|
||||||
pub use home::Home;
|
pub use home::Home;
|
||||||
mod navbar;
|
mod navbar;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use crate::route::Route;
|
use crate::route::Route;
|
||||||
use dioxus::logger::tracing::debug;
|
use dioxus::logger::tracing::debug;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use crate::components::navbar::LoadingSpinner;
|
use crate::components::navbar::LoadingSpinner;
|
||||||
use dioxus::logger::tracing::{debug, error};
|
use dioxus::logger::tracing::{debug, error};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
@@ -49,7 +63,7 @@ pub fn Setting() -> Element {
|
|||||||
let config = config.read().clone();
|
let config = config.read().clone();
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
if let Err(e) = service.read().restart(config).await {
|
if let Err(e) = service.read().restart(config).await {
|
||||||
ServiceManager::show_error(&format!("发送重启命令失败:{}", e));
|
ServiceManager::show_error(&format!("发送重启命令失败:{e}"));
|
||||||
}
|
}
|
||||||
// reset the status when you're done
|
// reset the status when you're done
|
||||||
loading.set(false);
|
loading.set(false);
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
mod components;
|
mod components;
|
||||||
mod route;
|
mod route;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
mod router;
|
mod router;
|
||||||
|
|
||||||
pub use router::Route;
|
pub use router::Route;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use crate::components::Navbar;
|
use crate::components::Navbar;
|
||||||
use crate::views::{HomeViews, SettingViews};
|
use crate::views::{HomeViews, SettingViews};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use keyring::Entry;
|
use keyring::Entry;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
@@ -201,7 +215,7 @@ impl RustFSConfig {
|
|||||||
/// Clear the stored configuration from the system keyring
|
/// Clear the stored configuration from the system keyring
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// Returns `Ok(())` if the configuration was successfully cleared, or an error if the operation failed.
|
/// `Ok(())` if the configuration was successfully cleared, or an error if the operation failed.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
@@ -288,7 +302,7 @@ mod tests {
|
|||||||
|
|
||||||
for (input, expected) in test_cases {
|
for (input, expected) in test_cases {
|
||||||
let result = RustFSConfig::extract_host_port(input);
|
let result = RustFSConfig::extract_host_port(input);
|
||||||
assert_eq!(result, expected, "Failed for input: {}", input);
|
assert_eq!(result, expected, "Failed for input: {input}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +320,7 @@ mod tests {
|
|||||||
|
|
||||||
for input in invalid_cases {
|
for input in invalid_cases {
|
||||||
let result = RustFSConfig::extract_host_port(input);
|
let result = RustFSConfig::extract_host_port(input);
|
||||||
assert_eq!(result, None, "Should be None for input: {}", input);
|
assert_eq!(result, None, "Should be None for input: {input}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case: empty host but valid port should still work
|
// Special case: empty host but valid port should still work
|
||||||
@@ -437,7 +451,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_debug_format() {
|
fn test_debug_format() {
|
||||||
let config = RustFSConfig::default_config();
|
let config = RustFSConfig::default_config();
|
||||||
let debug_str = format!("{:?}", config);
|
let debug_str = format!("{config:?}");
|
||||||
|
|
||||||
assert!(debug_str.contains("RustFSConfig"));
|
assert!(debug_str.contains("RustFSConfig"));
|
||||||
assert!(debug_str.contains("address"));
|
assert!(debug_str.contains("address"));
|
||||||
@@ -484,14 +498,14 @@ mod tests {
|
|||||||
fn test_very_long_strings() {
|
fn test_very_long_strings() {
|
||||||
let long_string = "a".repeat(1000);
|
let long_string = "a".repeat(1000);
|
||||||
let config = RustFSConfig {
|
let config = RustFSConfig {
|
||||||
address: format!("{}:9000", long_string),
|
address: format!("{long_string}:9000"),
|
||||||
host: long_string.clone(),
|
host: long_string.clone(),
|
||||||
port: "9000".to_string(),
|
port: "9000".to_string(),
|
||||||
access_key: long_string.clone(),
|
access_key: long_string.clone(),
|
||||||
secret_key: long_string.clone(),
|
secret_key: long_string.clone(),
|
||||||
domain_name: format!("{}.com", long_string),
|
domain_name: format!("{long_string}.com"),
|
||||||
volume_name: format!("/data/{}", long_string),
|
volume_name: format!("/data/{long_string}"),
|
||||||
console_address: format!("{}:9001", long_string),
|
console_address: format!("{long_string}:9001"),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(config.host.len(), 1000);
|
assert_eq!(config.host.len(), 1000);
|
||||||
@@ -525,14 +539,14 @@ mod tests {
|
|||||||
host: "127.0.0.1".to_string(),
|
host: "127.0.0.1".to_string(),
|
||||||
port: "9000".to_string(),
|
port: "9000".to_string(),
|
||||||
access_key: "用户名".to_string(),
|
access_key: "用户名".to_string(),
|
||||||
secret_key: "密码123".to_string(),
|
secret_key: "密码 123".to_string(),
|
||||||
domain_name: "测试.com".to_string(),
|
domain_name: "测试.com".to_string(),
|
||||||
volume_name: "/数据/存储".to_string(),
|
volume_name: "/数据/存储".to_string(),
|
||||||
console_address: "127.0.0.1:9001".to_string(),
|
console_address: "127.0.0.1:9001".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(config.access_key, "用户名");
|
assert_eq!(config.access_key, "用户名");
|
||||||
assert_eq!(config.secret_key, "密码123");
|
assert_eq!(config.secret_key, "密码 123");
|
||||||
assert_eq!(config.domain_name, "测试.com");
|
assert_eq!(config.domain_name, "测试.com");
|
||||||
assert_eq!(config.volume_name, "/数据/存储");
|
assert_eq!(config.volume_name, "/数据/存储");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use crate::utils::RustFSConfig;
|
use crate::utils::RustFSConfig;
|
||||||
use dioxus::logger::tracing::{debug, error, info};
|
use dioxus::logger::tracing::{debug, error, info};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
@@ -11,7 +25,7 @@ use tokio::fs;
|
|||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::sync::{mpsc, Mutex};
|
use tokio::sync::{Mutex, mpsc};
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
#[folder = "$CARGO_MANIFEST_DIR/embedded-rustfs/"]
|
#[folder = "$CARGO_MANIFEST_DIR/embedded-rustfs/"]
|
||||||
@@ -184,7 +198,7 @@ impl ServiceManager {
|
|||||||
let cached_hash = fs::read_to_string(&hash_path).await?;
|
let cached_hash = fs::read_to_string(&hash_path).await?;
|
||||||
let expected_hash = RUSTFS_HASH.lock().await;
|
let expected_hash = RUSTFS_HASH.lock().await;
|
||||||
if cached_hash == *expected_hash {
|
if cached_hash == *expected_hash {
|
||||||
println!("Use cached rustfs: {:?}", executable_path);
|
println!("Use cached rustfs: {executable_path:?}");
|
||||||
return Ok(executable_path);
|
return Ok(executable_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,23 +249,23 @@ impl ServiceManager {
|
|||||||
match cmd {
|
match cmd {
|
||||||
ServiceCommand::Start(config) => {
|
ServiceCommand::Start(config) => {
|
||||||
if let Err(e) = Self::start_service(&config).await {
|
if let Err(e) = Self::start_service(&config).await {
|
||||||
Self::show_error(&format!("启动服务失败:{}", e));
|
Self::show_error(&format!("启动服务失败:{e}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ServiceCommand::Stop => {
|
ServiceCommand::Stop => {
|
||||||
if let Err(e) = Self::stop_service().await {
|
if let Err(e) = Self::stop_service().await {
|
||||||
Self::show_error(&format!("停止服务失败:{}", e));
|
Self::show_error(&format!("停止服务失败:{e}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ServiceCommand::Restart(config) => {
|
ServiceCommand::Restart(config) => {
|
||||||
if Self::check_service_status().await.is_some() {
|
if Self::check_service_status().await.is_some() {
|
||||||
if let Err(e) = Self::stop_service().await {
|
if let Err(e) = Self::stop_service().await {
|
||||||
Self::show_error(&format!("重启服务失败:{}", e));
|
Self::show_error(&format!("重启服务失败:{e}"));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(e) = Self::start_service(&config).await {
|
if let Err(e) = Self::start_service(&config).await {
|
||||||
Self::show_error(&format!("重启服务失败:{}", e));
|
Self::show_error(&format!("重启服务失败:{e}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,7 +297,7 @@ impl ServiceManager {
|
|||||||
async fn start_service(config: &RustFSConfig) -> Result<(), Box<dyn Error>> {
|
async fn start_service(config: &RustFSConfig) -> Result<(), Box<dyn Error>> {
|
||||||
// Check if the service is already running
|
// Check if the service is already running
|
||||||
if let Some(existing_pid) = Self::check_service_status().await {
|
if let Some(existing_pid) = Self::check_service_status().await {
|
||||||
return Err(format!("服务已经在运行,PID: {}", existing_pid).into());
|
return Err(format!("服务已经在运行,PID: {existing_pid}").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the service program
|
// Prepare the service program
|
||||||
@@ -304,7 +318,7 @@ impl ServiceManager {
|
|||||||
let ports = vec![main_port, console_port];
|
let ports = vec![main_port, console_port];
|
||||||
for port in ports {
|
for port in ports {
|
||||||
if Self::is_port_in_use(host, port).await {
|
if Self::is_port_in_use(host, port).await {
|
||||||
return Err(format!("端口 {} 已被占用", port).into());
|
return Err(format!("端口 {port} 已被占用").into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,7 +341,7 @@ impl ServiceManager {
|
|||||||
|
|
||||||
// Check if the service started successfully
|
// Check if the service started successfully
|
||||||
if Self::is_port_in_use(host, main_port).await {
|
if Self::is_port_in_use(host, main_port).await {
|
||||||
Self::show_info(&format!("服务启动成功!进程 ID: {}", process_pid));
|
Self::show_info(&format!("服务启动成功!进程 ID: {process_pid}"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@@ -387,7 +401,7 @@ impl ServiceManager {
|
|||||||
/// println!("{:?}", result);
|
/// println!("{:?}", result);
|
||||||
/// ```
|
/// ```
|
||||||
async fn is_port_in_use(host: &str, port: u16) -> bool {
|
async fn is_port_in_use(host: &str, port: u16) -> bool {
|
||||||
TcpStream::connect(format!("{}:{}", host, port)).await.is_ok()
|
TcpStream::connect(format!("{host}:{port}")).await.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show an error message
|
/// Show an error message
|
||||||
@@ -674,7 +688,7 @@ mod tests {
|
|||||||
message: "Test message".to_string(),
|
message: "Test message".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let debug_str = format!("{:?}", result);
|
let debug_str = format!("{result:?}");
|
||||||
assert!(debug_str.contains("ServiceOperationResult"));
|
assert!(debug_str.contains("ServiceOperationResult"));
|
||||||
assert!(debug_str.contains("success: true"));
|
assert!(debug_str.contains("success: true"));
|
||||||
assert!(debug_str.contains("Test message"));
|
assert!(debug_str.contains("Test message"));
|
||||||
@@ -691,8 +705,8 @@ mod tests {
|
|||||||
let cloned_manager = service_manager.clone();
|
let cloned_manager = service_manager.clone();
|
||||||
|
|
||||||
// Both should be valid (we can't test much more without async runtime)
|
// Both should be valid (we can't test much more without async runtime)
|
||||||
assert!(format!("{:?}", service_manager).contains("ServiceManager"));
|
assert!(format!("{service_manager:?}").contains("ServiceManager"));
|
||||||
assert!(format!("{:?}", cloned_manager).contains("ServiceManager"));
|
assert!(format!("{cloned_manager:?}").contains("ServiceManager"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,7 +724,7 @@ mod tests {
|
|||||||
|
|
||||||
for (input, expected) in test_cases {
|
for (input, expected) in test_cases {
|
||||||
let result = ServiceManager::extract_port(input);
|
let result = ServiceManager::extract_port(input);
|
||||||
assert_eq!(result, expected, "Failed for input: {}", input);
|
assert_eq!(result, expected, "Failed for input: {input}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -729,7 +743,7 @@ mod tests {
|
|||||||
|
|
||||||
for input in invalid_cases {
|
for input in invalid_cases {
|
||||||
let result = ServiceManager::extract_port(input);
|
let result = ServiceManager::extract_port(input);
|
||||||
assert_eq!(result, None, "Should be None for input: {}", input);
|
assert_eq!(result, None, "Should be None for input: {input}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case: empty host but valid port should still work
|
// Special case: empty host but valid port should still work
|
||||||
@@ -746,10 +760,10 @@ mod tests {
|
|||||||
assert_eq!(ServiceManager::extract_port("host:0"), Some(0));
|
assert_eq!(ServiceManager::extract_port("host:0"), Some(0));
|
||||||
assert_eq!(ServiceManager::extract_port("host:65535"), Some(65535));
|
assert_eq!(ServiceManager::extract_port("host:65535"), Some(65535));
|
||||||
assert_eq!(ServiceManager::extract_port("host:65536"), None); // Out of range
|
assert_eq!(ServiceManager::extract_port("host:65536"), None); // Out of range
|
||||||
// IPv6-like address - extract_port takes the second part after split(':')
|
// IPv6-like address - extract_port takes the second part after split(':')
|
||||||
// For "::1:8080", split(':') gives ["", "", "1", "8080"], nth(1) gives ""
|
// For "::1:8080", split(':') gives ["", "", "1", "8080"], nth(1) gives ""
|
||||||
assert_eq!(ServiceManager::extract_port("::1:8080"), None); // Second part is empty
|
assert_eq!(ServiceManager::extract_port("::1:8080"), None); // Second part is empty
|
||||||
// For "[::1]:8080", split(':') gives ["[", "", "1]", "8080"], nth(1) gives ""
|
// For "[::1]:8080", split(':') gives ["[", "", "1]", "8080"], nth(1) gives ""
|
||||||
assert_eq!(ServiceManager::extract_port("[::1]:8080"), None); // Second part is empty
|
assert_eq!(ServiceManager::extract_port("[::1]:8080"), None); // Second part is empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use dioxus::logger::tracing::debug;
|
use dioxus::logger::tracing::debug;
|
||||||
use tracing_appender::non_blocking::WorkerGuard;
|
use tracing_appender::non_blocking::WorkerGuard;
|
||||||
use tracing_appender::rolling::{RollingFileAppender, Rotation};
|
use tracing_appender::rolling::{RollingFileAppender, Rotation};
|
||||||
@@ -94,7 +108,7 @@ mod tests {
|
|||||||
|
|
||||||
// We can't actually build it without creating directories,
|
// We can't actually build it without creating directories,
|
||||||
// but we can verify the builder pattern works
|
// but we can verify the builder pattern works
|
||||||
let debug_str = format!("{:?}", builder);
|
let debug_str = format!("{builder:?}");
|
||||||
// The actual debug format might be different, so just check it's not empty
|
// The actual debug format might be different, so just check it's not empty
|
||||||
assert!(!debug_str.is_empty());
|
assert!(!debug_str.is_empty());
|
||||||
// Check that it contains some expected parts
|
// Check that it contains some expected parts
|
||||||
@@ -112,10 +126,10 @@ mod tests {
|
|||||||
let never = Rotation::NEVER;
|
let never = Rotation::NEVER;
|
||||||
|
|
||||||
// Test that rotation types can be created and formatted
|
// Test that rotation types can be created and formatted
|
||||||
assert!(!format!("{:?}", daily).is_empty());
|
assert!(!format!("{daily:?}").is_empty());
|
||||||
assert!(!format!("{:?}", hourly).is_empty());
|
assert!(!format!("{hourly:?}").is_empty());
|
||||||
assert!(!format!("{:?}", minutely).is_empty());
|
assert!(!format!("{minutely:?}").is_empty());
|
||||||
assert!(!format!("{:?}", never).is_empty());
|
assert!(!format!("{never:?}").is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -159,10 +173,10 @@ mod tests {
|
|||||||
let error_filter = tracing_subscriber::EnvFilter::new("error");
|
let error_filter = tracing_subscriber::EnvFilter::new("error");
|
||||||
|
|
||||||
// Test that filters can be created
|
// Test that filters can be created
|
||||||
assert!(!format!("{:?}", info_filter).is_empty());
|
assert!(!format!("{info_filter:?}").is_empty());
|
||||||
assert!(!format!("{:?}", debug_filter).is_empty());
|
assert!(!format!("{debug_filter:?}").is_empty());
|
||||||
assert!(!format!("{:?}", warn_filter).is_empty());
|
assert!(!format!("{warn_filter:?}").is_empty());
|
||||||
assert!(!format!("{:?}", error_filter).is_empty());
|
assert!(!format!("{error_filter:?}").is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -201,7 +215,7 @@ mod tests {
|
|||||||
assert_eq!(suffix, "log");
|
assert_eq!(suffix, "log");
|
||||||
|
|
||||||
// Test that these would create valid filenames
|
// Test that these would create valid filenames
|
||||||
let sample_filename = format!("{}.2024-01-01.{}", prefix, suffix);
|
let sample_filename = format!("{prefix}.2024-01-01.{suffix}");
|
||||||
assert_eq!(sample_filename, "rustfs-cli.2024-01-01.log");
|
assert_eq!(sample_filename, "rustfs-cli.2024-01-01.log");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +257,7 @@ mod tests {
|
|||||||
assert_eq!(logs_dir_name, "logs");
|
assert_eq!(logs_dir_name, "logs");
|
||||||
|
|
||||||
// Test path joining
|
// Test path joining
|
||||||
let combined = format!("{}/{}", rustfs_dir_name, logs_dir_name);
|
let combined = format!("{rustfs_dir_name}/{logs_dir_name}");
|
||||||
assert_eq!(combined, "rustfs/logs");
|
assert_eq!(combined, "rustfs/logs");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod helper;
|
mod helper;
|
||||||
mod logger;
|
mod logger;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use crate::route::Route;
|
use crate::route::Route;
|
||||||
use dioxus::logger::tracing::info;
|
use dioxus::logger::tracing::info;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use crate::components::Home;
|
use crate::components::Home;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod home;
|
mod home;
|
||||||
mod setting;
|
mod setting;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use crate::components::Setting;
|
use crate::components::Setting;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2024 RustFS Team
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: "all",
|
mode: "all",
|
||||||
content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"],
|
content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"],
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "common"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
async-trait.workspace = true
|
|
||||||
lazy_static.workspace = true
|
|
||||||
scopeguard = "1.2.0"
|
|
||||||
tokio.workspace = true
|
|
||||||
tonic = { workspace = true }
|
|
||||||
tracing-error.workspace = true
|
|
||||||
@@ -1,339 +0,0 @@
|
|||||||
use tracing_error::{SpanTrace, SpanTraceStatus};
|
|
||||||
|
|
||||||
pub type StdError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
|
||||||
|
|
||||||
pub type Result<T = (), E = Error> = std::result::Result<T, E>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Error {
|
|
||||||
inner: Box<dyn std::error::Error + Send + Sync + 'static>,
|
|
||||||
span_trace: SpanTrace,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error {
|
|
||||||
/// Create a new error from a `std::error::Error`.
|
|
||||||
#[must_use]
|
|
||||||
#[track_caller]
|
|
||||||
pub fn new<T: std::error::Error + Send + Sync + 'static>(source: T) -> Self {
|
|
||||||
Self::from_std_error(source.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new error from a `std::error::Error`.
|
|
||||||
#[must_use]
|
|
||||||
#[track_caller]
|
|
||||||
pub fn from_std_error(inner: StdError) -> Self {
|
|
||||||
Self {
|
|
||||||
inner,
|
|
||||||
span_trace: SpanTrace::capture(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new error from a string.
|
|
||||||
#[must_use]
|
|
||||||
#[track_caller]
|
|
||||||
pub fn from_string(s: impl Into<String>) -> Self {
|
|
||||||
Self::msg(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new error from a string.
|
|
||||||
#[must_use]
|
|
||||||
#[track_caller]
|
|
||||||
pub fn msg(s: impl Into<String>) -> Self {
|
|
||||||
Self::from_std_error(s.into().into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the inner type is the same as `T`.
|
|
||||||
#[inline]
|
|
||||||
pub fn is<T: std::error::Error + 'static>(&self) -> bool {
|
|
||||||
self.inner.is::<T>()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns some reference to the inner value if it is of type `T`, or
|
|
||||||
/// `None` if it isn't.
|
|
||||||
#[inline]
|
|
||||||
pub fn downcast_ref<T: std::error::Error + 'static>(&self) -> Option<&T> {
|
|
||||||
self.inner.downcast_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns some mutable reference to the inner value if it is of type `T`, or
|
|
||||||
/// `None` if it isn't.
|
|
||||||
#[inline]
|
|
||||||
pub fn downcast_mut<T: std::error::Error + 'static>(&mut self) -> Option<&mut T> {
|
|
||||||
self.inner.downcast_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_io_err(&self) -> Option<std::io::Error> {
|
|
||||||
self.downcast_ref::<std::io::Error>()
|
|
||||||
.map(|e| std::io::Error::new(e.kind(), e.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inner_string(&self) -> String {
|
|
||||||
self.inner.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: std::error::Error + Send + Sync + 'static> From<T> for Error {
|
|
||||||
fn from(e: T) -> Self {
|
|
||||||
Self::new(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.inner)?;
|
|
||||||
|
|
||||||
if self.span_trace.status() != SpanTraceStatus::EMPTY {
|
|
||||||
write!(f, "\nspan_trace:\n{}", self.span_trace)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct CustomTestError {
|
|
||||||
message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for CustomTestError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "Custom test error: {}", self.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for CustomTestError {}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct AnotherTestError;
|
|
||||||
|
|
||||||
impl std::fmt::Display for AnotherTestError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "Another test error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for AnotherTestError {}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_new_from_std_error() {
|
|
||||||
let io_error = io::Error::new(io::ErrorKind::NotFound, "File not found");
|
|
||||||
let error = Error::new(io_error);
|
|
||||||
|
|
||||||
assert!(error.inner_string().contains("File not found"));
|
|
||||||
assert!(error.is::<io::Error>());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_from_std_error() {
|
|
||||||
let io_error = io::Error::new(io::ErrorKind::PermissionDenied, "Permission denied");
|
|
||||||
let boxed_error: StdError = Box::new(io_error);
|
|
||||||
let error = Error::from_std_error(boxed_error);
|
|
||||||
|
|
||||||
assert!(error.inner_string().contains("Permission denied"));
|
|
||||||
assert!(error.is::<io::Error>());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_from_string() {
|
|
||||||
let error = Error::from_string("Test error message");
|
|
||||||
assert_eq!(error.inner_string(), "Test error message");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_msg() {
|
|
||||||
let error = Error::msg("Another test message");
|
|
||||||
assert_eq!(error.inner_string(), "Another test message");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_msg_with_string() {
|
|
||||||
let message = String::from("String message");
|
|
||||||
let error = Error::msg(message);
|
|
||||||
assert_eq!(error.inner_string(), "String message");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_is_type_checking() {
|
|
||||||
let io_error = io::Error::new(io::ErrorKind::InvalidInput, "Invalid input");
|
|
||||||
let error = Error::new(io_error);
|
|
||||||
|
|
||||||
assert!(error.is::<io::Error>());
|
|
||||||
assert!(!error.is::<CustomTestError>());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_downcast_ref() {
|
|
||||||
let io_error = io::Error::new(io::ErrorKind::TimedOut, "Operation timed out");
|
|
||||||
let error = Error::new(io_error);
|
|
||||||
|
|
||||||
let downcast_io = error.downcast_ref::<io::Error>();
|
|
||||||
assert!(downcast_io.is_some());
|
|
||||||
assert_eq!(downcast_io.unwrap().kind(), io::ErrorKind::TimedOut);
|
|
||||||
|
|
||||||
let downcast_custom = error.downcast_ref::<CustomTestError>();
|
|
||||||
assert!(downcast_custom.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_downcast_mut() {
|
|
||||||
let io_error = io::Error::new(io::ErrorKind::Interrupted, "Operation interrupted");
|
|
||||||
let mut error = Error::new(io_error);
|
|
||||||
|
|
||||||
let downcast_io = error.downcast_mut::<io::Error>();
|
|
||||||
assert!(downcast_io.is_some());
|
|
||||||
assert_eq!(downcast_io.unwrap().kind(), io::ErrorKind::Interrupted);
|
|
||||||
|
|
||||||
let downcast_custom = error.downcast_mut::<CustomTestError>();
|
|
||||||
assert!(downcast_custom.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_to_io_err() {
|
|
||||||
// Test with IO error
|
|
||||||
let original_io_error = io::Error::new(io::ErrorKind::BrokenPipe, "Broken pipe");
|
|
||||||
let error = Error::new(original_io_error);
|
|
||||||
|
|
||||||
let converted_io_error = error.to_io_err();
|
|
||||||
assert!(converted_io_error.is_some());
|
|
||||||
let io_err = converted_io_error.unwrap();
|
|
||||||
assert_eq!(io_err.kind(), io::ErrorKind::BrokenPipe);
|
|
||||||
assert!(io_err.to_string().contains("Broken pipe"));
|
|
||||||
|
|
||||||
// Test with non-IO error
|
|
||||||
let custom_error = CustomTestError {
|
|
||||||
message: "Not an IO error".to_string(),
|
|
||||||
};
|
|
||||||
let error = Error::new(custom_error);
|
|
||||||
|
|
||||||
let converted_io_error = error.to_io_err();
|
|
||||||
assert!(converted_io_error.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_inner_string() {
|
|
||||||
let custom_error = CustomTestError {
|
|
||||||
message: "Test message".to_string(),
|
|
||||||
};
|
|
||||||
let error = Error::new(custom_error);
|
|
||||||
|
|
||||||
assert_eq!(error.inner_string(), "Custom test error: Test message");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_from_trait() {
|
|
||||||
let io_error = io::Error::new(io::ErrorKind::UnexpectedEof, "Unexpected EOF");
|
|
||||||
let error: Error = io_error.into();
|
|
||||||
|
|
||||||
assert!(error.is::<io::Error>());
|
|
||||||
assert!(error.inner_string().contains("Unexpected EOF"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_display() {
|
|
||||||
let custom_error = CustomTestError {
|
|
||||||
message: "Display test".to_string(),
|
|
||||||
};
|
|
||||||
let error = Error::new(custom_error);
|
|
||||||
|
|
||||||
let display_string = format!("{}", error);
|
|
||||||
assert!(display_string.contains("Custom test error: Display test"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_debug() {
|
|
||||||
let error = Error::msg("Debug test");
|
|
||||||
let debug_string = format!("{:?}", error);
|
|
||||||
|
|
||||||
assert!(debug_string.contains("Error"));
|
|
||||||
assert!(debug_string.contains("inner"));
|
|
||||||
assert!(debug_string.contains("span_trace"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_multiple_error_types() {
|
|
||||||
let errors = vec![
|
|
||||||
Error::new(io::Error::new(io::ErrorKind::NotFound, "Not found")),
|
|
||||||
Error::new(CustomTestError {
|
|
||||||
message: "Custom".to_string(),
|
|
||||||
}),
|
|
||||||
Error::new(AnotherTestError),
|
|
||||||
Error::msg("String error"),
|
|
||||||
];
|
|
||||||
|
|
||||||
assert!(errors[0].is::<io::Error>());
|
|
||||||
assert!(errors[1].is::<CustomTestError>());
|
|
||||||
assert!(errors[2].is::<AnotherTestError>());
|
|
||||||
assert!(!errors[3].is::<io::Error>());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_chain_compatibility() {
|
|
||||||
// Test that our Error type works well with error chains
|
|
||||||
let io_error = io::Error::new(io::ErrorKind::InvalidData, "Invalid data");
|
|
||||||
let error = Error::new(io_error);
|
|
||||||
|
|
||||||
// Should be able to convert back to Result
|
|
||||||
let result: Result<(), Error> = Err(error);
|
|
||||||
assert!(result.is_err());
|
|
||||||
|
|
||||||
// Test the error from the result
|
|
||||||
if let Err(err) = result {
|
|
||||||
assert!(err.is::<io::Error>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_result_type_alias() {
|
|
||||||
// Test the Result type alias
|
|
||||||
fn test_function() -> Result<String> {
|
|
||||||
Ok("Success".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_function_with_error() -> Result<String> {
|
|
||||||
Err(Error::msg("Test error"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let success_result = test_function();
|
|
||||||
assert!(success_result.is_ok());
|
|
||||||
assert_eq!(success_result.unwrap(), "Success");
|
|
||||||
|
|
||||||
let error_result = test_function_with_error();
|
|
||||||
assert!(error_result.is_err());
|
|
||||||
assert_eq!(error_result.unwrap_err().inner_string(), "Test error");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_with_empty_message() {
|
|
||||||
let error = Error::msg("");
|
|
||||||
assert_eq!(error.inner_string(), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_with_unicode_message() {
|
|
||||||
let unicode_message = "错误信息 🚨 Error message with émojis and ñon-ASCII";
|
|
||||||
let error = Error::msg(unicode_message);
|
|
||||||
assert_eq!(error.inner_string(), unicode_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_with_very_long_message() {
|
|
||||||
let long_message = "A".repeat(10000);
|
|
||||||
let error = Error::msg(&long_message);
|
|
||||||
assert_eq!(error.inner_string(), long_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_span_trace_capture() {
|
|
||||||
// Test that span trace is captured (though we can't easily test the content)
|
|
||||||
let error = Error::msg("Span trace test");
|
|
||||||
let display_string = format!("{}", error);
|
|
||||||
|
|
||||||
// The error should at least contain the message
|
|
||||||
assert!(display_string.contains("Span trace test"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
pub mod bucket_stats;
|
|
||||||
pub mod error;
|
|
||||||
pub mod globals;
|
|
||||||
pub mod last_minute;
|
|
||||||
|
|
||||||
// is ','
|
|
||||||
pub static DEFAULT_DELIMITER: u8 = 44;
|
|
||||||
|
|
||||||
/// Defers evaluation of a block of code until the end of the scope.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! defer {
|
|
||||||
($($body:tt)*) => {
|
|
||||||
let _guard = {
|
|
||||||
pub struct Guard<F: FnOnce()>(Option<F>);
|
|
||||||
|
|
||||||
impl<F: FnOnce()> Drop for Guard<F> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
(self.0).take().map(|f| f());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Guard(Some(|| {
|
|
||||||
let _ = { $($body)* };
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "lock"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
async-trait.workspace = true
|
|
||||||
backon.workspace = true
|
|
||||||
common.workspace = true
|
|
||||||
lazy_static.workspace = true
|
|
||||||
protos.workspace = true
|
|
||||||
rand.workspace = true
|
|
||||||
serde.workspace = true
|
|
||||||
serde_json.workspace = true
|
|
||||||
tokio.workspace = true
|
|
||||||
tonic.workspace = true
|
|
||||||
tracing.workspace = true
|
|
||||||
tracing-error.workspace = true
|
|
||||||
url.workspace = true
|
|
||||||
uuid.workspace = true
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
|
||||||
pub struct LockArgs {
|
|
||||||
pub uid: String,
|
|
||||||
pub resources: Vec<String>,
|
|
||||||
pub owner: String,
|
|
||||||
pub source: String,
|
|
||||||
pub quorum: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for LockArgs {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"LockArgs[ uid: {}, resources: {:?}, owner: {}, source:{}, quorum: {} ]",
|
|
||||||
self.uid, self.resources, self.owner, self.source, self.quorum
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "protos"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "gproto"
|
|
||||||
path = "src/main.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
#async-backtrace = { workspace = true, optional = true }
|
|
||||||
common.workspace = true
|
|
||||||
flatbuffers = { workspace = true }
|
|
||||||
prost = { workspace = true }
|
|
||||||
protobuf = { workspace = true }
|
|
||||||
tokio = { workspace = true }
|
|
||||||
tonic = { workspace = true, features = ["transport"] }
|
|
||||||
tower = { workspace = true }
|
|
||||||
prost-build = { workspace = true }
|
|
||||||
tonic-build = { workspace = true }
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
pub mod models;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#![allow(unused_imports)]
|
|
||||||
#![allow(clippy::all)]
|
|
||||||
pub mod proto_gen;
|
|
||||||
|
|
||||||
mod flatbuffers_generated;
|
|
||||||
pub use flatbuffers_generated::models::*;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
pub mod node_service;
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "workers"
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
rust-version.workspace = true
|
|
||||||
version.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
common.workspace = true
|
|
||||||
tokio.workspace = true
|
|
||||||
tracing.workspace = true
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
pub mod workers;
|
|
||||||
30
crates/appauth/Cargo.toml
Normal file
30
crates/appauth/Cargo.toml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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]
|
||||||
|
name = "rustfs-appauth"
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
base64-simd = { workspace = true }
|
||||||
|
rsa = { workspace = true }
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
37
crates/appauth/README.md
Normal file
37
crates/appauth/README.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[](https://rustfs.com)
|
||||||
|
|
||||||
|
# RustFS AppAuth - Application Authentication
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<strong>Application-level authentication and authorization module for RustFS distributed object storage</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/rustfs/rustfs/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/rustfs/rustfs/actions/workflows/ci.yml/badge.svg" /></a>
|
||||||
|
<a href="https://docs.rustfs.com/en/">📖 Documentation</a>
|
||||||
|
· <a href="https://github.com/rustfs/rustfs/issues">🐛 Bug Reports</a>
|
||||||
|
· <a href="https://github.com/rustfs/rustfs/discussions">💬 Discussions</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 Overview
|
||||||
|
|
||||||
|
**RustFS AppAuth** provides application-level authentication and authorization capabilities for the [RustFS](https://rustfs.com) distributed object storage system. For the complete RustFS experience, please visit the [main RustFS repository](https://github.com/rustfs/rustfs).
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
- JWT-based authentication with secure token management
|
||||||
|
- RBAC (Role-Based Access Control) for fine-grained permissions
|
||||||
|
- Multi-tenant application isolation and management
|
||||||
|
- OAuth 2.0 and OpenID Connect integration
|
||||||
|
- API key management and rotation
|
||||||
|
- Session management with configurable expiration
|
||||||
|
|
||||||
|
## 📚 Documentation
|
||||||
|
|
||||||
|
For comprehensive documentation, examples, and usage guides, please visit the main [RustFS repository](https://github.com/rustfs/rustfs).
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
This project is licensed under the Apache License 2.0 - see the [LICENSE](../../LICENSE) file for details.
|
||||||
15
crates/appauth/src/lib.rs
Normal file
15
crates/appauth/src/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
pub mod token;
|
||||||
@@ -1,10 +1,25 @@
|
|||||||
use common::error::Result;
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use rsa::Pkcs1v15Encrypt;
|
use rsa::Pkcs1v15Encrypt;
|
||||||
use rsa::{
|
use rsa::{
|
||||||
pkcs8::{DecodePrivateKey, DecodePublicKey},
|
|
||||||
RsaPrivateKey, RsaPublicKey,
|
RsaPrivateKey, RsaPublicKey,
|
||||||
|
pkcs8::{DecodePrivateKey, DecodePublicKey},
|
||||||
|
rand_core::OsRng,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::io::{Error, Result};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
pub struct Token {
|
pub struct Token {
|
||||||
@@ -18,8 +33,8 @@ pub struct Token {
|
|||||||
// 返回 base64 处理的加密字符串
|
// 返回 base64 处理的加密字符串
|
||||||
pub fn gencode(token: &Token, key: &str) -> Result<String> {
|
pub fn gencode(token: &Token, key: &str) -> Result<String> {
|
||||||
let data = serde_json::to_vec(token)?;
|
let data = serde_json::to_vec(token)?;
|
||||||
let public_key = RsaPublicKey::from_public_key_pem(key)?;
|
let public_key = RsaPublicKey::from_public_key_pem(key).map_err(Error::other)?;
|
||||||
let encrypted_data = public_key.encrypt(&mut rand::thread_rng(), Pkcs1v15Encrypt, &data)?;
|
let encrypted_data = public_key.encrypt(&mut OsRng, Pkcs1v15Encrypt, &data).map_err(Error::other)?;
|
||||||
Ok(base64_simd::URL_SAFE_NO_PAD.encode_to_string(&encrypted_data))
|
Ok(base64_simd::URL_SAFE_NO_PAD.encode_to_string(&encrypted_data))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,9 +43,11 @@ pub fn gencode(token: &Token, key: &str) -> Result<String> {
|
|||||||
// [key] 私钥字符串
|
// [key] 私钥字符串
|
||||||
// 返回 Token 对象
|
// 返回 Token 对象
|
||||||
pub fn parse(token: &str, key: &str) -> Result<Token> {
|
pub fn parse(token: &str, key: &str) -> Result<Token> {
|
||||||
let encrypted_data = base64_simd::URL_SAFE_NO_PAD.decode_to_vec(token.as_bytes())?;
|
let encrypted_data = base64_simd::URL_SAFE_NO_PAD
|
||||||
let private_key = RsaPrivateKey::from_pkcs8_pem(key)?;
|
.decode_to_vec(token.as_bytes())
|
||||||
let decrypted_data = private_key.decrypt(Pkcs1v15Encrypt, &encrypted_data)?;
|
.map_err(Error::other)?;
|
||||||
|
let private_key = RsaPrivateKey::from_pkcs8_pem(key).map_err(Error::other)?;
|
||||||
|
let decrypted_data = private_key.decrypt(Pkcs1v15Encrypt, &encrypted_data).map_err(Error::other)?;
|
||||||
let res: Token = serde_json::from_slice(&decrypted_data)?;
|
let res: Token = serde_json::from_slice(&decrypted_data)?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
@@ -49,19 +66,19 @@ pub fn parse_license(license: &str) -> Result<Token> {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
static TEST_PRIVATE_KEY:&str ="-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCj86SrJIuxSxR6\nBJ/dlJEUIj6NeBRnhLQlCDdovuz61+7kJXVcxaR66w4m8W7SLEUP+IlPtnn6vmiG\n7XMhGNHIr7r1JsEVVLhZmL3tKI66DEZl786ZhG81BWqUlmcooIPS8UEPZNqJXLuz\nVGhxNyVGbj/tV7QC2pSISnKaixc+nrhxvo7w56p5qrm9tik0PjTgfZsUePkoBsSN\npoRkAauS14MAzK6HGB75CzG3dZqXUNWSWVocoWtQbZUwFGXyzU01ammsHQDvc2xu\nK1RQpd1qYH5bOWZ0N0aPFwT0r59HztFXg9sbjsnuhO1A7OiUOkc6iGVuJ0wm/9nA\nwZIBqzgjAgMBAAECggEAPMpeSEbotPhNw2BrllE76ec4omPfzPJbiU+em+wPGoNu\nRJHPDnMKJbl6Kd5jZPKdOOrCnxfd6qcnQsBQa/kz7+GYxMV12l7ra+1Cnujm4v0i\nLTHZvPpp8ZLsjeOmpF3AAzsJEJgon74OqtOlVjVIUPEYKvzV9ijt4gsYq0zfdYv0\nhrTMzyrGM4/UvKLsFIBROAfCeWfA7sXLGH8JhrRAyDrtCPzGtyyAmzoHKHtHafcB\nuyPFw/IP8otAgpDk5iiQPNkH0WwzAQIm12oHuNUa66NwUK4WEjXTnDg8KeWLHHNv\nIfN8vdbZchMUpMIvvkr7is315d8f2cHCB5gEO+GWAQKBgQDR/0xNll+FYaiUKCPZ\nvkOCAd3l5mRhsqnjPQ/6Ul1lAyYWpoJSFMrGGn/WKTa/FVFJRTGbBjwP+Mx10bfb\ngUg2GILDTISUh54fp4zngvTi9w4MWGKXrb7I1jPkM3vbJfC/v2fraQ/r7qHPpO2L\nf6ZbGxasIlSvr37KeGoelwcAQQKBgQDH3hmOTS2Hl6D4EXdq5meHKrfeoicGN7m8\noQK7u8iwn1R9zK5nh6IXxBhKYNXNwdCQtBZVRvFjjZ56SZJb7lKqa1BcTsgJfZCy\nnI3Uu4UykrECAH8AVCVqBXUDJmeA2yE+gDAtYEjvhSDHpUfWxoGHr0B/Oqk2Lxc/\npRy1qV5fYwKBgBWSL/hYVf+RhIuTg/s9/BlCr9SJ0g3nGGRrRVTlWQqjRCpXeFOO\nJzYqSq9pFGKUggEQxoOyJEFPwVDo9gXqRcyov+Xn2kaXl7qQr3yoixc1YZALFDWY\nd1ySBEqQr0xXnV9U/gvEgwotPRnjSzNlLWV2ZuHPtPtG/7M0o1H5GZMBAoGAKr3N\nW0gX53o+my4pCnxRQW+aOIsWq1a5aqRIEFudFGBOUkS2Oz+fI1P1GdrRfhnnfzpz\n2DK+plp/vIkFOpGhrf4bBlJ2psjqa7fdANRFLMaAAfyXLDvScHTQTCcnVUAHQPVq\n2BlSH56pnugyj7SNuLV6pnql+wdhAmRN2m9o1h8CgYAbX2juSr4ioXwnYjOUdrIY\n4+ERvHcXdjoJmmPcAm4y5NbSqLXyU0FQmplNMt2A5LlniWVJ9KNdjAQUt60FZw/+\nr76LdxXaHNZghyx0BOs7mtq5unSQXamZ8KixasfhE9uz3ij1jXjG6hafWkS8/68I\nuWbaZqgvy7a9oPHYlKH7Jg==\n-----END PRIVATE KEY-----\n";
|
static TEST_PRIVATE_KEY: &str = "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCj86SrJIuxSxR6\nBJ/dlJEUIj6NeBRnhLQlCDdovuz61+7kJXVcxaR66w4m8W7SLEUP+IlPtnn6vmiG\n7XMhGNHIr7r1JsEVVLhZmL3tKI66DEZl786ZhG81BWqUlmcooIPS8UEPZNqJXLuz\nVGhxNyVGbj/tV7QC2pSISnKaixc+nrhxvo7w56p5qrm9tik0PjTgfZsUePkoBsSN\npoRkAauS14MAzK6HGB75CzG3dZqXUNWSWVocoWtQbZUwFGXyzU01ammsHQDvc2xu\nK1RQpd1qYH5bOWZ0N0aPFwT0r59HztFXg9sbjsnuhO1A7OiUOkc6iGVuJ0wm/9nA\nwZIBqzgjAgMBAAECggEAPMpeSEbotPhNw2BrllE76ec4omPfzPJbiU+em+wPGoNu\nRJHPDnMKJbl6Kd5jZPKdOOrCnxfd6qcnQsBQa/kz7+GYxMV12l7ra+1Cnujm4v0i\nLTHZvPpp8ZLsjeOmpF3AAzsJEJgon74OqtOlVjVIUPEYKvzV9ijt4gsYq0zfdYv0\nhrTMzyrGM4/UvKLsFIBROAfCeWfA7sXLGH8JhrRAyDrtCPzGtyyAmzoHKHtHafcB\nuyPFw/IP8otAgpDk5iiQPNkH0WwzAQIm12oHuNUa66NwUK4WEjXTnDg8KeWLHHNv\nIfN8vdbZchMUpMIvvkr7is315d8f2cHCB5gEO+GWAQKBgQDR/0xNll+FYaiUKCPZ\nvkOCAd3l5mRhsqnjPQ/6Ul1lAyYWpoJSFMrGGn/WKTa/FVFJRTGbBjwP+Mx10bfb\ngUg2GILDTISUh54fp4zngvTi9w4MWGKXrb7I1jPkM3vbJfC/v2fraQ/r7qHPpO2L\nf6ZbGxasIlSvr37KeGoelwcAQQKBgQDH3hmOTS2Hl6D4EXdq5meHKrfeoicGN7m8\noQK7u8iwn1R9zK5nh6IXxBhKYNXNwdCQtBZVRvFjjZ56SZJb7lKqa1BcTsgJfZCy\nnI3Uu4UykrECAH8AVCVqBXUDJmeA2yE+gDAtYEjvhSDHpUfWxoGHr0B/Oqk2Lxc/\npRy1qV5fYwKBgBWSL/hYVf+RhIuTg/s9/BlCr9SJ0g3nGGRrRVTlWQqjRCpXeFOO\nJzYqSq9pFGKUggEQxoOyJEFPwVDo9gXqRcyov+Xn2kaXl7qQr3yoixc1YZALFDWY\nd1ySBEqQr0xXnV9U/gvEgwotPRnjSzNlLWV2ZuHPtPtG/7M0o1H5GZMBAoGAKr3N\nW0gX53o+my4pCnxRQW+aOIsWq1a5aqRIEFudFGBOUkS2Oz+fI1P1GdrRfhnnfzpz\n2DK+plp/vIkFOpGhrf4bBlJ2psjqa7fdANRFLMaAAfyXLDvScHTQTCcnVUAHQPVq\n2BlSH56pnugyj7SNuLV6pnql+wdhAmRN2m9o1h8CgYAbX2juSr4ioXwnYjOUdrIY\n4+ERvHcXdjoJmmPcAm4y5NbSqLXyU0FQmplNMt2A5LlniWVJ9KNdjAQUt60FZw/+\nr76LdxXaHNZghyx0BOs7mtq5unSQXamZ8KixasfhE9uz3ij1jXjG6hafWkS8/68I\nuWbaZqgvy7a9oPHYlKH7Jg==\n-----END PRIVATE KEY-----\n";
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use rsa::{
|
use rsa::{
|
||||||
pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding},
|
|
||||||
RsaPrivateKey,
|
RsaPrivateKey,
|
||||||
|
pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding},
|
||||||
};
|
};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gencode_and_parse() {
|
fn test_gencode_and_parse() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = OsRng;
|
||||||
let bits = 2048;
|
let bits = 2048;
|
||||||
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("Failed to generate private key");
|
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("Failed to generate private key");
|
||||||
let public_key = RsaPublicKey::from(&private_key);
|
let public_key = RsaPublicKey::from(&private_key);
|
||||||
@@ -84,7 +101,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_invalid_token() {
|
fn test_parse_invalid_token() {
|
||||||
let private_key_pem = RsaPrivateKey::new(&mut rand::thread_rng(), 2048)
|
let private_key_pem = RsaPrivateKey::new(&mut OsRng, 2048)
|
||||||
.expect("Failed to generate private key")
|
.expect("Failed to generate private key")
|
||||||
.to_pkcs8_pem(LineEnding::LF)
|
.to_pkcs8_pem(LineEnding::LF)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
29
crates/common/Cargo.toml
Normal file
29
crates/common/Cargo.toml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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]
|
||||||
|
name = "rustfs-common"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lazy_static.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
tonic = { workspace = true }
|
||||||
37
crates/common/README.md
Normal file
37
crates/common/README.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[](https://rustfs.com)
|
||||||
|
|
||||||
|
# RustFS Common - Shared Components
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<strong>Shared components and common utilities module for RustFS distributed object storage</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/rustfs/rustfs/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/rustfs/rustfs/actions/workflows/ci.yml/badge.svg" /></a>
|
||||||
|
<a href="https://docs.rustfs.com/en/">📖 Documentation</a>
|
||||||
|
· <a href="https://github.com/rustfs/rustfs/issues">🐛 Bug Reports</a>
|
||||||
|
· <a href="https://github.com/rustfs/rustfs/discussions">💬 Discussions</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 Overview
|
||||||
|
|
||||||
|
**RustFS Common** provides shared components and common utilities for the [RustFS](https://rustfs.com) distributed object storage system. For the complete RustFS experience, please visit the [main RustFS repository](https://github.com/rustfs/rustfs).
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
- Shared data structures and type definitions
|
||||||
|
- Common error handling and result types
|
||||||
|
- Utility functions used across modules
|
||||||
|
- Configuration structures and validation
|
||||||
|
- Logging and tracing infrastructure
|
||||||
|
- Cross-platform compatibility helpers
|
||||||
|
|
||||||
|
## 📚 Documentation
|
||||||
|
|
||||||
|
For comprehensive documentation, examples, and usage guides, please visit the main [RustFS repository](https://github.com/rustfs/rustfs).
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
This project is licensed under the Apache License 2.0 - see the [LICENSE](../../LICENSE) file for details.
|
||||||
@@ -1,19 +1,33 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::last_minute::{self};
|
use crate::last_minute::{self};
|
||||||
pub struct ReplicationLatency {
|
pub struct ReplicationLatency {
|
||||||
// 单个和多部分 PUT 请求的延迟
|
// Delays for single and multipart PUT requests
|
||||||
upload_histogram: last_minute::LastMinuteHistogram,
|
upload_histogram: last_minute::LastMinuteHistogram,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReplicationLatency {
|
impl ReplicationLatency {
|
||||||
// 合并两个 ReplicationLatency
|
// Merge two ReplicationLatency
|
||||||
pub fn merge(&mut self, other: &mut ReplicationLatency) -> &ReplicationLatency {
|
pub fn merge(&mut self, other: &mut ReplicationLatency) -> &ReplicationLatency {
|
||||||
self.upload_histogram.merge(&other.upload_histogram);
|
self.upload_histogram.merge(&other.upload_histogram);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取上传延迟(按对象大小区间分类)
|
// Get upload delay (categorized by object size interval)
|
||||||
pub fn get_upload_latency(&mut self) -> HashMap<String, u64> {
|
pub fn get_upload_latency(&mut self) -> HashMap<String, u64> {
|
||||||
let mut ret = HashMap::new();
|
let mut ret = HashMap::new();
|
||||||
let avg = self.upload_histogram.get_avg_data();
|
let avg = self.upload_histogram.get_avg_data();
|
||||||
@@ -27,7 +41,7 @@ impl ReplicationLatency {
|
|||||||
self.upload_histogram.add(size, during);
|
self.upload_histogram.add(size, during);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模拟从 size tag 到字符串的转换
|
// Simulate the conversion from size tag to string
|
||||||
fn size_tag_to_string(&self, tag: usize) -> String {
|
fn size_tag_to_string(&self, tag: usize) -> String {
|
||||||
match tag {
|
match tag {
|
||||||
0 => String::from("Size < 1 KiB"),
|
0 => String::from("Size < 1 KiB"),
|
||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -71,7 +85,7 @@ impl std::fmt::Display for SizeCategory {
|
|||||||
SizeCategory::SizeGreaterThan1GiB => "SizeGreaterThan1GiB",
|
SizeCategory::SizeGreaterThan1GiB => "SizeGreaterThan1GiB",
|
||||||
SizeCategory::SizeLastElemMarker => "SizeLastElemMarker",
|
SizeCategory::SizeLastElemMarker => "SizeLastElemMarker",
|
||||||
};
|
};
|
||||||
write!(f, "{}", s)
|
write!(f, "{s}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -639,7 +653,7 @@ mod tests {
|
|||||||
assert_eq!(elem.n, cloned.n);
|
assert_eq!(elem.n, cloned.n);
|
||||||
|
|
||||||
// Test Debug trait
|
// Test Debug trait
|
||||||
let debug_str = format!("{:?}", elem);
|
let debug_str = format!("{elem:?}");
|
||||||
assert!(debug_str.contains("100"));
|
assert!(debug_str.contains("100"));
|
||||||
assert!(debug_str.contains("200"));
|
assert!(debug_str.contains("200"));
|
||||||
assert!(debug_str.contains("5"));
|
assert!(debug_str.contains("5"));
|
||||||
@@ -755,8 +769,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
test_latency.totals[expected_idx].n, 1,
|
test_latency.totals[expected_idx].n, 1,
|
||||||
"Failed for timestamp {} (expected index {})",
|
"Failed for timestamp {timestamp} (expected index {expected_idx})"
|
||||||
timestamp, expected_idx
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -796,7 +809,7 @@ mod tests {
|
|||||||
n: 789,
|
n: 789,
|
||||||
};
|
};
|
||||||
|
|
||||||
let debug_str = format!("{:?}", elem);
|
let debug_str = format!("{elem:?}");
|
||||||
assert!(debug_str.contains("123"));
|
assert!(debug_str.contains("123"));
|
||||||
assert!(debug_str.contains("456"));
|
assert!(debug_str.contains("456"));
|
||||||
assert!(debug_str.contains("789"));
|
assert!(debug_str.contains("789"));
|
||||||
41
crates/common/src/lib.rs
Normal file
41
crates/common/src/lib.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
pub mod bucket_stats;
|
||||||
|
// pub mod error;
|
||||||
|
pub mod globals;
|
||||||
|
pub mod last_minute;
|
||||||
|
|
||||||
|
// is ','
|
||||||
|
pub static DEFAULT_DELIMITER: u8 = 44;
|
||||||
|
|
||||||
|
/// Defers evaluation of a block of code until the end of the scope.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! defer {
|
||||||
|
($($body:tt)*) => {
|
||||||
|
let _guard = {
|
||||||
|
pub struct Guard<F: FnOnce()>(Option<F>);
|
||||||
|
|
||||||
|
impl<F: FnOnce()> Drop for Guard<F> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
(self.0).take().map(|f| f());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Guard(Some(|| {
|
||||||
|
let _ = { $($body)* };
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,3 +1,17 @@
|
|||||||
|
# Copyright 2024 RustFS Team
|
||||||
|
#
|
||||||
|
# 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]
|
[package]
|
||||||
name = "rustfs-config"
|
name = "rustfs-config"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
@@ -7,22 +21,17 @@ rust-version.workspace = true
|
|||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
config = { workspace = true }
|
const-str = { workspace = true, optional = true }
|
||||||
const-str = { workspace = true }
|
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["sync", "rt"] }
|
|
||||||
once_cell = { workspace = true }
|
|
||||||
thiserror = { workspace = true }
|
|
||||||
tracing = { workspace = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
tokio = { workspace = true, features = [
|
|
||||||
"sync",
|
|
||||||
"rt",
|
|
||||||
"macros",
|
|
||||||
"rt-multi-thread",
|
|
||||||
] }
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
constants = ["dep:const-str"]
|
||||||
|
notify = ["dep:const-str"]
|
||||||
|
observability = []
|
||||||
|
|
||||||
|
|||||||
37
crates/config/README.md
Normal file
37
crates/config/README.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[](https://rustfs.com)
|
||||||
|
|
||||||
|
# RustFS Config - Configuration Management
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<strong>Configuration management and validation module for RustFS distributed object storage</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/rustfs/rustfs/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/rustfs/rustfs/actions/workflows/ci.yml/badge.svg" /></a>
|
||||||
|
<a href="https://docs.rustfs.com/en/">📖 Documentation</a>
|
||||||
|
· <a href="https://github.com/rustfs/rustfs/issues">🐛 Bug Reports</a>
|
||||||
|
· <a href="https://github.com/rustfs/rustfs/discussions">💬 Discussions</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 Overview
|
||||||
|
|
||||||
|
**RustFS Config** provides configuration management and validation capabilities for the [RustFS](https://rustfs.com) distributed object storage system. For the complete RustFS experience, please visit the [main RustFS repository](https://github.com/rustfs/rustfs).
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
- Multi-format configuration support (TOML, YAML, JSON, ENV)
|
||||||
|
- Environment variable integration and override
|
||||||
|
- Configuration validation and type safety
|
||||||
|
- Hot-reload capabilities for dynamic updates
|
||||||
|
- Default value management and fallbacks
|
||||||
|
- Secure credential handling and encryption
|
||||||
|
|
||||||
|
## 📚 Documentation
|
||||||
|
|
||||||
|
For comprehensive documentation, examples, and usage guides, please visit the main [RustFS repository](https://github.com/rustfs/rustfs).
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
This project is licensed under the Apache License 2.0 - see the [LICENSE](../../LICENSE) file for details.
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
use crate::event::config::NotifierConfig;
|
|
||||||
use crate::ObservabilityConfig;
|
|
||||||
|
|
||||||
/// RustFs configuration
|
|
||||||
pub struct RustFsConfig {
|
|
||||||
pub observability: ObservabilityConfig,
|
|
||||||
pub event: NotifierConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RustFsConfig {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
observability: ObservabilityConfig::new(),
|
|
||||||
event: NotifierConfig::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for RustFsConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rustfs_config_new() {
|
|
||||||
let config = RustFsConfig::new();
|
|
||||||
|
|
||||||
// Verify that observability config is properly initialized
|
|
||||||
assert!(!config.observability.sinks.is_empty(), "Observability sinks should not be empty");
|
|
||||||
assert!(config.observability.logger.is_some(), "Logger config should be present");
|
|
||||||
|
|
||||||
// Verify that event config is properly initialized
|
|
||||||
assert!(!config.event.store_path.is_empty(), "Event store path should not be empty");
|
|
||||||
assert!(config.event.channel_capacity > 0, "Channel capacity should be positive");
|
|
||||||
assert!(!config.event.adapters.is_empty(), "Event adapters should not be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rustfs_config_default() {
|
|
||||||
let config = RustFsConfig::default();
|
|
||||||
|
|
||||||
// Default should be equivalent to new()
|
|
||||||
let new_config = RustFsConfig::new();
|
|
||||||
|
|
||||||
// Compare observability config
|
|
||||||
assert_eq!(config.observability.sinks.len(), new_config.observability.sinks.len());
|
|
||||||
assert_eq!(config.observability.logger.is_some(), new_config.observability.logger.is_some());
|
|
||||||
|
|
||||||
// Compare event config
|
|
||||||
assert_eq!(config.event.store_path, new_config.event.store_path);
|
|
||||||
assert_eq!(config.event.channel_capacity, new_config.event.channel_capacity);
|
|
||||||
assert_eq!(config.event.adapters.len(), new_config.event.adapters.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rustfs_config_components_independence() {
|
|
||||||
let mut config = RustFsConfig::new();
|
|
||||||
|
|
||||||
// Modify observability config
|
|
||||||
config.observability.sinks.clear();
|
|
||||||
|
|
||||||
// Event config should remain unchanged
|
|
||||||
assert!(!config.event.adapters.is_empty(), "Event adapters should remain unchanged");
|
|
||||||
assert!(config.event.channel_capacity > 0, "Channel capacity should remain unchanged");
|
|
||||||
|
|
||||||
// Create new config to verify independence
|
|
||||||
let new_config = RustFsConfig::new();
|
|
||||||
assert!(!new_config.observability.sinks.is_empty(), "New config should have default sinks");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rustfs_config_observability_integration() {
|
|
||||||
let config = RustFsConfig::new();
|
|
||||||
|
|
||||||
// Test observability config properties
|
|
||||||
assert!(config.observability.otel.endpoint.is_empty() || !config.observability.otel.endpoint.is_empty());
|
|
||||||
assert!(config.observability.otel.use_stdout.is_some());
|
|
||||||
assert!(config.observability.otel.sample_ratio.is_some());
|
|
||||||
assert!(config.observability.otel.meter_interval.is_some());
|
|
||||||
assert!(config.observability.otel.service_name.is_some());
|
|
||||||
assert!(config.observability.otel.service_version.is_some());
|
|
||||||
assert!(config.observability.otel.environment.is_some());
|
|
||||||
assert!(config.observability.otel.logger_level.is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rustfs_config_event_integration() {
|
|
||||||
let config = RustFsConfig::new();
|
|
||||||
|
|
||||||
// Test event config properties
|
|
||||||
assert!(!config.event.store_path.is_empty(), "Store path should not be empty");
|
|
||||||
assert!(
|
|
||||||
config.event.channel_capacity >= 1000,
|
|
||||||
"Channel capacity should be reasonable for production"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test that store path is a valid path format
|
|
||||||
let store_path = &config.event.store_path;
|
|
||||||
assert!(!store_path.contains('\0'), "Store path should not contain null characters");
|
|
||||||
|
|
||||||
// Test adapters configuration
|
|
||||||
for adapter in &config.event.adapters {
|
|
||||||
// Each adapter should have a valid configuration
|
|
||||||
match adapter {
|
|
||||||
crate::event::adapters::AdapterConfig::Webhook(_) => {
|
|
||||||
// Webhook adapter should be properly configured
|
|
||||||
}
|
|
||||||
crate::event::adapters::AdapterConfig::Kafka(_) => {
|
|
||||||
// Kafka adapter should be properly configured
|
|
||||||
}
|
|
||||||
crate::event::adapters::AdapterConfig::Mqtt(_) => {
|
|
||||||
// MQTT adapter should be properly configured
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rustfs_config_memory_usage() {
|
|
||||||
// Test that config doesn't use excessive memory
|
|
||||||
let config = RustFsConfig::new();
|
|
||||||
|
|
||||||
// Basic memory usage checks
|
|
||||||
assert!(std::mem::size_of_val(&config) < 10000, "Config should not use excessive memory");
|
|
||||||
|
|
||||||
// Test that strings are not excessively long
|
|
||||||
assert!(config.event.store_path.len() < 1000, "Store path should not be excessively long");
|
|
||||||
|
|
||||||
// Test that collections are reasonably sized
|
|
||||||
assert!(config.observability.sinks.len() < 100, "Sinks collection should be reasonably sized");
|
|
||||||
assert!(config.event.adapters.len() < 100, "Adapters collection should be reasonably sized");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rustfs_config_serialization_compatibility() {
|
|
||||||
let config = RustFsConfig::new();
|
|
||||||
|
|
||||||
// Test that observability config can be serialized (it has Serialize trait)
|
|
||||||
let observability_json = serde_json::to_string(&config.observability);
|
|
||||||
assert!(observability_json.is_ok(), "Observability config should be serializable");
|
|
||||||
|
|
||||||
// Test that event config can be serialized (it has Serialize trait)
|
|
||||||
let event_json = serde_json::to_string(&config.event);
|
|
||||||
assert!(event_json.is_ok(), "Event config should be serializable");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rustfs_config_debug_format() {
|
|
||||||
let config = RustFsConfig::new();
|
|
||||||
|
|
||||||
// Test that observability config has Debug trait
|
|
||||||
let observability_debug = format!("{:?}", config.observability);
|
|
||||||
assert!(!observability_debug.is_empty(), "Observability config should have debug output");
|
|
||||||
assert!(
|
|
||||||
observability_debug.contains("ObservabilityConfig"),
|
|
||||||
"Debug output should contain type name"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test that event config has Debug trait
|
|
||||||
let event_debug = format!("{:?}", config.event);
|
|
||||||
assert!(!event_debug.is_empty(), "Event config should have debug output");
|
|
||||||
assert!(event_debug.contains("NotifierConfig"), "Debug output should contain type name");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rustfs_config_clone_behavior() {
|
|
||||||
let config = RustFsConfig::new();
|
|
||||||
|
|
||||||
// Test that observability config can be cloned
|
|
||||||
let observability_clone = config.observability.clone();
|
|
||||||
assert_eq!(observability_clone.sinks.len(), config.observability.sinks.len());
|
|
||||||
|
|
||||||
// Test that event config can be cloned
|
|
||||||
let event_clone = config.event.clone();
|
|
||||||
assert_eq!(event_clone.store_path, config.event.store_path);
|
|
||||||
assert_eq!(event_clone.channel_capacity, config.event.channel_capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rustfs_config_environment_independence() {
|
|
||||||
// Test that config creation doesn't depend on specific environment variables
|
|
||||||
// This test ensures the config can be created in any environment
|
|
||||||
|
|
||||||
let config1 = RustFsConfig::new();
|
|
||||||
let config2 = RustFsConfig::new();
|
|
||||||
|
|
||||||
// Both configs should have the same structure
|
|
||||||
assert_eq!(config1.observability.sinks.len(), config2.observability.sinks.len());
|
|
||||||
assert_eq!(config1.event.adapters.len(), config2.event.adapters.len());
|
|
||||||
|
|
||||||
// Store paths should be consistent
|
|
||||||
assert_eq!(config1.event.store_path, config2.event.store_path);
|
|
||||||
assert_eq!(config1.event.channel_capacity, config2.event.channel_capacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2024 RustFS Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
use const_str::concat;
|
use const_str::concat;
|
||||||
|
|
||||||
/// Application name
|
/// Application name
|
||||||
@@ -101,7 +115,7 @@ pub const DEFAULT_LOG_FILENAME: &str = "rustfs.log";
|
|||||||
/// It is used to store the logs of the application.
|
/// It is used to store the logs of the application.
|
||||||
/// Default value: logs
|
/// Default value: logs
|
||||||
/// Environment variable: RUSTFS_OBSERVABILITY_LOG_DIRECTORY
|
/// Environment variable: RUSTFS_OBSERVABILITY_LOG_DIRECTORY
|
||||||
pub const DEFAULT_LOG_DIR: &str = "logs";
|
pub const DEFAULT_LOG_DIR: &str = "deploy/logs";
|
||||||
|
|
||||||
/// Default log rotation size mb for rustfs
|
/// Default log rotation size mb for rustfs
|
||||||
/// This is the default log rotation size for rustfs.
|
/// This is the default log rotation size for rustfs.
|
||||||
@@ -200,7 +214,7 @@ mod tests {
|
|||||||
// Test port related constants
|
// Test port related constants
|
||||||
assert_eq!(DEFAULT_PORT, 9000);
|
assert_eq!(DEFAULT_PORT, 9000);
|
||||||
|
|
||||||
assert_eq!(DEFAULT_CONSOLE_PORT, 9002);
|
assert_eq!(DEFAULT_CONSOLE_PORT, 9001);
|
||||||
|
|
||||||
assert_ne!(DEFAULT_PORT, DEFAULT_CONSOLE_PORT, "Main port and console port should be different");
|
assert_ne!(DEFAULT_PORT, DEFAULT_CONSOLE_PORT, "Main port and console port should be different");
|
||||||
}
|
}
|
||||||
@@ -215,7 +229,7 @@ mod tests {
|
|||||||
"Address should contain the default port"
|
"Address should contain the default port"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(DEFAULT_CONSOLE_ADDRESS, ":9002");
|
assert_eq!(DEFAULT_CONSOLE_ADDRESS, ":9001");
|
||||||
assert!(DEFAULT_CONSOLE_ADDRESS.starts_with(':'), "Console address should start with colon");
|
assert!(DEFAULT_CONSOLE_ADDRESS.starts_with(':'), "Console address should start with colon");
|
||||||
assert!(
|
assert!(
|
||||||
DEFAULT_CONSOLE_ADDRESS.contains(&DEFAULT_CONSOLE_PORT.to_string()),
|
DEFAULT_CONSOLE_ADDRESS.contains(&DEFAULT_CONSOLE_PORT.to_string()),
|
||||||
@@ -231,10 +245,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_const_str_concat_functionality() {
|
fn test_const_str_concat_functionality() {
|
||||||
// Test const_str::concat macro functionality
|
// Test const_str::concat macro functionality
|
||||||
let expected_address = format!(":{}", DEFAULT_PORT);
|
let expected_address = format!(":{DEFAULT_PORT}");
|
||||||
assert_eq!(DEFAULT_ADDRESS, expected_address);
|
assert_eq!(DEFAULT_ADDRESS, expected_address);
|
||||||
|
|
||||||
let expected_console_address = format!(":{}", DEFAULT_CONSOLE_PORT);
|
let expected_console_address = format!(":{DEFAULT_CONSOLE_PORT}");
|
||||||
assert_eq!(DEFAULT_CONSOLE_ADDRESS, expected_console_address);
|
assert_eq!(DEFAULT_CONSOLE_ADDRESS, expected_console_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,9 +270,9 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for constant in &string_constants {
|
for constant in &string_constants {
|
||||||
assert!(!constant.is_empty(), "String constant should not be empty: {}", constant);
|
assert!(!constant.is_empty(), "String constant should not be empty: {constant}");
|
||||||
assert!(!constant.starts_with(' '), "String constant should not start with space: {}", constant);
|
assert!(!constant.starts_with(' '), "String constant should not start with space: {constant}");
|
||||||
assert!(!constant.ends_with(' '), "String constant should not end with space: {}", constant);
|
assert!(!constant.ends_with(' '), "String constant should not end with space: {constant}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,8 +298,8 @@ mod tests {
|
|||||||
|
|
||||||
// These are default values, should be changed in production environments
|
// These are default values, should be changed in production environments
|
||||||
println!("Security Warning: Default credentials detected!");
|
println!("Security Warning: Default credentials detected!");
|
||||||
println!("Access Key: {}", DEFAULT_ACCESS_KEY);
|
println!("Access Key: {DEFAULT_ACCESS_KEY}");
|
||||||
println!("Secret Key: {}", DEFAULT_SECRET_KEY);
|
println!("Secret Key: {DEFAULT_SECRET_KEY}");
|
||||||
println!("These should be changed in production environments!");
|
println!("These should be changed in production environments!");
|
||||||
|
|
||||||
// Verify that key lengths meet minimum security requirements
|
// Verify that key lengths meet minimum security requirements
|
||||||
@@ -312,11 +326,11 @@ mod tests {
|
|||||||
let ports = [DEFAULT_PORT, DEFAULT_CONSOLE_PORT];
|
let ports = [DEFAULT_PORT, DEFAULT_CONSOLE_PORT];
|
||||||
let mut unique_ports = std::collections::HashSet::new();
|
let mut unique_ports = std::collections::HashSet::new();
|
||||||
for port in &ports {
|
for port in &ports {
|
||||||
assert!(unique_ports.insert(port), "Port {} is duplicated", port);
|
assert!(unique_ports.insert(port), "Port {port} is duplicated");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address format consistency
|
// Address format consistency
|
||||||
assert_eq!(DEFAULT_ADDRESS, format!(":{}", DEFAULT_PORT));
|
assert_eq!(DEFAULT_ADDRESS, format!(":{DEFAULT_PORT}"));
|
||||||
assert_eq!(DEFAULT_CONSOLE_ADDRESS, format!(":{}", DEFAULT_CONSOLE_PORT));
|
assert_eq!(DEFAULT_CONSOLE_ADDRESS, format!(":{DEFAULT_CONSOLE_PORT}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user