mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-01 20:26:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73961e24d8 | ||
|
|
00c1dfec4f | ||
|
|
ce1e8e58ae | ||
|
|
2c0fdf3693 | ||
|
|
94635e9b5c | ||
|
|
4bd6bea429 | ||
|
|
5d081d5c3c | ||
|
|
1aae7a3f1b | ||
|
|
ea8a7a5584 | ||
|
|
192983b464 | ||
|
|
b2a96cee21 | ||
|
|
03aec9ce7e | ||
|
|
d71a1fd077 | ||
|
|
1a5de36257 | ||
|
|
4e97b6146a | ||
|
|
b2a6111090 | ||
|
|
ddec170bc9 | ||
|
|
306cf70c4a | ||
|
|
70e5a1b632 | ||
|
|
ffe028f8d0 | ||
|
|
37ab8909b0 | ||
|
|
b557faf175 | ||
|
|
eb49bd5638 | ||
|
|
15c7d6e7bc | ||
|
|
94f824e1ce | ||
|
|
7315d1d039 | ||
|
|
d4343ab7da | ||
|
|
ae56b2c00f | ||
|
|
6d2fda27d2 | ||
|
|
3bce568c32 | ||
|
|
e236978472 | ||
|
|
67476d830a | ||
|
|
42cc8d8aa3 | ||
|
|
b1dac20c70 | ||
|
|
6610ec739d | ||
|
|
d7cadd9e5b | ||
|
|
526e1e0b4f | ||
|
|
c18960a1d5 | ||
|
|
a178e45713 | ||
|
|
09c7564219 | ||
|
|
2aef5b8b32 | ||
|
|
8212dd58e0 | ||
|
|
14a917c262 | ||
|
|
184e982898 | ||
|
|
70ad3ccf4f | ||
|
|
8fd7b24b80 | ||
|
|
8b2552f944 | ||
|
|
09befe871f | ||
|
|
f3d4576790 | ||
|
|
57d6f2bc6b | ||
|
|
893a0eea57 | ||
|
|
520f9600b2 | ||
|
|
6ccc1095a9 | ||
|
|
32da0c330c | ||
|
|
7f049e32fe | ||
|
|
3f0a8543a8 | ||
|
|
350d80d907 | ||
|
|
2988e69b1e | ||
|
|
ad01c63b84 | ||
|
|
4aa39ef33f | ||
|
|
3885374edf | ||
|
|
6994e88192 | ||
|
|
cb1e7aa27d | ||
|
|
7c4a556940 | ||
|
|
1d6a9e66b6 | ||
|
|
d1fd403582 | ||
|
|
c57e0a98ee | ||
|
|
a4b8903210 | ||
|
|
a7df63667d | ||
|
|
bdc4d67de8 | ||
|
|
3aa4dc6ad5 | ||
|
|
4792e07228 | ||
|
|
f3fdfb07b8 | ||
|
|
8e9ad7db5a | ||
|
|
8c345bdc7f | ||
|
|
bc96e73b8d | ||
|
|
a23629b2fe | ||
|
|
3d5fc72d55 | ||
|
|
88a669dd19 | ||
|
|
f76c318a1f | ||
|
|
2364fab927 | ||
|
|
0a137e675d | ||
|
|
94ad557d97 | ||
|
|
e27e52c05b | ||
|
|
94c702402e | ||
|
|
82ee31965d | ||
|
|
e6ebafc094 | ||
|
|
60ae6082c2 | ||
|
|
868b4f8944 | ||
|
|
a71eff0694 | ||
|
|
36c5a59ed8 | ||
|
|
1e35b4929f | ||
|
|
1093e8e6d7 | ||
|
|
cdb31c5b61 | ||
|
|
8e98d7326b | ||
|
|
97c6e0efb1 | ||
|
|
9438738693 | ||
|
|
a69f0b62c2 | ||
|
|
9ae66f55fc | ||
|
|
4524ceae6b | ||
|
|
39b1e1b630 | ||
|
|
61af76c3bb | ||
|
|
89eb87c1d1 | ||
|
|
846c56e005 | ||
|
|
3551ca2fcf | ||
|
|
b597baf488 | ||
|
|
460d56d283 | ||
|
|
fd1dc1ab25 | ||
|
|
b4be56bb3b | ||
|
|
96800ffed5 | ||
|
|
7034fafb81 | ||
|
|
47eb9485b8 | ||
|
|
40ed4215f0 | ||
|
|
1384529eb7 | ||
|
|
94ea10e504 | ||
|
|
cae53cb238 | ||
|
|
4d881cdb61 | ||
|
|
9464f3b18c | ||
|
|
7a02fab289 | ||
|
|
6bc01e2f31 | ||
|
|
8ebdb0cb45 | ||
|
|
349550f41d | ||
|
|
89f633f3a9 | ||
|
|
9a21698caa | ||
|
|
4aa1fb7331 | ||
|
|
0af5c88a6e | ||
|
|
1b1c8e4af5 | ||
|
|
2c0e2e11df | ||
|
|
846858fb43 | ||
|
|
42fea7e7d6 | ||
|
|
db24ea8ad8 | ||
|
|
93f257c3c1 | ||
|
|
9b40f7f3af | ||
|
|
1f03fcccb1 | ||
|
|
dd13764a5c | ||
|
|
76b1c5ce67 | ||
|
|
72db181d68 | ||
|
|
44d810d163 | ||
|
|
180aa88a92 | ||
|
|
a4a0d56fc4 | ||
|
|
e58d48ed38 | ||
|
|
2232cfb5b7 | ||
|
|
2dfbe210e0 | ||
|
|
a87f57e47c | ||
|
|
931cb9e581 | ||
|
|
d2fe0fae33 | ||
|
|
c6ec5269f4 | ||
|
|
a9b4f0df04 | ||
|
|
261ba0208e | ||
|
|
71e837d6f1 | ||
|
|
50e93c3629 | ||
|
|
bc74b57a0b | ||
|
|
dffb59f350 | ||
|
|
295b1fcccc | ||
|
|
1cfae9f5c9 | ||
|
|
7a5128e9ee | ||
|
|
5593b0579c | ||
|
|
fa070474d5 | ||
|
|
9f4ecd50ac | ||
|
|
ceb058ddd9 | ||
|
|
7f26676f57 | ||
|
|
b1493520ee | ||
|
|
dff63e9024 | ||
|
|
4dba102137 | ||
|
|
bbc95dac47 | ||
|
|
aa7703c6dd | ||
|
|
f34205e110 | ||
|
|
104ea7b029 | ||
|
|
d6ee4bf65e | ||
|
|
41d396edc4 | ||
|
|
a2fdb3e277 | ||
|
|
35bc67f030 | ||
|
|
f5246b748d | ||
|
|
11a9f993a8 | ||
|
|
7ff248d5cd | ||
|
|
85d80fd36d | ||
|
|
abe3cc6df2 | ||
|
|
869d23b099 | ||
|
|
86ed27f602 | ||
|
|
2347c21cdd | ||
|
|
2454020a92 | ||
|
|
4f87ee7755 | ||
|
|
c20e1edd99 | ||
|
|
ec53eec94f | ||
|
|
3ab2c0e5c0 | ||
|
|
9d44b89c21 | ||
|
|
368d8e1a81 | ||
|
|
0a6703c7f7 | ||
|
|
14a2cc83bf | ||
|
|
fa25d5c43a | ||
|
|
ff2ec58897 | ||
|
|
83ee4c5fe2 | ||
|
|
72cbda3481 | ||
|
|
d30eafd0f3 | ||
|
|
9f9e0deafb | ||
|
|
357aa818fe | ||
|
|
3d229fe45c | ||
|
|
79b8e6a8c3 | ||
|
|
c1aeded2dd | ||
|
|
49ecb8d11c | ||
|
|
d02f13c2d1 | ||
|
|
7c85cb332d | ||
|
|
f15a737a0a | ||
|
|
bc888226fc | ||
|
|
8b3d76b24d | ||
|
|
bb4beebce3 | ||
|
|
ae5aa2bf04 | ||
|
|
10dc72ca54 | ||
|
|
0e796a5582 | ||
|
|
68f11b9687 | ||
|
|
1ae7f509f5 | ||
|
|
89d32a0670 | ||
|
|
7dd93a88ac | ||
|
|
17c6e8e39f | ||
|
|
b7cdde14ae | ||
|
|
3176bf126a | ||
|
|
9b0d02ed39 | ||
|
|
e752efc624 | ||
|
|
5b352ec1d2 | ||
|
|
6e4c94e2e1 | ||
|
|
4aae87f405 | ||
|
|
64db9db3de | ||
|
|
bb02803fca | ||
|
|
4062daf416 | ||
|
|
e28a8f802f | ||
|
|
19e5179159 | ||
|
|
e965c92c93 | ||
|
|
b58187df18 | ||
|
|
ca1bdb4661 | ||
|
|
566ec62aec | ||
|
|
622297f1a7 | ||
|
|
1a1e5778c3 | ||
|
|
14dd971890 | ||
|
|
c514710b7b | ||
|
|
d310711de0 | ||
|
|
9eda9a6e82 | ||
|
|
77a7d7253f | ||
|
|
e8d7bb42e2 | ||
|
|
6a2bcd03aa | ||
|
|
958f880b70 | ||
|
|
a8eefda452 | ||
|
|
105578a2f2 | ||
|
|
049f8f2e2a | ||
|
|
b7e34016d4 | ||
|
|
4ecba915f3 | ||
|
|
daca2d22a5 | ||
|
|
4b5c0e3fa9 | ||
|
|
6d55716fc3 | ||
|
|
bbbbbd70a4 | ||
|
|
743ad690b6 | ||
|
|
2305508b65 | ||
|
|
800cbc004c | ||
|
|
7e92517571 | ||
|
|
7fdd29c234 | ||
|
|
b0a7fd67cf | ||
|
|
7f1383a41e | ||
|
|
3817e05dd0 | ||
|
|
3cedb21bb7 | ||
|
|
dfe5bfbe2b | ||
|
|
aa7eb31bea | ||
|
|
2fc524ed30 | ||
|
|
02935ff145 | ||
|
|
1d9e30d8c0 | ||
|
|
67d870dfc2 | ||
|
|
4f5b018a02 | ||
|
|
7f4e33ec15 | ||
|
|
691ed50957 | ||
|
|
f08d67eeb7 | ||
|
|
a180394f75 | ||
|
|
b6a2b43efe | ||
|
|
c29f79a593 | ||
|
|
5738049d5a | ||
|
|
7a002c03fd | ||
|
|
123ab2f9c9 | ||
|
|
c0ac0dd5f2 | ||
|
|
a8b15c676c | ||
|
|
8be731a56d | ||
|
|
6f5f39dcbb | ||
|
|
531907acab | ||
|
|
fcf7f6c022 | ||
|
|
549e341c6f | ||
|
|
ce8ef60c59 | ||
|
|
6317f6a8a9 | ||
|
|
8732cc24c8 | ||
|
|
683581c412 | ||
|
|
7701a3008c | ||
|
|
6a286a5ea9 | ||
|
|
4db7754cd6 | ||
|
|
996231c01d | ||
|
|
50eaf59912 | ||
|
|
e0e8cbc501 | ||
|
|
47871f8389 | ||
|
|
5d59f2b191 | ||
|
|
1ff39c0cf9 | ||
|
|
a3c18ec82c | ||
|
|
993283bb1d | ||
|
|
5b90a39954 | ||
|
|
9231971c88 | ||
|
|
ed07e76f0e | ||
|
|
51bd015179 | ||
|
|
66583e7827 | ||
|
|
b208996153 | ||
|
|
80ddd4c7c8 | ||
|
|
9b118c6f76 | ||
|
|
fed9a96501 | ||
|
|
2fdf766fe5 | ||
|
|
ea0b41d91c | ||
|
|
1d47574531 | ||
|
|
ec6320b037 | ||
|
|
644a613986 | ||
|
|
ca6becfeec | ||
|
|
c464e2e0d5 | ||
|
|
9f0957b4d0 | ||
|
|
907f9b51cd | ||
|
|
7d981a1c0e | ||
|
|
71307ce2e6 | ||
|
|
6701ac3a61 | ||
|
|
f65c055c44 | ||
|
|
391170c3f4 | ||
|
|
bc603e534f | ||
|
|
cfb5440b04 | ||
|
|
d525a57bf3 | ||
|
|
3af8ac8f1e | ||
|
|
84b7b83517 | ||
|
|
830a97475f | ||
|
|
93615b2a49 | ||
|
|
dd2b9d93cc | ||
|
|
5c9a3bb8cf | ||
|
|
21636c98a5 | ||
|
|
02076f01a6 | ||
|
|
0edbc87857 | ||
|
|
d51a724101 | ||
|
|
db027ed9c0 | ||
|
|
b67ebe67a6 | ||
|
|
beb406bbbb | ||
|
|
411c0df3fe | ||
|
|
1cea6c73d3 | ||
|
|
25be96832a | ||
|
|
06f23aa675 | ||
|
|
0701feeb17 | ||
|
|
171c27ddf1 | ||
|
|
2f0cdcdceb | ||
|
|
423ce57cde | ||
|
|
bf745bdccb | ||
|
|
a0df993cef | ||
|
|
52ce8cc4f2 | ||
|
|
94883e2fad | ||
|
|
f3ab6769e9 | ||
|
|
31571dd96e | ||
|
|
b25d03340b | ||
|
|
390a724c0c | ||
|
|
921535001a | ||
|
|
68cf6a9229 | ||
|
|
db7766323e | ||
|
|
302738ee74 |
@@ -3,8 +3,8 @@ updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "weekly"
|
||||
- package-ecosystem: gomod
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
interval: weekly
|
||||
|
||||
@@ -4,9 +4,14 @@ on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
cleanup:
|
||||
permissions:
|
||||
contents: write # for mknejp/delete-release-assets to delete release assets
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -21,6 +26,8 @@ jobs:
|
||||
weed-*
|
||||
|
||||
build_dev_linux_windows:
|
||||
permissions:
|
||||
contents: write # for wangyoucao577/go-release-action to upload release assets
|
||||
needs: cleanup
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
@@ -68,12 +75,15 @@ jobs:
|
||||
asset_name: "weed-${{ env.BUILD_TIME }}-${{ matrix.goos }}-${{ matrix.goarch }}"
|
||||
|
||||
build_dev_darwin:
|
||||
permissions:
|
||||
contents: write # for wangyoucao577/go-release-action to upload release assets
|
||||
needs: build_dev_linux_windows
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
goos: [darwin]
|
||||
goarch: [amd64, arm64]
|
||||
# goarch: [amd64, arm64]
|
||||
goarch: [amd64]
|
||||
|
||||
steps:
|
||||
|
||||
|
||||
@@ -11,9 +11,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
build-release-binaries_windows:
|
||||
permissions:
|
||||
contents: write # for wangyoucao577/go-release-action to upload release assets
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -11,9 +11,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
build-release-binaries_linux:
|
||||
permissions:
|
||||
contents: write # for wangyoucao577/go-release-action to upload release assets
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -11,14 +11,20 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
build-release-binaries_darwin:
|
||||
permissions:
|
||||
contents: write # for wangyoucao577/go-release-action to upload release assets
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
goos: [darwin]
|
||||
goarch: [amd64, arm64]
|
||||
# goarch: [amd64, arm64]
|
||||
goarch: [amd64]
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
|
||||
@@ -11,9 +11,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
build-release-binaries_freebsd:
|
||||
permissions:
|
||||
contents: write # for wangyoucao577/go-release-action to upload release assets
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
-
|
||||
name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@e5622373a38e60fb6d795a4421e56882f2d7a681 # v3
|
||||
uses: docker/metadata-action@f2a13332ac1ce8c0a71aeac48a150dbb1838ab67 # v3
|
||||
with:
|
||||
images: |
|
||||
chrislusf/seaweedfs
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
-
|
||||
name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@e5622373a38e60fb6d795a4421e56882f2d7a681 # v3
|
||||
uses: docker/metadata-action@f2a13332ac1ce8c0a71aeac48a150dbb1838ab67 # v3
|
||||
with:
|
||||
images: |
|
||||
chrislusf/seaweedfs
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
-
|
||||
name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@e5622373a38e60fb6d795a4421e56882f2d7a681 # v3
|
||||
uses: docker/metadata-action@f2a13332ac1ce8c0a71aeac48a150dbb1838ab67 # v3
|
||||
with:
|
||||
images: |
|
||||
chrislusf/seaweedfs
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
-
|
||||
name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@e5622373a38e60fb6d795a4421e56882f2d7a681 # v3
|
||||
uses: docker/metadata-action@f2a13332ac1ce8c0a71aeac48a150dbb1838ab67 # v3
|
||||
with:
|
||||
images: |
|
||||
chrislusf/seaweedfs
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
-
|
||||
name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@e5622373a38e60fb6d795a4421e56882f2d7a681 # v3
|
||||
uses: docker/metadata-action@f2a13332ac1ce8c0a71aeac48a150dbb1838ab67 # v3
|
||||
with:
|
||||
images: |
|
||||
chrislusf/seaweedfs
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
name: 'Dependency Review'
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@dcd71f646680f2efd8db4afa5ad64fdcba30e748
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@3f943b86c9a289f4e632c632695e2e0898d9d67d
|
||||
@@ -32,13 +32,9 @@ jobs:
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
cd weed; go get -v -t -d ./...
|
||||
if [ -f Gopkg.toml ]; then
|
||||
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
dep ensure
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: cd weed; go build -v .
|
||||
run: cd weed; go build -tags "elastic gocdk sqlite hdfs" -v .
|
||||
|
||||
- name: Test
|
||||
run: cd weed; go test -v ./...
|
||||
run: cd weed; go test -tags "elastic gocdk sqlite hdfs" -v ./...
|
||||
|
||||
@@ -55,6 +55,8 @@ Temporary Items
|
||||
# Mongo Explorer plugin:
|
||||
# .idea/mongoSettings.xml
|
||||
|
||||
## vscode
|
||||
.vscode
|
||||
## File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
@@ -75,6 +77,8 @@ com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
|
||||
workspace/
|
||||
|
||||
test_data
|
||||
build
|
||||
target
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
BINARY = weed
|
||||
|
||||
SOURCE_DIR = .
|
||||
|
||||
all: install
|
||||
|
||||
install:
|
||||
cd weed; go install
|
||||
|
||||
full_install:
|
||||
cd weed; go install -tags "elastic gocdk sqlite hdfs"
|
||||
@@ -42,6 +42,7 @@ Your support will be really appreciated by me and other supporters!
|
||||
- [SeaweedFS on Slack](https://join.slack.com/t/seaweedfs/shared_invite/enQtMzI4MTMwMjU2MzA3LTEyYzZmZWYzOGQ3MDJlZWMzYmI0OTE4OTJiZjJjODBmMzUxNmYwODg0YjY3MTNlMjBmZDQ1NzQ5NDJhZWI2ZmY)
|
||||
- [SeaweedFS on Twitter](https://twitter.com/SeaweedFS)
|
||||
- [SeaweedFS on Telegram](https://t.me/Seaweedfs)
|
||||
- [SeaweedFS on Reddit](https://www.reddit.com/r/SeaweedFS/)
|
||||
- [SeaweedFS Mailing List](https://groups.google.com/d/forum/seaweedfs)
|
||||
- [Wiki Documentation](https://github.com/chrislusf/seaweedfs/wiki)
|
||||
- [SeaweedFS White Paper](https://github.com/chrislusf/seaweedfs/wiki/SeaweedFS_Architecture.pdf)
|
||||
@@ -54,6 +55,7 @@ Table of Contents
|
||||
* [Quick Start](#quick-start)
|
||||
* [Quick Start for S3 API on Docker](#quick-start-for-s3-api-on-docker)
|
||||
* [Quick Start with Single Binary](#quick-start-with-single-binary)
|
||||
* [Quick Start SeaweedFS S3 on AWS](#quick-start-seaweedfs-s3-on-aws)
|
||||
* [Introduction](#introduction)
|
||||
* [Features](#features)
|
||||
* [Additional Features](#additional-features)
|
||||
@@ -82,6 +84,9 @@ Table of Contents
|
||||
|
||||
Also, to increase capacity, just add more volume servers by running `weed volume -dir="/some/data/dir2" -mserver="<master_host>:9333" -port=8081` locally, or on a different machine, or on thousands of machines. That is it!
|
||||
|
||||
## Quick Start SeaweedFS S3 on AWS ##
|
||||
* Setup fast production-ready [SeaweedFS S3 on AWS with cloudformation](https://aws.amazon.com/marketplace/pp/prodview-nzelz5gprlrjc)
|
||||
|
||||
## Introduction ##
|
||||
|
||||
SeaweedFS is a simple and highly scalable distributed file system. There are two objectives:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.17-alpine as builder
|
||||
FROM golang:1.18-alpine as builder
|
||||
RUN apk add git g++ fuse
|
||||
RUN mkdir -p /go/src/github.com/chrislusf/
|
||||
RUN git clone https://github.com/chrislusf/seaweedfs /go/src/github.com/chrislusf/seaweedfs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.17-alpine as builder
|
||||
FROM golang:1.18-alpine as builder
|
||||
RUN apk add git g++ fuse
|
||||
RUN mkdir -p /go/src/github.com/chrislusf/
|
||||
RUN git clone https://github.com/chrislusf/seaweedfs /go/src/github.com/chrislusf/seaweedfs
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
FROM golang:1.17-buster as builder
|
||||
FROM golang:1.18-buster as builder
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y build-essential libsnappy-dev zlib1g-dev libbz2-dev libgflags-dev liblz4-dev libzstd-dev
|
||||
|
||||
ENV ROCKSDB_VERSION v6.22.1
|
||||
ENV ROCKSDB_VERSION v7.0.4
|
||||
|
||||
# build RocksDB
|
||||
RUN cd /tmp && \
|
||||
|
||||
@@ -55,6 +55,9 @@ cluster: build
|
||||
2clusters: build
|
||||
docker-compose -f compose/local-clusters-compose.yml -p seaweedfs up
|
||||
|
||||
hashicorp_raft: build
|
||||
docker-compose -f compose/local-hashicorp-raft-compose.yml -p seaweedfs up
|
||||
|
||||
s3tests: build s3tests_build
|
||||
docker-compose -f compose/local-s3tests-compose.yml -p seaweedfs up
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
master0:
|
||||
image: chrislusf/seaweedfs:local
|
||||
ports:
|
||||
- 9333:9333
|
||||
- 19333:19333
|
||||
command: "-v=4 master -volumeSizeLimitMB 100 -raftHashicorp -ip=master0 -port=9333 -peers=master1:9334,master2:9335 -mdir=/data"
|
||||
volumes:
|
||||
- ./master/0:/data
|
||||
environment:
|
||||
WEED_MASTER_VOLUME_GROWTH_COPY_1: 1
|
||||
WEED_MASTER_VOLUME_GROWTH_COPY_2: 2
|
||||
WEED_MASTER_VOLUME_GROWTH_COPY_OTHER: 1
|
||||
master1:
|
||||
image: chrislusf/seaweedfs:local
|
||||
ports:
|
||||
- 9334:9334
|
||||
- 19334:19334
|
||||
command: "-v=4 master -volumeSizeLimitMB 100 -raftHashicorp -ip=master1 -port=9334 -peers=master0:9333,master2:9335 -mdir=/data"
|
||||
volumes:
|
||||
- ./master/1:/data
|
||||
environment:
|
||||
WEED_MASTER_VOLUME_GROWTH_COPY_1: 1
|
||||
WEED_MASTER_VOLUME_GROWTH_COPY_2: 2
|
||||
WEED_MASTER_VOLUME_GROWTH_COPY_OTHER: 1
|
||||
master2:
|
||||
image: chrislusf/seaweedfs:local
|
||||
ports:
|
||||
- 9335:9335
|
||||
- 19335:19335
|
||||
command: "-v=4 master -volumeSizeLimitMB 100 -raftHashicorp -ip=master2 -port=9335 -peers=master0:9333,master1:9334 -mdir=/data"
|
||||
volumes:
|
||||
- ./master/2:/data
|
||||
environment:
|
||||
WEED_MASTER_VOLUME_GROWTH_COPY_1: 1
|
||||
WEED_MASTER_VOLUME_GROWTH_COPY_2: 2
|
||||
WEED_MASTER_VOLUME_GROWTH_COPY_OTHER: 1
|
||||
volume1:
|
||||
image: chrislusf/seaweedfs:local
|
||||
ports:
|
||||
- 8080:8080
|
||||
- 18080:18080
|
||||
command: 'volume -dataCenter=dc1 -rack=v1 -mserver="master0:9333,master1:9334,master2:9335" -port=8080 -ip=volume1 -publicUrl=localhost:8080 -preStopSeconds=1'
|
||||
depends_on:
|
||||
- master0
|
||||
- master1
|
||||
volume2:
|
||||
image: chrislusf/seaweedfs:local
|
||||
ports:
|
||||
- 8082:8082
|
||||
- 18082:18082
|
||||
command: 'volume -dataCenter=dc2 -rack=v2 -mserver="master0:9333,master1:9334,master2:9335" -port=8082 -ip=volume2 -publicUrl=localhost:8082 -preStopSeconds=1'
|
||||
depends_on:
|
||||
- master0
|
||||
- master1
|
||||
volume3:
|
||||
image: chrislusf/seaweedfs:local
|
||||
ports:
|
||||
- 8083:8083
|
||||
- 18083:18083
|
||||
command: 'volume -dataCenter=dc3 -rack=v3 -mserver="master0:9333,master1:9334,master2:9335" -port=8083 -ip=volume3 -publicUrl=localhost:8083 -preStopSeconds=1'
|
||||
depends_on:
|
||||
- master0
|
||||
- master1
|
||||
filer:
|
||||
image: chrislusf/seaweedfs:local
|
||||
ports:
|
||||
- 8888:8888
|
||||
- 18888:18888
|
||||
- 8111:8111
|
||||
command: 'filer -defaultReplicaPlacement=100 -iam -master="master0:9333,master1:9334,master2:9335"'
|
||||
depends_on:
|
||||
- master0
|
||||
- master1
|
||||
- volume1
|
||||
- volume2
|
||||
s3:
|
||||
image: chrislusf/seaweedfs:local
|
||||
ports:
|
||||
- 8333:8333
|
||||
command: '-v=9 s3 -ip.bind="s3" -filer="filer:8888"'
|
||||
depends_on:
|
||||
- master0
|
||||
- master1
|
||||
- volume1
|
||||
- volume2
|
||||
- filer
|
||||
@@ -24,7 +24,7 @@ services:
|
||||
- 8888:8888
|
||||
- 18888:18888
|
||||
- 8000:8000
|
||||
command: 'filer -master="master:9333" -s3 -s3.config=/etc/seaweedfs/s3.json -s3.port=8000'
|
||||
command: 'filer -master="master:9333" -s3 -s3.config=/etc/seaweedfs/s3.json -s3.port=8000 -s3.allowEmptyFolder=false -s3.allowDeleteBucketNotEmpty=false'
|
||||
volumes:
|
||||
- ./s3.json:/etc/seaweedfs/s3.json
|
||||
depends_on:
|
||||
@@ -38,7 +38,7 @@ services:
|
||||
S3TEST_CONF: "s3tests.conf"
|
||||
NOSETESTS_OPTIONS: "--verbose --logging-level=ERROR --with-xunit --failure-detail s3tests_boto3.functional.test_s3"
|
||||
NOSETESTS_ATTR: "!tagging,!fails_on_aws,!encryption,!bucket-policy,!versioning,!fails_on_rgw,!bucket-policy,!fails_with_subdomain,!policy_status,!object-lock,!lifecycle,!cors,!user-policy"
|
||||
NOSETESTS_EXCLUDE: "(get_bucket_encryption|put_bucket_encryption|bucket_list_delimiter_basic|bucket_listv2_delimiter_basic|bucket_listv2_encoding_basic|bucket_list_encoding_basic|bucket_list_delimiter_prefix|bucket_listv2_delimiter_prefix_ends_with_delimiter|bucket_list_delimiter_prefix_ends_with_delimiter|bucket_list_delimiter_alt|bucket_listv2_delimiter_alt|bucket_list_delimiter_prefix_underscore|bucket_list_delimiter_percentage|bucket_listv2_delimiter_percentage|bucket_list_delimiter_whitespace|bucket_listv2_delimiter_whitespace|bucket_list_delimiter_dot|bucket_listv2_delimiter_dot|bucket_list_delimiter_unreadable|bucket_listv2_delimiter_unreadable|bucket_listv2_fetchowner_defaultempty|bucket_listv2_fetchowner_empty|bucket_list_prefix_delimiter_alt|bucket_listv2_prefix_delimiter_alt|bucket_list_prefix_delimiter_prefix_not_exist|bucket_listv2_prefix_delimiter_prefix_not_exist|bucket_list_prefix_delimiter_delimiter_not_exist|bucket_listv2_prefix_delimiter_delimiter_not_exist|bucket_list_prefix_delimiter_prefix_delimiter_not_exist|bucket_listv2_prefix_delimiter_prefix_delimiter_not_exist|bucket_list_maxkeys_none|bucket_listv2_maxkeys_none|bucket_list_maxkeys_invalid|bucket_listv2_continuationtoken_empty|bucket_list_return_data|bucket_list_objects_anonymous|bucket_listv2_objects_anonymous|bucket_notexist|bucketv2_notexist|bucket_delete_nonempty|bucket_concurrent_set_canned_acl|object_write_to_nonexist_bucket|object_requestid_matches_header_on_error|object_set_get_metadata_none_to_good|object_set_get_metadata_none_to_empty|object_set_get_metadata_overwrite_to_empty|post_object_anonymous_request|post_object_authenticated_request|post_object_authenticated_no_content_type|post_object_authenticated_request_bad_access_key|post_object_set_success_code|post_object_set_invalid_success_code|post_object_upload_larger_than_chunk|post_object_set_key_from_filename|post_object_ignored_header|post_object_case_insensitive_condition_fields|post_object_escaped_field_values|post_object_success_redirect_action|post_object_invalid_signature|post_object_invalid_access_key|post_object_missing_policy_condition|post_object_user_specified_header|post_object_request_missing_policy_specified_field|post_object_expired_policy|post_object_invalid_request_field_value|get_object_ifunmodifiedsince_good|put_object_ifmatch_failed|object_raw_get_bucket_gone|object_delete_key_bucket_gone|object_raw_get_bucket_acl|object_raw_get_object_acl|object_raw_response_headers|object_raw_authenticated_bucket_gone|object_raw_get_x_amz_expires_out_max_range|object_raw_get_x_amz_expires_out_positive_range|object_anon_put_write_access|object_raw_put_authenticated_expired|bucket_create_exists|bucket_create_naming_bad_short_one|bucket_create_naming_bad_short_two|bucket_get_location|bucket_acl_default|bucket_acl_canned|bucket_acl_canned_publicreadwrite|bucket_acl_canned_authenticatedread|object_acl_default|object_acl_canned_during_create|object_acl_canned|object_acl_canned_publicreadwrite|object_acl_canned_authenticatedread|object_acl_canned_bucketownerread|object_acl_canned_bucketownerfullcontrol|object_acl_full_control_verify_attributes|bucket_acl_canned_private_to_private|bucket_acl_grant_nonexist_user|bucket_acl_no_grants|bucket_acl_grant_email_not_exist|bucket_acl_revoke_all|bucket_recreate_not_overriding|object_copy_verify_contenttype|object_copy_to_itself_with_metadata|object_copy_not_owned_bucket|object_copy_not_owned_object_bucket|object_copy_retaining_metadata|object_copy_replacing_metadata|multipart_upload_empty|multipart_copy_invalid_range|multipart_copy_special_names|multipart_upload_resend_part|multipart_upload_size_too_small|abort_multipart_upload_not_found|multipart_upload_missing_part|multipart_upload_incorrect_etag|100_continue|ranged_request_invalid_range|ranged_request_empty_object|access_bucket)"
|
||||
NOSETESTS_EXCLUDE: "(get_bucket_encryption|put_bucket_encryption|bucket_list_delimiter_basic|bucket_listv2_delimiter_basic|bucket_listv2_encoding_basic|bucket_list_encoding_basic|bucket_list_delimiter_prefix|bucket_listv2_delimiter_prefix_ends_with_delimiter|bucket_list_delimiter_prefix_ends_with_delimiter|bucket_list_delimiter_alt|bucket_listv2_delimiter_alt|bucket_list_delimiter_prefix_underscore|bucket_list_delimiter_percentage|bucket_listv2_delimiter_percentage|bucket_list_delimiter_whitespace|bucket_listv2_delimiter_whitespace|bucket_list_delimiter_dot|bucket_listv2_delimiter_dot|bucket_list_delimiter_unreadable|bucket_listv2_delimiter_unreadable|bucket_listv2_fetchowner_defaultempty|bucket_listv2_fetchowner_empty|bucket_list_prefix_delimiter_alt|bucket_listv2_prefix_delimiter_alt|bucket_list_prefix_delimiter_prefix_not_exist|bucket_listv2_prefix_delimiter_prefix_not_exist|bucket_list_prefix_delimiter_delimiter_not_exist|bucket_listv2_prefix_delimiter_delimiter_not_exist|bucket_list_prefix_delimiter_prefix_delimiter_not_exist|bucket_listv2_prefix_delimiter_prefix_delimiter_not_exist|bucket_list_maxkeys_none|bucket_listv2_maxkeys_none|bucket_list_maxkeys_invalid|bucket_listv2_continuationtoken_empty|bucket_list_return_data|bucket_list_objects_anonymous|bucket_listv2_objects_anonymous|bucket_concurrent_set_canned_acl|object_write_to_nonexist_bucket|object_requestid_matches_header_on_error|object_set_get_metadata_none_to_good|object_set_get_metadata_none_to_empty|object_set_get_metadata_overwrite_to_empty|post_object_anonymous_request|post_object_authenticated_request|post_object_authenticated_no_content_type|post_object_authenticated_request_bad_access_key|post_object_set_success_code|post_object_set_invalid_success_code|post_object_upload_larger_than_chunk|post_object_set_key_from_filename|post_object_ignored_header|post_object_case_insensitive_condition_fields|post_object_escaped_field_values|post_object_success_redirect_action|post_object_invalid_signature|post_object_invalid_access_key|post_object_missing_policy_condition|post_object_user_specified_header|post_object_request_missing_policy_specified_field|post_object_expired_policy|post_object_invalid_request_field_value|get_object_ifunmodifiedsince_good|put_object_ifmatch_failed|object_raw_get_bucket_gone|object_delete_key_bucket_gone|object_raw_get_bucket_acl|object_raw_get_object_acl|object_raw_response_headers|object_raw_authenticated_bucket_gone|object_raw_get_x_amz_expires_out_max_range|object_raw_get_x_amz_expires_out_positive_range|object_anon_put_write_access|object_raw_put_authenticated_expired|bucket_create_exists|bucket_create_naming_bad_short_one|bucket_create_naming_bad_short_two|bucket_get_location|bucket_acl_default|bucket_acl_canned|bucket_acl_canned_publicreadwrite|bucket_acl_canned_authenticatedread|object_acl_default|object_acl_canned_during_create|object_acl_canned|object_acl_canned_publicreadwrite|object_acl_canned_authenticatedread|object_acl_canned_bucketownerread|object_acl_canned_bucketownerfullcontrol|object_acl_full_control_verify_attributes|bucket_acl_canned_private_to_private|bucket_acl_grant_nonexist_user|bucket_acl_no_grants|bucket_acl_grant_email_not_exist|bucket_acl_revoke_all|bucket_recreate_not_overriding|object_copy_verify_contenttype|object_copy_to_itself_with_metadata|object_copy_not_owned_bucket|object_copy_not_owned_object_bucket|object_copy_retaining_metadata|object_copy_replacing_metadata|multipart_upload_empty|multipart_copy_invalid_range|multipart_copy_special_names|multipart_upload_resend_part|multipart_upload_size_too_small|abort_multipart_upload_not_found|multipart_upload_missing_part|100_continue|ranged_request_invalid_range|ranged_request_empty_object|access_bucket|list_multipart_upload_owner|multipart_upload_small)"
|
||||
depends_on:
|
||||
- master
|
||||
- volume
|
||||
|
||||
@@ -28,3 +28,4 @@ sleep_minutes = 17 # sleep minutes between each script execution
|
||||
region = "us-east-2"
|
||||
bucket = "volume_bucket" # an existing bucket
|
||||
endpoint = "http://server2:8333"
|
||||
storage_class = "STANDARD_IA"
|
||||
|
||||
@@ -4,13 +4,13 @@ go 1.18
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.100.2 // indirect
|
||||
cloud.google.com/go/pubsub v1.19.0
|
||||
cloud.google.com/go/storage v1.21.0
|
||||
cloud.google.com/go/pubsub v1.20.0
|
||||
cloud.google.com/go/storage v1.22.0
|
||||
github.com/Azure/azure-pipeline-go v0.2.3
|
||||
github.com/Azure/azure-storage-blob-go v0.14.0
|
||||
github.com/OneOfOne/xxhash v1.2.2
|
||||
github.com/Shopify/sarama v1.23.1
|
||||
github.com/aws/aws-sdk-go v1.43.25
|
||||
github.com/OneOfOne/xxhash v1.2.8
|
||||
github.com/Shopify/sarama v1.32.0
|
||||
github.com/aws/aws-sdk-go v1.43.45
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72
|
||||
github.com/bwmarrin/snowflake v0.3.0
|
||||
@@ -33,11 +33,10 @@ require (
|
||||
github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4
|
||||
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect
|
||||
github.com/fclairamb/ftpserverlib v0.17.0
|
||||
github.com/frankban/quicktest v1.7.2 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/go-errors/errors v1.1.1 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-redsync/redsync/v4 v4.4.1
|
||||
github.com/go-redsync/redsync/v4 v4.5.0
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/go-zookeeper/zk v1.0.2 // indirect
|
||||
@@ -50,9 +49,9 @@ require (
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/wire v0.5.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0
|
||||
github.com/googleapis/gax-go/v2 v2.3.0 // indirect
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.0 // indirect
|
||||
@@ -65,46 +64,45 @@ require (
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/karlseguin/ccache/v2 v2.0.8
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/klauspost/cpuid v1.2.1 // indirect
|
||||
github.com/klauspost/reedsolomon v1.9.2
|
||||
github.com/klauspost/compress v1.15.1 // indirect
|
||||
github.com/klauspost/reedsolomon v1.9.16
|
||||
github.com/kurin/blazer v0.5.3
|
||||
github.com/lib/pq v1.10.4
|
||||
github.com/linxGnu/grocksdb v1.6.38
|
||||
github.com/magiconair/properties v1.8.1 // indirect
|
||||
github.com/mailru/easyjson v0.7.1 // indirect
|
||||
github.com/mattn/go-ieproxy v0.0.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/lib/pq v1.10.5
|
||||
github.com/linxGnu/grocksdb v1.7.0
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-ieproxy v0.0.3 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/olivere/elastic/v7 v7.0.19
|
||||
github.com/pelletier/go-toml v1.7.0 // indirect
|
||||
github.com/olivere/elastic/v7 v7.0.32
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/peterh/liner v1.2.2
|
||||
github.com/pierrec/lz4 v2.2.7+incompatible // indirect
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/posener/complete v1.2.3
|
||||
github.com/pquerna/cachecontrol v0.1.0
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/prometheus/client_golang v1.12.1
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/seaweedfs/goexif v2.0.0+incompatible
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/afero v1.7.0 // indirect
|
||||
github.com/spf13/cast v1.3.0 // indirect
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/viper v1.4.0
|
||||
github.com/spf13/viper v1.11.0
|
||||
github.com/streadway/amqp v1.0.0
|
||||
github.com/stretchr/testify v1.7.1
|
||||
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965
|
||||
github.com/tidwall/gjson v1.14.0
|
||||
github.com/tidwall/gjson v1.14.1
|
||||
github.com/tidwall/match v1.1.1
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tsuna/gohbase v0.0.0-20201125011725-348991136365
|
||||
@@ -114,88 +112,105 @@ require (
|
||||
github.com/viant/ptrie v0.3.0
|
||||
github.com/viant/toolbox v0.33.2 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.0.2 // indirect
|
||||
github.com/xdg-go/scram v1.1.0 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.0
|
||||
go.mongodb.org/mongo-driver v1.8.4
|
||||
go.etcd.io/etcd/client/v3 v3.5.4
|
||||
go.mongodb.org/mongo-driver v1.9.0
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
gocloud.dev v0.24.0
|
||||
gocloud.dev/pubsub/natspubsub v0.20.0
|
||||
gocloud.dev/pubsub/rabbitpubsub v0.24.0
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect
|
||||
gocloud.dev v0.25.0
|
||||
gocloud.dev/pubsub/natspubsub v0.25.0
|
||||
gocloud.dev/pubsub/rabbitpubsub v0.25.0
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
||||
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd
|
||||
golang.org/x/image v0.0.0-20200119044424-58c23975cae1
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5
|
||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/api v0.73.0
|
||||
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
|
||||
google.golang.org/api v0.75.0
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6 // indirect
|
||||
google.golang.org/grpc v1.45.0
|
||||
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4 // indirect
|
||||
google.golang.org/grpc v1.46.0
|
||||
google.golang.org/protobuf v1.28.0
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect
|
||||
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.3.0 // indirect
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect
|
||||
modernc.org/b v1.0.0 // indirect
|
||||
modernc.org/cc/v3 v3.35.24 // indirect
|
||||
modernc.org/ccgo/v3 v3.15.17 // indirect
|
||||
modernc.org/libc v1.14.12 // indirect
|
||||
modernc.org/cc/v3 v3.35.26 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.2 // indirect
|
||||
modernc.org/libc v1.15.0 // indirect
|
||||
modernc.org/mathutil v1.4.1 // indirect
|
||||
modernc.org/memory v1.0.7 // indirect
|
||||
modernc.org/opt v0.1.1 // indirect
|
||||
modernc.org/sqlite v1.15.3
|
||||
modernc.org/sqlite v1.17.0
|
||||
modernc.org/strutil v1.1.1 // indirect
|
||||
modernc.org/token v1.0.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fluent/fluent-logger-golang v1.8.0
|
||||
github.com/Jille/raft-grpc-transport v1.2.0
|
||||
github.com/arangodb/go-driver v1.3.1
|
||||
github.com/fluent/fluent-logger-golang v1.9.0
|
||||
github.com/hanwen/go-fuse/v2 v2.1.0
|
||||
github.com/hashicorp/raft v1.3.8
|
||||
github.com/hashicorp/raft-boltdb v0.0.0-20220329195025-15018e9b97e0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.5.0 // indirect
|
||||
cloud.google.com/go/iam v0.1.1 // indirect
|
||||
github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.9.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.7.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.4.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 // indirect
|
||||
github.com/aws/smithy-go v1.8.0 // indirect
|
||||
cloud.google.com/go/compute v1.6.0 // indirect
|
||||
cloud.google.com/go/iam v0.3.0 // indirect
|
||||
github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e // indirect
|
||||
github.com/armon/go-metrics v0.3.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.16.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.15.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.11.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.17.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.18.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.16.3 // indirect
|
||||
github.com/aws/smithy-go v1.11.2 // indirect
|
||||
github.com/boltdb/bolt v1.3.1 // indirect
|
||||
github.com/d4l3k/messagediff v1.2.1 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fclairamb/go-log v0.1.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 // indirect
|
||||
github.com/googleapis/go-type-adapters v1.0.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.2.0 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-msgpack v1.1.5 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.7 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.1+incompatible // indirect
|
||||
github.com/nats-io/nats.go v1.11.0 // indirect
|
||||
github.com/nats-io/nats.go v1.13.1-0.20220121202836-972a071d373d // indirect
|
||||
github.com/nats-io/nkeys v0.3.0 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.0 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.4 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
go.uber.org/zap v1.19.0 // indirect
|
||||
golang.org/x/mod v0.5.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
lukechampine.com/uint128 v1.1.1 // indirect
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v1
|
||||
description: SeaweedFS
|
||||
name: seaweedfs
|
||||
appVersion: "2.96"
|
||||
version: "2.96"
|
||||
appVersion: "3.01"
|
||||
version: "3.01"
|
||||
|
||||
@@ -154,13 +154,16 @@ spec:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
-master={{ range $index := until (.Values.master.replicas | int) }}${SEAWEEDFS_FULLNAME}-master-{{ $index }}.${SEAWEEDFS_FULLNAME}-master:{{ $.Values.master.port }}{{ if lt $index (sub ($.Values.master.replicas | int) 1) }},{{ end }}{{ end }}
|
||||
{{- if or (.Values.global.enableSecurity) (.Values.filer.extraVolumeMounts) }}
|
||||
volumeMounts:
|
||||
- name: seaweedfs-filer-log-volume
|
||||
mountPath: "/logs/"
|
||||
- mountPath: /etc/sw
|
||||
name: config-users
|
||||
readOnly: true
|
||||
{{- if .Values.filer.enablePVC }}
|
||||
- name: data-filer
|
||||
mountPath: /data
|
||||
{{- end }}
|
||||
{{- if .Values.global.enableSecurity }}
|
||||
- name: security-config
|
||||
readOnly: true
|
||||
@@ -183,7 +186,6 @@ spec:
|
||||
mountPath: /usr/local/share/ca-certificates/client/
|
||||
{{- end }}
|
||||
{{ tpl .Values.filer.extraVolumeMounts . | nindent 12 | trim }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.filer.port }}
|
||||
name: swfs-filer
|
||||
@@ -250,16 +252,18 @@ spec:
|
||||
nodeSelector:
|
||||
{{ tpl .Values.filer.nodeSelector . | indent 8 | trim }}
|
||||
{{- end }}
|
||||
{{/* volumeClaimTemplates:*/}}
|
||||
{{/* - metadata:*/}}
|
||||
{{/* name: data-{{ .Release.Namespace }}*/}}
|
||||
{{/* spec:*/}}
|
||||
{{/* accessModes:*/}}
|
||||
{{/* - ReadWriteOnce*/}}
|
||||
{{/* resources:*/}}
|
||||
{{/* requests:*/}}
|
||||
{{/* storage: {{ .Values.filer.storage }}*/}}
|
||||
{{/* {{- if .Values.filer.storageClass }}*/}}
|
||||
{{/* storageClassName: {{ .Values.filer.storageClass }}*/}}
|
||||
{{/* {{- end }}*/}}
|
||||
{{- if .Values.filer.enablePVC }}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data-filer
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.filer.storage }}
|
||||
{{- if .Values.filer.storageClass }}
|
||||
storageClassName: {{ .Values.filer.storageClass }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -41,8 +41,7 @@ master:
|
||||
grpcPort: 19333
|
||||
ipBind: "0.0.0.0"
|
||||
volumePreallocate: false
|
||||
#Master stops directing writes to oversized volumes
|
||||
volumeSizeLimitMB: 30000
|
||||
volumeSizeLimitMB: 1000
|
||||
loggingOverrideLevel: null
|
||||
#number of seconds between heartbeats, default 5
|
||||
pulseSeconds: null
|
||||
@@ -62,6 +61,8 @@ master:
|
||||
extraVolumes: ""
|
||||
extraVolumeMounts: ""
|
||||
|
||||
# enablePVC will create a pvc for filer for data persistence.
|
||||
enablePVC: false
|
||||
# storage and storageClass are the settings for configuring stateful
|
||||
# storage for the master pods. storage should be set to the disk size of
|
||||
# the attached volume. storageClass is the class of storage which defaults
|
||||
@@ -358,6 +359,7 @@ filer:
|
||||
WEED_MYSQL_CONNECTION_MAX_LIFETIME_SECONDS: "600"
|
||||
# enable usage of memsql as filer backend
|
||||
WEED_MYSQL_INTERPOLATEPARAMS: "true"
|
||||
# if you want to use leveldb2, then should enable "enablePVC". or you may lose your data.
|
||||
WEED_LEVELDB2_ENABLED: "false"
|
||||
# with http DELETE, by default the filer would check whether a folder is empty.
|
||||
# recursive_delete will delete all sub folders and files, similar to "rm -Rf"
|
||||
|
||||
@@ -48,6 +48,9 @@ service SeaweedFiler {
|
||||
rpc Statistics (StatisticsRequest) returns (StatisticsResponse) {
|
||||
}
|
||||
|
||||
rpc Ping (PingRequest) returns (PingResponse) {
|
||||
}
|
||||
|
||||
rpc GetFilerConfiguration (GetFilerConfigurationRequest) returns (GetFilerConfigurationResponse) {
|
||||
}
|
||||
|
||||
@@ -311,6 +314,16 @@ message StatisticsResponse {
|
||||
uint64 file_count = 6;
|
||||
}
|
||||
|
||||
message PingRequest {
|
||||
string target = 1; // default to ping itself
|
||||
string target_type = 2;
|
||||
}
|
||||
message PingResponse {
|
||||
int64 start_time_ns = 1;
|
||||
int64 remote_time_ns = 2;
|
||||
int64 stop_time_ns = 3;
|
||||
}
|
||||
|
||||
message GetFilerConfigurationRequest {
|
||||
}
|
||||
message GetFilerConfigurationResponse {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-common</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<version>3.2.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<properties>
|
||||
<seaweedfs.client.version>2.85</seaweedfs.client.version>
|
||||
<hadoop.version>3.1.4</hadoop.version>
|
||||
<hadoop.version>3.2.3</hadoop.version>
|
||||
</properties>
|
||||
|
||||
<groupId>com.github.chrislusf</groupId>
|
||||
|
||||
+104
-53
@@ -9,11 +9,21 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
MasterType = "master"
|
||||
FilerType = "filer"
|
||||
BrokerType = "broker"
|
||||
MasterType = "master"
|
||||
VolumeServerType = "volumeServer"
|
||||
FilerType = "filer"
|
||||
BrokerType = "broker"
|
||||
)
|
||||
|
||||
type FilerGroup string
|
||||
type Filers struct {
|
||||
filers map[pb.ServerAddress]*ClusterNode
|
||||
leaders *Leaders
|
||||
}
|
||||
type Leaders struct {
|
||||
leaders [3]pb.ServerAddress
|
||||
}
|
||||
|
||||
type ClusterNode struct {
|
||||
Address pb.ServerAddress
|
||||
Version string
|
||||
@@ -21,42 +31,50 @@ type ClusterNode struct {
|
||||
createdTs time.Time
|
||||
}
|
||||
|
||||
type Leaders struct {
|
||||
leaders [3]pb.ServerAddress
|
||||
}
|
||||
|
||||
type Cluster struct {
|
||||
filers map[pb.ServerAddress]*ClusterNode
|
||||
filersLock sync.RWMutex
|
||||
filerLeaders *Leaders
|
||||
brokers map[pb.ServerAddress]*ClusterNode
|
||||
brokersLock sync.RWMutex
|
||||
filerGroup2filers map[FilerGroup]*Filers
|
||||
filersLock sync.RWMutex
|
||||
brokers map[pb.ServerAddress]*ClusterNode
|
||||
brokersLock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewCluster() *Cluster {
|
||||
return &Cluster{
|
||||
filers: make(map[pb.ServerAddress]*ClusterNode),
|
||||
filerLeaders: &Leaders{},
|
||||
brokers: make(map[pb.ServerAddress]*ClusterNode),
|
||||
filerGroup2filers: make(map[FilerGroup]*Filers),
|
||||
brokers: make(map[pb.ServerAddress]*ClusterNode),
|
||||
}
|
||||
}
|
||||
|
||||
func (cluster *Cluster) AddClusterNode(nodeType string, address pb.ServerAddress, version string) []*master_pb.KeepConnectedResponse {
|
||||
func (cluster *Cluster) getFilers(filerGroup FilerGroup, createIfNotFound bool) *Filers {
|
||||
cluster.filersLock.Lock()
|
||||
defer cluster.filersLock.Unlock()
|
||||
filers, found := cluster.filerGroup2filers[filerGroup]
|
||||
if !found && createIfNotFound {
|
||||
filers = &Filers{
|
||||
filers: make(map[pb.ServerAddress]*ClusterNode),
|
||||
leaders: &Leaders{},
|
||||
}
|
||||
cluster.filerGroup2filers[filerGroup] = filers
|
||||
}
|
||||
return filers
|
||||
}
|
||||
|
||||
func (cluster *Cluster) AddClusterNode(ns, nodeType string, address pb.ServerAddress, version string) []*master_pb.KeepConnectedResponse {
|
||||
filerGroup := FilerGroup(ns)
|
||||
switch nodeType {
|
||||
case FilerType:
|
||||
cluster.filersLock.Lock()
|
||||
defer cluster.filersLock.Unlock()
|
||||
if existingNode, found := cluster.filers[address]; found {
|
||||
filers := cluster.getFilers(filerGroup, true)
|
||||
if existingNode, found := filers.filers[address]; found {
|
||||
existingNode.counter++
|
||||
return nil
|
||||
}
|
||||
cluster.filers[address] = &ClusterNode{
|
||||
filers.filers[address] = &ClusterNode{
|
||||
Address: address,
|
||||
Version: version,
|
||||
counter: 1,
|
||||
createdTs: time.Now(),
|
||||
}
|
||||
return cluster.ensureFilerLeaders(true, nodeType, address)
|
||||
return cluster.ensureFilerLeaders(filers, true, filerGroup, nodeType, address)
|
||||
case BrokerType:
|
||||
cluster.brokersLock.Lock()
|
||||
defer cluster.brokersLock.Unlock()
|
||||
@@ -80,22 +98,34 @@ func (cluster *Cluster) AddClusterNode(nodeType string, address pb.ServerAddress
|
||||
},
|
||||
}
|
||||
case MasterType:
|
||||
return []*master_pb.KeepConnectedResponse{
|
||||
{
|
||||
ClusterNodeUpdate: &master_pb.ClusterNodeUpdate{
|
||||
NodeType: nodeType,
|
||||
Address: string(address),
|
||||
IsAdd: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cluster *Cluster) RemoveClusterNode(nodeType string, address pb.ServerAddress) []*master_pb.KeepConnectedResponse {
|
||||
func (cluster *Cluster) RemoveClusterNode(ns string, nodeType string, address pb.ServerAddress) []*master_pb.KeepConnectedResponse {
|
||||
filerGroup := FilerGroup(ns)
|
||||
switch nodeType {
|
||||
case FilerType:
|
||||
cluster.filersLock.Lock()
|
||||
defer cluster.filersLock.Unlock()
|
||||
if existingNode, found := cluster.filers[address]; !found {
|
||||
filers := cluster.getFilers(filerGroup, false)
|
||||
if filers == nil {
|
||||
return nil
|
||||
}
|
||||
if existingNode, found := filers.filers[address]; !found {
|
||||
return nil
|
||||
} else {
|
||||
existingNode.counter--
|
||||
if existingNode.counter <= 0 {
|
||||
delete(cluster.filers, address)
|
||||
return cluster.ensureFilerLeaders(false, nodeType, address)
|
||||
delete(filers.filers, address)
|
||||
return cluster.ensureFilerLeaders(filers, false, filerGroup, nodeType, address)
|
||||
}
|
||||
}
|
||||
case BrokerType:
|
||||
@@ -119,16 +149,29 @@ func (cluster *Cluster) RemoveClusterNode(nodeType string, address pb.ServerAddr
|
||||
}
|
||||
}
|
||||
case MasterType:
|
||||
return []*master_pb.KeepConnectedResponse{
|
||||
{
|
||||
ClusterNodeUpdate: &master_pb.ClusterNodeUpdate{
|
||||
NodeType: nodeType,
|
||||
Address: string(address),
|
||||
IsAdd: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cluster *Cluster) ListClusterNode(nodeType string) (nodes []*ClusterNode) {
|
||||
func (cluster *Cluster) ListClusterNode(filerGroup FilerGroup, nodeType string) (nodes []*ClusterNode) {
|
||||
switch nodeType {
|
||||
case FilerType:
|
||||
filers := cluster.getFilers(filerGroup, false)
|
||||
if filers == nil {
|
||||
return
|
||||
}
|
||||
cluster.filersLock.RLock()
|
||||
defer cluster.filersLock.RUnlock()
|
||||
for _, node := range cluster.filers {
|
||||
for _, node := range filers.filers {
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
case BrokerType:
|
||||
@@ -142,41 +185,48 @@ func (cluster *Cluster) ListClusterNode(nodeType string) (nodes []*ClusterNode)
|
||||
return
|
||||
}
|
||||
|
||||
func (cluster *Cluster) IsOneLeader(address pb.ServerAddress) bool {
|
||||
return cluster.filerLeaders.isOneLeader(address)
|
||||
func (cluster *Cluster) IsOneLeader(filerGroup FilerGroup, address pb.ServerAddress) bool {
|
||||
filers := cluster.getFilers(filerGroup, false)
|
||||
if filers == nil {
|
||||
return false
|
||||
}
|
||||
return filers.leaders.isOneLeader(address)
|
||||
}
|
||||
|
||||
func (cluster *Cluster) ensureFilerLeaders(isAdd bool, nodeType string, address pb.ServerAddress) (result []*master_pb.KeepConnectedResponse) {
|
||||
func (cluster *Cluster) ensureFilerLeaders(filers *Filers, isAdd bool, filerGroup FilerGroup, nodeType string, address pb.ServerAddress) (result []*master_pb.KeepConnectedResponse) {
|
||||
if isAdd {
|
||||
if cluster.filerLeaders.addLeaderIfVacant(address) {
|
||||
if filers.leaders.addLeaderIfVacant(address) {
|
||||
// has added the address as one leader
|
||||
result = append(result, &master_pb.KeepConnectedResponse{
|
||||
ClusterNodeUpdate: &master_pb.ClusterNodeUpdate{
|
||||
NodeType: nodeType,
|
||||
Address: string(address),
|
||||
IsLeader: true,
|
||||
IsAdd: true,
|
||||
FilerGroup: string(filerGroup),
|
||||
NodeType: nodeType,
|
||||
Address: string(address),
|
||||
IsLeader: true,
|
||||
IsAdd: true,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
result = append(result, &master_pb.KeepConnectedResponse{
|
||||
ClusterNodeUpdate: &master_pb.ClusterNodeUpdate{
|
||||
NodeType: nodeType,
|
||||
Address: string(address),
|
||||
IsLeader: false,
|
||||
IsAdd: true,
|
||||
FilerGroup: string(filerGroup),
|
||||
NodeType: nodeType,
|
||||
Address: string(address),
|
||||
IsLeader: false,
|
||||
IsAdd: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if cluster.filerLeaders.removeLeaderIfExists(address) {
|
||||
if filers.leaders.removeLeaderIfExists(address) {
|
||||
|
||||
result = append(result, &master_pb.KeepConnectedResponse{
|
||||
ClusterNodeUpdate: &master_pb.ClusterNodeUpdate{
|
||||
NodeType: nodeType,
|
||||
Address: string(address),
|
||||
IsLeader: true,
|
||||
IsAdd: false,
|
||||
FilerGroup: string(filerGroup),
|
||||
NodeType: nodeType,
|
||||
Address: string(address),
|
||||
IsLeader: true,
|
||||
IsAdd: false,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -184,8 +234,8 @@ func (cluster *Cluster) ensureFilerLeaders(isAdd bool, nodeType string, address
|
||||
var shortestDuration int64 = math.MaxInt64
|
||||
now := time.Now()
|
||||
var candidateAddress pb.ServerAddress
|
||||
for _, node := range cluster.filers {
|
||||
if cluster.filerLeaders.isOneLeader(node.Address) {
|
||||
for _, node := range filers.filers {
|
||||
if filers.leaders.isOneLeader(node.Address) {
|
||||
continue
|
||||
}
|
||||
duration := now.Sub(node.createdTs).Nanoseconds()
|
||||
@@ -195,7 +245,7 @@ func (cluster *Cluster) ensureFilerLeaders(isAdd bool, nodeType string, address
|
||||
}
|
||||
}
|
||||
if candidateAddress != "" {
|
||||
cluster.filerLeaders.addLeaderIfVacant(candidateAddress)
|
||||
filers.leaders.addLeaderIfVacant(candidateAddress)
|
||||
// added a new leader
|
||||
result = append(result, &master_pb.KeepConnectedResponse{
|
||||
ClusterNodeUpdate: &master_pb.ClusterNodeUpdate{
|
||||
@@ -209,10 +259,11 @@ func (cluster *Cluster) ensureFilerLeaders(isAdd bool, nodeType string, address
|
||||
} else {
|
||||
result = append(result, &master_pb.KeepConnectedResponse{
|
||||
ClusterNodeUpdate: &master_pb.ClusterNodeUpdate{
|
||||
NodeType: nodeType,
|
||||
Address: string(address),
|
||||
IsLeader: false,
|
||||
IsAdd: false,
|
||||
FilerGroup: string(filerGroup),
|
||||
NodeType: nodeType,
|
||||
Address: string(address),
|
||||
IsLeader: false,
|
||||
IsAdd: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,39 +9,39 @@ import (
|
||||
func TestClusterAddRemoveNodes(t *testing.T) {
|
||||
c := NewCluster()
|
||||
|
||||
c.AddClusterNode("filer", pb.ServerAddress("111:1"), "23.45")
|
||||
c.AddClusterNode("filer", pb.ServerAddress("111:2"), "23.45")
|
||||
c.AddClusterNode("", "filer", pb.ServerAddress("111:1"), "23.45")
|
||||
c.AddClusterNode("", "filer", pb.ServerAddress("111:2"), "23.45")
|
||||
assert.Equal(t, []pb.ServerAddress{
|
||||
pb.ServerAddress("111:1"),
|
||||
pb.ServerAddress("111:2"),
|
||||
}, c.filerLeaders.GetLeaders())
|
||||
}, c.getFilers("", false).leaders.GetLeaders())
|
||||
|
||||
c.AddClusterNode("filer", pb.ServerAddress("111:3"), "23.45")
|
||||
c.AddClusterNode("filer", pb.ServerAddress("111:4"), "23.45")
|
||||
c.AddClusterNode("", "filer", pb.ServerAddress("111:3"), "23.45")
|
||||
c.AddClusterNode("", "filer", pb.ServerAddress("111:4"), "23.45")
|
||||
assert.Equal(t, []pb.ServerAddress{
|
||||
pb.ServerAddress("111:1"),
|
||||
pb.ServerAddress("111:2"),
|
||||
pb.ServerAddress("111:3"),
|
||||
}, c.filerLeaders.GetLeaders())
|
||||
}, c.getFilers("", false).leaders.GetLeaders())
|
||||
|
||||
c.AddClusterNode("filer", pb.ServerAddress("111:5"), "23.45")
|
||||
c.AddClusterNode("filer", pb.ServerAddress("111:6"), "23.45")
|
||||
c.RemoveClusterNode("filer", pb.ServerAddress("111:4"))
|
||||
c.AddClusterNode("", "filer", pb.ServerAddress("111:5"), "23.45")
|
||||
c.AddClusterNode("", "filer", pb.ServerAddress("111:6"), "23.45")
|
||||
c.RemoveClusterNode("", "filer", pb.ServerAddress("111:4"))
|
||||
assert.Equal(t, []pb.ServerAddress{
|
||||
pb.ServerAddress("111:1"),
|
||||
pb.ServerAddress("111:2"),
|
||||
pb.ServerAddress("111:3"),
|
||||
}, c.filerLeaders.GetLeaders())
|
||||
}, c.getFilers("", false).leaders.GetLeaders())
|
||||
|
||||
// remove oldest
|
||||
c.RemoveClusterNode("filer", pb.ServerAddress("111:1"))
|
||||
c.RemoveClusterNode("", "filer", pb.ServerAddress("111:1"))
|
||||
assert.Equal(t, []pb.ServerAddress{
|
||||
pb.ServerAddress("111:6"),
|
||||
pb.ServerAddress("111:2"),
|
||||
pb.ServerAddress("111:3"),
|
||||
}, c.filerLeaders.GetLeaders())
|
||||
}, c.getFilers("", false).leaders.GetLeaders())
|
||||
|
||||
// remove oldest
|
||||
c.RemoveClusterNode("filer", pb.ServerAddress("111:1"))
|
||||
c.RemoveClusterNode("", "filer", pb.ServerAddress("111:1"))
|
||||
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func runBenchmark(cmd *Command, args []string) bool {
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
b.masterClient = wdclient.NewMasterClient(b.grpcDialOption, "client", "", "", pb.ServerAddresses(*b.masters).ToAddressMap())
|
||||
b.masterClient = wdclient.NewMasterClient(b.grpcDialOption, "", "client", "", "", pb.ServerAddresses(*b.masters).ToAddressMap())
|
||||
go b.masterClient.KeepConnectedToMaster()
|
||||
b.masterClient.WaitUntilConnected()
|
||||
|
||||
|
||||
+21
-15
@@ -5,6 +5,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/reflection"
|
||||
@@ -36,6 +37,7 @@ type FilerOptions struct {
|
||||
port *int
|
||||
portGrpc *int
|
||||
publicPort *int
|
||||
filerGroup *string
|
||||
collection *string
|
||||
defaultReplicaPlacement *string
|
||||
disableDirListing *bool
|
||||
@@ -58,6 +60,7 @@ type FilerOptions struct {
|
||||
func init() {
|
||||
cmdFiler.Run = runFiler // break init cycle
|
||||
f.mastersString = cmdFiler.Flag.String("master", "localhost:9333", "comma-separated master servers")
|
||||
f.filerGroup = cmdFiler.Flag.String("filerGroup", "", "share metadata with other filers in the same filerGroup")
|
||||
f.collection = cmdFiler.Flag.String("collection", "", "all data will be stored in this default collection")
|
||||
f.ip = cmdFiler.Flag.String("ip", util.DetectedHostAddress(), "filer server http listen ip address")
|
||||
f.bindIp = cmdFiler.Flag.String("ip.bind", "", "ip address to bind to. If empty, default to same as -ip option.")
|
||||
@@ -89,6 +92,7 @@ func init() {
|
||||
filerS3Options.config = cmdFiler.Flag.String("s3.config", "", "path to the config file")
|
||||
filerS3Options.auditLogConfig = cmdFiler.Flag.String("s3.auditLogConfig", "", "path to the audit log config file")
|
||||
filerS3Options.allowEmptyFolder = cmdFiler.Flag.Bool("s3.allowEmptyFolder", true, "allow empty folders")
|
||||
filerS3Options.allowDeleteBucketNotEmpty = cmdFiler.Flag.Bool("s3.allowDeleteBucketNotEmpty", true, "allow recursive deleting all entries along with bucket")
|
||||
|
||||
// start webdav on filer
|
||||
filerStartWebDav = cmdFiler.Flag.Bool("webdav", false, "whether to start webdav gateway")
|
||||
@@ -199,6 +203,7 @@ func (fo *FilerOptions) startFiler() {
|
||||
|
||||
fs, nfs_err := weed_server.NewFilerServer(defaultMux, publicVolumeMux, &weed_server.FilerOption{
|
||||
Masters: fo.masters,
|
||||
FilerGroup: *fo.filerGroup,
|
||||
Collection: *fo.collection,
|
||||
DefaultReplication: *fo.defaultReplicaPlacement,
|
||||
DisableDirListing: *fo.disableDirListing,
|
||||
@@ -247,18 +252,6 @@ func (fo *FilerOptions) startFiler() {
|
||||
glog.Fatalf("Filer listener error: %v", e)
|
||||
}
|
||||
|
||||
// start on local unix socket
|
||||
if *fo.localSocket == "" {
|
||||
*fo.localSocket = fmt.Sprintf("/tmp/seaweefs-filer-%d.sock", *fo.port)
|
||||
if err := os.Remove(*fo.localSocket); err != nil && !os.IsNotExist(err) {
|
||||
glog.Fatalf("Failed to remove %s, error: %s", *fo.localSocket, err.Error())
|
||||
}
|
||||
}
|
||||
filerSocketListener, err := net.Listen("unix", *fo.localSocket)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to listen on %s: %v", *fo.localSocket, err)
|
||||
}
|
||||
|
||||
// starting grpc server
|
||||
grpcPort := *fo.portGrpc
|
||||
grpcL, grpcLocalL, err := util.NewIpAndLocalListeners(*fo.bindIp, grpcPort, 0)
|
||||
@@ -274,9 +267,22 @@ func (fo *FilerOptions) startFiler() {
|
||||
go grpcS.Serve(grpcL)
|
||||
|
||||
httpS := &http.Server{Handler: defaultMux}
|
||||
go func() {
|
||||
httpS.Serve(filerSocketListener)
|
||||
}()
|
||||
if runtime.GOOS != "windows" {
|
||||
if *fo.localSocket == "" {
|
||||
*fo.localSocket = fmt.Sprintf("/tmp/seaweefs-filer-%d.sock", *fo.port)
|
||||
if err := os.Remove(*fo.localSocket); err != nil && !os.IsNotExist(err) {
|
||||
glog.Fatalf("Failed to remove %s, error: %s", *fo.localSocket, err.Error())
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
// start on local unix socket
|
||||
filerSocketListener, err := net.Listen("unix", *fo.localSocket)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to listen on %s: %v", *fo.localSocket, err)
|
||||
}
|
||||
httpS.Serve(filerSocketListener)
|
||||
}()
|
||||
}
|
||||
if filerLocalListener != nil {
|
||||
go func() {
|
||||
if err := httpS.Serve(filerLocalListener); err != nil {
|
||||
|
||||
@@ -71,7 +71,7 @@ var cmdFilerCopy = &Command{
|
||||
It can copy one or a list of files or folders.
|
||||
|
||||
If copying a whole folder recursively:
|
||||
All files under the folder and subfolders will be copyed.
|
||||
All files under the folder and sub folders will be copied.
|
||||
Optional parameter "-include" allows you to specify the file name patterns.
|
||||
|
||||
If "maxMB" is set to a positive number, files larger than it would be split into chunks.
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb"
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
elastic "github.com/olivere/elastic/v7"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -124,72 +121,3 @@ func runFilerMetaTail(cmd *Command, args []string) bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type EsDocument struct {
|
||||
Dir string `json:"dir,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
IsDirectory bool `json:"isDir,omitempty"`
|
||||
Size uint64 `json:"size,omitempty"`
|
||||
Uid uint32 `json:"uid,omitempty"`
|
||||
Gid uint32 `json:"gid,omitempty"`
|
||||
UserName string `json:"userName,omitempty"`
|
||||
Collection string `json:"collection,omitempty"`
|
||||
Crtime int64 `json:"crtime,omitempty"`
|
||||
Mtime int64 `json:"mtime,omitempty"`
|
||||
Mime string `json:"mime,omitempty"`
|
||||
}
|
||||
|
||||
func toEsEntry(event *filer_pb.EventNotification) (*EsDocument, string) {
|
||||
entry := event.NewEntry
|
||||
dir, name := event.NewParentPath, entry.Name
|
||||
id := util.Md5String([]byte(util.NewFullPath(dir, name)))
|
||||
esEntry := &EsDocument{
|
||||
Dir: dir,
|
||||
Name: name,
|
||||
IsDirectory: entry.IsDirectory,
|
||||
Size: entry.Attributes.FileSize,
|
||||
Uid: entry.Attributes.Uid,
|
||||
Gid: entry.Attributes.Gid,
|
||||
UserName: entry.Attributes.UserName,
|
||||
Collection: entry.Attributes.Collection,
|
||||
Crtime: entry.Attributes.Crtime,
|
||||
Mtime: entry.Attributes.Mtime,
|
||||
Mime: entry.Attributes.Mime,
|
||||
}
|
||||
return esEntry, id
|
||||
}
|
||||
|
||||
func sendToElasticSearchFunc(servers string, esIndex string) (func(resp *filer_pb.SubscribeMetadataResponse) error, error) {
|
||||
options := []elastic.ClientOptionFunc{}
|
||||
options = append(options, elastic.SetURL(strings.Split(servers, ",")...))
|
||||
options = append(options, elastic.SetSniff(false))
|
||||
options = append(options, elastic.SetHealthcheck(false))
|
||||
client, err := elastic.NewClient(options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(resp *filer_pb.SubscribeMetadataResponse) error {
|
||||
event := resp.EventNotification
|
||||
if event.OldEntry != nil &&
|
||||
(event.NewEntry == nil || resp.Directory != event.NewParentPath || event.OldEntry.Name != event.NewEntry.Name) {
|
||||
// delete or not update the same file
|
||||
dir, name := resp.Directory, event.OldEntry.Name
|
||||
id := util.Md5String([]byte(util.NewFullPath(dir, name)))
|
||||
println("delete", id)
|
||||
_, err := client.Delete().Index(esIndex).Id(id).Do(context.Background())
|
||||
return err
|
||||
}
|
||||
if event.NewEntry != nil {
|
||||
// add a new file or update the same file
|
||||
esEntry, id := toEsEntry(event)
|
||||
value, err := jsoniter.Marshal(esEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println(string(value))
|
||||
_, err = client.Index().Index(esIndex).Id(id).BodyJson(string(value)).Do(context.Background())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
//go:build elastic
|
||||
// +build elastic
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/util"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
elastic "github.com/olivere/elastic/v7"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type EsDocument struct {
|
||||
Dir string `json:"dir,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
IsDirectory bool `json:"isDir,omitempty"`
|
||||
Size uint64 `json:"size,omitempty"`
|
||||
Uid uint32 `json:"uid,omitempty"`
|
||||
Gid uint32 `json:"gid,omitempty"`
|
||||
UserName string `json:"userName,omitempty"`
|
||||
Collection string `json:"collection,omitempty"`
|
||||
Crtime int64 `json:"crtime,omitempty"`
|
||||
Mtime int64 `json:"mtime,omitempty"`
|
||||
Mime string `json:"mime,omitempty"`
|
||||
}
|
||||
|
||||
func toEsEntry(event *filer_pb.EventNotification) (*EsDocument, string) {
|
||||
entry := event.NewEntry
|
||||
dir, name := event.NewParentPath, entry.Name
|
||||
id := util.Md5String([]byte(util.NewFullPath(dir, name)))
|
||||
esEntry := &EsDocument{
|
||||
Dir: dir,
|
||||
Name: name,
|
||||
IsDirectory: entry.IsDirectory,
|
||||
Size: entry.Attributes.FileSize,
|
||||
Uid: entry.Attributes.Uid,
|
||||
Gid: entry.Attributes.Gid,
|
||||
UserName: entry.Attributes.UserName,
|
||||
Collection: entry.Attributes.Collection,
|
||||
Crtime: entry.Attributes.Crtime,
|
||||
Mtime: entry.Attributes.Mtime,
|
||||
Mime: entry.Attributes.Mime,
|
||||
}
|
||||
return esEntry, id
|
||||
}
|
||||
|
||||
func sendToElasticSearchFunc(servers string, esIndex string) (func(resp *filer_pb.SubscribeMetadataResponse) error, error) {
|
||||
options := []elastic.ClientOptionFunc{}
|
||||
options = append(options, elastic.SetURL(strings.Split(servers, ",")...))
|
||||
options = append(options, elastic.SetSniff(false))
|
||||
options = append(options, elastic.SetHealthcheck(false))
|
||||
client, err := elastic.NewClient(options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(resp *filer_pb.SubscribeMetadataResponse) error {
|
||||
event := resp.EventNotification
|
||||
if event.OldEntry != nil &&
|
||||
(event.NewEntry == nil || resp.Directory != event.NewParentPath || event.OldEntry.Name != event.NewEntry.Name) {
|
||||
// delete or not update the same file
|
||||
dir, name := resp.Directory, event.OldEntry.Name
|
||||
id := util.Md5String([]byte(util.NewFullPath(dir, name)))
|
||||
println("delete", id)
|
||||
_, err := client.Delete().Index(esIndex).Id(id).Do(context.Background())
|
||||
return err
|
||||
}
|
||||
if event.NewEntry != nil {
|
||||
// add a new file or update the same file
|
||||
esEntry, id := toEsEntry(event)
|
||||
value, err := jsoniter.Marshal(esEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println(string(value))
|
||||
_, err = client.Index().Index(esIndex).Id(id).BodyJson(string(value)).Do(context.Background())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//go:build !elastic
|
||||
// +build !elastic
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
|
||||
)
|
||||
|
||||
func sendToElasticSearchFunc(servers string, esIndex string) (func(resp *filer_pb.SubscribeMetadataResponse) error, error) {
|
||||
return func(resp *filer_pb.SubscribeMetadataResponse) error {
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
@@ -267,7 +267,10 @@ func genProcessFunction(sourcePath string, targetPath string, dataSink sink.Repl
|
||||
return nil
|
||||
}
|
||||
key := buildKey(dataSink, message, targetPath, sourceOldKey, sourcePath)
|
||||
return dataSink.DeleteEntry(key, message.OldEntry.IsDirectory, message.DeleteChunks, message.Signatures)
|
||||
if !dataSink.IsIncremental() {
|
||||
return dataSink.DeleteEntry(key, message.OldEntry.IsDirectory, message.DeleteChunks, message.Signatures)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handle new entries
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
_ "github.com/chrislusf/seaweedfs/weed/replication/sink/localsink"
|
||||
_ "github.com/chrislusf/seaweedfs/weed/replication/sink/s3sink"
|
||||
|
||||
_ "github.com/chrislusf/seaweedfs/weed/filer/arangodb"
|
||||
_ "github.com/chrislusf/seaweedfs/weed/filer/cassandra"
|
||||
_ "github.com/chrislusf/seaweedfs/weed/filer/elastic/v7"
|
||||
_ "github.com/chrislusf/seaweedfs/weed/filer/etcd"
|
||||
|
||||
+38
-15
@@ -1,9 +1,9 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"golang.org/x/exp/slices"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -48,6 +48,8 @@ type MasterOptions struct {
|
||||
metricsHttpPort *int
|
||||
heartbeatInterval *time.Duration
|
||||
electionTimeout *time.Duration
|
||||
raftHashicorp *bool
|
||||
raftBootstrap *bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -71,6 +73,8 @@ func init() {
|
||||
m.raftResumeState = cmdMaster.Flag.Bool("resumeState", false, "resume previous state on start master server")
|
||||
m.heartbeatInterval = cmdMaster.Flag.Duration("heartbeatInterval", 300*time.Millisecond, "heartbeat interval of master servers, and will be randomly multiplied by [1, 1.25)")
|
||||
m.electionTimeout = cmdMaster.Flag.Duration("electionTimeout", 10*time.Second, "election timeout of master servers")
|
||||
m.raftHashicorp = cmdMaster.Flag.Bool("raftHashicorp", false, "use hashicorp raft")
|
||||
m.raftBootstrap = cmdMaster.Flag.Bool("raftBootstrap", false, "Whether to bootstrap the Raft cluster")
|
||||
}
|
||||
|
||||
var cmdMaster = &Command{
|
||||
@@ -134,7 +138,7 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) {
|
||||
|
||||
masterPeers := make(map[string]pb.ServerAddress)
|
||||
for _, peer := range peers {
|
||||
masterPeers[peer.String()] = peer
|
||||
masterPeers[string(peer)] = peer
|
||||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
@@ -156,13 +160,25 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) {
|
||||
RaftResumeState: *masterOption.raftResumeState,
|
||||
HeartbeatInterval: *masterOption.heartbeatInterval,
|
||||
ElectionTimeout: *masterOption.electionTimeout,
|
||||
RaftBootstrap: *m.raftBootstrap,
|
||||
}
|
||||
raftServer, err := weed_server.NewRaftServer(raftServerOption)
|
||||
if raftServer == nil {
|
||||
glog.Fatalf("please verify %s is writable, see https://github.com/chrislusf/seaweedfs/issues/717: %s", *masterOption.metaFolder, err)
|
||||
var raftServer *weed_server.RaftServer
|
||||
var err error
|
||||
if *m.raftHashicorp {
|
||||
if raftServer, err = weed_server.NewHashicorpRaftServer(raftServerOption); err != nil {
|
||||
glog.Fatalf("NewHashicorpRaftServer: %s", err)
|
||||
}
|
||||
} else {
|
||||
raftServer, err = weed_server.NewRaftServer(raftServerOption)
|
||||
if raftServer == nil {
|
||||
glog.Fatalf("please verify %s is writable, see https://github.com/chrislusf/seaweedfs/issues/717: %s", *masterOption.metaFolder, err)
|
||||
}
|
||||
}
|
||||
ms.SetRaftServer(raftServer)
|
||||
r.HandleFunc("/cluster/status", raftServer.StatusHandler).Methods("GET")
|
||||
if *m.raftHashicorp {
|
||||
r.HandleFunc("/raft/stats", raftServer.StatsRaftHandler).Methods("GET")
|
||||
}
|
||||
// starting grpc server
|
||||
grpcPort := *masterOption.portGrpc
|
||||
grpcL, grpcLocalL, err := util.NewIpAndLocalListeners(*masterOption.ipBind, grpcPort, 0)
|
||||
@@ -171,7 +187,11 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) {
|
||||
}
|
||||
grpcS := pb.NewGrpcServer(security.LoadServerTLS(util.GetViper(), "grpc.master"))
|
||||
master_pb.RegisterSeaweedServer(grpcS, ms)
|
||||
protobuf.RegisterRaftServer(grpcS, raftServer)
|
||||
if *m.raftHashicorp {
|
||||
raftServer.TransportManager.Register(grpcS)
|
||||
} else {
|
||||
protobuf.RegisterRaftServer(grpcS, raftServer)
|
||||
}
|
||||
reflection.Register(grpcS)
|
||||
glog.V(0).Infof("Start Seaweed Master %s grpc server at %s:%d", util.Version(), *masterOption.ipBind, grpcPort)
|
||||
if grpcLocalL != nil {
|
||||
@@ -179,14 +199,17 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) {
|
||||
}
|
||||
go grpcS.Serve(grpcL)
|
||||
|
||||
go func() {
|
||||
time.Sleep(1500 * time.Millisecond)
|
||||
if ms.Topo.RaftServer.Leader() == "" && ms.Topo.RaftServer.IsLogEmpty() && isTheFirstOne(myMasterAddress, peers) {
|
||||
if ms.MasterClient.FindLeaderFromOtherPeers(myMasterAddress) == "" {
|
||||
raftServer.DoJoinCommand()
|
||||
timeSleep := 1500 * time.Millisecond
|
||||
if !*m.raftHashicorp {
|
||||
go func() {
|
||||
time.Sleep(timeSleep)
|
||||
if ms.Topo.RaftServer.Leader() == "" && ms.Topo.RaftServer.IsLogEmpty() && isTheFirstOne(myMasterAddress, peers) {
|
||||
if ms.MasterClient.FindLeaderFromOtherPeers(myMasterAddress) == "" {
|
||||
raftServer.DoJoinCommand()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}()
|
||||
}
|
||||
|
||||
go ms.MasterClient.KeepConnectedToMaster()
|
||||
|
||||
@@ -251,8 +274,8 @@ func checkPeers(masterIp string, masterPort int, masterGrpcPort int, peers strin
|
||||
}
|
||||
|
||||
func isTheFirstOne(self pb.ServerAddress, peers []pb.ServerAddress) bool {
|
||||
sort.Slice(peers, func(i, j int) bool {
|
||||
return strings.Compare(string(peers[i]), string(peers[j])) < 0
|
||||
slices.SortFunc(peers, func(a, b pb.ServerAddress) bool {
|
||||
return strings.Compare(string(a), string(b)) < 0
|
||||
})
|
||||
if len(peers) <= 0 {
|
||||
return true
|
||||
|
||||
@@ -29,6 +29,7 @@ type MountOptions struct {
|
||||
readOnly *bool
|
||||
debug *bool
|
||||
debugPort *int
|
||||
localSocket *string
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -63,6 +64,7 @@ func init() {
|
||||
mountOptions.readOnly = cmdMount.Flag.Bool("readOnly", false, "read only")
|
||||
mountOptions.debug = cmdMount.Flag.Bool("debug", false, "serves runtime profiling data, e.g., http://localhost:<debug.port>/debug/pprof/goroutine?debug=2")
|
||||
mountOptions.debugPort = cmdMount.Flag.Int("debug.port", 6061, "http port for debugging")
|
||||
mountOptions.localSocket = cmdMount.Flag.String("localSocket", "", "default to /tmp/seaweedfs-mount-<mount_dir_hash>.sock")
|
||||
|
||||
mountCpuProfile = cmdMount.Flag.String("cpuprofile", "", "cpu profile output file")
|
||||
mountMemProfile = cmdMount.Flag.String("memprofile", "", "memory profile output file")
|
||||
|
||||
@@ -12,9 +12,12 @@ import (
|
||||
"github.com/chrislusf/seaweedfs/weed/mount/unmount"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/mount_pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/security"
|
||||
"github.com/chrislusf/seaweedfs/weed/storage/types"
|
||||
"github.com/hanwen/go-fuse/v2/fuse"
|
||||
"google.golang.org/grpc/reflection"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/user"
|
||||
@@ -98,6 +101,22 @@ func RunMount(option *MountOptions, umask os.FileMode) bool {
|
||||
|
||||
unmount.Unmount(dir)
|
||||
|
||||
// start on local unix socket
|
||||
if *option.localSocket == "" {
|
||||
mountDirHash := util.HashToInt32([]byte(dir))
|
||||
if mountDirHash < 0 {
|
||||
mountDirHash = -mountDirHash
|
||||
}
|
||||
*option.localSocket = fmt.Sprintf("/tmp/seaweefs-mount-%d.sock", mountDirHash)
|
||||
}
|
||||
if err := os.Remove(*option.localSocket); err != nil && !os.IsNotExist(err) {
|
||||
glog.Fatalf("Failed to remove %s, error: %s", *option.localSocket, err.Error())
|
||||
}
|
||||
montSocketListener, err := net.Listen("unix", *option.localSocket)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to listen on %s: %v", *option.localSocket, err)
|
||||
}
|
||||
|
||||
// detect mount folder mode
|
||||
if *option.dirAutoCreate {
|
||||
os.MkdirAll(dir, os.FileMode(0777)&^umask)
|
||||
@@ -229,6 +248,11 @@ func RunMount(option *MountOptions, umask os.FileMode) bool {
|
||||
unmount.Unmount(dir)
|
||||
})
|
||||
|
||||
grpcS := pb.NewGrpcServer()
|
||||
mount_pb.RegisterSeaweedMountServer(grpcS, seaweedFileSystem)
|
||||
reflection.Register(grpcS)
|
||||
go grpcS.Serve(montSocketListener)
|
||||
|
||||
seaweedFileSystem.StartBackgroundTasks()
|
||||
|
||||
fmt.Printf("This is SeaweedFS version %s %s %s\n", util.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
+22
-19
@@ -24,17 +24,18 @@ var (
|
||||
)
|
||||
|
||||
type S3Options struct {
|
||||
filer *string
|
||||
bindIp *string
|
||||
port *int
|
||||
config *string
|
||||
domainName *string
|
||||
tlsPrivateKey *string
|
||||
tlsCertificate *string
|
||||
metricsHttpPort *int
|
||||
allowEmptyFolder *bool
|
||||
auditLogConfig *string
|
||||
localFilerSocket *string
|
||||
filer *string
|
||||
bindIp *string
|
||||
port *int
|
||||
config *string
|
||||
domainName *string
|
||||
tlsPrivateKey *string
|
||||
tlsCertificate *string
|
||||
metricsHttpPort *int
|
||||
allowEmptyFolder *bool
|
||||
allowDeleteBucketNotEmpty *bool
|
||||
auditLogConfig *string
|
||||
localFilerSocket *string
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -49,6 +50,7 @@ func init() {
|
||||
s3StandaloneOptions.tlsCertificate = cmdS3.Flag.String("cert.file", "", "path to the TLS certificate file")
|
||||
s3StandaloneOptions.metricsHttpPort = cmdS3.Flag.Int("metricsPort", 0, "Prometheus metrics listen port")
|
||||
s3StandaloneOptions.allowEmptyFolder = cmdS3.Flag.Bool("allowEmptyFolder", true, "allow empty folders")
|
||||
s3StandaloneOptions.allowDeleteBucketNotEmpty = cmdS3.Flag.Bool("allowDeleteBucketNotEmpty", true, "allow recursive deleting all entries along with bucket")
|
||||
}
|
||||
|
||||
var cmdS3 = &Command{
|
||||
@@ -178,14 +180,15 @@ func (s3opt *S3Options) startS3Server() bool {
|
||||
router := mux.NewRouter().SkipClean(true)
|
||||
|
||||
_, s3ApiServer_err := s3api.NewS3ApiServer(router, &s3api.S3ApiServerOption{
|
||||
Filer: filerAddress,
|
||||
Port: *s3opt.port,
|
||||
Config: *s3opt.config,
|
||||
DomainName: *s3opt.domainName,
|
||||
BucketsPath: filerBucketsPath,
|
||||
GrpcDialOption: grpcDialOption,
|
||||
AllowEmptyFolder: *s3opt.allowEmptyFolder,
|
||||
LocalFilerSocket: s3opt.localFilerSocket,
|
||||
Filer: filerAddress,
|
||||
Port: *s3opt.port,
|
||||
Config: *s3opt.config,
|
||||
DomainName: *s3opt.domainName,
|
||||
BucketsPath: filerBucketsPath,
|
||||
GrpcDialOption: grpcDialOption,
|
||||
AllowEmptyFolder: *s3opt.allowEmptyFolder,
|
||||
AllowDeleteBucketNotEmpty: *s3opt.allowDeleteBucketNotEmpty,
|
||||
LocalFilerSocket: s3opt.localFilerSocket,
|
||||
})
|
||||
if s3ApiServer_err != nil {
|
||||
glog.Fatalf("S3 API Server startup error: %v", s3ApiServer_err)
|
||||
|
||||
@@ -285,6 +285,16 @@ healthcheck_enabled = false
|
||||
index.max_result_window = 10000
|
||||
|
||||
|
||||
[arangodb] # in development dont use it
|
||||
enabled = false
|
||||
db_name = "seaweedfs"
|
||||
servers=["http://localhost:8529"] # list of servers to connect to
|
||||
# only basic auth supported for now
|
||||
username=""
|
||||
password=""
|
||||
# skip tls cert validation
|
||||
insecure_skip_verify = true
|
||||
|
||||
|
||||
##########################
|
||||
##########################
|
||||
|
||||
@@ -36,6 +36,7 @@ aws_secret_access_key = "" # if empty, loads from the shared credentials fil
|
||||
region = "us-east-2"
|
||||
bucket = "your_bucket_name" # an existing bucket
|
||||
endpoint = ""
|
||||
storage_class = "STANDARD_IA"
|
||||
|
||||
# create this number of logical volumes if no more writable volumes
|
||||
# count_x means how many copies of data.
|
||||
|
||||
@@ -101,6 +101,7 @@ func init() {
|
||||
masterOptions.heartbeatInterval = cmdServer.Flag.Duration("master.heartbeatInterval", 300*time.Millisecond, "heartbeat interval of master servers, and will be randomly multiplied by [1, 1.25)")
|
||||
masterOptions.electionTimeout = cmdServer.Flag.Duration("master.electionTimeout", 10*time.Second, "election timeout of master servers")
|
||||
|
||||
filerOptions.filerGroup = cmdServer.Flag.String("filer.filerGroup", "", "share metadata with other filers in the same filerGroup")
|
||||
filerOptions.collection = cmdServer.Flag.String("filer.collection", "", "all data will be stored in this collection")
|
||||
filerOptions.port = cmdServer.Flag.Int("filer.port", 8888, "filer server http listen port")
|
||||
filerOptions.portGrpc = cmdServer.Flag.Int("filer.port.grpc", 0, "filer server grpc listen port")
|
||||
@@ -138,6 +139,7 @@ func init() {
|
||||
s3Options.config = cmdServer.Flag.String("s3.config", "", "path to the config file")
|
||||
s3Options.auditLogConfig = cmdServer.Flag.String("s3.auditLogConfig", "", "path to the audit log config file")
|
||||
s3Options.allowEmptyFolder = cmdServer.Flag.Bool("s3.allowEmptyFolder", true, "allow empty folders")
|
||||
s3Options.allowDeleteBucketNotEmpty = cmdServer.Flag.Bool("s3.allowDeleteBucketNotEmpty", true, "allow recursive deleting all entries along with bucket")
|
||||
|
||||
iamOptions.port = cmdServer.Flag.Int("iam.port", 8111, "iam server http listen port")
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ var (
|
||||
func init() {
|
||||
cmdShell.Run = runShell // break init cycle
|
||||
shellOptions.Masters = cmdShell.Flag.String("master", "", "comma-separated master servers, e.g. localhost:9333")
|
||||
shellOptions.FilerGroup = cmdShell.Flag.String("filerGroup", "", "filerGroup for the filers")
|
||||
shellInitialFiler = cmdShell.Flag.String("filer", "", "filer host and port, e.g. localhost:8888")
|
||||
shellCluster = cmdShell.Flag.String("cluster", "", "cluster defined in shell.toml")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
package arangodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
"github.com/arangodb/go-driver/http"
|
||||
"github.com/chrislusf/seaweedfs/weed/filer"
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
filer.Stores = append(filer.Stores, &ArangodbStore{})
|
||||
}
|
||||
|
||||
var (
|
||||
BUCKET_PREFIX = "/buckets"
|
||||
DEFAULT_COLLECTION = "seaweed_no_bucket"
|
||||
KVMETA_COLLECTION = "seaweed_kvmeta"
|
||||
)
|
||||
|
||||
type ArangodbStore struct {
|
||||
connect driver.Connection
|
||||
client driver.Client
|
||||
database driver.Database
|
||||
kvCollection driver.Collection
|
||||
|
||||
buckets map[string]driver.Collection
|
||||
mu sync.RWMutex
|
||||
|
||||
databaseName string
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
Key string `json:"_key"`
|
||||
Directory string `json:"directory,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Ttl string `json:"ttl,omitempty"`
|
||||
|
||||
//arangodb does not support binary blobs
|
||||
//we encode byte slice into uint64 slice
|
||||
//see helpers.go
|
||||
Meta []uint64 `json:"meta"`
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) GetName() string {
|
||||
return "arangodb"
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) Initialize(configuration util.Configuration, prefix string) (err error) {
|
||||
store.buckets = make(map[string]driver.Collection, 3)
|
||||
store.databaseName = configuration.GetString(prefix + "db_name")
|
||||
return store.connection(configuration.GetStringSlice(prefix+"servers"),
|
||||
configuration.GetString(prefix+"username"),
|
||||
configuration.GetString(prefix+"password"),
|
||||
configuration.GetBool(prefix+"insecure_skip_verify"),
|
||||
)
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) connection(uris []string, user string, pass string, insecure bool) (err error) {
|
||||
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
|
||||
store.connect, err = http.NewConnection(http.ConnectionConfig{
|
||||
Endpoints: uris,
|
||||
TLSConfig: &tls.Config{
|
||||
InsecureSkipVerify: insecure,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
store.client, err = driver.NewClient(driver.ClientConfig{
|
||||
Connection: store.connect,
|
||||
Authentication: driver.BasicAuthentication(user, pass),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ok, err := store.client.DatabaseExists(ctx, store.databaseName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
store.database, err = store.client.Database(ctx, store.databaseName)
|
||||
} else {
|
||||
store.database, err = store.client.CreateDatabase(ctx, store.databaseName, &driver.CreateDatabaseOptions{})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if store.kvCollection, err = store.ensureCollection(ctx, KVMETA_COLLECTION); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type key int
|
||||
|
||||
const (
|
||||
transactionKey key = 0
|
||||
)
|
||||
|
||||
func (store *ArangodbStore) BeginTransaction(ctx context.Context) (context.Context, error) {
|
||||
keys := make([]string, 0, len(store.buckets)+1)
|
||||
for k := range store.buckets {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
keys = append(keys, store.kvCollection.Name())
|
||||
txn, err := store.database.BeginTransaction(ctx, driver.TransactionCollections{
|
||||
Exclusive: keys,
|
||||
}, &driver.BeginTransactionOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return context.WithValue(ctx, transactionKey, txn), nil
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) CommitTransaction(ctx context.Context) error {
|
||||
val := ctx.Value(transactionKey)
|
||||
cast, ok := val.(driver.TransactionID)
|
||||
if !ok {
|
||||
return fmt.Errorf("txn cast fail %s:", val)
|
||||
}
|
||||
err := store.database.CommitTransaction(ctx, cast, &driver.CommitTransactionOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) RollbackTransaction(ctx context.Context) error {
|
||||
val := ctx.Value(transactionKey)
|
||||
cast, ok := val.(driver.TransactionID)
|
||||
if !ok {
|
||||
return fmt.Errorf("txn cast fail %s:", val)
|
||||
}
|
||||
err := store.database.AbortTransaction(ctx, cast, &driver.AbortTransactionOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) InsertEntry(ctx context.Context, entry *filer.Entry) (err error) {
|
||||
dir, name := entry.FullPath.DirAndName()
|
||||
meta, err := entry.EncodeAttributesAndChunks()
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode %s: %s", entry.FullPath, err)
|
||||
}
|
||||
|
||||
if len(entry.Chunks) > 50 {
|
||||
meta = util.MaybeGzipData(meta)
|
||||
}
|
||||
model := &Model{
|
||||
Key: hashString(string(entry.FullPath)),
|
||||
Directory: dir,
|
||||
Name: name,
|
||||
Meta: bytesToArray(meta),
|
||||
}
|
||||
if entry.TtlSec > 0 {
|
||||
model.Ttl = time.Now().Add(time.Second * time.Duration(entry.TtlSec)).Format(time.RFC3339)
|
||||
} else {
|
||||
model.Ttl = ""
|
||||
}
|
||||
|
||||
targetCollection, err := store.extractBucketCollection(ctx, entry.FullPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = targetCollection.CreateDocument(ctx, model)
|
||||
if driver.IsConflict(err) {
|
||||
return store.UpdateEntry(ctx, entry)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("InsertEntry %s: %v", entry.FullPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) UpdateEntry(ctx context.Context, entry *filer.Entry) (err error) {
|
||||
dir, name := entry.FullPath.DirAndName()
|
||||
meta, err := entry.EncodeAttributesAndChunks()
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode %s: %s", entry.FullPath, err)
|
||||
}
|
||||
|
||||
if len(entry.Chunks) > 50 {
|
||||
meta = util.MaybeGzipData(meta)
|
||||
}
|
||||
model := &Model{
|
||||
Key: hashString(string(entry.FullPath)),
|
||||
Directory: dir,
|
||||
Name: name,
|
||||
Meta: bytesToArray(meta),
|
||||
}
|
||||
if entry.TtlSec > 0 {
|
||||
model.Ttl = time.Now().Add(time.Duration(entry.TtlSec) * time.Second).Format(time.RFC3339)
|
||||
} else {
|
||||
model.Ttl = "none"
|
||||
}
|
||||
targetCollection, err := store.extractBucketCollection(ctx, entry.FullPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = targetCollection.UpdateDocument(ctx, model.Key, model)
|
||||
if err != nil {
|
||||
return fmt.Errorf("UpdateEntry %s: %v", entry.FullPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) FindEntry(ctx context.Context, fullpath util.FullPath) (entry *filer.Entry, err error) {
|
||||
var data Model
|
||||
targetCollection, err := store.extractBucketCollection(ctx, fullpath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = targetCollection.ReadDocument(ctx, hashString(string(fullpath)), &data)
|
||||
if err != nil {
|
||||
if driver.IsNotFound(err) {
|
||||
return nil, filer_pb.ErrNotFound
|
||||
}
|
||||
glog.Errorf("find %s: %v", fullpath, err)
|
||||
return nil, filer_pb.ErrNotFound
|
||||
}
|
||||
if len(data.Meta) == 0 {
|
||||
return nil, filer_pb.ErrNotFound
|
||||
}
|
||||
entry = &filer.Entry{
|
||||
FullPath: fullpath,
|
||||
}
|
||||
err = entry.DecodeAttributesAndChunks(util.MaybeDecompressData(arrayToBytes(data.Meta)))
|
||||
if err != nil {
|
||||
return entry, fmt.Errorf("decode %s : %v", entry.FullPath, err)
|
||||
}
|
||||
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) DeleteEntry(ctx context.Context, fullpath util.FullPath) (err error) {
|
||||
targetCollection, err := store.extractBucketCollection(ctx, fullpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = targetCollection.RemoveDocument(ctx, hashString(string(fullpath)))
|
||||
if err != nil && !driver.IsNotFound(err) {
|
||||
glog.Errorf("find %s: %v", fullpath, err)
|
||||
return fmt.Errorf("delete %s : %v", fullpath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// this runs in log time
|
||||
func (store *ArangodbStore) DeleteFolderChildren(ctx context.Context, fullpath util.FullPath) (err error) {
|
||||
var query string
|
||||
targetCollection, err := store.extractBucketCollection(ctx, fullpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
query = query + fmt.Sprintf(`
|
||||
for d in %s
|
||||
filter starts_with(d.directory, "%s/") || d.directory == "%s"
|
||||
remove d._key in %s`,
|
||||
targetCollection.Name(),
|
||||
strings.Join(strings.Split(string(fullpath), "/"), ","),
|
||||
string(fullpath),
|
||||
targetCollection.Name(),
|
||||
)
|
||||
cur, err := store.database.Query(ctx, query, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete %s : %v", fullpath, err)
|
||||
}
|
||||
defer cur.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) ListDirectoryEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) {
|
||||
return store.ListDirectoryPrefixedEntries(ctx, dirPath, startFileName, includeStartFile, limit, "", eachEntryFunc)
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) ListDirectoryPrefixedEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, prefix string, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) {
|
||||
targetCollection, err := store.extractBucketCollection(ctx, dirPath+"/")
|
||||
if err != nil {
|
||||
return lastFileName, err
|
||||
}
|
||||
query := "for d in " + targetCollection.Name()
|
||||
if includeStartFile {
|
||||
query = query + " filter d.name >= \"" + startFileName + "\" "
|
||||
} else {
|
||||
query = query + " filter d.name > \"" + startFileName + "\" "
|
||||
}
|
||||
if prefix != "" {
|
||||
query = query + fmt.Sprintf(`&& starts_with(d.name, "%s")`, prefix)
|
||||
}
|
||||
query = query + `
|
||||
filter d.directory == @dir
|
||||
sort d.name asc
|
||||
`
|
||||
if limit > 0 {
|
||||
query = query + "limit " + strconv.Itoa(int(limit))
|
||||
}
|
||||
query = query + "\n return d"
|
||||
cur, err := store.database.Query(ctx, query, map[string]interface{}{"dir": dirPath})
|
||||
if err != nil {
|
||||
return lastFileName, fmt.Errorf("failed to list directory entries: find error: %w", err)
|
||||
}
|
||||
defer cur.Close()
|
||||
for cur.HasMore() {
|
||||
var data Model
|
||||
_, err = cur.ReadDocument(ctx, &data)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
entry := &filer.Entry{
|
||||
FullPath: util.NewFullPath(data.Directory, data.Name),
|
||||
}
|
||||
lastFileName = data.Name
|
||||
converted := arrayToBytes(data.Meta)
|
||||
if decodeErr := entry.DecodeAttributesAndChunks(util.MaybeDecompressData(converted)); decodeErr != nil {
|
||||
err = decodeErr
|
||||
glog.V(0).Infof("list %s : %v", entry.FullPath, err)
|
||||
break
|
||||
}
|
||||
|
||||
if !eachEntryFunc(entry) {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
return lastFileName, err
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) Shutdown() {
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package arangodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/arangodb/go-driver"
|
||||
"time"
|
||||
|
||||
"github.com/chrislusf/seaweedfs/weed/filer"
|
||||
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
)
|
||||
|
||||
var _ filer.BucketAware = (*ArangodbStore)(nil)
|
||||
|
||||
func (store *ArangodbStore) OnBucketCreation(bucket string) {
|
||||
timeout, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
// create the collection && add to cache
|
||||
_, err := store.ensureBucket(timeout, bucket)
|
||||
if err != nil {
|
||||
glog.Errorf("bucket create %s: %v", bucket, err)
|
||||
}
|
||||
}
|
||||
func (store *ArangodbStore) OnBucketDeletion(bucket string) {
|
||||
timeout, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
collection, err := store.ensureBucket(timeout, bucket)
|
||||
if err != nil {
|
||||
glog.Errorf("bucket delete %s: %v", bucket, err)
|
||||
return
|
||||
}
|
||||
err = collection.Remove(timeout)
|
||||
if err != nil && !driver.IsNotFound(err) {
|
||||
glog.Errorf("bucket delete %s: %v", bucket, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
func (store *ArangodbStore) CanDropWholeBucket() bool {
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package arangodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
"github.com/chrislusf/seaweedfs/weed/filer"
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
)
|
||||
|
||||
func (store *ArangodbStore) KvPut(ctx context.Context, key []byte, value []byte) (err error) {
|
||||
model := &Model{
|
||||
Key: hashString(".kvstore." + string(key)),
|
||||
Directory: ".kvstore." + string(key),
|
||||
Meta: bytesToArray(value),
|
||||
}
|
||||
|
||||
exists, err := store.kvCollection.DocumentExists(ctx, model.Key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("kv put: %v", err)
|
||||
}
|
||||
if exists {
|
||||
_, err = store.kvCollection.UpdateDocument(ctx, model.Key, model)
|
||||
} else {
|
||||
_, err = store.kvCollection.CreateDocument(ctx, model)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("kv put: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (store *ArangodbStore) KvGet(ctx context.Context, key []byte) (value []byte, err error) {
|
||||
var model Model
|
||||
_, err = store.kvCollection.ReadDocument(ctx, hashString(".kvstore."+string(key)), &model)
|
||||
if driver.IsNotFound(err) {
|
||||
return nil, filer.ErrKvNotFound
|
||||
}
|
||||
if err != nil {
|
||||
glog.Errorf("kv get: %s %v", string(key), err)
|
||||
return nil, filer.ErrKvNotFound
|
||||
}
|
||||
return arrayToBytes(model.Meta), nil
|
||||
}
|
||||
|
||||
func (store *ArangodbStore) KvDelete(ctx context.Context, key []byte) (err error) {
|
||||
_, err = store.kvCollection.RemoveDocument(ctx, hashString(".kvstore."+string(key)))
|
||||
if err != nil {
|
||||
glog.Errorf("kv del: %v", err)
|
||||
return filer.ErrKvNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package arangodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
"github.com/chrislusf/seaweedfs/weed/util"
|
||||
)
|
||||
|
||||
//convert a string into arango-key safe hex bytes hash
|
||||
func hashString(dir string) string {
|
||||
h := md5.New()
|
||||
io.WriteString(h, dir)
|
||||
b := h.Sum(nil)
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
// convert slice of bytes into slice of uint64
|
||||
// the first uint64 indicates the length in bytes
|
||||
func bytesToArray(bs []byte) []uint64 {
|
||||
out := make([]uint64, 0, 2+len(bs)/8)
|
||||
out = append(out, uint64(len(bs)))
|
||||
for len(bs)%8 != 0 {
|
||||
bs = append(bs, 0)
|
||||
}
|
||||
for i := 0; i < len(bs); i = i + 8 {
|
||||
out = append(out, binary.BigEndian.Uint64(bs[i:]))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// convert from slice of uint64 back to bytes
|
||||
// if input length is 0 or 1, will return nil
|
||||
func arrayToBytes(xs []uint64) []byte {
|
||||
if len(xs) < 2 {
|
||||
return nil
|
||||
}
|
||||
first := xs[0]
|
||||
out := make([]byte, len(xs)*8) // i think this can actually be len(xs)*8-8, but i dont think an extra 8 bytes hurts...
|
||||
for i := 1; i < len(xs); i = i + 1 {
|
||||
binary.BigEndian.PutUint64(out[((i-1)*8):], xs[i])
|
||||
}
|
||||
return out[:first]
|
||||
}
|
||||
|
||||
// gets the collection the bucket points to from filepath
|
||||
func (store *ArangodbStore) extractBucketCollection(ctx context.Context, fullpath util.FullPath) (c driver.Collection, err error) {
|
||||
bucket, _ := extractBucket(fullpath)
|
||||
if bucket == "" {
|
||||
bucket = DEFAULT_COLLECTION
|
||||
}
|
||||
c, err = store.ensureBucket(ctx, bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, err
|
||||
}
|
||||
|
||||
// called by extractBucketCollection
|
||||
func extractBucket(fullpath util.FullPath) (string, string) {
|
||||
if !strings.HasPrefix(string(fullpath), BUCKET_PREFIX+"/") {
|
||||
return "", string(fullpath)
|
||||
}
|
||||
if strings.Count(string(fullpath), "/") < 3 {
|
||||
return "", string(fullpath)
|
||||
}
|
||||
bucketAndObjectKey := string(fullpath)[len(BUCKET_PREFIX+"/"):]
|
||||
t := strings.Index(bucketAndObjectKey, "/")
|
||||
bucket := bucketAndObjectKey
|
||||
shortPath := "/"
|
||||
if t > 0 {
|
||||
bucket = bucketAndObjectKey[:t]
|
||||
shortPath = string(util.FullPath(bucketAndObjectKey[t:]))
|
||||
}
|
||||
return bucket, shortPath
|
||||
}
|
||||
|
||||
// get bucket collection from cache. if not exist, creates the buckets collection and grab it
|
||||
func (store *ArangodbStore) ensureBucket(ctx context.Context, bucket string) (bc driver.Collection, err error) {
|
||||
var ok bool
|
||||
store.mu.RLock()
|
||||
bc, ok = store.buckets[bucket]
|
||||
store.mu.RUnlock()
|
||||
if ok {
|
||||
return bc, nil
|
||||
}
|
||||
store.mu.Lock()
|
||||
defer store.mu.Unlock()
|
||||
store.buckets[bucket], err = store.ensureCollection(ctx, bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return store.buckets[bucket], nil
|
||||
}
|
||||
|
||||
// creates collection if not exist, ensures indices if not exist
|
||||
func (store *ArangodbStore) ensureCollection(ctx context.Context, name string) (c driver.Collection, err error) {
|
||||
ok, err := store.database.CollectionExists(ctx, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
c, err = store.database.Collection(ctx, name)
|
||||
} else {
|
||||
c, err = store.database.CreateCollection(ctx, name, &driver.CreateCollectionOptions{})
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// ensure indices
|
||||
if _, _, err = c.EnsurePersistentIndex(ctx, []string{"directory", "name"},
|
||||
&driver.EnsurePersistentIndexOptions{
|
||||
Name: "directory_name_multi", Unique: true,
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
if _, _, err = c.EnsurePersistentIndex(ctx, []string{"directory"},
|
||||
&driver.EnsurePersistentIndexOptions{Name: "IDX_directory"}); err != nil {
|
||||
return
|
||||
}
|
||||
if _, _, err = c.EnsureTTLIndex(ctx, "ttl", 1,
|
||||
&driver.EnsureTTLIndexOptions{Name: "IDX_TTL"}); err != nil {
|
||||
return
|
||||
}
|
||||
if _, _, err = c.EnsurePersistentIndex(ctx, []string{"name"}, &driver.EnsurePersistentIndexOptions{
|
||||
Name: "IDX_name",
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
##arangodb
|
||||
|
||||
database: https://github.com/arangodb/arangodb
|
||||
go driver: https://github.com/arangodb/go-driver
|
||||
|
||||
options:
|
||||
|
||||
```
|
||||
[arangodb]
|
||||
enabled=true
|
||||
db_name="seaweedfs"
|
||||
servers=["http://localhost:8529"]
|
||||
#basic auth
|
||||
user="root"
|
||||
pass="test"
|
||||
|
||||
# tls settings
|
||||
insecure_skip_verify=true
|
||||
```
|
||||
|
||||
i test using this dev database:
|
||||
`docker run -p 8529:8529 -e ARANGO_ROOT_PASSWORD=test arangodb/arangodb:3.9.0`
|
||||
|
||||
|
||||
## features i don't personally need but are missing
|
||||
[ ] provide tls cert to arango
|
||||
[ ] authentication that is not basic auth
|
||||
[ ] synchronise endpoint interval config
|
||||
[ ] automatic creation of custom index
|
||||
[ ] configure default arangodb collection sharding rules
|
||||
[ ] configure default arangodb collection replication rules
|
||||
|
||||
|
||||
## complexity
|
||||
|
||||
ok, so if https://www.arangodb.com/docs/stable/indexing-index-basics.html#persistent-index is correct
|
||||
|
||||
O(1)
|
||||
- InsertEntry
|
||||
- UpdateEntry
|
||||
- FindEntry
|
||||
- DeleteEntry
|
||||
- KvPut
|
||||
- KvGet
|
||||
- KvDelete
|
||||
|
||||
O(log(BUCKET_SIZE))
|
||||
- DeleteFolderChildren
|
||||
|
||||
O(log(DIRECTORY_SIZE))
|
||||
- ListDirectoryEntries
|
||||
- ListDirectoryPrefixedEntries
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
|
||||
Package elastic is for elastic filer store.
|
||||
|
||||
The referenced "github.com/olivere/elastic/v7" library is too big when compiled.
|
||||
So this is only compiled in "make full_install".
|
||||
|
||||
*/
|
||||
package elastic
|
||||
@@ -1,3 +1,6 @@
|
||||
//go:build elastic
|
||||
// +build elastic
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//go:build elastic
|
||||
// +build elastic
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
|
||||
+11
-10
@@ -4,8 +4,8 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/wdclient"
|
||||
"golang.org/x/exp/slices"
|
||||
"math"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
|
||||
@@ -23,6 +23,9 @@ func TotalSize(chunks []*filer_pb.FileChunk) (size uint64) {
|
||||
}
|
||||
|
||||
func FileSize(entry *filer_pb.Entry) (size uint64) {
|
||||
if entry == nil || entry.Attributes == nil {
|
||||
return 0
|
||||
}
|
||||
fileSize := entry.Attributes.FileSize
|
||||
if entry.RemoteEntry != nil {
|
||||
if entry.RemoteEntry.RemoteMtime > entry.Attributes.Mtime {
|
||||
@@ -251,19 +254,17 @@ func NonOverlappingVisibleIntervals(lookupFileIdFn wdclient.LookupFileIdFunction
|
||||
if true {
|
||||
return visibles2, err
|
||||
}
|
||||
|
||||
sort.Slice(chunks, func(i, j int) bool {
|
||||
if chunks[i].Mtime == chunks[j].Mtime {
|
||||
filer_pb.EnsureFid(chunks[i])
|
||||
filer_pb.EnsureFid(chunks[j])
|
||||
if chunks[i].Fid == nil || chunks[j].Fid == nil {
|
||||
slices.SortFunc(chunks, func(a, b *filer_pb.FileChunk) bool {
|
||||
if a.Mtime == b.Mtime {
|
||||
filer_pb.EnsureFid(a)
|
||||
filer_pb.EnsureFid(b)
|
||||
if a.Fid == nil || b.Fid == nil {
|
||||
return true
|
||||
}
|
||||
return chunks[i].Fid.FileKey < chunks[j].Fid.FileKey
|
||||
return a.Fid.FileKey < b.Fid.FileKey
|
||||
}
|
||||
return chunks[i].Mtime < chunks[j].Mtime // keep this to make tests run
|
||||
return a.Mtime < b.Mtime
|
||||
})
|
||||
|
||||
for _, chunk := range chunks {
|
||||
|
||||
// glog.V(0).Infof("merge [%d,%d)", chunk.Offset, chunk.Offset+int64(chunk.Size))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package filer
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"golang.org/x/exp/slices"
|
||||
"testing"
|
||||
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
@@ -34,11 +34,11 @@ func TestCompactFileChunksRealCase(t *testing.T) {
|
||||
}
|
||||
|
||||
func printChunks(name string, chunks []*filer_pb.FileChunk) {
|
||||
sort.Slice(chunks, func(i, j int) bool {
|
||||
if chunks[i].Offset == chunks[j].Offset {
|
||||
return chunks[i].Mtime < chunks[j].Mtime
|
||||
slices.SortFunc(chunks, func(a, b *filer_pb.FileChunk) bool {
|
||||
if a.Offset == b.Offset {
|
||||
return a.Mtime < b.Mtime
|
||||
}
|
||||
return chunks[i].Offset < chunks[j].Offset
|
||||
return a.Offset < b.Offset
|
||||
})
|
||||
for _, chunk := range chunks {
|
||||
glog.V(0).Infof("%s chunk %s [%10d,%10d)", name, chunk.GetFileIdString(), chunk.Offset, chunk.Offset+int64(chunk.Size))
|
||||
|
||||
@@ -2,7 +2,7 @@ package filer
|
||||
|
||||
import (
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
|
||||
"sort"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func readResolvedChunks(chunks []*filer_pb.FileChunk) (visibles []VisibleInterval) {
|
||||
@@ -22,17 +22,14 @@ func readResolvedChunks(chunks []*filer_pb.FileChunk) (visibles []VisibleInterva
|
||||
isStart: false,
|
||||
})
|
||||
}
|
||||
sort.Slice(points, func(i, j int) bool {
|
||||
if points[i].x != points[j].x {
|
||||
return points[i].x < points[j].x
|
||||
slices.SortFunc(points, func(a, b *Point) bool {
|
||||
if a.x != b.x {
|
||||
return a.x < b.x
|
||||
}
|
||||
if points[i].ts != points[j].ts {
|
||||
return points[i].ts < points[j].ts
|
||||
if a.ts != b.ts {
|
||||
return a.ts < b.ts
|
||||
}
|
||||
if !points[i].isStart {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return !a.isStart
|
||||
})
|
||||
|
||||
var prevX int64
|
||||
|
||||
+4
-3
@@ -49,10 +49,10 @@ type Filer struct {
|
||||
UniqueFileId uint32
|
||||
}
|
||||
|
||||
func NewFiler(masters map[string]pb.ServerAddress, grpcDialOption grpc.DialOption,
|
||||
filerHost pb.ServerAddress, collection string, replication string, dataCenter string, notifyFn func()) *Filer {
|
||||
func NewFiler(masters map[string]pb.ServerAddress, grpcDialOption grpc.DialOption, filerHost pb.ServerAddress,
|
||||
filerGroup string, collection string, replication string, dataCenter string, notifyFn func()) *Filer {
|
||||
f := &Filer{
|
||||
MasterClient: wdclient.NewMasterClient(grpcDialOption, cluster.FilerType, filerHost, dataCenter, masters),
|
||||
MasterClient: wdclient.NewMasterClient(grpcDialOption, filerGroup, cluster.FilerType, filerHost, dataCenter, masters),
|
||||
fileIdDeletionQueue: util.NewUnboundedQueue(),
|
||||
GrpcDialOption: grpcDialOption,
|
||||
FilerConf: NewFilerConf(),
|
||||
@@ -84,6 +84,7 @@ func (f *Filer) ListExistingPeerUpdates() (existingNodes []*master_pb.ClusterNod
|
||||
if grpcErr := pb.WithMasterClient(false, f.MasterClient.GetMaster(), f.GrpcDialOption, func(client master_pb.SeaweedClient) error {
|
||||
resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
|
||||
ClientType: cluster.FilerType,
|
||||
FilerGroup: f.MasterClient.FilerGroup,
|
||||
})
|
||||
|
||||
glog.V(0).Infof("the cluster has %d filers\n", len(resp.ClusterNodes))
|
||||
|
||||
@@ -25,9 +25,7 @@ func (f *Filer) DeleteEntryMetaAndData(ctx context.Context, p util.FullPath, isR
|
||||
if findErr != nil {
|
||||
return findErr
|
||||
}
|
||||
|
||||
isDeleteCollection := f.isBucket(entry)
|
||||
|
||||
if entry.IsDirectory() {
|
||||
// delete the folder children, not including the folder itself
|
||||
err = f.doBatchDeleteFolderMetaAndData(ctx, entry, isRecursive, ignoreRecursiveError, shouldDeleteChunks && !isDeleteCollection, isDeleteCollection, isFromOtherCluster, signatures, func(chunks []*filer_pb.FileChunk) error {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func TestCreateAndFind(t *testing.T) {
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil)
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", "", nil)
|
||||
dir := t.TempDir()
|
||||
store := &LevelDBStore{}
|
||||
store.initialize(dir)
|
||||
@@ -65,7 +65,7 @@ func TestCreateAndFind(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEmptyRoot(t *testing.T) {
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil)
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", "", nil)
|
||||
dir := t.TempDir()
|
||||
store := &LevelDBStore{}
|
||||
store.initialize(dir)
|
||||
@@ -87,7 +87,7 @@ func TestEmptyRoot(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkInsertEntry(b *testing.B) {
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil)
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", "", nil)
|
||||
dir := b.TempDir()
|
||||
store := &LevelDBStore{}
|
||||
store.initialize(dir)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestCreateAndFind(t *testing.T) {
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil)
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", "", nil)
|
||||
dir := t.TempDir()
|
||||
store := &LevelDB2Store{}
|
||||
store.initialize(dir, 2)
|
||||
@@ -62,7 +62,7 @@ func TestCreateAndFind(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEmptyRoot(t *testing.T) {
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil)
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", "", nil)
|
||||
dir := t.TempDir()
|
||||
store := &LevelDB2Store{}
|
||||
store.initialize(dir, 2)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestCreateAndFind(t *testing.T) {
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil)
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", "", nil)
|
||||
dir := t.TempDir()
|
||||
store := &LevelDB3Store{}
|
||||
store.initialize(dir)
|
||||
@@ -62,7 +62,7 @@ func TestCreateAndFind(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEmptyRoot(t *testing.T) {
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil)
|
||||
testFiler := filer.NewFiler(nil, nil, "", "", "", "", "", nil)
|
||||
dir := t.TempDir()
|
||||
store := &LevelDB3Store{}
|
||||
store.initialize(dir)
|
||||
|
||||
@@ -3,7 +3,7 @@ package redis
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"golang.org/x/exp/slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -157,8 +157,8 @@ func (store *UniversalRedisStore) ListDirectoryEntries(ctx context.Context, dirP
|
||||
}
|
||||
|
||||
// sort
|
||||
sort.Slice(members, func(i, j int) bool {
|
||||
return strings.Compare(members[i], members[j]) < 0
|
||||
slices.SortFunc(members, func(a, b string) bool {
|
||||
return strings.Compare(a, b) < 0
|
||||
})
|
||||
|
||||
// limit
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
|
||||
Package sqlite is for sqlite filer store.
|
||||
|
||||
The referenced "modernc.org/sqlite" library is too big when compiled.
|
||||
So this is only compiled in "make full_install".
|
||||
|
||||
*/
|
||||
package sqlite
|
||||
@@ -1,5 +1,6 @@
|
||||
//go:build linux || darwin || windows
|
||||
//go:build (linux || darwin || windows) && sqlite
|
||||
// +build linux darwin windows
|
||||
// +build sqlite
|
||||
|
||||
// limited GOOS due to modernc.org/libc/unistd
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:build !linux && !darwin && !windows && !s390 && !ppc64le && !mips64
|
||||
// +build !linux,!darwin,!windows,!s390,!ppc64le,!mips64
|
||||
//go:build !linux && !darwin && !windows && !s390 && !ppc64le && !mips64 && !sqlite
|
||||
// +build !linux,!darwin,!windows,!s390,!ppc64le,!mips64,!sqlite
|
||||
|
||||
// limited GOOS due to modernc.org/libc/unistd
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package filer
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"golang.org/x/exp/slices"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
@@ -39,11 +40,11 @@ func isSameChunks(a, b []*filer_pb.FileChunk) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
sort.Slice(a, func(i, j int) bool {
|
||||
return strings.Compare(a[i].ETag, a[j].ETag) < 0
|
||||
slices.SortFunc(a, func(i, j *filer_pb.FileChunk) bool {
|
||||
return strings.Compare(i.ETag, j.ETag) < 0
|
||||
})
|
||||
sort.Slice(b, func(i, j int) bool {
|
||||
return strings.Compare(b[i].ETag, b[j].ETag) < 0
|
||||
slices.SortFunc(b, func(i, j *filer_pb.FileChunk) bool {
|
||||
return strings.Compare(i.ETag, j.ETag) < 0
|
||||
})
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i].ETag != b[i].ETag {
|
||||
@@ -179,8 +180,8 @@ var _ = io.ReaderAt(&ChunkStreamReader{})
|
||||
func doNewChunkStreamReader(lookupFileIdFn wdclient.LookupFileIdFunctionType, chunks []*filer_pb.FileChunk) *ChunkStreamReader {
|
||||
|
||||
chunkViews := ViewFromChunks(lookupFileIdFn, chunks, 0, math.MaxInt64)
|
||||
sort.Slice(chunkViews, func(i, j int) bool {
|
||||
return chunkViews[i].LogicOffset < chunkViews[j].LogicOffset
|
||||
slices.SortFunc(chunkViews, func(a, b *ChunkView) bool {
|
||||
return a.LogicOffset < b.LogicOffset
|
||||
})
|
||||
|
||||
var totalSize int64
|
||||
|
||||
@@ -4,10 +4,6 @@ import (
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -16,6 +12,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
)
|
||||
|
||||
@@ -155,6 +156,22 @@ func (iama *IamApiServer) GetUser(s3cfg *iam_pb.S3ApiConfiguration, userName str
|
||||
return resp, fmt.Errorf(iam.ErrCodeNoSuchEntityException)
|
||||
}
|
||||
|
||||
func (iama *IamApiServer) UpdateUser(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp UpdateUserResponse, err error) {
|
||||
userName := values.Get("UserName")
|
||||
newUserName := values.Get("NewUserName")
|
||||
if newUserName != "" {
|
||||
for _, ident := range s3cfg.Identities {
|
||||
if userName == ident.Name {
|
||||
ident.Name = newUserName
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return resp, nil
|
||||
}
|
||||
return resp, fmt.Errorf(iam.ErrCodeNoSuchEntityException)
|
||||
}
|
||||
|
||||
func GetPolicyDocument(policy *string) (policyDocument PolicyDocument, err error) {
|
||||
if err = json.Unmarshal([]byte(*policy), &policyDocument); err != nil {
|
||||
return PolicyDocument{}, err
|
||||
@@ -360,6 +377,35 @@ func (iama *IamApiServer) DeleteAccessKey(s3cfg *iam_pb.S3ApiConfiguration, valu
|
||||
return resp
|
||||
}
|
||||
|
||||
// handleImplicitUsername adds username who signs the request to values if 'username' is not specified
|
||||
// According to https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/create-access-key.html/
|
||||
// "If you do not specify a user name, IAM determines the user name implicitly based on the Amazon Web
|
||||
// Services access key ID signing the request."
|
||||
func handleImplicitUsername(r *http.Request, values url.Values) {
|
||||
if len(r.Header["Authorization"]) == 0 || values.Get("UserName") != "" {
|
||||
return
|
||||
}
|
||||
// get username who signs the request. For a typical Authorization:
|
||||
// "AWS4-HMAC-SHA256 Credential=197FSAQ7HHTA48X64O3A/20220420/test1/iam/aws4_request, SignedHeaders=content-type;
|
||||
// host;x-amz-date, Signature=6757dc6b3d7534d67e17842760310e99ee695408497f6edc4fdb84770c252dc8",
|
||||
// the "test1" will be extracted as the username
|
||||
glog.V(4).Infof("Authorization field: %v", r.Header["Authorization"][0])
|
||||
s := strings.Split(r.Header["Authorization"][0], "Credential=")
|
||||
if len(s) < 2 {
|
||||
return
|
||||
}
|
||||
s = strings.Split(s[1], ",")
|
||||
if len(s) < 2 {
|
||||
return
|
||||
}
|
||||
s = strings.Split(s[0], "/")
|
||||
if len(s) < 5 {
|
||||
return
|
||||
}
|
||||
userName := s[2]
|
||||
values.Set("UserName", userName)
|
||||
}
|
||||
|
||||
func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
|
||||
@@ -384,6 +430,7 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
|
||||
response = iama.ListUsers(s3cfg, values)
|
||||
changed = false
|
||||
case "ListAccessKeys":
|
||||
handleImplicitUsername(r, values)
|
||||
response = iama.ListAccessKeys(s3cfg, values)
|
||||
changed = false
|
||||
case "CreateUser":
|
||||
@@ -396,6 +443,13 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
changed = false
|
||||
case "UpdateUser":
|
||||
response, err = iama.UpdateUser(s3cfg, values)
|
||||
if err != nil {
|
||||
glog.Errorf("UpdateUser: %+v", err)
|
||||
s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
|
||||
return
|
||||
}
|
||||
case "DeleteUser":
|
||||
userName := values.Get("UserName")
|
||||
response, err = iama.DeleteUser(s3cfg, userName)
|
||||
@@ -404,8 +458,10 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
case "CreateAccessKey":
|
||||
handleImplicitUsername(r, values)
|
||||
response = iama.CreateAccessKey(s3cfg, values)
|
||||
case "DeleteAccessKey":
|
||||
handleImplicitUsername(r, values)
|
||||
response = iama.DeleteAccessKey(s3cfg, values)
|
||||
case "CreatePolicy":
|
||||
response, err = iama.CreatePolicy(s3cfg, values)
|
||||
@@ -431,6 +487,7 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
|
||||
case "DeleteUserPolicy":
|
||||
if response, err = iama.DeleteUserPolicy(s3cfg, values); err != nil {
|
||||
writeIamErrorResponse(w, r, err, "user", values.Get("UserName"), nil)
|
||||
return
|
||||
}
|
||||
default:
|
||||
errNotImplemented := s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
|
||||
@@ -66,6 +66,11 @@ type GetUserResponse struct {
|
||||
} `xml:"GetUserResult"`
|
||||
}
|
||||
|
||||
type UpdateUserResponse struct {
|
||||
CommonResponse
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ UpdateUserResponse"`
|
||||
}
|
||||
|
||||
type CreateAccessKeyResponse struct {
|
||||
CommonResponse
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ CreateAccessKeyResponse"`
|
||||
|
||||
@@ -49,7 +49,7 @@ var s3ApiConfigure IamS3ApiConfig
|
||||
func NewIamApiServer(router *mux.Router, option *IamServerOption) (iamApiServer *IamApiServer, err error) {
|
||||
s3ApiConfigure = IamS3ApiConfigure{
|
||||
option: option,
|
||||
masterClient: wdclient.NewMasterClient(option.GrpcDialOption, "iam", "", "", option.Masters),
|
||||
masterClient: wdclient.NewMasterClient(option.GrpcDialOption, "", "iam", "", "", option.Masters),
|
||||
}
|
||||
s3Option := s3api.S3ApiServerOption{Filer: option.Filer}
|
||||
iamApiServer = &IamApiServer{
|
||||
|
||||
@@ -2,6 +2,11 @@ package iamapi
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
@@ -9,9 +14,6 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var GetS3ApiConfiguration func(s3cfg *iam_pb.S3ApiConfiguration) (err error)
|
||||
@@ -161,8 +163,20 @@ func TestGetUserPolicy(t *testing.T) {
|
||||
assert.Equal(t, http.StatusOK, response.Code)
|
||||
}
|
||||
|
||||
func TestDeleteUser(t *testing.T) {
|
||||
func TestUpdateUser(t *testing.T) {
|
||||
userName := aws.String("Test")
|
||||
newUserName := aws.String("Test-New")
|
||||
params := &iam.UpdateUserInput{NewUserName: newUserName, UserName: userName}
|
||||
req, _ := iam.New(session.New()).UpdateUserRequest(params)
|
||||
_ = req.Build()
|
||||
out := UpdateUserResponse{}
|
||||
response, err := executeRequest(req.HTTPRequest, out)
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, http.StatusOK, response.Code)
|
||||
}
|
||||
|
||||
func TestDeleteUser(t *testing.T) {
|
||||
userName := aws.String("Test-New")
|
||||
params := &iam.DeleteUserInput{UserName: userName}
|
||||
req, _ := iam.New(session.New()).DeleteUserRequest(params)
|
||||
_ = req.Build()
|
||||
@@ -179,3 +193,24 @@ func executeRequest(req *http.Request, v interface{}) (*httptest.ResponseRecorde
|
||||
apiRouter.ServeHTTP(rr, req)
|
||||
return rr, xml.Unmarshal(rr.Body.Bytes(), &v)
|
||||
}
|
||||
|
||||
func TestHandleImplicitUsername(t *testing.T) {
|
||||
var tests = []struct {
|
||||
r *http.Request
|
||||
values url.Values
|
||||
userName string
|
||||
}{
|
||||
{&http.Request{}, url.Values{}, ""},
|
||||
{&http.Request{Header: http.Header{"Authorization": []string{"AWS4-HMAC-SHA256 Credential=197FSAQ7HHTA48X64O3A/20220420/test1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=6757dc6b3d7534d67e17842760310e99ee695408497f6edc4fdb84770c252dc8"}}}, url.Values{}, "test1"},
|
||||
{&http.Request{Header: http.Header{"Authorization": []string{"AWS4-HMAC-SHA256 =197FSAQ7HHTA48X64O3A/20220420/test1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=6757dc6b3d7534d67e17842760310e99ee695408497f6edc4fdb84770c252dc8"}}}, url.Values{}, ""},
|
||||
{&http.Request{Header: http.Header{"Authorization": []string{"AWS4-HMAC-SHA256 Credential=197FSAQ7HHTA48X64O3A/20220420/test1/iam/aws4_request SignedHeaders=content-type;host;x-amz-date Signature=6757dc6b3d7534d67e17842760310e99ee695408497f6edc4fdb84770c252dc8"}}}, url.Values{}, ""},
|
||||
{&http.Request{Header: http.Header{"Authorization": []string{"AWS4-HMAC-SHA256 Credential=197FSAQ7HHTA48X64O3A/20220420/test1/iam, SignedHeaders=content-type;host;x-amz-date, Signature=6757dc6b3d7534d67e17842760310e99ee695408497f6edc4fdb84770c252dc8"}}}, url.Values{}, ""},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
handleImplicitUsername(test.r, test.values)
|
||||
if un := test.values.Get("UserName"); un != test.userName {
|
||||
t.Errorf("No.%d: Got: %v, Expected: %v", i, un, test.userName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/util"
|
||||
"golang.org/x/exp/slices"
|
||||
"io"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -76,8 +76,8 @@ func (fh *FileHandle) addChunks(chunks []*filer_pb.FileChunk) {
|
||||
}
|
||||
|
||||
// sort incoming chunks
|
||||
sort.Slice(chunks, func(i, j int) bool {
|
||||
return lessThan(chunks[i], chunks[j])
|
||||
slices.SortFunc(chunks, func(a, b *filer_pb.FileChunk) bool {
|
||||
return lessThan(a, b)
|
||||
})
|
||||
|
||||
glog.V(4).Infof("%s existing %d chunks adds %d more", fh.FullPath(), len(fh.entry.Chunks), len(chunks))
|
||||
|
||||
@@ -49,6 +49,7 @@ func (i *FileHandleToInode) AcquireFileHandle(wfs *WFS, inode uint64, entry *fil
|
||||
} else {
|
||||
fh.counter++
|
||||
}
|
||||
fh.entry = entry
|
||||
return fh
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/chrislusf/seaweedfs/weed/util"
|
||||
"github.com/chrislusf/seaweedfs/weed/util/mem"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -14,11 +15,12 @@ var (
|
||||
type ActualChunkIndex int
|
||||
|
||||
type SwapFile struct {
|
||||
dir string
|
||||
file *os.File
|
||||
logicToActualChunkIndex map[LogicChunkIndex]ActualChunkIndex
|
||||
chunkSize int64
|
||||
freeActualChunkList []ActualChunkIndex
|
||||
dir string
|
||||
file *os.File
|
||||
logicToActualChunkIndex map[LogicChunkIndex]ActualChunkIndex
|
||||
logicToActualChunkIndexLock sync.Mutex
|
||||
chunkSize int64
|
||||
freeActualChunkList []ActualChunkIndex
|
||||
}
|
||||
|
||||
type SwapFileChunk struct {
|
||||
@@ -52,6 +54,8 @@ func (sf *SwapFile) NewTempFileChunk(logicChunkIndex LogicChunkIndex) (tc *SwapF
|
||||
return nil
|
||||
}
|
||||
}
|
||||
sf.logicToActualChunkIndexLock.Lock()
|
||||
defer sf.logicToActualChunkIndexLock.Unlock()
|
||||
actualChunkIndex, found := sf.logicToActualChunkIndex[logicChunkIndex]
|
||||
if !found {
|
||||
if len(sf.freeActualChunkList) > 0 {
|
||||
@@ -72,6 +76,9 @@ func (sf *SwapFile) NewTempFileChunk(logicChunkIndex LogicChunkIndex) (tc *SwapF
|
||||
}
|
||||
|
||||
func (sc *SwapFileChunk) FreeResource() {
|
||||
sc.swapfile.logicToActualChunkIndexLock.Lock()
|
||||
defer sc.swapfile.logicToActualChunkIndexLock.Unlock()
|
||||
|
||||
sc.swapfile.freeActualChunkList = append(sc.swapfile.freeActualChunkList, sc.actualChunkIndex)
|
||||
delete(sc.swapfile.logicToActualChunkIndex, sc.logicChunkIndex)
|
||||
}
|
||||
|
||||
@@ -187,6 +187,9 @@ func (up *UploadPipeline) moveToSealed(memChunk PageChunk, logicChunkIndex Logic
|
||||
|
||||
func (up *UploadPipeline) Shutdown() {
|
||||
up.swapFile.FreeResource()
|
||||
|
||||
up.sealedChunksLock.Lock()
|
||||
defer up.sealedChunksLock.Unlock()
|
||||
for logicChunkIndex, sealedChunk := range up.sealedChunks {
|
||||
sealedChunk.FreeReference(fmt.Sprintf("%s uploadpipeline shutdown chunk %d", up.filepath, logicChunkIndex))
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/chrislusf/seaweedfs/weed/mount/meta_cache"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/mount_pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/storage/types"
|
||||
"github.com/chrislusf/seaweedfs/weed/util"
|
||||
"github.com/chrislusf/seaweedfs/weed/util/chunk_cache"
|
||||
@@ -59,6 +60,7 @@ type WFS struct {
|
||||
// https://dl.acm.org/doi/fullHtml/10.1145/3310148
|
||||
// follow https://github.com/hanwen/go-fuse/blob/master/fuse/api.go
|
||||
fuse.RawFileSystem
|
||||
mount_pb.UnimplementedSeaweedMountServer
|
||||
fs.Inode
|
||||
option *Option
|
||||
metaCache *meta_cache.MetaCache
|
||||
@@ -129,6 +131,9 @@ func (wfs *WFS) maybeReadEntry(inode uint64) (path util.FullPath, fh *FileHandle
|
||||
}
|
||||
var found bool
|
||||
if fh, found = wfs.fhmap.FindFileHandle(inode); found {
|
||||
if fh.entry.Attributes == nil {
|
||||
fh.entry.Attributes = &filer_pb.FuseAttributes{}
|
||||
}
|
||||
return path, fh, fh.entry, fuse.OK
|
||||
}
|
||||
entry, status = wfs.maybeLoadEntry(path)
|
||||
|
||||
@@ -98,15 +98,14 @@ func (wfs *WFS) SetAttr(cancel <-chan struct{}, input *fuse.SetAttrIn, out *fuse
|
||||
}
|
||||
}
|
||||
|
||||
if mtime, ok := input.GetMTime(); ok {
|
||||
entry.Attributes.Mtime = mtime.Unix()
|
||||
}
|
||||
|
||||
if atime, ok := input.GetATime(); ok {
|
||||
entry.Attributes.Mtime = atime.Unix()
|
||||
}
|
||||
|
||||
entry.Attributes.Mtime = time.Now().Unix()
|
||||
if mtime, ok := input.GetMTime(); ok {
|
||||
entry.Attributes.Mtime = mtime.Unix()
|
||||
}
|
||||
|
||||
out.AttrValid = 1
|
||||
wfs.setAttrByPbEntry(&out.Attr, input.NodeId, entry)
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ func (wfs *WFS) AcquireHandle(inode uint64, uid, gid uint32) (fileHandle *FileHa
|
||||
_, _, entry, status = wfs.maybeReadEntry(inode)
|
||||
if status == fuse.OK {
|
||||
fileHandle = wfs.fhmap.AcquireFileHandle(wfs, inode, entry)
|
||||
fileHandle.entry = entry
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/mount_pb"
|
||||
)
|
||||
|
||||
func (wfs *WFS) Configure(ctx context.Context, request *mount_pb.ConfigureRequest) (*mount_pb.ConfigureResponse, error) {
|
||||
if wfs.option.Collection == "" {
|
||||
return nil, fmt.Errorf("mount quota only works when mounted to a new folder with a collection")
|
||||
}
|
||||
glog.V(0).Infof("quota changed from %d to %d", wfs.option.Quota, request.CollectionCapacity)
|
||||
wfs.option.Quota = request.GetCollectionCapacity()
|
||||
return &mount_pb.ConfigureResponse{}, nil
|
||||
}
|
||||
@@ -10,12 +10,14 @@ import (
|
||||
|
||||
func (wfs *WFS) loopCheckQuota() {
|
||||
|
||||
if wfs.option.Quota <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
|
||||
time.Sleep(61 * time.Second)
|
||||
|
||||
if wfs.option.Quota <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
err := wfs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
|
||||
|
||||
request := &filer_pb.StatisticsRequest{
|
||||
@@ -47,7 +49,6 @@ func (wfs *WFS) loopCheckQuota() {
|
||||
glog.Warningf("read quota usage: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(61 * time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
|
||||
Package gocdk_pub_sub is for Azure Service Bus and RabbitMQ.
|
||||
|
||||
The referenced "gocloud.dev/pubsub" library is too big when compiled.
|
||||
So this is only compiled in "make full_install".
|
||||
|
||||
*/
|
||||
package gocdk_pub_sub
|
||||
@@ -1,3 +1,6 @@
|
||||
//go:build gocdk
|
||||
// +build gocdk
|
||||
|
||||
// Package gocdk_pub_sub supports the Go CDK (Cloud Development Kit) PubSub API,
|
||||
// which in turn supports many providers, including Amazon SNS/SQS, Azure Service Bus,
|
||||
// Google Cloud PubSub, and RabbitMQ.
|
||||
|
||||
@@ -8,6 +8,7 @@ gen:
|
||||
protoc filer.proto --go_out=./filer_pb --go-grpc_out=./filer_pb --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative
|
||||
protoc remote.proto --go_out=./remote_pb --go-grpc_out=./remote_pb --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative
|
||||
protoc iam.proto --go_out=./iam_pb --go-grpc_out=./iam_pb --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative
|
||||
protoc mount.proto --go_out=./mount_pb --go-grpc_out=./mount_pb --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative
|
||||
protoc messaging.proto --go_out=./messaging_pb --go-grpc_out=./messaging_pb --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative
|
||||
# protoc filer.proto --java_out=../../other/java/client/src/main/java
|
||||
cp filer.proto ../../other/java/client/src/main/proto
|
||||
|
||||
@@ -48,6 +48,9 @@ service SeaweedFiler {
|
||||
rpc Statistics (StatisticsRequest) returns (StatisticsResponse) {
|
||||
}
|
||||
|
||||
rpc Ping (PingRequest) returns (PingResponse) {
|
||||
}
|
||||
|
||||
rpc GetFilerConfiguration (GetFilerConfigurationRequest) returns (GetFilerConfigurationResponse) {
|
||||
}
|
||||
|
||||
@@ -311,6 +314,16 @@ message StatisticsResponse {
|
||||
uint64 file_count = 6;
|
||||
}
|
||||
|
||||
message PingRequest {
|
||||
string target = 1; // default to ping itself
|
||||
string target_type = 2;
|
||||
}
|
||||
message PingResponse {
|
||||
int64 start_time_ns = 1;
|
||||
int64 remote_time_ns = 2;
|
||||
int64 stop_time_ns = 3;
|
||||
}
|
||||
|
||||
message GetFilerConfigurationRequest {
|
||||
}
|
||||
message GetFilerConfigurationResponse {
|
||||
|
||||
+539
-377
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@ type SeaweedFilerClient interface {
|
||||
CollectionList(ctx context.Context, in *CollectionListRequest, opts ...grpc.CallOption) (*CollectionListResponse, error)
|
||||
DeleteCollection(ctx context.Context, in *DeleteCollectionRequest, opts ...grpc.CallOption) (*DeleteCollectionResponse, error)
|
||||
Statistics(ctx context.Context, in *StatisticsRequest, opts ...grpc.CallOption) (*StatisticsResponse, error)
|
||||
Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error)
|
||||
GetFilerConfiguration(ctx context.Context, in *GetFilerConfigurationRequest, opts ...grpc.CallOption) (*GetFilerConfigurationResponse, error)
|
||||
SubscribeMetadata(ctx context.Context, in *SubscribeMetadataRequest, opts ...grpc.CallOption) (SeaweedFiler_SubscribeMetadataClient, error)
|
||||
SubscribeLocalMetadata(ctx context.Context, in *SubscribeMetadataRequest, opts ...grpc.CallOption) (SeaweedFiler_SubscribeLocalMetadataClient, error)
|
||||
@@ -212,6 +213,15 @@ func (c *seaweedFilerClient) Statistics(ctx context.Context, in *StatisticsReque
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *seaweedFilerClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) {
|
||||
out := new(PingResponse)
|
||||
err := c.cc.Invoke(ctx, "/filer_pb.SeaweedFiler/Ping", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *seaweedFilerClient) GetFilerConfiguration(ctx context.Context, in *GetFilerConfigurationRequest, opts ...grpc.CallOption) (*GetFilerConfigurationResponse, error) {
|
||||
out := new(GetFilerConfigurationResponse)
|
||||
err := c.cc.Invoke(ctx, "/filer_pb.SeaweedFiler/GetFilerConfiguration", in, out, opts...)
|
||||
@@ -369,6 +379,7 @@ type SeaweedFilerServer interface {
|
||||
CollectionList(context.Context, *CollectionListRequest) (*CollectionListResponse, error)
|
||||
DeleteCollection(context.Context, *DeleteCollectionRequest) (*DeleteCollectionResponse, error)
|
||||
Statistics(context.Context, *StatisticsRequest) (*StatisticsResponse, error)
|
||||
Ping(context.Context, *PingRequest) (*PingResponse, error)
|
||||
GetFilerConfiguration(context.Context, *GetFilerConfigurationRequest) (*GetFilerConfigurationResponse, error)
|
||||
SubscribeMetadata(*SubscribeMetadataRequest, SeaweedFiler_SubscribeMetadataServer) error
|
||||
SubscribeLocalMetadata(*SubscribeMetadataRequest, SeaweedFiler_SubscribeLocalMetadataServer) error
|
||||
@@ -423,6 +434,9 @@ func (UnimplementedSeaweedFilerServer) DeleteCollection(context.Context, *Delete
|
||||
func (UnimplementedSeaweedFilerServer) Statistics(context.Context, *StatisticsRequest) (*StatisticsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Statistics not implemented")
|
||||
}
|
||||
func (UnimplementedSeaweedFilerServer) Ping(context.Context, *PingRequest) (*PingResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
|
||||
}
|
||||
func (UnimplementedSeaweedFilerServer) GetFilerConfiguration(context.Context, *GetFilerConfigurationRequest) (*GetFilerConfigurationResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetFilerConfiguration not implemented")
|
||||
}
|
||||
@@ -700,6 +714,24 @@ func _SeaweedFiler_Statistics_Handler(srv interface{}, ctx context.Context, dec
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SeaweedFiler_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PingRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SeaweedFilerServer).Ping(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/filer_pb.SeaweedFiler/Ping",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SeaweedFilerServer).Ping(ctx, req.(*PingRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SeaweedFiler_GetFilerConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetFilerConfigurationRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -909,6 +941,10 @@ var SeaweedFiler_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "Statistics",
|
||||
Handler: _SeaweedFiler_Statistics_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Ping",
|
||||
Handler: _SeaweedFiler_Ping_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetFilerConfiguration",
|
||||
Handler: _SeaweedFiler_GetFilerConfiguration_Handler,
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/util"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
@@ -206,6 +207,14 @@ func WithMasterClient(streamingMode bool, master ServerAddress, grpcDialOption g
|
||||
|
||||
}
|
||||
|
||||
func WithVolumeServerClient(streamingMode bool, volumeServer ServerAddress, grpcDialOption grpc.DialOption, fn func(client volume_server_pb.VolumeServerClient) error) error {
|
||||
return WithGrpcClient(streamingMode, func(grpcConnection *grpc.ClientConn) error {
|
||||
client := volume_server_pb.NewVolumeServerClient(grpcConnection)
|
||||
return fn(client)
|
||||
}, volumeServer.ToGrpcAddress(), grpcDialOption)
|
||||
|
||||
}
|
||||
|
||||
func WithOneOfGrpcMasterClients(streamingMode bool, masterGrpcAddresses map[string]ServerAddress, grpcDialOption grpc.DialOption, fn func(client master_pb.SeaweedClient) error) (err error) {
|
||||
|
||||
for _, masterGrpcAddress := range masterGrpcAddresses {
|
||||
|
||||
+51
-1
@@ -35,7 +35,14 @@ service Seaweed {
|
||||
}
|
||||
rpc ReleaseAdminToken (ReleaseAdminTokenRequest) returns (ReleaseAdminTokenResponse) {
|
||||
}
|
||||
|
||||
rpc Ping (PingRequest) returns (PingResponse) {
|
||||
}
|
||||
rpc RaftListClusterServers (RaftListClusterServersRequest) returns (RaftListClusterServersResponse) {
|
||||
}
|
||||
rpc RaftAddServer (RaftAddServerRequest) returns (RaftAddServerResponse) {
|
||||
}
|
||||
rpc RaftRemoveServer (RaftRemoveServerRequest) returns (RaftRemoveServerResponse) {
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
@@ -130,6 +137,7 @@ message KeepConnectedRequest {
|
||||
string client_type = 1;
|
||||
string client_address = 3;
|
||||
string version = 4;
|
||||
string filer_group = 5;
|
||||
}
|
||||
|
||||
message VolumeLocation {
|
||||
@@ -140,6 +148,8 @@ message VolumeLocation {
|
||||
string leader = 5; // optional when leader is not itself
|
||||
string data_center = 6; // optional when DataCenter is in use
|
||||
uint32 grpc_port = 7;
|
||||
repeated uint32 new_ec_vids = 8;
|
||||
repeated uint32 deleted_ec_vids = 9;
|
||||
}
|
||||
|
||||
message ClusterNodeUpdate {
|
||||
@@ -147,6 +157,7 @@ message ClusterNodeUpdate {
|
||||
string address = 2;
|
||||
bool is_leader = 3;
|
||||
bool is_add = 4;
|
||||
string filer_group = 5;
|
||||
}
|
||||
|
||||
message KeepConnectedResponse {
|
||||
@@ -281,6 +292,8 @@ message LookupEcVolumeResponse {
|
||||
|
||||
message VacuumVolumeRequest {
|
||||
float garbage_threshold = 1;
|
||||
uint32 volume_id = 2;
|
||||
string collection = 3;
|
||||
}
|
||||
message VacuumVolumeResponse {
|
||||
}
|
||||
@@ -299,6 +312,7 @@ message GetMasterConfigurationResponse {
|
||||
|
||||
message ListClusterNodesRequest {
|
||||
string client_type = 1;
|
||||
string filer_group = 2;
|
||||
}
|
||||
message ListClusterNodesResponse {
|
||||
message ClusterNode {
|
||||
@@ -328,3 +342,39 @@ message ReleaseAdminTokenRequest {
|
||||
}
|
||||
message ReleaseAdminTokenResponse {
|
||||
}
|
||||
|
||||
message PingRequest {
|
||||
string target = 1; // default to ping itself
|
||||
string target_type = 2;
|
||||
}
|
||||
message PingResponse {
|
||||
int64 start_time_ns = 1;
|
||||
int64 remote_time_ns = 2;
|
||||
int64 stop_time_ns = 3;
|
||||
}
|
||||
|
||||
message RaftAddServerRequest {
|
||||
string id = 1;
|
||||
string address = 2;
|
||||
bool voter = 3;
|
||||
}
|
||||
message RaftAddServerResponse {
|
||||
}
|
||||
|
||||
message RaftRemoveServerRequest {
|
||||
string id = 1;
|
||||
bool force = 2;
|
||||
}
|
||||
message RaftRemoveServerResponse {
|
||||
}
|
||||
|
||||
message RaftListClusterServersRequest {
|
||||
}
|
||||
message RaftListClusterServersResponse {
|
||||
message ClusterServers {
|
||||
string id = 1;
|
||||
string address = 2;
|
||||
string suffrage = 3; //
|
||||
}
|
||||
repeated ClusterServers cluster_servers = 1;
|
||||
}
|
||||
|
||||
+1206
-486
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,10 @@ type SeaweedClient interface {
|
||||
ListClusterNodes(ctx context.Context, in *ListClusterNodesRequest, opts ...grpc.CallOption) (*ListClusterNodesResponse, error)
|
||||
LeaseAdminToken(ctx context.Context, in *LeaseAdminTokenRequest, opts ...grpc.CallOption) (*LeaseAdminTokenResponse, error)
|
||||
ReleaseAdminToken(ctx context.Context, in *ReleaseAdminTokenRequest, opts ...grpc.CallOption) (*ReleaseAdminTokenResponse, error)
|
||||
Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error)
|
||||
RaftListClusterServers(ctx context.Context, in *RaftListClusterServersRequest, opts ...grpc.CallOption) (*RaftListClusterServersResponse, error)
|
||||
RaftAddServer(ctx context.Context, in *RaftAddServerRequest, opts ...grpc.CallOption) (*RaftAddServerResponse, error)
|
||||
RaftRemoveServer(ctx context.Context, in *RaftRemoveServerRequest, opts ...grpc.CallOption) (*RaftRemoveServerResponse, error)
|
||||
}
|
||||
|
||||
type seaweedClient struct {
|
||||
@@ -212,6 +216,42 @@ func (c *seaweedClient) ReleaseAdminToken(ctx context.Context, in *ReleaseAdminT
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *seaweedClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) {
|
||||
out := new(PingResponse)
|
||||
err := c.cc.Invoke(ctx, "/master_pb.Seaweed/Ping", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *seaweedClient) RaftListClusterServers(ctx context.Context, in *RaftListClusterServersRequest, opts ...grpc.CallOption) (*RaftListClusterServersResponse, error) {
|
||||
out := new(RaftListClusterServersResponse)
|
||||
err := c.cc.Invoke(ctx, "/master_pb.Seaweed/RaftListClusterServers", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *seaweedClient) RaftAddServer(ctx context.Context, in *RaftAddServerRequest, opts ...grpc.CallOption) (*RaftAddServerResponse, error) {
|
||||
out := new(RaftAddServerResponse)
|
||||
err := c.cc.Invoke(ctx, "/master_pb.Seaweed/RaftAddServer", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *seaweedClient) RaftRemoveServer(ctx context.Context, in *RaftRemoveServerRequest, opts ...grpc.CallOption) (*RaftRemoveServerResponse, error) {
|
||||
out := new(RaftRemoveServerResponse)
|
||||
err := c.cc.Invoke(ctx, "/master_pb.Seaweed/RaftRemoveServer", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SeaweedServer is the server API for Seaweed service.
|
||||
// All implementations must embed UnimplementedSeaweedServer
|
||||
// for forward compatibility
|
||||
@@ -230,6 +270,10 @@ type SeaweedServer interface {
|
||||
ListClusterNodes(context.Context, *ListClusterNodesRequest) (*ListClusterNodesResponse, error)
|
||||
LeaseAdminToken(context.Context, *LeaseAdminTokenRequest) (*LeaseAdminTokenResponse, error)
|
||||
ReleaseAdminToken(context.Context, *ReleaseAdminTokenRequest) (*ReleaseAdminTokenResponse, error)
|
||||
Ping(context.Context, *PingRequest) (*PingResponse, error)
|
||||
RaftListClusterServers(context.Context, *RaftListClusterServersRequest) (*RaftListClusterServersResponse, error)
|
||||
RaftAddServer(context.Context, *RaftAddServerRequest) (*RaftAddServerResponse, error)
|
||||
RaftRemoveServer(context.Context, *RaftRemoveServerRequest) (*RaftRemoveServerResponse, error)
|
||||
mustEmbedUnimplementedSeaweedServer()
|
||||
}
|
||||
|
||||
@@ -279,6 +323,18 @@ func (UnimplementedSeaweedServer) LeaseAdminToken(context.Context, *LeaseAdminTo
|
||||
func (UnimplementedSeaweedServer) ReleaseAdminToken(context.Context, *ReleaseAdminTokenRequest) (*ReleaseAdminTokenResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ReleaseAdminToken not implemented")
|
||||
}
|
||||
func (UnimplementedSeaweedServer) Ping(context.Context, *PingRequest) (*PingResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
|
||||
}
|
||||
func (UnimplementedSeaweedServer) RaftListClusterServers(context.Context, *RaftListClusterServersRequest) (*RaftListClusterServersResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RaftListClusterServers not implemented")
|
||||
}
|
||||
func (UnimplementedSeaweedServer) RaftAddServer(context.Context, *RaftAddServerRequest) (*RaftAddServerResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RaftAddServer not implemented")
|
||||
}
|
||||
func (UnimplementedSeaweedServer) RaftRemoveServer(context.Context, *RaftRemoveServerRequest) (*RaftRemoveServerResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RaftRemoveServer not implemented")
|
||||
}
|
||||
func (UnimplementedSeaweedServer) mustEmbedUnimplementedSeaweedServer() {}
|
||||
|
||||
// UnsafeSeaweedServer may be embedded to opt out of forward compatibility for this service.
|
||||
@@ -560,6 +616,78 @@ func _Seaweed_ReleaseAdminToken_Handler(srv interface{}, ctx context.Context, de
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Seaweed_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PingRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SeaweedServer).Ping(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/master_pb.Seaweed/Ping",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SeaweedServer).Ping(ctx, req.(*PingRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Seaweed_RaftListClusterServers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RaftListClusterServersRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SeaweedServer).RaftListClusterServers(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/master_pb.Seaweed/RaftListClusterServers",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SeaweedServer).RaftListClusterServers(ctx, req.(*RaftListClusterServersRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Seaweed_RaftAddServer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RaftAddServerRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SeaweedServer).RaftAddServer(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/master_pb.Seaweed/RaftAddServer",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SeaweedServer).RaftAddServer(ctx, req.(*RaftAddServerRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Seaweed_RaftRemoveServer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RaftRemoveServerRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SeaweedServer).RaftRemoveServer(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/master_pb.Seaweed/RaftRemoveServer",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SeaweedServer).RaftRemoveServer(ctx, req.(*RaftRemoveServerRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Seaweed_ServiceDesc is the grpc.ServiceDesc for Seaweed service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -615,6 +743,22 @@ var Seaweed_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ReleaseAdminToken",
|
||||
Handler: _Seaweed_ReleaseAdminToken_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Ping",
|
||||
Handler: _Seaweed_Ping_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RaftListClusterServers",
|
||||
Handler: _Seaweed_RaftListClusterServers_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RaftAddServer",
|
||||
Handler: _Seaweed_RaftAddServer_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RaftRemoveServer",
|
||||
Handler: _Seaweed_RaftRemoveServer_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package messaging_pb;
|
||||
|
||||
option go_package = "github.com/chrislusf/seaweedfs/weed/pb/mount_pb";
|
||||
option java_package = "seaweedfs.client";
|
||||
option java_outer_classname = "MountProto";
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
service SeaweedMount {
|
||||
|
||||
rpc Configure (ConfigureRequest) returns (ConfigureResponse) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
message ConfigureRequest {
|
||||
int64 collection_capacity = 1;
|
||||
}
|
||||
|
||||
message ConfigureResponse {
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.17.3
|
||||
// source: mount.proto
|
||||
|
||||
package mount_pb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ConfigureRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
CollectionCapacity int64 `protobuf:"varint,1,opt,name=collection_capacity,json=collectionCapacity,proto3" json:"collection_capacity,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ConfigureRequest) Reset() {
|
||||
*x = ConfigureRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_mount_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ConfigureRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ConfigureRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ConfigureRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_mount_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConfigureRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ConfigureRequest) Descriptor() ([]byte, []int) {
|
||||
return file_mount_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ConfigureRequest) GetCollectionCapacity() int64 {
|
||||
if x != nil {
|
||||
return x.CollectionCapacity
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ConfigureResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *ConfigureResponse) Reset() {
|
||||
*x = ConfigureResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_mount_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ConfigureResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ConfigureResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ConfigureResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_mount_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConfigureResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ConfigureResponse) Descriptor() ([]byte, []int) {
|
||||
return file_mount_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
var File_mount_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_mount_proto_rawDesc = []byte{
|
||||
0x0a, 0x0b, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x6d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x62, 0x22, 0x43, 0x0a, 0x10, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x61,
|
||||
0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, 0x6f,
|
||||
0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79,
|
||||
0x22, 0x13, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x5e, 0x0a, 0x0c, 0x53, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64,
|
||||
0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x4e, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75,
|
||||
0x72, 0x65, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x70,
|
||||
0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x70,
|
||||
0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x4f, 0x0a, 0x10, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64,
|
||||
0x66, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x0a, 0x4d, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, 0x77,
|
||||
0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_mount_proto_rawDescOnce sync.Once
|
||||
file_mount_proto_rawDescData = file_mount_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_mount_proto_rawDescGZIP() []byte {
|
||||
file_mount_proto_rawDescOnce.Do(func() {
|
||||
file_mount_proto_rawDescData = protoimpl.X.CompressGZIP(file_mount_proto_rawDescData)
|
||||
})
|
||||
return file_mount_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_mount_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_mount_proto_goTypes = []interface{}{
|
||||
(*ConfigureRequest)(nil), // 0: messaging_pb.ConfigureRequest
|
||||
(*ConfigureResponse)(nil), // 1: messaging_pb.ConfigureResponse
|
||||
}
|
||||
var file_mount_proto_depIdxs = []int32{
|
||||
0, // 0: messaging_pb.SeaweedMount.Configure:input_type -> messaging_pb.ConfigureRequest
|
||||
1, // 1: messaging_pb.SeaweedMount.Configure:output_type -> messaging_pb.ConfigureResponse
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_mount_proto_init() }
|
||||
func file_mount_proto_init() {
|
||||
if File_mount_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_mount_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ConfigureRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_mount_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ConfigureResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_mount_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_mount_proto_goTypes,
|
||||
DependencyIndexes: file_mount_proto_depIdxs,
|
||||
MessageInfos: file_mount_proto_msgTypes,
|
||||
}.Build()
|
||||
File_mount_proto = out.File
|
||||
file_mount_proto_rawDesc = nil
|
||||
file_mount_proto_goTypes = nil
|
||||
file_mount_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package mount_pb
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// SeaweedMountClient is the client API for SeaweedMount service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type SeaweedMountClient interface {
|
||||
Configure(ctx context.Context, in *ConfigureRequest, opts ...grpc.CallOption) (*ConfigureResponse, error)
|
||||
}
|
||||
|
||||
type seaweedMountClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewSeaweedMountClient(cc grpc.ClientConnInterface) SeaweedMountClient {
|
||||
return &seaweedMountClient{cc}
|
||||
}
|
||||
|
||||
func (c *seaweedMountClient) Configure(ctx context.Context, in *ConfigureRequest, opts ...grpc.CallOption) (*ConfigureResponse, error) {
|
||||
out := new(ConfigureResponse)
|
||||
err := c.cc.Invoke(ctx, "/messaging_pb.SeaweedMount/Configure", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SeaweedMountServer is the server API for SeaweedMount service.
|
||||
// All implementations must embed UnimplementedSeaweedMountServer
|
||||
// for forward compatibility
|
||||
type SeaweedMountServer interface {
|
||||
Configure(context.Context, *ConfigureRequest) (*ConfigureResponse, error)
|
||||
mustEmbedUnimplementedSeaweedMountServer()
|
||||
}
|
||||
|
||||
// UnimplementedSeaweedMountServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedSeaweedMountServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedSeaweedMountServer) Configure(context.Context, *ConfigureRequest) (*ConfigureResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Configure not implemented")
|
||||
}
|
||||
func (UnimplementedSeaweedMountServer) mustEmbedUnimplementedSeaweedMountServer() {}
|
||||
|
||||
// UnsafeSeaweedMountServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to SeaweedMountServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeSeaweedMountServer interface {
|
||||
mustEmbedUnimplementedSeaweedMountServer()
|
||||
}
|
||||
|
||||
func RegisterSeaweedMountServer(s grpc.ServiceRegistrar, srv SeaweedMountServer) {
|
||||
s.RegisterService(&SeaweedMount_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _SeaweedMount_Configure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ConfigureRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SeaweedMountServer).Configure(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/messaging_pb.SeaweedMount/Configure",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SeaweedMountServer).Configure(ctx, req.(*ConfigureRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// SeaweedMount_ServiceDesc is the grpc.ServiceDesc for SeaweedMount service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var SeaweedMount_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "messaging_pb.SeaweedMount",
|
||||
HandlerType: (*SeaweedMountServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Configure",
|
||||
Handler: _SeaweedMount_Configure_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "mount.proto",
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func TestJsonpMarshalUnmarshal(t *testing.T) {
|
||||
rawJson := `{
|
||||
"backendType":"aws",
|
||||
"backendId":"temp",
|
||||
"FileSize":12
|
||||
"fileSize":12
|
||||
}`
|
||||
|
||||
tv1 := &volume_server_pb.RemoteFile{}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.6.1
|
||||
// protoc v3.17.3
|
||||
// source: remote.proto
|
||||
|
||||
package remote_pb
|
||||
|
||||
@@ -89,7 +89,7 @@ func (sa ServerAddresses) ToAddresses() (addresses []ServerAddress) {
|
||||
func (sa ServerAddresses) ToAddressMap() (addresses map[string]ServerAddress) {
|
||||
addresses = make(map[string]ServerAddress)
|
||||
for _, address := range sa.ToAddresses() {
|
||||
addresses[address.String()] = address
|
||||
addresses[string(address)] = address
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -107,6 +107,10 @@ service VolumeServer {
|
||||
|
||||
rpc VolumeNeedleStatus (VolumeNeedleStatusRequest) returns (VolumeNeedleStatusResponse) {
|
||||
}
|
||||
|
||||
rpc Ping (PingRequest) returns (PingResponse) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
@@ -573,3 +577,13 @@ message VolumeNeedleStatusResponse {
|
||||
uint32 crc = 5;
|
||||
string ttl = 6;
|
||||
}
|
||||
|
||||
message PingRequest {
|
||||
string target = 1; // default to ping itself
|
||||
string target_type = 2;
|
||||
}
|
||||
message PingResponse {
|
||||
int64 start_time_ns = 1;
|
||||
int64 remote_time_ns = 2;
|
||||
int64 stop_time_ns = 3;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user