mirror of
https://github.com/usatiuk/dhfs.git
synced 2025-10-28 20:47:49 +01:00
Compare commits
144 Commits
gpt-rocks
...
0849df60ae
| Author | SHA1 | Date | |
|---|---|---|---|
| 0849df60ae | |||
| 9cb5c226f9 | |||
| 87c404828c | |||
| b074e8eb44 | |||
| eb5b0ae03c | |||
| c329c1f982 | |||
| 4e7b13227b | |||
| db51d7280c | |||
| 70fecb389b | |||
| 6e9a2b25f6 | |||
| b84ef95703 | |||
| c0735801b9 | |||
| b506ced9d5 | |||
| 46bc9fa810 | |||
| 8ab034402d | |||
| d94d11ec8b | |||
| 5beaad2d32 | |||
| c4484d21e5 | |||
| 2766ef1bae | |||
| 58de85c078 | |||
| cc9da86440 | |||
| e6c9e6aee9 | |||
| 62265355c4 | |||
| 854bce1627 | |||
| 1b19c77bb6 | |||
| 7aa968a569 | |||
| e348c39be1 | |||
| 1b54830651 | |||
| bc5f0b816c | |||
| 9ff914bdaa | |||
| 1cee6f62b8 | |||
| 81703a9406 | |||
| 1757034e0b | |||
| d9765a51d8 | |||
| 99ef560b95 | |||
| f87eb365c3 | |||
| 8d3244fe64 | |||
| 0a8985c93f | |||
| a8cf483eee | |||
| f7338f4e80 | |||
| b89b182c58 | |||
| ad4ce72fdd | |||
| 26ba65fdce | |||
| 697add66d5 | |||
| a53fc5e973 | |||
| b034591091 | |||
| 07133a7186 | |||
| 8cbecf1714 | |||
| 16ba692019 | |||
| e5be1e6164 | |||
| c74fdfc5a6 | |||
| c4268ab35b | |||
| 2ab6e3c3f7 | |||
| ec8546bd69 | |||
| 6ecef94b90 | |||
| e7f22d783f | |||
| bed55162d7 | |||
| f43c6db4f0 | |||
| 56a15f4672 | |||
| 85a1fa09ab | |||
| cca0b410cf | |||
| d94abfee97 | |||
|
|
6bd92ad7cd | ||
| 1965d93f25 | |||
| f6685f45f9 | |||
| 060ab1767d | |||
| 89d87095c8 | |||
| 7425c1f312 | |||
| 428eca325f | |||
| 005bc35496 | |||
| 6685575ca5 | |||
| 1ae813aacd | |||
| e81671251a | |||
| add26bb156 | |||
| 4060045f15 | |||
| 75b484d5b2 | |||
| 1d9dc8ed4d | |||
| 7a85704862 | |||
| 367eedd540 | |||
| d01b9204f7 | |||
| 67fdacc3ff | |||
| 6ed9051be1 | |||
| abf95ba847 | |||
| 6a9f64439f | |||
| ceb9342b45 | |||
| ca354ba09c | |||
| 81af021292 | |||
| 0c04079258 | |||
| 2e2eb3ac97 | |||
| e2e756e7c5 | |||
| 04e932ed62 | |||
| aeec66389d | |||
| adc7356d4a | |||
| 16da05292f | |||
| b0149b7251 | |||
| 24416c1e87 | |||
| 34db870fc6 | |||
| 0e62a29ce0 | |||
| 7de5f91fd2 | |||
| ac68208b1a | |||
| 4e0675940e | |||
| 4f5f347b3c | |||
| bd5395e03f | |||
| f56f564e8b | |||
| eaa413e200 | |||
| f3e4d99fcb | |||
| 1c71b26ed8 | |||
| e6f95ef028 | |||
| 59e8f6a6b4 | |||
| 0292df7f0e | |||
| a6a4101bb0 | |||
| 59fa5dcf28 | |||
| 0f5fb8b8b6 | |||
| c087dd8971 | |||
| 14ddddd0ff | |||
| 9859378627 | |||
| e167c21d40 | |||
| 7dc8f486ea | |||
| da1a996e6f | |||
| bb52a3af0e | |||
| de0b868349 | |||
| d4d4e150c1 | |||
| c9b0400d50 | |||
| 94218330b1 | |||
| dbe2a72f7c | |||
| 643c53c894 | |||
| 29fdd3eb08 | |||
| e6ead10e7f | |||
| 04c5685fd5 | |||
| 7061117f56 | |||
| 67852fb37e | |||
| d48cc18e85 | |||
| 77177414eb | |||
| 83e0f6eb0a | |||
| a5727c01b1 | |||
| 711c4f5e28 | |||
| 45556f2b74 | |||
| 146870c281 | |||
| 9178e7ee2d | |||
| 7c605135c5 | |||
| 491afd454b | |||
| bb65aab166 | |||
| a4810c7ee4 | |||
| e42e076b77 |
142
.github/workflows/server.yml
vendored
142
.github/workflows/server.yml
vendored
@@ -20,26 +20,21 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: "recursive"
|
||||||
|
|
||||||
- name: Install sudo for ACT
|
- name: Install sudo for ACT
|
||||||
run: apt-get update && apt-get install -y sudo
|
run: apt-get update && apt-get install -y sudo
|
||||||
if: env.ACT=='true'
|
if: env.ACT=='true'
|
||||||
|
|
||||||
- name: Install fuse and maven
|
- name: Install FUSE
|
||||||
run: sudo apt-get update && sudo apt-get install -y libfuse2
|
run: sudo apt-get update && sudo apt-get install -y libfuse2 libfuse3-dev libfuse3-3 fuse3
|
||||||
|
|
||||||
- name: Download maven
|
- name: User allow other for fuse
|
||||||
run: |
|
run: echo "user_allow_other" | sudo tee -a /etc/fuse.conf
|
||||||
cd "$HOME"
|
|
||||||
mkdir maven-bin
|
|
||||||
curl -s -L https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz | tar xvz --strip-components=1 -C maven-bin
|
|
||||||
echo "$HOME"/maven-bin/bin >> $GITHUB_PATH
|
|
||||||
|
|
||||||
- name: Maven info
|
- name: Dump fuse.conf
|
||||||
run: |
|
run: cat /etc/fuse.conf
|
||||||
echo $GITHUB_PATH
|
|
||||||
echo $PATH
|
|
||||||
mvn -v
|
|
||||||
|
|
||||||
- name: Set up JDK 21
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
@@ -48,6 +43,9 @@ jobs:
|
|||||||
distribution: "zulu"
|
distribution: "zulu"
|
||||||
cache: maven
|
cache: maven
|
||||||
|
|
||||||
|
- name: Build LazyFS
|
||||||
|
run: cd thirdparty/lazyfs/ && ./build.sh
|
||||||
|
|
||||||
- name: Test with Maven
|
- name: Test with Maven
|
||||||
run: cd dhfs-parent && mvn -T $(nproc) --batch-mode --update-snapshots package verify
|
run: cd dhfs-parent && mvn -T $(nproc) --batch-mode --update-snapshots package verify
|
||||||
|
|
||||||
@@ -57,7 +55,7 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: DHFS Server Package
|
name: DHFS Server Package
|
||||||
path: dhfs-parent/server/target/quarkus-app
|
path: dhfs-parent/dhfs-fuse/target/quarkus-app
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
@@ -89,102 +87,6 @@ jobs:
|
|||||||
name: Webui
|
name: Webui
|
||||||
path: webui/dist
|
path: webui/dist
|
||||||
|
|
||||||
build-native-libs:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
cross: "linux/amd64"
|
|
||||||
- os: ubuntu-latest
|
|
||||||
cross: "linux/arm64"
|
|
||||||
- os: macos-latest
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
env:
|
|
||||||
DO_LOCAL_BUILD: ${{ matrix.os == 'macos-latest' }}
|
|
||||||
DOCKER_PLATFORM: ${{ matrix.cross || 'NATIVE' }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set SANITIZED_DOCKER_PLATFORM
|
|
||||||
run: echo "SANITIZED_DOCKER_PLATFORM=$(echo $DOCKER_PLATFORM | tr / _ )" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set DOCKER_BUILDER_IMAGE
|
|
||||||
run: echo "DOCKER_BUILDER_IMAGE=dhfs_lib_builder-${{matrix.os}}-$SANITIZED_DOCKER_PLATFORM" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Build config
|
|
||||||
run: |
|
|
||||||
echo DO_LOCAL_BUILD: $DO_LOCAL_BUILD
|
|
||||||
echo DOCKER_PLATFORM: $DOCKER_PLATFORM
|
|
||||||
echo SANITIZED_DOCKER_PLATFORM: $SANITIZED_DOCKER_PLATFORM
|
|
||||||
echo DOCKER_BUILDER_IMAGE: $DOCKER_BUILDER_IMAGE
|
|
||||||
|
|
||||||
- name: Set up JDK 21
|
|
||||||
if: ${{ env.DO_LOCAL_BUILD == 'TRUE' }}
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
java-version: "21"
|
|
||||||
distribution: "zulu"
|
|
||||||
cache: maven
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
if: ${{ env.DO_LOCAL_BUILD != 'TRUE' }}
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
if: ${{ env.DO_LOCAL_BUILD != 'TRUE' }}
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Build Docker builder image
|
|
||||||
if: ${{ env.DO_LOCAL_BUILD != 'TRUE' }}
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: ./libdhfs_support/builder
|
|
||||||
file: ./libdhfs_support/builder/Dockerfile
|
|
||||||
push: false
|
|
||||||
platforms: ${{ env.DOCKER_PLATFORM }}
|
|
||||||
tags: ${{ env.DOCKER_BUILDER_IMAGE }}
|
|
||||||
cache-from: type=gha,scope=build-${{ env.DOCKER_BUILDER_IMAGE }}
|
|
||||||
cache-to: type=gha,mode=max,scope=build-${{ env.DOCKER_BUILDER_IMAGE }}
|
|
||||||
load: true
|
|
||||||
|
|
||||||
- name: Build the library
|
|
||||||
run: |
|
|
||||||
CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Release" libdhfs_support/builder/cross-build.sh both build "$(pwd)/result"
|
|
||||||
|
|
||||||
- name: Upload build
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: NativeLib-${{ matrix.os }}-${{ env.SANITIZED_DOCKER_PLATFORM }}
|
|
||||||
path: result
|
|
||||||
|
|
||||||
merge-native-libs:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build-native-libs]
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: downloaded-libs
|
|
||||||
|
|
||||||
- name: Merge all
|
|
||||||
run: rsync -av downloaded-libs/NativeLib*/* result/
|
|
||||||
|
|
||||||
- name: Check that libs exists
|
|
||||||
run: |
|
|
||||||
test -f "result/Linux-x86_64/libdhfs_support.so" || exit 1
|
|
||||||
|
|
||||||
- name: Upload
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: NativeLibs
|
|
||||||
path: result
|
|
||||||
|
|
||||||
publish-docker:
|
publish-docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
@@ -194,7 +96,7 @@ jobs:
|
|||||||
# with sigstore/fulcio when running outside of PRs.
|
# with sigstore/fulcio when running outside of PRs.
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
needs: [build-webui, merge-native-libs, build-dhfs]
|
needs: [build-webui, build-dhfs]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -212,12 +114,6 @@ jobs:
|
|||||||
name: Webui
|
name: Webui
|
||||||
path: webui-dist-downloaded
|
path: webui-dist-downloaded
|
||||||
|
|
||||||
- name: Download native libs
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: NativeLibs
|
|
||||||
path: dhfs-native-downloaded
|
|
||||||
|
|
||||||
- name: Show all the files
|
- name: Show all the files
|
||||||
run: find .
|
run: find .
|
||||||
|
|
||||||
@@ -293,7 +189,7 @@ jobs:
|
|||||||
# with sigstore/fulcio when running outside of PRs.
|
# with sigstore/fulcio when running outside of PRs.
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
needs: [build-webui, merge-native-libs, build-dhfs]
|
needs: [build-webui, build-dhfs]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -309,11 +205,6 @@ jobs:
|
|||||||
name: Webui
|
name: Webui
|
||||||
path: webui-dist-downloaded
|
path: webui-dist-downloaded
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: NativeLibs
|
|
||||||
path: dhfs-native-downloaded
|
|
||||||
|
|
||||||
- name: Show all the files
|
- name: Show all the files
|
||||||
run: find .
|
run: find .
|
||||||
|
|
||||||
@@ -321,14 +212,11 @@ jobs:
|
|||||||
run: mkdir -p run-wrapper-out/dhfs/data && mkdir -p run-wrapper-out/dhfs/fuse && mkdir -p run-wrapper-out/dhfs/app
|
run: mkdir -p run-wrapper-out/dhfs/data && mkdir -p run-wrapper-out/dhfs/fuse && mkdir -p run-wrapper-out/dhfs/app
|
||||||
|
|
||||||
- name: Copy DHFS
|
- name: Copy DHFS
|
||||||
run: cp -r ./dhfs-package-downloaded "run-wrapper-out/dhfs/app/DHFS Package"
|
run: cp -r ./dhfs-package-downloaded "run-wrapper-out/dhfs/app/Server"
|
||||||
|
|
||||||
- name: Copy Webui
|
- name: Copy Webui
|
||||||
run: cp -r ./webui-dist-downloaded "run-wrapper-out/dhfs/app/Webui"
|
run: cp -r ./webui-dist-downloaded "run-wrapper-out/dhfs/app/Webui"
|
||||||
|
|
||||||
- name: Copy Webui
|
|
||||||
run: cp -r ./dhfs-native-downloaded "run-wrapper-out/dhfs/app/NativeLibs"
|
|
||||||
|
|
||||||
- name: Copy run wrapper
|
- name: Copy run wrapper
|
||||||
run: cp -r ./run-wrapper/* "run-wrapper-out/dhfs/app/"
|
run: cp -r ./run-wrapper/* "run-wrapper-out/dhfs/app/"
|
||||||
|
|
||||||
|
|||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "thirdparty/lazyfs/lazyfs"]
|
||||||
|
path = thirdparty/lazyfs/lazyfs
|
||||||
|
url = git@github.com:dsrhaslab/lazyfs.git
|
||||||
@@ -9,8 +9,6 @@ COPY ./dhfs-package-downloaded/*.jar .
|
|||||||
COPY ./dhfs-package-downloaded/app .
|
COPY ./dhfs-package-downloaded/app .
|
||||||
COPY ./dhfs-package-downloaded/quarkus .
|
COPY ./dhfs-package-downloaded/quarkus .
|
||||||
|
|
||||||
WORKDIR /usr/src/app/native-libs
|
|
||||||
COPY ./dhfs-native-downloaded/. .
|
|
||||||
WORKDIR /usr/src/app/webui
|
WORKDIR /usr/src/app/webui
|
||||||
COPY ./webui-dist-downloaded/. .
|
COPY ./webui-dist-downloaded/. .
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ Syncthing and allowing you to stream your files like Google Drive File Stream
|
|||||||
This is a simple wrapper around the jar/web ui distribution that allows you to run/stop
|
This is a simple wrapper around the jar/web ui distribution that allows you to run/stop
|
||||||
the DHFS server in the background, and update itself (hopefully!)
|
the DHFS server in the background, and update itself (hopefully!)
|
||||||
|
|
||||||
## How to use it and how it works?
|
## How to use it?
|
||||||
|
|
||||||
TODO 😁
|
|
||||||
|
Unpack the run-wrapper and run the `run` script. The filesystem should be mounted to the `fuse` folder in the run-wrapper root directory.
|
||||||
|
|
||||||
|
Then, a web interface will be available at `losthost:8080`, that can be used to connect with other peers.
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Main 2" type="QsApplicationConfigurationType" factoryName="QuarkusApplication">
|
<configuration default="false" name="Main 2" type="QsApplicationConfigurationType" factoryName="QuarkusApplication">
|
||||||
<option name="MAIN_CLASS_NAME" value="com.usatiuk.dhfs.Main"/>
|
<option name="MAIN_CLASS_NAME" value="com.usatiuk.dhfsfuse.Main" />
|
||||||
<module name="server"/>
|
<module name="dhfs-fuse" />
|
||||||
<option name="VM_PARAMETERS"
|
<option name="VM_PARAMETERS" value="-XX:+UnlockDiagnosticVMOptions -XX:+UseParallelGC -XX:+DebugNonSafepoints --enable-preview --add-exports java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-exports java.base/jdk.internal.access=ALL-UNNAMED -ea -Xmx512M -Ddhfs.webui.root=$ProjectFileDir$/../webui/dist -Ddhfs.fuse.root=${HOME}/dhfs_test/2/fuse -Ddhfs.objects.persistence.files.root=${HOME}/dhfs_test/2/data -Ddhfs.objects.persistence.stuff.root=${HOME}/dhfs_test/2/data/stuff -Ddhfs.objects.peerdiscovery.broadcast=false -Dquarkus.http.port=9020 -Dquarkus.http.ssl-port=9021 -Ddhfs.peerdiscovery.preset-uuid=22000000-0000-0000-0000-000000000000 -Ddhfs.peerdiscovery.static-peers=11000000-0000-0000-0000-000000000000:127.0.0.1:9010:9011" />
|
||||||
value="-XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints --add-exports java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-exports java.base/jdk.internal.access=ALL-UNNAMED -ea -Dcom.usatiuk.dhfs.supportlib.native-path=$ProjectFileDir$/target/classes/native -Xmx2G -Ddhfs.webui.root=$ProjectFileDir$/../webui/dist -Ddhfs.fuse.root=${HOME}/dhfs_test/2/fuse -Ddhfs.objects.persistence.files.root=${HOME}/dhfs_test/2/data -Ddhfs.objects.persistence.stuff.root=${HOME}/dhfs_test/2/data/stuff -Ddhfs.objects.peerdiscovery.broadcast=false -Dquarkus.http.port=9020 -Dquarkus.http.ssl-port=9021 -Ddhfs.peerdiscovery.preset-uuid=22000000-0000-0000-0000-000000000000 -Ddhfs.peerdiscovery.static-peers=11000000-0000-0000-0000-000000000000:127.0.0.1:9010:9011"/>
|
<extension name="coverage">
|
||||||
<extension name="coverage">
|
<pattern>
|
||||||
<pattern>
|
<option name="PATTERN" value="com.usatiuk.dhfs.remoteobj.*" />
|
||||||
<option name="PATTERN" value="com.usatiuk.dhfs.*"/>
|
<option name="ENABLED" value="true" />
|
||||||
<option name="ENABLED" value="true"/>
|
</pattern>
|
||||||
</pattern>
|
</extension>
|
||||||
</extension>
|
<method v="2">
|
||||||
<method v="2">
|
<option name="Make" enabled="true" />
|
||||||
<option name="Make" enabled="true"/>
|
</method>
|
||||||
</method>
|
</configuration>
|
||||||
</configuration>
|
|
||||||
</component>
|
</component>
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Main" type="QsApplicationConfigurationType" factoryName="QuarkusApplication"
|
<configuration default="false" name="Main" type="QsApplicationConfigurationType" factoryName="QuarkusApplication" nameIsGenerated="true">
|
||||||
nameIsGenerated="true">
|
<option name="MAIN_CLASS_NAME" value="com.usatiuk.dhfsfuse.Main" />
|
||||||
<option name="MAIN_CLASS_NAME" value="com.usatiuk.dhfs.Main"/>
|
<module name="dhfs-fuse" />
|
||||||
<module name="server"/>
|
<option name="VM_PARAMETERS" value="-XX:+UnlockDiagnosticVMOptions -XX:+UseZGC -XX:+ZGenerational --enable-preview -XX:+DebugNonSafepoints --add-exports java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-exports java.base/jdk.internal.access=ALL-UNNAMED -ea -Xmx1G -Ddhfs.webui.root=$ProjectFileDir$/../webui/dist -Ddhfs.fuse.root=${HOME}/dhfs_test/1/fuse -Ddhfs.objects.persistence.files.root=${HOME}/dhfs_test/1/data -Ddhfs.objects.persistence.stuff.root=${HOME}/dhfs_test/1/data/stuff -Ddhfs.objects.peerdiscovery.broadcast=true -Dquarkus.http.port=9010 -Dquarkus.http.ssl-port=9011 -Ddhfs.peerdiscovery.preset-uuid=11000000-0000-0000-0000-000000000000 -Ddhfs.peerdiscovery.static-peers=22000000-0000-0000-0000-000000000000:127.0.0.1:9020:9021 -Dquarkus.http.host=0.0.0.0" />
|
||||||
<option name="VM_PARAMETERS"
|
<extension name="coverage">
|
||||||
value="-XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints --add-exports java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-exports java.base/jdk.internal.access=ALL-UNNAMED -ea -Dcom.usatiuk.dhfs.supportlib.native-path=$ProjectFileDir$/target/classes/native -Xmx2G -Ddhfs.webui.root=$ProjectFileDir$/../webui/dist -Ddhfs.fuse.root=${HOME}/dhfs_test/1/fuse -Ddhfs.objects.persistence.files.root=${HOME}/dhfs_test/1/data -Ddhfs.objects.persistence.stuff.root=${HOME}/dhfs_test/1/data/stuff -Ddhfs.objects.peerdiscovery.broadcast=false -Dquarkus.http.port=9010 -Dquarkus.http.ssl-port=9011 -Ddhfs.peerdiscovery.preset-uuid=11000000-0000-0000-0000-000000000000 -Ddhfs.peerdiscovery.static-peers=22000000-0000-0000-0000-000000000000:127.0.0.1:9020:9021"/>
|
<pattern>
|
||||||
<extension name="coverage">
|
<option name="PATTERN" value="com.usatiuk.dhfs.remoteobj.*" />
|
||||||
<pattern>
|
<option name="ENABLED" value="true" />
|
||||||
<option name="PATTERN" value="com.usatiuk.dhfs.*"/>
|
</pattern>
|
||||||
<option name="ENABLED" value="true"/>
|
</extension>
|
||||||
</pattern>
|
<method v="2">
|
||||||
</extension>
|
<option name="Make" enabled="true" />
|
||||||
<method v="2">
|
</method>
|
||||||
<option name="Make" enabled="true"/>
|
</configuration>
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
</component>
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>com.usatiuk</groupId>
|
|
||||||
<artifactId>autoprotomap-parent</artifactId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>autoprotomap-deployment</artifactId>
|
|
||||||
<name>Autoprotomap - Deployment</name>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-arc-deployment</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.usatiuk</groupId>
|
|
||||||
<artifactId>autoprotomap</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-junit5-internal</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-grpc-deployment</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.commons</groupId>
|
|
||||||
<artifactId>commons-collections4</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>default-compile</id>
|
|
||||||
<configuration>
|
|
||||||
<annotationProcessorPaths>
|
|
||||||
<path>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-extension-processor</artifactId>
|
|
||||||
<version>${quarkus.platform.version}</version>
|
|
||||||
</path>
|
|
||||||
</annotationProcessorPaths>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.deployment;
|
|
||||||
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoMirror;
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoSerializer;
|
|
||||||
import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
|
|
||||||
import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
|
|
||||||
import io.quarkus.deployment.annotations.BuildProducer;
|
|
||||||
import io.quarkus.deployment.annotations.BuildStep;
|
|
||||||
import io.quarkus.deployment.builditem.ApplicationIndexBuildItem;
|
|
||||||
import io.quarkus.gizmo.ClassCreator;
|
|
||||||
import io.quarkus.gizmo.SignatureBuilder;
|
|
||||||
import jakarta.inject.Singleton;
|
|
||||||
import org.jboss.jandex.ClassType;
|
|
||||||
import org.jboss.jandex.Type;
|
|
||||||
|
|
||||||
class AutoprotomapProcessor {
|
|
||||||
@BuildStep
|
|
||||||
ProtoIndexBuildItem index(ApplicationIndexBuildItem jandex) {
|
|
||||||
var ret = new ProtoIndexBuildItem();
|
|
||||||
var annot = jandex.getIndex().getAnnotations(ProtoMirror.class);
|
|
||||||
for (var a : annot) {
|
|
||||||
var protoTarget = jandex.getIndex().getClassByName(((ClassType) a.value().value()).name());
|
|
||||||
// if (!messageImplementors.contains(protoTarget))
|
|
||||||
// throw new IllegalArgumentException("Expected " + protoTarget + " to be a proto message");
|
|
||||||
System.out.println("Found: " + a.name().toString() + " at " + protoTarget.name().toString() + " of " + a.target().asClass().name().toString());
|
|
||||||
ret.protoMsgToObj.put(protoTarget, a.target().asClass());
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
@BuildStep
|
|
||||||
void generateProtoSerializer(ApplicationIndexBuildItem jandex,
|
|
||||||
ProtoIndexBuildItem protoIndex,
|
|
||||||
BuildProducer<GeneratedBeanBuildItem> generatedClasses) {
|
|
||||||
try {
|
|
||||||
for (var o : protoIndex.protoMsgToObj.entrySet()) {
|
|
||||||
System.out.println("Generating " + o.getKey().toString() + " -> " + o.getValue().toString());
|
|
||||||
var gizmoAdapter = new GeneratedBeanGizmoAdaptor(generatedClasses);
|
|
||||||
|
|
||||||
var msgType = io.quarkus.gizmo.Type.classType(o.getKey().name());
|
|
||||||
var objType = io.quarkus.gizmo.Type.classType(o.getValue().name());
|
|
||||||
|
|
||||||
var type = io.quarkus.gizmo.Type.ParameterizedType.parameterizedType(
|
|
||||||
io.quarkus.gizmo.Type.classType(ProtoSerializer.class),
|
|
||||||
msgType, objType);
|
|
||||||
|
|
||||||
var msgJType = Type.create(o.getKey().name(), Type.Kind.CLASS);
|
|
||||||
var objJType = Type.create(o.getValue().name(), Type.Kind.CLASS);
|
|
||||||
|
|
||||||
try (ClassCreator classCreator = ClassCreator.builder()
|
|
||||||
.className("com.usatiuk.autoprotomap.generated.for" + o.getKey().simpleName())
|
|
||||||
.signature(SignatureBuilder.forClass().addInterface(type))
|
|
||||||
.classOutput(gizmoAdapter)
|
|
||||||
.setFinal(true)
|
|
||||||
.build()) {
|
|
||||||
classCreator.addAnnotation(Singleton.class);
|
|
||||||
|
|
||||||
var generator = new ProtoSerializerGenerator(
|
|
||||||
jandex.getIndex(),
|
|
||||||
protoIndex,
|
|
||||||
classCreator,
|
|
||||||
msgJType,
|
|
||||||
objJType
|
|
||||||
);
|
|
||||||
|
|
||||||
generator.generate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(e + "\n");
|
|
||||||
for (var el : e.getStackTrace()) {
|
|
||||||
sb.append(el.toString() + "\n");
|
|
||||||
}
|
|
||||||
System.out.println(sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.deployment;
|
|
||||||
|
|
||||||
public class Constants {
|
|
||||||
public static final String FIELD_PREFIX = "_";
|
|
||||||
|
|
||||||
public static String capitalize(String str) {
|
|
||||||
return str.substring(0, 1).toUpperCase() + str.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String stripPrefix(String str, String prefix) {
|
|
||||||
if (str.startsWith(prefix)) {
|
|
||||||
return str.substring(prefix.length());
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.deployment;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface Effect {
|
|
||||||
void apply();
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.deployment;
|
|
||||||
|
|
||||||
import io.quarkus.builder.item.SimpleBuildItem;
|
|
||||||
import org.apache.commons.collections4.BidiMap;
|
|
||||||
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
|
|
||||||
import org.jboss.jandex.ClassInfo;
|
|
||||||
|
|
||||||
public final class ProtoIndexBuildItem extends SimpleBuildItem {
|
|
||||||
BidiMap<ClassInfo, ClassInfo> protoMsgToObj = new DualHashBidiMap<>();
|
|
||||||
}
|
|
||||||
@@ -1,342 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.deployment;
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
import com.google.protobuf.Message;
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoSerializer;
|
|
||||||
import io.quarkus.gizmo.*;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
import org.jboss.jandex.*;
|
|
||||||
import org.jboss.jandex.Type;
|
|
||||||
import org.objectweb.asm.Opcodes;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.IntConsumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static com.usatiuk.autoprotomap.deployment.Constants.*;
|
|
||||||
|
|
||||||
public class ProtoSerializerGenerator {
|
|
||||||
private final Index index;
|
|
||||||
private final ProtoIndexBuildItem protoIndex;
|
|
||||||
private final ClassCreator classCreator;
|
|
||||||
private final HashSet<Pair<ClassInfo, ClassInfo>> externalSerializers = new HashSet<>();
|
|
||||||
private final Type topMessageType;
|
|
||||||
private final Type topObjectType;
|
|
||||||
|
|
||||||
public ProtoSerializerGenerator(Index index, ProtoIndexBuildItem protoIndex, ClassCreator classCreator, Type topMessageType, Type topObjectType) {
|
|
||||||
this.index = index;
|
|
||||||
this.protoIndex = protoIndex;
|
|
||||||
this.classCreator = classCreator;
|
|
||||||
this.topMessageType = topMessageType;
|
|
||||||
this.topObjectType = topObjectType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private FieldDescriptor getOutsideSerializer(ClassInfo messageClass, ClassInfo objectClass) {
|
|
||||||
var name = messageClass.name().withoutPackagePrefix() + objectClass.name().withoutPackagePrefix() + "serializer";
|
|
||||||
var msgType = io.quarkus.gizmo.Type.classType(messageClass.name());
|
|
||||||
var objType = io.quarkus.gizmo.Type.classType(objectClass.name());
|
|
||||||
var type = io.quarkus.gizmo.Type.ParameterizedType.parameterizedType(
|
|
||||||
io.quarkus.gizmo.Type.classType(ProtoSerializer.class),
|
|
||||||
msgType, objType);
|
|
||||||
var sig = SignatureBuilder.forField().setType(type).build();
|
|
||||||
var fd = FieldDescriptor.of(classCreator.getClassName(), name, ProtoSerializer.class);
|
|
||||||
if (externalSerializers.add(Pair.of(messageClass, objectClass))) {
|
|
||||||
var fc = classCreator.getFieldCreator(fd);
|
|
||||||
fc.addAnnotation(Inject.class);
|
|
||||||
fc.setSignature(sig);
|
|
||||||
fc.setModifiers(Opcodes.ACC_PUBLIC);
|
|
||||||
}
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void traverseHierarchy(Index index, ClassInfo klass, Consumer<ClassInfo> visitor) {
|
|
||||||
var cur = klass;
|
|
||||||
while (true) {
|
|
||||||
visitor.accept(cur);
|
|
||||||
|
|
||||||
var next = cur.superClassType().name();
|
|
||||||
if (next.equals(DotName.OBJECT_NAME) || next.equals(DotName.RECORD_NAME)) break;
|
|
||||||
cur = index.getClassByName(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList<FieldInfo> findAllFields(Index index, ClassInfo klass) {
|
|
||||||
ArrayList<FieldInfo> ret = new ArrayList<>();
|
|
||||||
traverseHierarchy(index, klass, cur -> {
|
|
||||||
ret.addAll(cur.fields());
|
|
||||||
});
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateBuilderUse(BytecodeCreator bytecodeCreator,
|
|
||||||
ResultHandle builder,
|
|
||||||
Type messageType, Type objectType,
|
|
||||||
ResultHandle object) {
|
|
||||||
var builderType = Type.create(DotName.createComponentized(messageType.name(), "Builder", true), Type.Kind.CLASS);
|
|
||||||
|
|
||||||
var objectClass = index.getClassByName(objectType.name().toString());
|
|
||||||
|
|
||||||
Function<String, String> getterGetter = objectClass.isRecord()
|
|
||||||
? Function.identity()
|
|
||||||
: s -> "get" + capitalize(stripPrefix(s, FIELD_PREFIX));
|
|
||||||
|
|
||||||
for (var f : findAllFields(index, objectClass)) {
|
|
||||||
var consideredFieldName = stripPrefix(f.name(), FIELD_PREFIX);
|
|
||||||
|
|
||||||
Supplier<ResultHandle> get = () -> {
|
|
||||||
if ((f.flags() & Opcodes.ACC_PUBLIC) != 0)
|
|
||||||
return bytecodeCreator.readInstanceField(f, object);
|
|
||||||
else {
|
|
||||||
var fieldGetter = getterGetter.apply(f.name());
|
|
||||||
return bytecodeCreator.invokeVirtualMethod(
|
|
||||||
MethodDescriptor.ofMethod(objectType.toString(), fieldGetter, f.type().name().toString()), object);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Effect doSimpleCopy = () -> {
|
|
||||||
var setter = MethodDescriptor.ofMethod(builderType.name().toString(), "set" + capitalize(consideredFieldName),
|
|
||||||
builderType.name().toString(), f.type().toString());
|
|
||||||
|
|
||||||
var val = get.get();
|
|
||||||
bytecodeCreator.invokeVirtualMethod(setter, builder, val);
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (f.type().kind()) {
|
|
||||||
case CLASS -> {
|
|
||||||
if (f.type().equals(Type.create(String.class)) || f.type().equals(Type.create(ByteString.class))) {
|
|
||||||
doSimpleCopy.apply();
|
|
||||||
} else {
|
|
||||||
var builderGetter = "get" + capitalize(f.name()) + "Builder";
|
|
||||||
var protoType = protoIndex.protoMsgToObj.inverseBidiMap().get(index.getClassByName(f.type().name()));
|
|
||||||
var nestedBuilderType = Type.create(DotName.createComponentized(protoType.name(), "Builder", true), Type.Kind.CLASS);
|
|
||||||
var nestedBuilder = bytecodeCreator.invokeVirtualMethod(
|
|
||||||
MethodDescriptor.ofMethod(builderType.toString(), builderGetter, nestedBuilderType.name().toString()), builder);
|
|
||||||
|
|
||||||
var val = get.get();
|
|
||||||
|
|
||||||
generateBuilderUse(bytecodeCreator, nestedBuilder, Type.create(protoType.name(), Type.Kind.CLASS), f.type(), val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case PRIMITIVE -> {
|
|
||||||
doSimpleCopy.apply();
|
|
||||||
}
|
|
||||||
case WILDCARD_TYPE -> throw new UnsupportedOperationException("Wildcards not supported yet");
|
|
||||||
case PARAMETERIZED_TYPE ->
|
|
||||||
throw new UnsupportedOperationException("Parametrized types not supported yet");
|
|
||||||
case ARRAY -> throw new UnsupportedOperationException("Arrays not supported yet");
|
|
||||||
default -> throw new IllegalStateException("Unexpected type: " + f.type());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResultHandle generateConstructorUse(
|
|
||||||
BytecodeCreator bytecodeCreator,
|
|
||||||
ClassCreator classCreator,
|
|
||||||
Type messageType, Type objectType,
|
|
||||||
ResultHandle message
|
|
||||||
) {
|
|
||||||
var constructor = findAllArgsConstructor(index, index.getClassByName(objectType.name()));
|
|
||||||
if (constructor == null) {
|
|
||||||
throw new IllegalStateException("No constructor found for type: " + objectType.name());
|
|
||||||
}
|
|
||||||
var argMap = new ResultHandle[constructor.parametersCount()];
|
|
||||||
|
|
||||||
for (int i = 0; i < argMap.length; i++) {
|
|
||||||
var type = constructor.parameterType(i);
|
|
||||||
var strippedName = stripPrefix(constructor.parameterName(i), FIELD_PREFIX);
|
|
||||||
|
|
||||||
IntConsumer doSimpleCopy = (arg) -> {
|
|
||||||
var call = MethodDescriptor.ofMethod(messageType.name().toString(), "get" + capitalize(strippedName),
|
|
||||||
type.name().toString());
|
|
||||||
argMap[arg] = bytecodeCreator.invokeVirtualMethod(call, message);
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (type.kind()) {
|
|
||||||
case CLASS -> {
|
|
||||||
if (type.equals(Type.create(String.class)) || type.equals(Type.create(ByteString.class))) {
|
|
||||||
doSimpleCopy.accept(i);
|
|
||||||
} else {
|
|
||||||
var nestedProtoType = protoIndex.protoMsgToObj.inverseBidiMap().get(index.getClassByName(type.name()));
|
|
||||||
var call = MethodDescriptor.ofMethod(messageType.name().toString(), "get" + capitalize(strippedName),
|
|
||||||
nestedProtoType.name().toString());
|
|
||||||
var nested = bytecodeCreator.invokeVirtualMethod(call, message);
|
|
||||||
argMap[i] = generateConstructorUse(bytecodeCreator, classCreator, Type.create(nestedProtoType.name(), Type.Kind.CLASS), type, nested);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case PRIMITIVE -> {
|
|
||||||
doSimpleCopy.accept(i);
|
|
||||||
}
|
|
||||||
case WILDCARD_TYPE -> throw new UnsupportedOperationException("Wildcards not supported yet");
|
|
||||||
case PARAMETERIZED_TYPE ->
|
|
||||||
throw new UnsupportedOperationException("Parametrized types not supported yet");
|
|
||||||
case ARRAY -> throw new UnsupportedOperationException("Arrays not supported yet");
|
|
||||||
default -> throw new IllegalStateException("Unexpected type: " + type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytecodeCreator.newInstance(constructor, argMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MethodInfo findAllArgsConstructor(Index index, ClassInfo klass) {
|
|
||||||
ArrayList<FieldInfo> fields = findAllFields(index, klass);
|
|
||||||
|
|
||||||
var fieldCount = fields.size();
|
|
||||||
var fieldNames = fields.stream().map(f -> stripPrefix(f.name(), FIELD_PREFIX)).sorted().toList();
|
|
||||||
var fieldNameToType = fields.stream().collect(Collectors.toMap(f -> stripPrefix(f.name(), FIELD_PREFIX), FieldInfo::type));
|
|
||||||
|
|
||||||
for (var m : klass.constructors()) {
|
|
||||||
if (m.parametersCount() != fieldCount) continue;
|
|
||||||
var parameterNames = m.parameters().stream().map(n -> stripPrefix(n.name(), FIELD_PREFIX)).sorted().toList();
|
|
||||||
if (!Objects.equals(fieldNames, parameterNames)) continue;
|
|
||||||
|
|
||||||
for (var p : m.parameters()) {
|
|
||||||
if (!Objects.equals(fieldNameToType.get(stripPrefix(p.name(), FIELD_PREFIX)), p.type())) continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generateAbstract() {
|
|
||||||
var kids = Stream.concat(index.getAllKnownSubclasses(topObjectType.name()).stream(),
|
|
||||||
index.getAllKnownImplementors(topObjectType.name()).stream())
|
|
||||||
.filter(k -> !k.isAbstract() && !k.isInterface()).toList();
|
|
||||||
|
|
||||||
try (MethodCreator method = classCreator.getMethodCreator("serialize",
|
|
||||||
Message.class, Object.class)) {
|
|
||||||
|
|
||||||
method.setModifiers(Opcodes.ACC_PUBLIC);
|
|
||||||
|
|
||||||
var builderType = Type.create(DotName.createComponentized(topMessageType.name(), "Builder", true), Type.Kind.CLASS);
|
|
||||||
|
|
||||||
var builder = method.invokeStaticMethod(MethodDescriptor.ofMethod(topMessageType.name().toString(), "newBuilder", builderType.name().toString()));
|
|
||||||
|
|
||||||
var arg = method.getMethodParam(0);
|
|
||||||
|
|
||||||
for (var nestedObjClass : kids) {
|
|
||||||
System.out.println("Generating " + nestedObjClass.name() + " serializer for " + topObjectType.name());
|
|
||||||
var nestedObjType = Type.create(nestedObjClass.name(), Type.Kind.CLASS);
|
|
||||||
var nestedMessageClass = protoIndex.protoMsgToObj.inverseBidiMap().get(nestedObjClass);
|
|
||||||
boolean doExternalCall = false;
|
|
||||||
if (nestedMessageClass == null) {
|
|
||||||
var msgInfo = index.getClassByName(topMessageType.name());
|
|
||||||
nestedMessageClass = index.getClassByName(msgInfo.method("get" + capitalize(nestedObjType.name().withoutPackagePrefix())).returnType().name());
|
|
||||||
doExternalCall = true;
|
|
||||||
}
|
|
||||||
var nestedMessageType = Type.create(nestedMessageClass.name(), Type.Kind.CLASS);
|
|
||||||
|
|
||||||
var statement = method.ifTrue(method.instanceOf(arg, nestedObjClass.name().toString()));
|
|
||||||
|
|
||||||
try (var branch = statement.trueBranch()) {
|
|
||||||
if (doExternalCall) {
|
|
||||||
var externalSerializer = getOutsideSerializer(nestedMessageClass, nestedObjClass);
|
|
||||||
var serializerLoaded = branch.readInstanceField(externalSerializer, branch.getThis());
|
|
||||||
var serialized = branch.invokeInterfaceMethod(
|
|
||||||
MethodDescriptor.ofMethod(ProtoSerializer.class,
|
|
||||||
"serialize", Message.class, Object.class),
|
|
||||||
serializerLoaded, arg);
|
|
||||||
branch.invokeVirtualMethod(MethodDescriptor.ofMethod(builderType.name().toString(),
|
|
||||||
"set" + capitalize(nestedObjType.name().withoutPackagePrefix()),
|
|
||||||
builderType.name().toString(), nestedMessageType.name().toString()), builder, serialized);
|
|
||||||
} else {
|
|
||||||
var nestedBuilderType = Type.create(DotName.createComponentized(nestedMessageType.name(), "Builder", true), Type.Kind.CLASS);
|
|
||||||
var nestedBuilder = branch.invokeVirtualMethod(MethodDescriptor.ofMethod(builderType.name().toString(),
|
|
||||||
"get" + capitalize(nestedObjType.name().withoutPackagePrefix()) + "Builder",
|
|
||||||
nestedBuilderType.name().toString()), builder);
|
|
||||||
generateBuilderUse(branch, nestedBuilder, nestedMessageType, nestedObjType, arg);
|
|
||||||
}
|
|
||||||
var result = branch.invokeVirtualMethod(MethodDescriptor.ofMethod(builderType.name().toString(), "build", topMessageType.name().toString()), builder);
|
|
||||||
branch.returnValue(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
method.throwException(IllegalArgumentException.class, "Unknown object type");
|
|
||||||
}
|
|
||||||
|
|
||||||
try (MethodCreator method = classCreator.getMethodCreator("deserialize",
|
|
||||||
Object.class, Message.class)) {
|
|
||||||
method.setModifiers(Opcodes.ACC_PUBLIC);
|
|
||||||
var arg = method.getMethodParam(0);
|
|
||||||
|
|
||||||
for (var nestedObjClass : kids) {
|
|
||||||
System.out.println("Generating " + nestedObjClass.name() + " deserializer for " + topObjectType.name());
|
|
||||||
var nestedObjType = Type.create(nestedObjClass.name(), Type.Kind.CLASS);
|
|
||||||
|
|
||||||
var nestedMessageClass = protoIndex.protoMsgToObj.inverseBidiMap().get(nestedObjClass);
|
|
||||||
boolean doExternalCall = false;
|
|
||||||
if (nestedMessageClass == null) {
|
|
||||||
var msgInfo = index.getClassByName(topMessageType.name());
|
|
||||||
nestedMessageClass = index.getClassByName(msgInfo.method("get" + capitalize(nestedObjType.name().withoutPackagePrefix())).returnType().name());
|
|
||||||
doExternalCall = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nestedMessageType = Type.create(nestedMessageClass.name(), Type.Kind.CLASS);
|
|
||||||
|
|
||||||
var typeCheck = method.invokeVirtualMethod(MethodDescriptor.ofMethod(topMessageType.name().toString(),
|
|
||||||
"has" + capitalize(nestedObjType.name().withoutPackagePrefix()), boolean.class), arg);
|
|
||||||
|
|
||||||
var statement = method.ifTrue(typeCheck);
|
|
||||||
|
|
||||||
try (var branch = statement.trueBranch()) {
|
|
||||||
var nestedMessage = branch.invokeVirtualMethod(MethodDescriptor.ofMethod(topMessageType.name().toString(),
|
|
||||||
"get" + capitalize(nestedObjType.name().withoutPackagePrefix()), nestedMessageType.name().toString()), arg);
|
|
||||||
if (doExternalCall) {
|
|
||||||
var externalSerializer = getOutsideSerializer(nestedMessageClass, nestedObjClass);
|
|
||||||
var serializerLoaded = branch.readInstanceField(externalSerializer, branch.getThis());
|
|
||||||
branch.returnValue(branch.invokeInterfaceMethod(
|
|
||||||
MethodDescriptor.ofMethod(ProtoSerializer.class,
|
|
||||||
"deserialize", Object.class, Message.class),
|
|
||||||
serializerLoaded, nestedMessage));
|
|
||||||
} else {
|
|
||||||
branch.returnValue(generateConstructorUse(branch, classCreator, nestedMessageType, nestedObjType, nestedMessage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
method.throwException(IllegalArgumentException.class, "Unknown object type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generate() {
|
|
||||||
var objInfo = index.getClassByName(topObjectType.name());
|
|
||||||
if (objInfo.isAbstract() || objInfo.isInterface()) {
|
|
||||||
generateAbstract();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try (MethodCreator method = classCreator.getMethodCreator("serialize",
|
|
||||||
Message.class, Object.class)) {
|
|
||||||
|
|
||||||
method.setModifiers(Opcodes.ACC_PUBLIC);
|
|
||||||
|
|
||||||
var builderType = Type.create(DotName.createComponentized(topMessageType.name(), "Builder", true), Type.Kind.CLASS);
|
|
||||||
|
|
||||||
var builder = method.invokeStaticMethod(MethodDescriptor.ofMethod(topMessageType.name().toString(), "newBuilder", builderType.name().toString()));
|
|
||||||
|
|
||||||
var arg = method.getMethodParam(0);
|
|
||||||
|
|
||||||
generateBuilderUse(method, builder, topMessageType, topObjectType, arg);
|
|
||||||
|
|
||||||
var result = method.invokeVirtualMethod(MethodDescriptor.ofMethod(builderType.name().toString(), "build", topMessageType.name().toString()), builder);
|
|
||||||
|
|
||||||
method.returnValue(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
try (MethodCreator method = classCreator.getMethodCreator("deserialize",
|
|
||||||
Object.class, Message.class)) {
|
|
||||||
method.setModifiers(Opcodes.ACC_PUBLIC);
|
|
||||||
|
|
||||||
var arg = method.getMethodParam(0);
|
|
||||||
|
|
||||||
method.returnValue(generateConstructorUse(method, classCreator, topMessageType, topObjectType, arg));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.test;
|
|
||||||
|
|
||||||
import io.quarkus.test.QuarkusDevModeTest;
|
|
||||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
|
||||||
import org.jboss.shrinkwrap.api.spec.JavaArchive;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
|
||||||
|
|
||||||
public class AutoprotomapDevModeTest {
|
|
||||||
|
|
||||||
// Start hot reload (DevMode) test with your extension loaded
|
|
||||||
@RegisterExtension
|
|
||||||
static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest()
|
|
||||||
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class));
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void writeYourOwnDevModeTest() {
|
|
||||||
// Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information
|
|
||||||
Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.test;
|
|
||||||
|
|
||||||
import io.quarkus.test.QuarkusUnitTest;
|
|
||||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
|
||||||
import org.jboss.shrinkwrap.api.spec.JavaArchive;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
|
||||||
|
|
||||||
public class AutoprotomapTest {
|
|
||||||
|
|
||||||
// Start unit test with your extension loaded
|
|
||||||
@RegisterExtension
|
|
||||||
static final QuarkusUnitTest unitTest = new QuarkusUnitTest()
|
|
||||||
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class));
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void writeYourOwnUnitTest() {
|
|
||||||
// Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information
|
|
||||||
Assertions.assertTrue(true, "Add some assertions to " + getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>com.usatiuk</groupId>
|
|
||||||
<artifactId>autoprotomap-parent</artifactId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>autoprotomap-integration-tests</artifactId>
|
|
||||||
<name>Autoprotomap - Integration Tests</name>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<skipITs>true</skipITs>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.usatiuk</groupId>
|
|
||||||
<artifactId>autoprotomap</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.usatiuk</groupId>
|
|
||||||
<artifactId>autoprotomap-deployment</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-junit5</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-grpc</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-maven-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<goals>
|
|
||||||
<goal>build</goal>
|
|
||||||
<goal>generate-code</goal>
|
|
||||||
<goal>generate-code-tests</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-failsafe-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<goals>
|
|
||||||
<goal>integration-test</goal>
|
|
||||||
<goal>verify</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
<configuration>
|
|
||||||
<systemPropertyVariables>
|
|
||||||
<native.image.path>${project.build.directory}/${project.build.finalName}-runner
|
|
||||||
</native.image.path>
|
|
||||||
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
|
||||||
<maven.home>${maven.home}</maven.home>
|
|
||||||
</systemPropertyVariables>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<profiles>
|
|
||||||
<profile>
|
|
||||||
<id>native</id>
|
|
||||||
<activation>
|
|
||||||
<property>
|
|
||||||
<name>native</name>
|
|
||||||
</property>
|
|
||||||
</activation>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<skipTests>${native.surefire.skip}</skipTests>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
<properties>
|
|
||||||
<skipITs>false</skipITs>
|
|
||||||
<quarkus.native.enabled>true</quarkus.native.enabled>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
</project>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.it;
|
|
||||||
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoMirror;
|
|
||||||
|
|
||||||
@ProtoMirror(AbstractProto.class)
|
|
||||||
public abstract class AbstractObject {
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.it;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Getter
|
|
||||||
public class CustomObject extends AbstractObject {
|
|
||||||
public int testNum = 0;
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.it;
|
|
||||||
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoSerializer;
|
|
||||||
import jakarta.inject.Singleton;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class CustomObjectSerializer implements ProtoSerializer<CustomObjectProto, CustomObject> {
|
|
||||||
@Override
|
|
||||||
public CustomObject deserialize(CustomObjectProto message) {
|
|
||||||
return new CustomObject(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CustomObjectProto serialize(CustomObject object) {
|
|
||||||
return CustomObjectProto.newBuilder().setTest(1).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.it;
|
|
||||||
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoMirror;
|
|
||||||
|
|
||||||
@ProtoMirror(InterfaceObjectProto.class)
|
|
||||||
public interface InterfaceObject {
|
|
||||||
String key();
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.it;
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoMirror;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
@ProtoMirror(NestedObjectProto.class)
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Getter
|
|
||||||
public class NestedObject extends AbstractObject {
|
|
||||||
public SimpleObject object;
|
|
||||||
public String _nestedName;
|
|
||||||
public ByteString _nestedSomeBytes;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.it;
|
|
||||||
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoMirror;
|
|
||||||
|
|
||||||
@ProtoMirror(RecordObjectProto.class)
|
|
||||||
public record RecordObject(String key) implements InterfaceObject {
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.it;
|
|
||||||
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoMirror;
|
|
||||||
|
|
||||||
@ProtoMirror(RecordObject2Proto.class)
|
|
||||||
public record RecordObject2(String key, int value) implements InterfaceObject {
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.it;
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoMirror;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
@ProtoMirror(SimpleObjectProto.class)
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Getter
|
|
||||||
public class SimpleObject extends AbstractObject {
|
|
||||||
public int numfield = 0;
|
|
||||||
private String name;
|
|
||||||
public ByteString someBytes;
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
option java_multiple_files = true;
|
|
||||||
option java_package = "com.usatiuk.autoprotomap.it";
|
|
||||||
option java_outer_classname = "TestProto";
|
|
||||||
|
|
||||||
package autoprotomap.test;
|
|
||||||
|
|
||||||
message SimpleObjectProto {
|
|
||||||
int32 numfield = 1;
|
|
||||||
string name = 2;
|
|
||||||
bytes someBytes = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message NestedObjectProto {
|
|
||||||
SimpleObjectProto object = 1;
|
|
||||||
string nestedName = 2;
|
|
||||||
bytes nestedSomeBytes = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CustomObjectProto {
|
|
||||||
int64 test = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AbstractProto {
|
|
||||||
oneof obj {
|
|
||||||
NestedObjectProto nestedObject = 1;
|
|
||||||
SimpleObjectProto simpleObject = 2;
|
|
||||||
CustomObjectProto customObject = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message RecordObjectProto {
|
|
||||||
string key = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message RecordObject2Proto {
|
|
||||||
string key = 1;
|
|
||||||
int32 value = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message InterfaceObjectProto {
|
|
||||||
oneof obj {
|
|
||||||
RecordObjectProto recordObject = 1;
|
|
||||||
RecordObject2Proto recordObject2 = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.it;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusIntegrationTest;
|
|
||||||
|
|
||||||
@QuarkusIntegrationTest
|
|
||||||
public class AutoprotomapResourceIT extends AutoprotomapResourceTest {
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.it;
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoSerializer;
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
public class AutoprotomapResourceTest {
|
|
||||||
@Inject
|
|
||||||
ProtoSerializer<SimpleObjectProto, SimpleObject> simpleProtoSerializer;
|
|
||||||
@Inject
|
|
||||||
ProtoSerializer<NestedObjectProto, NestedObject> nestedProtoSerializer;
|
|
||||||
@Inject
|
|
||||||
ProtoSerializer<AbstractProto, AbstractObject> abstractProtoSerializer;
|
|
||||||
@Inject
|
|
||||||
ProtoSerializer<InterfaceObjectProto, InterfaceObject> interfaceProtoSerializer;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSimple() {
|
|
||||||
var ret = simpleProtoSerializer.serialize(new SimpleObject(1234, "simple test", ByteString.copyFrom(new byte[]{1, 2, 3})));
|
|
||||||
Assertions.assertEquals(1234, ret.getNumfield());
|
|
||||||
Assertions.assertEquals("simple test", ret.getName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{1, 2, 3}), ret.getSomeBytes());
|
|
||||||
|
|
||||||
var des = simpleProtoSerializer.deserialize(ret);
|
|
||||||
Assertions.assertEquals(1234, des.getNumfield());
|
|
||||||
Assertions.assertEquals("simple test", des.getName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{1, 2, 3}), des.getSomeBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNested() {
|
|
||||||
var ret = nestedProtoSerializer.serialize(
|
|
||||||
new NestedObject(
|
|
||||||
new SimpleObject(333, "nested so", ByteString.copyFrom(new byte[]{1, 2, 3})),
|
|
||||||
"nested obj", ByteString.copyFrom(new byte[]{4, 5, 6})));
|
|
||||||
Assertions.assertEquals(333, ret.getObject().getNumfield());
|
|
||||||
Assertions.assertEquals("nested so", ret.getObject().getName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{1, 2, 3}), ret.getObject().getSomeBytes());
|
|
||||||
Assertions.assertEquals("nested obj", ret.getNestedName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{4, 5, 6}), ret.getNestedSomeBytes());
|
|
||||||
|
|
||||||
var des = nestedProtoSerializer.deserialize(ret);
|
|
||||||
Assertions.assertEquals(333, des.object.numfield);
|
|
||||||
Assertions.assertEquals(333, des.getObject().getNumfield());
|
|
||||||
Assertions.assertEquals("nested so", des.getObject().getName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{1, 2, 3}), des.getObject().getSomeBytes());
|
|
||||||
Assertions.assertEquals("nested obj", des.get_nestedName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{4, 5, 6}), des.get_nestedSomeBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAbstractSimple() {
|
|
||||||
var ret = abstractProtoSerializer.serialize(new SimpleObject(1234, "simple test", ByteString.copyFrom(new byte[]{1, 2, 3})));
|
|
||||||
Assertions.assertEquals(1234, ret.getSimpleObject().getNumfield());
|
|
||||||
Assertions.assertEquals("simple test", ret.getSimpleObject().getName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{1, 2, 3}), ret.getSimpleObject().getSomeBytes());
|
|
||||||
|
|
||||||
var des = (SimpleObject) abstractProtoSerializer.deserialize(ret);
|
|
||||||
Assertions.assertEquals(1234, des.getNumfield());
|
|
||||||
Assertions.assertEquals("simple test", des.getName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{1, 2, 3}), des.getSomeBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAbstractCustom() {
|
|
||||||
var ret = abstractProtoSerializer.serialize(new CustomObject(1234));
|
|
||||||
Assertions.assertEquals(1, ret.getCustomObject().getTest());
|
|
||||||
|
|
||||||
var des = (CustomObject) abstractProtoSerializer.deserialize(ret);
|
|
||||||
Assertions.assertEquals(2, des.getTestNum());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAbstractNested() {
|
|
||||||
var ret = abstractProtoSerializer.serialize(
|
|
||||||
new NestedObject(
|
|
||||||
new SimpleObject(333, "nested so", ByteString.copyFrom(new byte[]{1, 2, 3})),
|
|
||||||
"nested obj", ByteString.copyFrom(new byte[]{4, 5, 6})));
|
|
||||||
Assertions.assertEquals(333, ret.getNestedObject().getObject().getNumfield());
|
|
||||||
Assertions.assertEquals("nested so", ret.getNestedObject().getObject().getName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{1, 2, 3}), ret.getNestedObject().getObject().getSomeBytes());
|
|
||||||
Assertions.assertEquals("nested obj", ret.getNestedObject().getNestedName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{4, 5, 6}), ret.getNestedObject().getNestedSomeBytes());
|
|
||||||
|
|
||||||
var des = (NestedObject) abstractProtoSerializer.deserialize(ret);
|
|
||||||
Assertions.assertEquals(333, des.object.numfield);
|
|
||||||
Assertions.assertEquals(333, des.getObject().getNumfield());
|
|
||||||
Assertions.assertEquals("nested so", des.getObject().getName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{1, 2, 3}), des.getObject().getSomeBytes());
|
|
||||||
Assertions.assertEquals("nested obj", des.get_nestedName());
|
|
||||||
Assertions.assertEquals(ByteString.copyFrom(new byte[]{4, 5, 6}), des.get_nestedSomeBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInterface() {
|
|
||||||
var ret = interfaceProtoSerializer.serialize(new RecordObject("record test"));
|
|
||||||
Assertions.assertEquals("record test", ret.getRecordObject().getKey());
|
|
||||||
var des = (RecordObject) interfaceProtoSerializer.deserialize(ret);
|
|
||||||
Assertions.assertEquals("record test", des.key());
|
|
||||||
|
|
||||||
var ret2 = interfaceProtoSerializer.serialize(new RecordObject2("record test 2", 1234));
|
|
||||||
Assertions.assertEquals("record test 2", ret2.getRecordObject2().getKey());
|
|
||||||
Assertions.assertEquals(1234, ret2.getRecordObject2().getValue());
|
|
||||||
var des2 = (RecordObject2) interfaceProtoSerializer.deserialize(ret2);
|
|
||||||
Assertions.assertEquals("record test 2", des2.key());
|
|
||||||
Assertions.assertEquals(1234, des2.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>com.usatiuk.dhfs</groupId>
|
|
||||||
<artifactId>parent</artifactId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<groupId>com.usatiuk</groupId>
|
|
||||||
<artifactId>autoprotomap-parent</artifactId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
<packaging>pom</packaging>
|
|
||||||
<name>Autoprotomap - Parent</name>
|
|
||||||
|
|
||||||
<modules>
|
|
||||||
<module>deployment</module>
|
|
||||||
<module>runtime</module>
|
|
||||||
<module>integration-tests</module>
|
|
||||||
</modules>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>com.usatiuk</groupId>
|
|
||||||
<artifactId>autoprotomap-parent</artifactId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>autoprotomap</artifactId>
|
|
||||||
<name>Autoprotomap - Runtime</name>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-arc</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-grpc</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-extension-maven-plugin</artifactId>
|
|
||||||
<version>${quarkus.platform.version}</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>compile</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>extension-descriptor</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<deployment>${project.groupId}:${project.artifactId}-deployment:${project.version}
|
|
||||||
</deployment>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>default-compile</id>
|
|
||||||
<configuration>
|
|
||||||
<annotationProcessorPaths>
|
|
||||||
<path>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-extension-processor</artifactId>
|
|
||||||
<version>${quarkus.platform.version}</version>
|
|
||||||
</path>
|
|
||||||
</annotationProcessorPaths>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.usatiuk.autoprotomap.runtime;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.CLASS)
|
|
||||||
@Target(ElementType.TYPE)
|
|
||||||
public @interface ProtoMirror {
|
|
||||||
Class<?> value() default Object.class;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
name: Autoprotomap
|
|
||||||
#description: Do something useful.
|
|
||||||
metadata:
|
|
||||||
# keywords:
|
|
||||||
# - autoprotomap
|
|
||||||
# guide: ... # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension
|
|
||||||
# categories:
|
|
||||||
# - "miscellaneous"
|
|
||||||
# status: "preview"
|
|
||||||
164
dhfs-parent/dhfs-fs/pom.xml
Normal file
164
dhfs-parent/dhfs-fs/pom.xml
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.usatiuk.dhfs</groupId>
|
||||||
|
<artifactId>dhfs-fs</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.usatiuk.dhfs</groupId>
|
||||||
|
<artifactId>parent</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.testcontainers</groupId>
|
||||||
|
<artifactId>testcontainers</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcpkix-jdk18on</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.openhft</groupId>
|
||||||
|
<artifactId>zero-allocation-hashing</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-grpc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-arc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-client-jsonb</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-jsonb</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-scheduler</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-junit5</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-jboss-logmanager</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-collections4</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.pcollections</groupId>
|
||||||
|
<artifactId>pcollections</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-math3</artifactId>
|
||||||
|
<version>3.6.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.usatiuk.dhfs</groupId>
|
||||||
|
<artifactId>sync-base</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<forkCount>1C</forkCount>
|
||||||
|
<reuseForks>false</reuseForks>
|
||||||
|
<parallel>classes</parallel>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<junit.jupiter.execution.parallel.enabled>
|
||||||
|
false
|
||||||
|
</junit.jupiter.execution.parallel.enabled>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-failsafe-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<junit.jupiter.execution.parallel.enabled>
|
||||||
|
true
|
||||||
|
</junit.jupiter.execution.parallel.enabled>
|
||||||
|
<junit.jupiter.execution.parallel.mode.default>
|
||||||
|
concurrent
|
||||||
|
</junit.jupiter.execution.parallel.mode.default>
|
||||||
|
<junit.jupiter.execution.parallel.config.dynamic.factor>
|
||||||
|
0.5
|
||||||
|
</junit.jupiter.execution.parallel.config.dynamic.factor>
|
||||||
|
<junit.platform.output.capture.stdout>true</junit.platform.output.capture.stdout>
|
||||||
|
<junit.platform.output.capture.stderr>true</junit.platform.output.capture.stderr>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>${quarkus.platform.group-id}</groupId>
|
||||||
|
<artifactId>quarkus-maven-plugin</artifactId>
|
||||||
|
<version>${quarkus.platform.version}</version>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>quarkus-plugin</id>
|
||||||
|
<goals>
|
||||||
|
<goal>generate-code</goal>
|
||||||
|
<goal>generate-code-tests</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.usatiuk.dhfs.files.objects;
|
package com.usatiuk.dhfsfs.objects;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.usatiuk.dhfs.JDataRemote;
|
import com.usatiuk.dhfs.remoteobj.JDataRemote;
|
||||||
|
import com.usatiuk.dhfs.remoteobj.JDataRemoteDto;
|
||||||
import com.usatiuk.objects.JObjectKey;
|
import com.usatiuk.objects.JObjectKey;
|
||||||
import com.usatiuk.dhfs.repository.JDataRemoteDto;
|
|
||||||
|
|
||||||
public record ChunkData(JObjectKey key, ByteString data) implements JDataRemote, JDataRemoteDto {
|
public record ChunkData(JObjectKey key, ByteString data) implements JDataRemote, JDataRemoteDto {
|
||||||
@Override
|
@Override
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.usatiuk.dhfs.files.objects;
|
package com.usatiuk.dhfsfs.objects;
|
||||||
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoSerializer;
|
import com.usatiuk.dhfs.ProtoSerializer;
|
||||||
import com.usatiuk.objects.JObjectKey;
|
|
||||||
import com.usatiuk.dhfs.persistence.ChunkDataP;
|
import com.usatiuk.dhfs.persistence.ChunkDataP;
|
||||||
import com.usatiuk.dhfs.persistence.JObjectKeyP;
|
import com.usatiuk.dhfs.persistence.JObjectKeyP;
|
||||||
|
import com.usatiuk.objects.JObjectKey;
|
||||||
import jakarta.inject.Singleton;
|
import jakarta.inject.Singleton;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -19,7 +19,7 @@ public class ChunkDataProtoSerializer implements ProtoSerializer<ChunkDataP, Chu
|
|||||||
@Override
|
@Override
|
||||||
public ChunkDataP serialize(ChunkData object) {
|
public ChunkDataP serialize(ChunkData object) {
|
||||||
return ChunkDataP.newBuilder()
|
return ChunkDataP.newBuilder()
|
||||||
.setKey(JObjectKeyP.newBuilder().setName(object.key().name()).build())
|
.setKey(JObjectKeyP.newBuilder().setName(object.key().value()).build())
|
||||||
.setData(object.data())
|
.setData(object.data())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.usatiuk.dhfs.files.objects;
|
package com.usatiuk.dhfsfs.objects;
|
||||||
|
|
||||||
import com.usatiuk.dhfs.JDataRemote;
|
|
||||||
import com.usatiuk.objects.JObjectKey;
|
|
||||||
import com.usatiuk.dhfs.jmap.JMapHolder;
|
import com.usatiuk.dhfs.jmap.JMapHolder;
|
||||||
import com.usatiuk.dhfs.jmap.JMapLongKey;
|
import com.usatiuk.dhfs.jmap.JMapLongKey;
|
||||||
import com.usatiuk.dhfs.repository.JDataRemoteDto;
|
import com.usatiuk.dhfs.remoteobj.JDataRemote;
|
||||||
|
import com.usatiuk.dhfs.remoteobj.JDataRemoteDto;
|
||||||
|
import com.usatiuk.objects.JObjectKey;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -28,6 +28,10 @@ public record File(JObjectKey key, long mode, long cTime, long mTime,
|
|||||||
return new File(key, mode, cTime, mTime, symlink);
|
return new File(key, mode, cTime, mTime, symlink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public File withCurrentMTime() {
|
||||||
|
return new File(key, mode, cTime, System.currentTimeMillis(), symlink);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<JObjectKey> collectRefsTo() {
|
public Collection<JObjectKey> collectRefsTo() {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.usatiuk.dhfs.files.objects;
|
package com.usatiuk.dhfsfs.objects;
|
||||||
|
|
||||||
import com.usatiuk.dhfs.JDataRemote;
|
import com.usatiuk.dhfs.remoteobj.JDataRemote;
|
||||||
|
import com.usatiuk.dhfs.remoteobj.JDataRemoteDto;
|
||||||
import com.usatiuk.objects.JObjectKey;
|
import com.usatiuk.objects.JObjectKey;
|
||||||
import com.usatiuk.dhfs.repository.JDataRemoteDto;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.usatiuk.dhfs.files.objects;
|
package com.usatiuk.dhfsfs.objects;
|
||||||
|
|
||||||
import com.usatiuk.dhfs.jmap.JMapHelper;
|
import com.usatiuk.dhfs.jmap.JMapHelper;
|
||||||
import com.usatiuk.dhfs.repository.syncmap.DtoMapper;
|
import com.usatiuk.dhfs.syncmap.DtoMapper;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.usatiuk.dhfs.files.objects;
|
package com.usatiuk.dhfsfs.objects;
|
||||||
|
|
||||||
import com.usatiuk.objects.JObjectKey;
|
|
||||||
import com.usatiuk.dhfs.jmap.JMapHelper;
|
import com.usatiuk.dhfs.jmap.JMapHelper;
|
||||||
import com.usatiuk.dhfs.jmap.JMapLongKey;
|
import com.usatiuk.dhfs.jmap.JMapLongKey;
|
||||||
|
import com.usatiuk.objects.JObjectKey;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.usatiuk.dhfs.files.objects;
|
package com.usatiuk.dhfsfs.objects;
|
||||||
|
|
||||||
import com.usatiuk.autoprotomap.runtime.ProtoSerializer;
|
import com.usatiuk.dhfs.ProtoSerializer;
|
||||||
import com.usatiuk.dhfs.persistence.FileDtoP;
|
import com.usatiuk.dhfs.persistence.FileDtoP;
|
||||||
import com.usatiuk.dhfs.utils.SerializationHelper;
|
import com.usatiuk.utils.SerializationHelper;
|
||||||
import jakarta.inject.Singleton;
|
import jakarta.inject.Singleton;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -1,20 +1,15 @@
|
|||||||
package com.usatiuk.dhfs.files.objects;
|
package com.usatiuk.dhfsfs.objects;
|
||||||
|
|
||||||
import com.usatiuk.dhfs.PeerId;
|
|
||||||
import com.usatiuk.dhfs.RemoteObjectDataWrapper;
|
|
||||||
import com.usatiuk.dhfs.RemoteObjectMeta;
|
|
||||||
import com.usatiuk.dhfs.RemoteTransaction;
|
|
||||||
import com.usatiuk.dhfs.files.service.DhfsFileService;
|
|
||||||
import com.usatiuk.dhfs.jkleppmanntree.JKleppmannTreeManager;
|
import com.usatiuk.dhfs.jkleppmanntree.JKleppmannTreeManager;
|
||||||
import com.usatiuk.dhfs.jkleppmanntree.structs.JKleppmannTreeNodeMetaFile;
|
|
||||||
import com.usatiuk.dhfs.jmap.JMapHelper;
|
import com.usatiuk.dhfs.jmap.JMapHelper;
|
||||||
import com.usatiuk.dhfs.repository.ObjSyncHandler;
|
import com.usatiuk.dhfs.peersync.PeerId;
|
||||||
import com.usatiuk.dhfs.repository.PersistentPeerDataService;
|
import com.usatiuk.dhfs.peersync.PersistentPeerDataService;
|
||||||
import com.usatiuk.dhfs.repository.SyncHelper;
|
import com.usatiuk.dhfs.remoteobj.*;
|
||||||
|
import com.usatiuk.dhfsfs.service.DhfsFileService;
|
||||||
|
import com.usatiuk.kleppmanntree.AlreadyExistsException;
|
||||||
import com.usatiuk.objects.JObjectKey;
|
import com.usatiuk.objects.JObjectKey;
|
||||||
import com.usatiuk.objects.transaction.LockingStrategy;
|
import com.usatiuk.objects.transaction.LockingStrategy;
|
||||||
import com.usatiuk.objects.transaction.Transaction;
|
import com.usatiuk.objects.transaction.Transaction;
|
||||||
import com.usatiuk.kleppmanntree.AlreadyExistsException;
|
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.StatusRuntimeException;
|
import io.grpc.StatusRuntimeException;
|
||||||
import io.quarkus.logging.Log;
|
import io.quarkus.logging.Log;
|
||||||
@@ -48,11 +43,11 @@ public class FileSyncHandler implements ObjSyncHandler<File, FileDto> {
|
|||||||
DhfsFileService fileService;
|
DhfsFileService fileService;
|
||||||
|
|
||||||
private JKleppmannTreeManager.JKleppmannTree getTreeW() {
|
private JKleppmannTreeManager.JKleppmannTree getTreeW() {
|
||||||
return jKleppmannTreeManager.getTree(JObjectKey.of("fs"));
|
return jKleppmannTreeManager.getTree(JObjectKey.of("fs")).orElseThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private JKleppmannTreeManager.JKleppmannTree getTreeR() {
|
private JKleppmannTreeManager.JKleppmannTree getTreeR() {
|
||||||
return jKleppmannTreeManager.getTree(JObjectKey.of("fs"), LockingStrategy.OPTIMISTIC);
|
return jKleppmannTreeManager.getTree(JObjectKey.of("fs"), LockingStrategy.OPTIMISTIC).orElseThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resolveConflict(PeerId from, JObjectKey key, PMap<PeerId, Long> receivedChangelog,
|
private void resolveConflict(PeerId from, JObjectKey key, PMap<PeerId, Long> receivedChangelog,
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.usatiuk.dhfsfs.objects;
|
||||||
|
|
||||||
|
import com.usatiuk.dhfs.jkleppmanntree.structs.JKleppmannTreeNodeMeta;
|
||||||
|
import com.usatiuk.objects.JObjectKey;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record JKleppmannTreeNodeMetaDirectory(String name) implements JKleppmannTreeNodeMeta {
|
||||||
|
public JKleppmannTreeNodeMeta withName(String name) {
|
||||||
|
return new JKleppmannTreeNodeMetaDirectory(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<JObjectKey> collectRefsTo() {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.usatiuk.dhfsfs.objects;
|
||||||
|
|
||||||
|
import com.usatiuk.dhfs.jkleppmanntree.structs.JKleppmannTreeNodeMeta;
|
||||||
|
import com.usatiuk.objects.JObjectKey;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record JKleppmannTreeNodeMetaFile(String name, JObjectKey fileIno) implements JKleppmannTreeNodeMeta {
|
||||||
|
@Override
|
||||||
|
public JKleppmannTreeNodeMeta withName(String name) {
|
||||||
|
return new JKleppmannTreeNodeMetaFile(name, fileIno);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<JObjectKey> collectRefsTo() {
|
||||||
|
return List.of(fileIno);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +1,28 @@
|
|||||||
package com.usatiuk.dhfs.files.service;
|
package com.usatiuk.dhfsfs.service;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.UnsafeByteOperations;
|
import com.google.protobuf.UnsafeByteOperations;
|
||||||
import com.usatiuk.dhfs.JDataRemote;
|
|
||||||
import com.usatiuk.dhfs.RemoteObjectMeta;
|
|
||||||
import com.usatiuk.dhfs.RemoteTransaction;
|
|
||||||
import com.usatiuk.dhfs.files.objects.ChunkData;
|
|
||||||
import com.usatiuk.dhfs.files.objects.File;
|
|
||||||
import com.usatiuk.dhfs.jkleppmanntree.JKleppmannTreeManager;
|
import com.usatiuk.dhfs.jkleppmanntree.JKleppmannTreeManager;
|
||||||
import com.usatiuk.dhfs.jkleppmanntree.structs.JKleppmannTreeNode;
|
import com.usatiuk.dhfs.jkleppmanntree.structs.JKleppmannTreeNode;
|
||||||
|
import com.usatiuk.dhfs.jkleppmanntree.structs.JKleppmannTreeNodeHolder;
|
||||||
import com.usatiuk.dhfs.jkleppmanntree.structs.JKleppmannTreeNodeMeta;
|
import com.usatiuk.dhfs.jkleppmanntree.structs.JKleppmannTreeNodeMeta;
|
||||||
import com.usatiuk.dhfs.jkleppmanntree.structs.JKleppmannTreeNodeMetaDirectory;
|
|
||||||
import com.usatiuk.dhfs.jkleppmanntree.structs.JKleppmannTreeNodeMetaFile;
|
|
||||||
import com.usatiuk.dhfs.jmap.JMapEntry;
|
import com.usatiuk.dhfs.jmap.JMapEntry;
|
||||||
import com.usatiuk.dhfs.jmap.JMapHelper;
|
import com.usatiuk.dhfs.jmap.JMapHelper;
|
||||||
import com.usatiuk.dhfs.jmap.JMapLongKey;
|
import com.usatiuk.dhfs.jmap.JMapLongKey;
|
||||||
import com.usatiuk.dhfs.utils.StatusRuntimeExceptionNoStacktrace;
|
import com.usatiuk.dhfs.remoteobj.JDataRemote;
|
||||||
|
import com.usatiuk.dhfs.remoteobj.RemoteObjectMeta;
|
||||||
|
import com.usatiuk.dhfs.remoteobj.RemoteTransaction;
|
||||||
|
import com.usatiuk.dhfsfs.objects.ChunkData;
|
||||||
|
import com.usatiuk.dhfsfs.objects.File;
|
||||||
|
import com.usatiuk.dhfsfs.objects.JKleppmannTreeNodeMetaDirectory;
|
||||||
|
import com.usatiuk.dhfsfs.objects.JKleppmannTreeNodeMetaFile;
|
||||||
import com.usatiuk.objects.JData;
|
import com.usatiuk.objects.JData;
|
||||||
import com.usatiuk.objects.JObjectKey;
|
import com.usatiuk.objects.JObjectKey;
|
||||||
import com.usatiuk.objects.iterators.IteratorStart;
|
import com.usatiuk.objects.iterators.IteratorStart;
|
||||||
import com.usatiuk.objects.transaction.LockingStrategy;
|
import com.usatiuk.objects.transaction.LockingStrategy;
|
||||||
import com.usatiuk.objects.transaction.Transaction;
|
import com.usatiuk.objects.transaction.Transaction;
|
||||||
import com.usatiuk.objects.transaction.TransactionManager;
|
import com.usatiuk.objects.transaction.TransactionManager;
|
||||||
|
import com.usatiuk.utils.StatusRuntimeExceptionNoStacktrace;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.StatusRuntimeException;
|
import io.grpc.StatusRuntimeException;
|
||||||
import io.quarkus.logging.Log;
|
import io.quarkus.logging.Log;
|
||||||
@@ -33,55 +34,51 @@ import jakarta.inject.Inject;
|
|||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class DhfsFileServiceImpl implements DhfsFileService {
|
public class DhfsFileService {
|
||||||
|
@ConfigProperty(name = "dhfs.files.target_chunk_alignment")
|
||||||
|
int targetChunkAlignment;
|
||||||
|
@ConfigProperty(name = "dhfs.files.target_chunk_size")
|
||||||
|
int targetChunkSize;
|
||||||
|
@ConfigProperty(name = "dhfs.files.max_chunk_size", defaultValue = "524288")
|
||||||
|
int maxChunkSize;
|
||||||
|
@ConfigProperty(name = "dhfs.files.use_hash_for_chunks")
|
||||||
|
boolean useHashForChunks;
|
||||||
|
@ConfigProperty(name = "dhfs.files.allow_recursive_delete")
|
||||||
|
boolean allowRecursiveDelete;
|
||||||
|
@ConfigProperty(name = "dhfs.objects.ref_verification")
|
||||||
|
boolean refVerification;
|
||||||
|
@ConfigProperty(name = "dhfs.objects.write_log")
|
||||||
|
boolean writeLogging;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Transaction curTx;
|
Transaction curTx;
|
||||||
@Inject
|
@Inject
|
||||||
RemoteTransaction remoteTx;
|
RemoteTransaction remoteTx;
|
||||||
@Inject
|
@Inject
|
||||||
TransactionManager jObjectTxManager;
|
TransactionManager jObjectTxManager;
|
||||||
|
|
||||||
@ConfigProperty(name = "dhfs.files.target_chunk_alignment")
|
|
||||||
int targetChunkAlignment;
|
|
||||||
|
|
||||||
@ConfigProperty(name = "dhfs.files.target_chunk_size")
|
|
||||||
int targetChunkSize;
|
|
||||||
|
|
||||||
@ConfigProperty(name = "dhfs.files.use_hash_for_chunks")
|
|
||||||
boolean useHashForChunks;
|
|
||||||
|
|
||||||
@ConfigProperty(name = "dhfs.files.allow_recursive_delete")
|
|
||||||
boolean allowRecursiveDelete;
|
|
||||||
|
|
||||||
@ConfigProperty(name = "dhfs.objects.ref_verification")
|
|
||||||
boolean refVerification;
|
|
||||||
|
|
||||||
@ConfigProperty(name = "dhfs.objects.write_log")
|
|
||||||
boolean writeLogging;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
JKleppmannTreeManager jKleppmannTreeManager;
|
JKleppmannTreeManager jKleppmannTreeManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
JMapHelper jMapHelper;
|
JMapHelper jMapHelper;
|
||||||
|
|
||||||
private JKleppmannTreeManager.JKleppmannTree getTreeW() {
|
private JKleppmannTreeManager.JKleppmannTree getTreeW() {
|
||||||
return jKleppmannTreeManager.getTree(JObjectKey.of("fs"));
|
return jKleppmannTreeManager.getTree(JObjectKey.of("fs"), () -> new JKleppmannTreeNodeMetaDirectory(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
private JKleppmannTreeManager.JKleppmannTree getTreeR() {
|
private JKleppmannTreeManager.JKleppmannTree getTreeR() {
|
||||||
return jKleppmannTreeManager.getTree(JObjectKey.of("fs"), LockingStrategy.OPTIMISTIC);
|
return jKleppmannTreeManager.getTree(JObjectKey.of("fs"), LockingStrategy.OPTIMISTIC, () -> new JKleppmannTreeNodeMetaDirectory(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChunkData createChunk(ByteString bytes) {
|
private ChunkData createChunk(ByteString bytes) {
|
||||||
var newChunk = new ChunkData(JObjectKey.of(UUID.randomUUID().toString()), bytes);
|
var newChunk = new ChunkData(JObjectKey.of(UUID.randomUUID().toString()), bytes);
|
||||||
remoteTx.putData(newChunk);
|
remoteTx.putDataNew(newChunk);
|
||||||
return newChunk;
|
return newChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,25 +90,24 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
private JKleppmannTreeNode getDirEntryW(String name) {
|
private JKleppmannTreeNode getDirEntryW(String name) {
|
||||||
var res = getTreeW().traverse(StreamSupport.stream(Path.of(name).spliterator(), false).map(p -> p.toString()).toList());
|
var res = getTreeW().traverse(StreamSupport.stream(Path.of(name).spliterator(), false).map(p -> p.toString()).toList());
|
||||||
if (res == null) throw new StatusRuntimeExceptionNoStacktrace(Status.NOT_FOUND);
|
if (res == null) throw new StatusRuntimeExceptionNoStacktrace(Status.NOT_FOUND);
|
||||||
var ret = curTx.get(JKleppmannTreeNode.class, res).orElseThrow(() -> new StatusRuntimeException(Status.NOT_FOUND.withDescription("Tree node exists but not found as jObject: " + name)));
|
var ret = curTx.get(JKleppmannTreeNodeHolder.class, res).map(JKleppmannTreeNodeHolder::node).orElseThrow(() -> new StatusRuntimeException(Status.NOT_FOUND.withDescription("Tree node exists but not found as jObject: " + name)));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JKleppmannTreeNode getDirEntryR(String name) {
|
private JKleppmannTreeNode getDirEntryR(String name) {
|
||||||
var res = getTreeR().traverse(StreamSupport.stream(Path.of(name).spliterator(), false).map(p -> p.toString()).toList());
|
var res = getTreeR().traverse(StreamSupport.stream(Path.of(name).spliterator(), false).map(p -> p.toString()).toList());
|
||||||
if (res == null) throw new StatusRuntimeExceptionNoStacktrace(Status.NOT_FOUND);
|
if (res == null) throw new StatusRuntimeExceptionNoStacktrace(Status.NOT_FOUND);
|
||||||
var ret = curTx.get(JKleppmannTreeNode.class, res).orElseThrow(() -> new StatusRuntimeException(Status.NOT_FOUND.withDescription("Tree node exists but not found as jObject: " + name)));
|
var ret = curTx.get(JKleppmannTreeNodeHolder.class, res).map(JKleppmannTreeNodeHolder::node).orElseThrow(() -> new StatusRuntimeException(Status.NOT_FOUND.withDescription("Tree node exists but not found as jObject: " + name)));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<JKleppmannTreeNode> getDirEntryOpt(String name) {
|
private Optional<JKleppmannTreeNode> getDirEntryOpt(String name) {
|
||||||
var res = getTreeW().traverse(StreamSupport.stream(Path.of(name).spliterator(), false).map(p -> p.toString()).toList());
|
var res = getTreeW().traverse(StreamSupport.stream(Path.of(name).spliterator(), false).map(p -> p.toString()).toList());
|
||||||
if (res == null) return Optional.empty();
|
if (res == null) return Optional.empty();
|
||||||
var ret = curTx.get(JKleppmannTreeNode.class, res);
|
var ret = curTx.get(JKleppmannTreeNodeHolder.class, res).map(JKleppmannTreeNodeHolder::node);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<GetattrRes> getattr(JObjectKey uuid) {
|
public Optional<GetattrRes> getattr(JObjectKey uuid) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
var ref = curTx.get(JData.class, uuid).orElse(null);
|
var ref = curTx.get(JData.class, uuid).orElse(null);
|
||||||
@@ -124,7 +120,7 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
} else {
|
} else {
|
||||||
throw new StatusRuntimeException(Status.DATA_LOSS.withDescription("FsNode is not an FsNode: " + ref.key()));
|
throw new StatusRuntimeException(Status.DATA_LOSS.withDescription("FsNode is not an FsNode: " + ref.key()));
|
||||||
}
|
}
|
||||||
} else if (ref instanceof JKleppmannTreeNode) {
|
} else if (ref instanceof JKleppmannTreeNodeHolder) {
|
||||||
ret = new GetattrRes(100, 100, 0700, GetattrType.DIRECTORY);
|
ret = new GetattrRes(100, 100, 0700, GetattrType.DIRECTORY);
|
||||||
} else {
|
} else {
|
||||||
throw new StatusRuntimeException(Status.DATA_LOSS.withDescription("FsNode is not an FsNode: " + ref.key()));
|
throw new StatusRuntimeException(Status.DATA_LOSS.withDescription("FsNode is not an FsNode: " + ref.key()));
|
||||||
@@ -133,13 +129,12 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<JObjectKey> open(String name) {
|
public Optional<JObjectKey> open(String name) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
try {
|
try {
|
||||||
var ret = getDirEntryR(name);
|
var ret = getDirEntryR(name);
|
||||||
return switch (ret.meta()) {
|
return switch (ret.meta()) {
|
||||||
case JKleppmannTreeNodeMetaFile f -> Optional.of(f.getFileIno());
|
case JKleppmannTreeNodeMetaFile f -> Optional.of(f.fileIno());
|
||||||
case JKleppmannTreeNodeMetaDirectory f -> Optional.of(ret.key());
|
case JKleppmannTreeNodeMetaDirectory f -> Optional.of(ret.key());
|
||||||
default -> Optional.empty();
|
default -> Optional.empty();
|
||||||
};
|
};
|
||||||
@@ -157,7 +152,6 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
throw new StatusRuntimeExceptionNoStacktrace(Status.INVALID_ARGUMENT.withDescription("Not a directory: " + entry.key()));
|
throw new StatusRuntimeExceptionNoStacktrace(Status.INVALID_ARGUMENT.withDescription("Not a directory: " + entry.key()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<JObjectKey> create(String name, long mode) {
|
public Optional<JObjectKey> create(String name, long mode) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
Path path = Path.of(name);
|
Path path = Path.of(name);
|
||||||
@@ -183,18 +177,16 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//FIXME: Slow..
|
//FIXME: Slow..
|
||||||
@Override
|
|
||||||
public Pair<String, JObjectKey> inoToParent(JObjectKey ino) {
|
public Pair<String, JObjectKey> inoToParent(JObjectKey ino) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
return getTreeW().findParent(w -> {
|
return getTreeW().findParent(w -> {
|
||||||
if (w.meta() instanceof JKleppmannTreeNodeMetaFile f)
|
if (w.meta() instanceof JKleppmannTreeNodeMetaFile f)
|
||||||
return f.getFileIno().equals(ino);
|
return f.fileIno().equals(ino);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mkdir(String name, long mode) {
|
public void mkdir(String name, long mode) {
|
||||||
jObjectTxManager.executeTx(() -> {
|
jObjectTxManager.executeTx(() -> {
|
||||||
Path path = Path.of(name);
|
Path path = Path.of(name);
|
||||||
@@ -209,10 +201,11 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unlink(String name) {
|
public void unlink(String name) {
|
||||||
jObjectTxManager.executeTx(() -> {
|
jObjectTxManager.executeTx(() -> {
|
||||||
var node = getDirEntryOpt(name).orElse(null);
|
var node = getDirEntryOpt(name).orElse(null);
|
||||||
|
if (node == null)
|
||||||
|
throw new StatusRuntimeException(Status.NOT_FOUND.withDescription("File not found when trying to unlink: " + name));
|
||||||
if (node.meta() instanceof JKleppmannTreeNodeMetaDirectory f) {
|
if (node.meta() instanceof JKleppmannTreeNodeMetaDirectory f) {
|
||||||
if (!allowRecursiveDelete && !node.children().isEmpty())
|
if (!allowRecursiveDelete && !node.children().isEmpty())
|
||||||
throw new DirectoryNotEmptyException();
|
throw new DirectoryNotEmptyException();
|
||||||
@@ -221,7 +214,6 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Boolean rename(String from, String to) {
|
public Boolean rename(String from, String to) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
var node = getDirEntryW(from);
|
var node = getDirEntryW(from);
|
||||||
@@ -236,17 +228,16 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Boolean chmod(JObjectKey uuid, long mode) {
|
public Boolean chmod(JObjectKey uuid, long mode) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
var dent = curTx.get(JData.class, uuid).orElseThrow(() -> new StatusRuntimeExceptionNoStacktrace(Status.NOT_FOUND));
|
var dent = curTx.get(JData.class, uuid).orElseThrow(() -> new StatusRuntimeExceptionNoStacktrace(Status.NOT_FOUND));
|
||||||
|
|
||||||
if (dent instanceof JKleppmannTreeNode) {
|
if (dent instanceof JKleppmannTreeNodeHolder) {
|
||||||
return true;
|
return true;
|
||||||
} else if (dent instanceof RemoteObjectMeta) {
|
} else if (dent instanceof RemoteObjectMeta) {
|
||||||
var remote = remoteTx.getData(JDataRemote.class, uuid).orElse(null);
|
var remote = remoteTx.getData(JDataRemote.class, uuid).orElse(null);
|
||||||
if (remote instanceof File f) {
|
if (remote instanceof File f) {
|
||||||
remoteTx.putData(f.withMode(mode).withMTime(System.currentTimeMillis()));
|
remoteTx.putData(f.withMode(mode).withCurrentMTime());
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException(uuid + " is not a file");
|
throw new IllegalArgumentException(uuid + " is not a file");
|
||||||
@@ -257,7 +248,6 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterable<String> readDir(String name) {
|
public Iterable<String> readDir(String name) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
var found = getDirEntryW(name);
|
var found = getDirEntryW(name);
|
||||||
@@ -269,8 +259,7 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public ByteString read(JObjectKey fileUuid, long offset, int length) {
|
||||||
public Optional<ByteString> read(JObjectKey fileUuid, long offset, int length) {
|
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
if (length < 0)
|
if (length < 0)
|
||||||
throw new StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Length should be more than zero: " + length));
|
throw new StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Length should be more than zero: " + length));
|
||||||
@@ -280,12 +269,12 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
var file = remoteTx.getData(File.class, fileUuid).orElse(null);
|
var file = remoteTx.getData(File.class, fileUuid).orElse(null);
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
Log.error("File not found when trying to read: " + fileUuid);
|
Log.error("File not found when trying to read: " + fileUuid);
|
||||||
return Optional.empty();
|
throw new StatusRuntimeException(Status.NOT_FOUND.withDescription("File not found when trying to read: " + fileUuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
try (var it = jMapHelper.getIterator(file, IteratorStart.LE, JMapLongKey.of(offset))) {
|
try (var it = jMapHelper.getIterator(file, IteratorStart.LE, JMapLongKey.of(offset))) {
|
||||||
if (!it.hasNext())
|
if (!it.hasNext())
|
||||||
return Optional.of(ByteString.empty());
|
return ByteString.empty();
|
||||||
|
|
||||||
// if (it.peekNextKey().key() != offset) {
|
// if (it.peekNextKey().key() != offset) {
|
||||||
// Log.warnv("Read over the end of file: {0} {1} {2}, next chunk: {3}", fileUuid, offset, length, it.peekNextKey());
|
// Log.warnv("Read over the end of file: {0} {1} {2}, next chunk: {3}", fileUuid, offset, length, it.peekNextKey());
|
||||||
@@ -323,10 +312,10 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
chunk = it.next();
|
chunk = it.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Optional.of(buf);
|
return buf;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.error("Error reading file: " + fileUuid, e);
|
Log.error("Error reading file: " + fileUuid, e);
|
||||||
return Optional.empty();
|
throw new StatusRuntimeException(Status.INTERNAL.withDescription("Error reading file: " + fileUuid));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -350,7 +339,6 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
return num & -(1L << n);
|
return num & -(1L << n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long write(JObjectKey fileUuid, long offset, ByteString data) {
|
public Long write(JObjectKey fileUuid, long offset, ByteString data) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
if (offset < 0)
|
if (offset < 0)
|
||||||
@@ -358,25 +346,15 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
|
|
||||||
var file = remoteTx.getData(File.class, fileUuid, LockingStrategy.WRITE).orElse(null);
|
var file = remoteTx.getData(File.class, fileUuid, LockingStrategy.WRITE).orElse(null);
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
Log.error("File not found when trying to write: " + fileUuid);
|
throw new StatusRuntimeException(Status.NOT_FOUND.withDescription("File not found when trying to write: " + fileUuid));
|
||||||
return -1L;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (writeLogging) {
|
Map<Long, JObjectKey> removedChunks = new HashMap<>();
|
||||||
Log.info("Writing to file: " + file.key() + " size=" + size(fileUuid) + " "
|
|
||||||
+ offset + " " + data.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size(fileUuid) < offset) {
|
|
||||||
truncate(fileUuid, offset);
|
|
||||||
file = remoteTx.getData(File.class, fileUuid).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigableMap<Long, JObjectKey> removedChunks = new TreeMap<>();
|
|
||||||
|
|
||||||
long realOffset = targetChunkAlignment >= 0 ? alignDown(offset, targetChunkAlignment) : offset;
|
long realOffset = targetChunkAlignment >= 0 ? alignDown(offset, targetChunkAlignment) : offset;
|
||||||
long writeEnd = offset + data.size();
|
long writeEnd = offset + data.size();
|
||||||
long start = realOffset;
|
long start = realOffset;
|
||||||
|
long existingEnd = 0;
|
||||||
ByteString pendingPrefix = ByteString.empty();
|
ByteString pendingPrefix = ByteString.empty();
|
||||||
ByteString pendingSuffix = ByteString.empty();
|
ByteString pendingSuffix = ByteString.empty();
|
||||||
|
|
||||||
@@ -385,8 +363,8 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
var curEntry = it.next();
|
var curEntry = it.next();
|
||||||
long curChunkStart = curEntry.getKey().key();
|
long curChunkStart = curEntry.getKey().key();
|
||||||
var curChunkId = curEntry.getValue().ref();
|
var curChunkId = curEntry.getValue().ref();
|
||||||
long curChunkEnd = curChunkStart + getChunkSize(curChunkId);
|
long curChunkEnd = it.hasNext() ? it.peekNextKey().key() : curChunkStart + getChunkSize(curChunkId);
|
||||||
|
existingEnd = curChunkEnd;
|
||||||
if (curChunkEnd <= realOffset) break;
|
if (curChunkEnd <= realOffset) break;
|
||||||
|
|
||||||
removedChunks.put(curEntry.getKey().key(), curChunkId);
|
removedChunks.put(curEntry.getKey().key(), curChunkId);
|
||||||
@@ -408,19 +386,31 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Map<Long, JObjectKey> newChunks = new HashMap<>();
|
||||||
|
|
||||||
|
if (existingEnd < offset) {
|
||||||
|
if (!pendingPrefix.isEmpty()) {
|
||||||
|
int diff = Math.toIntExact(offset - existingEnd);
|
||||||
|
pendingPrefix = pendingPrefix.concat(UnsafeByteOperations.unsafeWrap(ByteBuffer.allocateDirect(diff)));
|
||||||
|
} else {
|
||||||
|
fillZeros(existingEnd, offset, newChunks);
|
||||||
|
start = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ByteString pendingWrites = pendingPrefix.concat(data).concat(pendingSuffix);
|
ByteString pendingWrites = pendingPrefix.concat(data).concat(pendingSuffix);
|
||||||
|
|
||||||
int combinedSize = pendingWrites.size();
|
int combinedSize = pendingWrites.size();
|
||||||
|
|
||||||
NavigableMap<Long, JObjectKey> newChunks = new TreeMap<>();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
int targetChunkSize = 1 << targetChunkAlignment;
|
|
||||||
int cur = 0;
|
int cur = 0;
|
||||||
while (cur < combinedSize) {
|
while (cur < combinedSize) {
|
||||||
int end;
|
int end;
|
||||||
|
|
||||||
if (targetChunkAlignment < 0)
|
if (combinedSize - cur < maxChunkSize)
|
||||||
|
end = combinedSize;
|
||||||
|
else if (targetChunkAlignment < 0)
|
||||||
end = combinedSize;
|
end = combinedSize;
|
||||||
else
|
else
|
||||||
end = Math.min(cur + targetChunkSize, combinedSize);
|
end = Math.min(cur + targetChunkSize, combinedSize);
|
||||||
@@ -436,22 +426,21 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (var e : removedChunks.entrySet()) {
|
for (var e : removedChunks.entrySet()) {
|
||||||
Log.tracev("Removing chunk {0}-{1}", e.getKey(), e.getValue());
|
// Log.tracev("Removing chunk {0}-{1}", e.getKey(), e.getValue());
|
||||||
jMapHelper.delete(file, JMapLongKey.of(e.getKey()));
|
jMapHelper.delete(file, JMapLongKey.of(e.getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var e : newChunks.entrySet()) {
|
for (var e : newChunks.entrySet()) {
|
||||||
Log.tracev("Adding chunk {0}-{1}", e.getKey(), e.getValue());
|
// Log.tracev("Adding chunk {0}-{1}", e.getKey(), e.getValue());
|
||||||
jMapHelper.put(file, JMapLongKey.of(e.getKey()), e.getValue());
|
jMapHelper.put(file, JMapLongKey.of(e.getKey()), e.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteTx.putData(file);
|
remoteTx.putData(file.withCurrentMTime());
|
||||||
|
|
||||||
return (long) data.size();
|
return (long) data.size();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Boolean truncate(JObjectKey fileUuid, long length) {
|
public Boolean truncate(JObjectKey fileUuid, long length) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
if (length < 0)
|
if (length < 0)
|
||||||
@@ -476,38 +465,7 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
NavigableMap<Long, JObjectKey> newChunks = new TreeMap<>();
|
NavigableMap<Long, JObjectKey> newChunks = new TreeMap<>();
|
||||||
|
|
||||||
if (curSize < length) {
|
if (curSize < length) {
|
||||||
long combinedSize = (length - curSize);
|
fillZeros(curSize, length, newChunks);
|
||||||
|
|
||||||
long start = curSize;
|
|
||||||
|
|
||||||
// Hack
|
|
||||||
HashMap<Long, ChunkData> zeroCache = new HashMap<>();
|
|
||||||
|
|
||||||
{
|
|
||||||
long cur = 0;
|
|
||||||
while (cur < combinedSize) {
|
|
||||||
long end;
|
|
||||||
|
|
||||||
if (targetChunkSize <= 0)
|
|
||||||
end = combinedSize;
|
|
||||||
else {
|
|
||||||
if ((combinedSize - cur) > (targetChunkSize * 1.5)) {
|
|
||||||
end = cur + targetChunkSize;
|
|
||||||
} else {
|
|
||||||
end = combinedSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!zeroCache.containsKey(end - cur))
|
|
||||||
zeroCache.put(end - cur, createChunk(UnsafeByteOperations.unsafeWrap(new byte[Math.toIntExact(end - cur)])));
|
|
||||||
|
|
||||||
ChunkData newChunkData = zeroCache.get(end - cur);
|
|
||||||
newChunks.put(start, newChunkData.key());
|
|
||||||
|
|
||||||
start += newChunkData.data().size();
|
|
||||||
cur = end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Pair<JMapLongKey, JMapEntry<JMapLongKey>> first;
|
// Pair<JMapLongKey, JMapEntry<JMapLongKey>> first;
|
||||||
Pair<JMapLongKey, JMapEntry<JMapLongKey>> last;
|
Pair<JMapLongKey, JMapEntry<JMapLongKey>> last;
|
||||||
@@ -558,36 +516,68 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
// file = file.withChunks(file.chunks().minusAll(removedChunks.keySet()).plusAll(newChunks)).withMTime(System.currentTimeMillis());
|
// file = file.withChunks(file.chunks().minusAll(removedChunks.keySet()).plusAll(newChunks)).withMTime(System.currentTimeMillis());
|
||||||
|
|
||||||
for (var e : removedChunks.entrySet()) {
|
for (var e : removedChunks.entrySet()) {
|
||||||
Log.tracev("Removing chunk {0}-{1}", e.getKey(), e.getValue());
|
// Log.tracev("Removing chunk {0}-{1}", e.getKey(), e.getValue());
|
||||||
jMapHelper.delete(file, JMapLongKey.of(e.getKey()));
|
jMapHelper.delete(file, JMapLongKey.of(e.getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var e : newChunks.entrySet()) {
|
for (var e : newChunks.entrySet()) {
|
||||||
Log.tracev("Adding chunk {0}-{1}", e.getKey(), e.getValue());
|
// Log.tracev("Adding chunk {0}-{1}", e.getKey(), e.getValue());
|
||||||
jMapHelper.put(file, JMapLongKey.of(e.getKey()), e.getValue());
|
jMapHelper.put(file, JMapLongKey.of(e.getKey()), e.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteTx.putData(file);
|
remoteTx.putData(file.withCurrentMTime());
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void fillZeros(long fillStart, long length, Map<Long, JObjectKey> newChunks) {
|
||||||
|
long combinedSize = (length - fillStart);
|
||||||
|
|
||||||
|
long start = fillStart;
|
||||||
|
|
||||||
|
// Hack
|
||||||
|
HashMap<Long, ChunkData> zeroCache = new HashMap<>();
|
||||||
|
|
||||||
|
{
|
||||||
|
long cur = 0;
|
||||||
|
while (cur < combinedSize) {
|
||||||
|
long end;
|
||||||
|
|
||||||
|
if (targetChunkSize <= 0)
|
||||||
|
end = combinedSize;
|
||||||
|
else {
|
||||||
|
if ((combinedSize - cur) > (targetChunkSize * 1.5)) {
|
||||||
|
end = cur + targetChunkSize;
|
||||||
|
} else {
|
||||||
|
end = combinedSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!zeroCache.containsKey(end - cur))
|
||||||
|
zeroCache.put(end - cur, createChunk(UnsafeByteOperations.unsafeWrap(ByteBuffer.allocateDirect(Math.toIntExact(end - cur)))));
|
||||||
|
|
||||||
|
ChunkData newChunkData = zeroCache.get(end - cur);
|
||||||
|
newChunks.put(start, newChunkData.key());
|
||||||
|
|
||||||
|
start += newChunkData.data().size();
|
||||||
|
cur = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String readlink(JObjectKey uuid) {
|
public String readlink(JObjectKey uuid) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
return readlinkBS(uuid).toStringUtf8();
|
return readlinkBS(uuid).toStringUtf8();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteString readlinkBS(JObjectKey uuid) {
|
public ByteString readlinkBS(JObjectKey uuid) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
var fileOpt = remoteTx.getData(File.class, uuid).orElseThrow(() -> new StatusRuntimeException(Status.NOT_FOUND.withDescription("File not found when trying to readlink: " + uuid)));
|
var fileOpt = remoteTx.getData(File.class, uuid).orElseThrow(() -> new StatusRuntimeException(Status.NOT_FOUND.withDescription("File not found when trying to readlink: " + uuid)));
|
||||||
return read(uuid, 0, Math.toIntExact(size(uuid))).get();
|
return read(uuid, 0, Math.toIntExact(size(uuid)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public JObjectKey symlink(String oldpath, String newpath) {
|
public JObjectKey symlink(String oldpath, String newpath) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
Path path = Path.of(newpath);
|
Path path = Path.of(newpath);
|
||||||
@@ -610,13 +600,12 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Boolean setTimes(JObjectKey fileUuid, long atimeMs, long mtimeMs) {
|
public Boolean setTimes(JObjectKey fileUuid, long atimeMs, long mtimeMs) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
var dent = curTx.get(JData.class, fileUuid).orElseThrow(() -> new StatusRuntimeExceptionNoStacktrace(Status.NOT_FOUND));
|
var dent = curTx.get(JData.class, fileUuid).orElseThrow(() -> new StatusRuntimeExceptionNoStacktrace(Status.NOT_FOUND));
|
||||||
|
|
||||||
// FIXME:
|
// FIXME:
|
||||||
if (dent instanceof JKleppmannTreeNode) {
|
if (dent instanceof JKleppmannTreeNodeHolder) {
|
||||||
return true;
|
return true;
|
||||||
} else if (dent instanceof RemoteObjectMeta) {
|
} else if (dent instanceof RemoteObjectMeta) {
|
||||||
var remote = remoteTx.getData(JDataRemote.class, fileUuid).orElse(null);
|
var remote = remoteTx.getData(JDataRemote.class, fileUuid).orElse(null);
|
||||||
@@ -632,7 +621,6 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long size(JObjectKey fileUuid) {
|
public long size(JObjectKey fileUuid) {
|
||||||
return jObjectTxManager.executeTx(() -> {
|
return jObjectTxManager.executeTx(() -> {
|
||||||
long realSize = 0;
|
long realSize = 0;
|
||||||
@@ -651,4 +639,8 @@ public class DhfsFileServiceImpl implements DhfsFileService {
|
|||||||
return realSize;
|
return realSize;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long write(JObjectKey fileUuid, long offset, byte[] data) {
|
||||||
|
return write(fileUuid, offset, UnsafeByteOperations.unsafeWrap(data));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs.files.service;
|
package com.usatiuk.dhfsfs.service;
|
||||||
|
|
||||||
public class DirectoryNotEmptyException extends RuntimeException {
|
public class DirectoryNotEmptyException extends RuntimeException {
|
||||||
@Override
|
@Override
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs.files.service;
|
package com.usatiuk.dhfsfs.service;
|
||||||
|
|
||||||
public record GetattrRes(long mtime, long ctime, long mode, GetattrType type) {
|
public record GetattrRes(long mtime, long ctime, long mode, GetattrType type) {
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs.files.service;
|
package com.usatiuk.dhfsfs.service;
|
||||||
|
|
||||||
public enum GetattrType {
|
public enum GetattrType {
|
||||||
FILE,
|
FILE,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs.files;
|
package com.usatiuk.dhfsfs;
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
import io.quarkus.test.junit.TestProfile;
|
import io.quarkus.test.junit.TestProfile;
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
package com.usatiuk.dhfs.files;
|
package com.usatiuk.dhfsfs;
|
||||||
|
|
||||||
import com.usatiuk.dhfs.RemoteTransaction;
|
import com.usatiuk.dhfs.remoteobj.RemoteTransaction;
|
||||||
import com.usatiuk.dhfs.TempDataProfile;
|
import com.usatiuk.dhfsfs.objects.File;
|
||||||
import com.usatiuk.dhfs.files.objects.File;
|
import com.usatiuk.dhfsfs.service.DhfsFileService;
|
||||||
import com.usatiuk.dhfs.files.service.DhfsFileService;
|
|
||||||
import com.usatiuk.kleppmanntree.AlreadyExistsException;
|
import com.usatiuk.kleppmanntree.AlreadyExistsException;
|
||||||
import com.usatiuk.objects.transaction.Transaction;
|
import com.usatiuk.objects.transaction.Transaction;
|
||||||
import com.usatiuk.objects.transaction.TransactionManager;
|
import com.usatiuk.objects.transaction.TransactionManager;
|
||||||
@@ -90,7 +89,7 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
// for (int start = 0; start < all.length(); start++) {
|
// for (int start = 0; start < all.length(); start++) {
|
||||||
// for (int end = start; end <= all.length(); end++) {
|
// for (int end = start; end <= all.length(); end++) {
|
||||||
// var read = fileService.read(fuuid.toString(), start, end - start);
|
// var read = fileService.read(fuuid.toString(), start, end - start);
|
||||||
// Assertions.assertArrayEquals(all.substring(start, end).getBytes(), read.get().toByteArray());
|
// Assertions.assertArrayEquals(all.substring(start, end).getBytes(), read.toByteArray());
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
@@ -111,17 +110,21 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
|
|
||||||
var uuid = ret.get();
|
var uuid = ret.get();
|
||||||
|
|
||||||
|
var curMtime = fileService.getattr(uuid).get().mtime();
|
||||||
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).toByteArray());
|
||||||
Assertions.assertArrayEquals(new byte[]{2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 2, 8).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 2, 8).toByteArray());
|
||||||
fileService.write(uuid, 4, new byte[]{10, 11, 12});
|
fileService.write(uuid, 4, new byte[]{10, 11, 12});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 10, 11, 12, 7, 8, 9}, fileService.read(uuid, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 10, 11, 12, 7, 8, 9}, fileService.read(uuid, 0, 10).toByteArray());
|
||||||
fileService.write(uuid, 10, new byte[]{13, 14});
|
fileService.write(uuid, 10, new byte[]{13, 14});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 10, 11, 12, 7, 8, 9, 13, 14}, fileService.read(uuid, 0, 12).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 10, 11, 12, 7, 8, 9, 13, 14}, fileService.read(uuid, 0, 12).toByteArray());
|
||||||
fileService.write(uuid, 6, new byte[]{15, 16});
|
fileService.write(uuid, 6, new byte[]{15, 16});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 10, 11, 15, 16, 8, 9, 13, 14}, fileService.read(uuid, 0, 12).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 10, 11, 15, 16, 8, 9, 13, 14}, fileService.read(uuid, 0, 12).toByteArray());
|
||||||
fileService.write(uuid, 3, new byte[]{17, 18});
|
fileService.write(uuid, 3, new byte[]{17, 18});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 17, 18, 11, 15, 16, 8, 9, 13, 14}, fileService.read(uuid, 0, 12).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 17, 18, 11, 15, 16, 8, 9, 13, 14}, fileService.read(uuid, 0, 12).toByteArray());
|
||||||
|
|
||||||
|
var newMtime = fileService.getattr(uuid).get().mtime();
|
||||||
|
Assertions.assertTrue(newMtime > curMtime);
|
||||||
|
|
||||||
fileService.unlink("/writeTest");
|
fileService.unlink("/writeTest");
|
||||||
Assertions.assertFalse(fileService.open("/writeTest").isPresent());
|
Assertions.assertFalse(fileService.open("/writeTest").isPresent());
|
||||||
@@ -135,7 +138,7 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
var uuid = ret.get();
|
var uuid = ret.get();
|
||||||
|
|
||||||
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).toByteArray());
|
||||||
|
|
||||||
fileService.unlink("/removeTest");
|
fileService.unlink("/removeTest");
|
||||||
Assertions.assertFalse(fileService.open("/removeTest").isPresent());
|
Assertions.assertFalse(fileService.open("/removeTest").isPresent());
|
||||||
@@ -149,12 +152,12 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
var uuid = ret.get();
|
var uuid = ret.get();
|
||||||
|
|
||||||
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).toByteArray());
|
||||||
|
|
||||||
fileService.truncate(uuid, 20);
|
fileService.truncate(uuid, 20);
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fileService.read(uuid, 0, 20).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fileService.read(uuid, 0, 20).toByteArray());
|
||||||
fileService.write(uuid, 5, new byte[]{10, 11, 12, 13, 14, 15, 16, 17});
|
fileService.write(uuid, 5, new byte[]{10, 11, 12, 13, 14, 15, 16, 17});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 15, 16, 17, 0, 0, 0, 0, 0, 0, 0}, fileService.read(uuid, 0, 20).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 15, 16, 17, 0, 0, 0, 0, 0, 0, 0}, fileService.read(uuid, 0, 20).toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@RepeatedTest(100)
|
@RepeatedTest(100)
|
||||||
@@ -166,11 +169,12 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
var uuid = ret.get();
|
var uuid = ret.get();
|
||||||
|
|
||||||
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).toByteArray());
|
||||||
|
|
||||||
fileService.truncate(uuid, 20);
|
fileService.truncate(uuid, 20);
|
||||||
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, fileService.read(uuid, 0, 20).toByteArray());
|
||||||
fileService.write(uuid, 10, new byte[]{11, 12, 13, 14, 15, 16, 17, 18, 19, 20});
|
fileService.write(uuid, 10, new byte[]{11, 12, 13, 14, 15, 16, 17, 18, 19, 20});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, fileService.read(uuid, 0, 20).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, fileService.read(uuid, 0, 20).toByteArray());
|
||||||
} finally {
|
} finally {
|
||||||
fileService.unlink("/truncateTest2");
|
fileService.unlink("/truncateTest2");
|
||||||
}
|
}
|
||||||
@@ -184,10 +188,10 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
var uuid = ret.get();
|
var uuid = ret.get();
|
||||||
|
|
||||||
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).toByteArray());
|
||||||
|
|
||||||
fileService.truncate(uuid, 7);
|
fileService.truncate(uuid, 7);
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6,}, fileService.read(uuid, 0, 20).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6,}, fileService.read(uuid, 0, 20).toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -197,14 +201,14 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
var uuid = ret.get();
|
var uuid = ret.get();
|
||||||
|
|
||||||
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).toByteArray());
|
||||||
|
|
||||||
Assertions.assertTrue(fileService.rename("/moveTest", "/movedTest"));
|
Assertions.assertTrue(fileService.rename("/moveTest", "/movedTest"));
|
||||||
Assertions.assertFalse(fileService.open("/moveTest").isPresent());
|
Assertions.assertFalse(fileService.open("/moveTest").isPresent());
|
||||||
Assertions.assertTrue(fileService.open("/movedTest").isPresent());
|
Assertions.assertTrue(fileService.open("/movedTest").isPresent());
|
||||||
|
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||||
fileService.read(fileService.open("/movedTest").get(), 0, 10).get().toByteArray());
|
fileService.read(fileService.open("/movedTest").get(), 0, 10).toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -217,9 +221,9 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
var uuid2 = ret2.get();
|
var uuid2 = ret2.get();
|
||||||
|
|
||||||
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).toByteArray());
|
||||||
fileService.write(uuid2, 0, new byte[]{11, 12, 13, 14, 15, 16, 17, 18, 19, 29});
|
fileService.write(uuid2, 0, new byte[]{11, 12, 13, 14, 15, 16, 17, 18, 19, 29});
|
||||||
Assertions.assertArrayEquals(new byte[]{11, 12, 13, 14, 15, 16, 17, 18, 19, 29}, fileService.read(uuid2, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{11, 12, 13, 14, 15, 16, 17, 18, 19, 29}, fileService.read(uuid2, 0, 10).toByteArray());
|
||||||
|
|
||||||
|
|
||||||
jObjectTxManager.run(() -> {
|
jObjectTxManager.run(() -> {
|
||||||
@@ -233,7 +237,7 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
Assertions.assertTrue(fileService.open("/moveOverTest2").isPresent());
|
Assertions.assertTrue(fileService.open("/moveOverTest2").isPresent());
|
||||||
|
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||||
fileService.read(fileService.open("/moveOverTest2").get(), 0, 10).get().toByteArray());
|
fileService.read(fileService.open("/moveOverTest2").get(), 0, 10).toByteArray());
|
||||||
|
|
||||||
// await().atMost(5, TimeUnit.SECONDS).until(() -> {
|
// await().atMost(5, TimeUnit.SECONDS).until(() -> {
|
||||||
// jObjectTxManager.run(() -> {
|
// jObjectTxManager.run(() -> {
|
||||||
@@ -251,8 +255,8 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
var uuid = ret.get();
|
var uuid = ret.get();
|
||||||
|
|
||||||
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).toByteArray());
|
||||||
Assertions.assertArrayEquals(new byte[]{}, fileService.read(uuid, 20, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{}, fileService.read(uuid, 20, 10).toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -262,13 +266,13 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
var uuid = ret.get();
|
var uuid = ret.get();
|
||||||
|
|
||||||
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).toByteArray());
|
||||||
fileService.write(uuid, 20, new byte[]{10, 11, 12, 13, 14, 15, 16, 17, 18, 19});
|
fileService.write(uuid, 20, new byte[]{10, 11, 12, 13, 14, 15, 16, 17, 18, 19});
|
||||||
Assertions.assertArrayEquals(new byte[]{
|
Assertions.assertArrayEquals(new byte[]{
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19
|
10, 11, 12, 13, 14, 15, 16, 17, 18, 19
|
||||||
}, fileService.read(uuid, 0, 30).get().toByteArray());
|
}, fileService.read(uuid, 0, 30).toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -278,7 +282,7 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
var uuid = ret.get();
|
var uuid = ret.get();
|
||||||
|
|
||||||
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
fileService.write(uuid, 0, new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).get().toByteArray());
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, fileService.read(uuid, 0, 10).toByteArray());
|
||||||
|
|
||||||
// var oldfile = jObjectManager.get(uuid).orElseThrow(IllegalStateException::new);
|
// var oldfile = jObjectManager.get(uuid).orElseThrow(IllegalStateException::new);
|
||||||
// var chunk = oldfile.runReadLocked(JObjectManager.ResolutionStrategy.LOCAL_ONLY, (m, d) -> d.extractRefs()).stream().toList().get(0);
|
// var chunk = oldfile.runReadLocked(JObjectManager.ResolutionStrategy.LOCAL_ONLY, (m, d) -> d.extractRefs()).stream().toList().get(0);
|
||||||
@@ -293,6 +297,6 @@ public abstract class DhfsFileServiceSimpleTestImpl {
|
|||||||
Assertions.assertTrue(fileService.open("/movedTest2").isPresent());
|
Assertions.assertTrue(fileService.open("/movedTest2").isPresent());
|
||||||
|
|
||||||
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
|
Assertions.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||||
fileService.read(fileService.open("/movedTest2").get(), 0, 10).get().toByteArray());
|
fileService.read(fileService.open("/movedTest2").get(), 0, 10).toByteArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs.files;
|
package com.usatiuk.dhfsfs;
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
import io.quarkus.test.junit.TestProfile;
|
import io.quarkus.test.junit.TestProfile;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs.files;
|
package com.usatiuk.dhfsfs;
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
import io.quarkus.test.junit.TestProfile;
|
import io.quarkus.test.junit.TestProfile;
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.usatiuk.dhfsfs;
|
||||||
|
|
||||||
|
import io.quarkus.test.junit.QuarkusTestProfile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
abstract public class TempDataProfile implements QuarkusTestProfile {
|
||||||
|
protected void getConfigOverrides(Map<String, String> toPut) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final public Map<String, String> getConfigOverrides() {
|
||||||
|
Path tempDirWithPrefix;
|
||||||
|
try {
|
||||||
|
tempDirWithPrefix = Files.createTempDirectory("dhfs-test");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
var ret = new HashMap<String, String>();
|
||||||
|
ret.put("dhfs.objects.persistence.files.root", tempDirWithPrefix.resolve("dhfs_root_test").toString());
|
||||||
|
ret.put("dhfs.fuse.root", tempDirWithPrefix.resolve("dhfs_fuse_root_test").toString());
|
||||||
|
getConfigOverrides(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.usatiuk.dhfsfs;
|
||||||
|
|
||||||
|
import io.quarkus.logging.Log;
|
||||||
|
import io.quarkus.runtime.ShutdownEvent;
|
||||||
|
import io.quarkus.runtime.StartupEvent;
|
||||||
|
import jakarta.annotation.Priority;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.enterprise.event.Observes;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class TestDataCleaner {
|
||||||
|
@ConfigProperty(name = "dhfs.objects.persistence.files.root")
|
||||||
|
String tempDirectory;
|
||||||
|
|
||||||
|
void init(@Observes @Priority(1) StartupEvent event) throws IOException {
|
||||||
|
try {
|
||||||
|
purgeDirectory(Path.of(tempDirectory).toFile());
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
Log.warn("Couldn't cleanup test data on init");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown(@Observes @Priority(1000000000) ShutdownEvent event) throws IOException {
|
||||||
|
purgeDirectory(Path.of(tempDirectory).toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void purgeDirectory(File dir) {
|
||||||
|
for (File file : Objects.requireNonNull(dir.listFiles())) {
|
||||||
|
if (file.isDirectory())
|
||||||
|
purgeDirectory(file);
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs.benchmarks;
|
package com.usatiuk.dhfsfs.benchmarks;
|
||||||
|
|
||||||
import io.quarkus.logging.Log;
|
import io.quarkus.logging.Log;
|
||||||
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
|
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.usatiuk.dhfs.benchmarks;
|
package com.usatiuk.dhfsfs.benchmarks;
|
||||||
|
|
||||||
import com.google.protobuf.UnsafeByteOperations;
|
import com.google.protobuf.UnsafeByteOperations;
|
||||||
import com.usatiuk.dhfs.TempDataProfile;
|
import com.usatiuk.dhfsfs.TempDataProfile;
|
||||||
import com.usatiuk.dhfs.files.service.DhfsFileService;
|
import com.usatiuk.dhfsfs.service.DhfsFileService;
|
||||||
import com.usatiuk.objects.JObjectKey;
|
import com.usatiuk.objects.JObjectKey;
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
import io.quarkus.test.junit.TestProfile;
|
import io.quarkus.test.junit.TestProfile;
|
||||||
@@ -5,7 +5,6 @@ dhfs.objects.ref_verification=true
|
|||||||
dhfs.objects.deletion.delay=0
|
dhfs.objects.deletion.delay=0
|
||||||
quarkus.log.category."com.usatiuk.dhfs".level=TRACE
|
quarkus.log.category."com.usatiuk.dhfs".level=TRACE
|
||||||
quarkus.log.category."com.usatiuk.dhfs".min-level=TRACE
|
quarkus.log.category."com.usatiuk.dhfs".min-level=TRACE
|
||||||
quarkus.class-loading.parent-first-artifacts=com.usatiuk.dhfs:supportlib
|
|
||||||
quarkus.http.test-port=0
|
quarkus.http.test-port=0
|
||||||
quarkus.http.test-ssl-port=0
|
quarkus.http.test-ssl-port=0
|
||||||
dhfs.local-discovery=false
|
dhfs.local-discovery=false
|
||||||
5
dhfs-parent/dhfs-fuse/.dockerignore
Normal file
5
dhfs-parent/dhfs-fuse/.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
*
|
||||||
|
!target/*-runner
|
||||||
|
!target/*-runner.jar
|
||||||
|
!target/lib/*
|
||||||
|
!target/quarkus-app/*
|
||||||
43
dhfs-parent/dhfs-fuse/.gitignore
vendored
Normal file
43
dhfs-parent/dhfs-fuse/.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#Maven
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
release.properties
|
||||||
|
.flattened-pom.xml
|
||||||
|
|
||||||
|
# Eclipse
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
bin/
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
.idea
|
||||||
|
*.ipr
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# NetBeans
|
||||||
|
nb-configuration.xml
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# OSX
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Vim
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# patch
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
|
||||||
|
# Local environment
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Plugin directory
|
||||||
|
/.quarkus/cli/plugins/
|
||||||
2
dhfs-parent/dhfs-fuse/Dockerfile
Normal file
2
dhfs-parent/dhfs-fuse/Dockerfile
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FROM azul/zulu-openjdk-debian:21-jre-latest
|
||||||
|
RUN apt update && apt install -y libfuse2 curl
|
||||||
43
dhfs-parent/dhfs-fuse/docker-compose.yml
Normal file
43
dhfs-parent/dhfs-fuse/docker-compose.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
version: "3.2"
|
||||||
|
|
||||||
|
services:
|
||||||
|
dhfs1:
|
||||||
|
build: .
|
||||||
|
privileged: true
|
||||||
|
devices:
|
||||||
|
- /dev/fuse
|
||||||
|
volumes:
|
||||||
|
- $HOME/dhfs/dhfs1:/dhfs_root
|
||||||
|
- $HOME/dhfs/dhfs1_f:/dhfs_root/fuse:rshared
|
||||||
|
- ./target/quarkus-app:/app
|
||||||
|
command: "java --add-exports java.base/sun.nio.ch=ALL-UNNAMED
|
||||||
|
-Ddhfs.objects.persistence.files.root=/dhfs_root/p
|
||||||
|
-Ddhfs.objects.root=/dhfs_root/d
|
||||||
|
-Ddhfs.fuse.root=/dhfs_root/fuse -Dquarkus.http.host=0.0.0.0
|
||||||
|
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
|
||||||
|
-jar /app/quarkus-run.jar"
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
- 8081:8443
|
||||||
|
- 5005:5005
|
||||||
|
dhfs2:
|
||||||
|
build: .
|
||||||
|
privileged: true
|
||||||
|
devices:
|
||||||
|
- /dev/fuse
|
||||||
|
volumes:
|
||||||
|
- $HOME/dhfs/dhfs2:/dhfs_root
|
||||||
|
- $HOME/dhfs/dhfs2_f:/dhfs_root/fuse:rshared
|
||||||
|
- ./target/quarkus-app:/app
|
||||||
|
command: "java --add-exports java.base/sun.nio.ch=ALL-UNNAMED
|
||||||
|
--add-exports java.base/jdk.internal.access=ALL-UNNAMED
|
||||||
|
--add-opens=java.base/java.nio=ALL-UNNAMED
|
||||||
|
-Ddhfs.objects.persistence.files.root=/dhfs_root/p
|
||||||
|
-Ddhfs.objects.root=/dhfs_root/d
|
||||||
|
-Ddhfs.fuse.root=/dhfs_root/fuse -Dquarkus.http.host=0.0.0.0
|
||||||
|
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5010
|
||||||
|
-jar /app/quarkus-run.jar"
|
||||||
|
ports:
|
||||||
|
- 8090:8080
|
||||||
|
- 8091:8443
|
||||||
|
- 5010:5010
|
||||||
172
dhfs-parent/dhfs-fuse/pom.xml
Normal file
172
dhfs-parent/dhfs-fuse/pom.xml
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.usatiuk.dhfs</groupId>
|
||||||
|
<artifactId>dhfs-fuse</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.usatiuk.dhfs</groupId>
|
||||||
|
<artifactId>parent</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.testcontainers</groupId>
|
||||||
|
<artifactId>testcontainers</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcpkix-jdk18on</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.openhft</groupId>
|
||||||
|
<artifactId>zero-allocation-hashing</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-grpc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-arc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-client-jsonb</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-jsonb</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-scheduler</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-junit5</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.serceman</groupId>
|
||||||
|
<artifactId>jnr-fuse</artifactId>
|
||||||
|
<version>0.5.8</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-jboss-logmanager</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-collections4</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.pcollections</groupId>
|
||||||
|
<artifactId>pcollections</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-math3</artifactId>
|
||||||
|
<version>3.6.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.usatiuk.dhfs</groupId>
|
||||||
|
<artifactId>dhfs-fs</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.usatiuk.dhfs</groupId>
|
||||||
|
<artifactId>utils</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<forkCount>1C</forkCount>
|
||||||
|
<reuseForks>false</reuseForks>
|
||||||
|
<parallel>classes</parallel>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<junit.jupiter.execution.parallel.enabled>
|
||||||
|
false
|
||||||
|
</junit.jupiter.execution.parallel.enabled>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-failsafe-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<forkCount>1C</forkCount>
|
||||||
|
<reuseForks>false</reuseForks>
|
||||||
|
<parallel>classes</parallel>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<junit.jupiter.execution.parallel.enabled>
|
||||||
|
false
|
||||||
|
</junit.jupiter.execution.parallel.enabled>
|
||||||
|
<junit.platform.output.capture.stdout>true</junit.platform.output.capture.stdout>
|
||||||
|
<junit.platform.output.capture.stderr>true</junit.platform.output.capture.stderr>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>${quarkus.platform.group-id}</groupId>
|
||||||
|
<artifactId>quarkus-maven-plugin</artifactId>
|
||||||
|
<version>${quarkus.platform.version}</version>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>quarkus-plugin</id>
|
||||||
|
<goals>
|
||||||
|
<goal>build</goal>
|
||||||
|
<goal>generate-code</goal>
|
||||||
|
<goal>generate-code-tests</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
97
dhfs-parent/dhfs-fuse/src/main/docker/Dockerfile.jvm
Normal file
97
dhfs-parent/dhfs-fuse/src/main/docker/Dockerfile.jvm
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
####
|
||||||
|
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
|
||||||
|
#
|
||||||
|
# Before building the container image run:
|
||||||
|
#
|
||||||
|
# ./mvnw package
|
||||||
|
#
|
||||||
|
# Then, build the image with:
|
||||||
|
#
|
||||||
|
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/server-jvm .
|
||||||
|
#
|
||||||
|
# Then run the container using:
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/server-jvm
|
||||||
|
#
|
||||||
|
# If you want to include the debug port into your docker image
|
||||||
|
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
|
||||||
|
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
|
||||||
|
# when running the container
|
||||||
|
#
|
||||||
|
# Then run the container using :
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/server-jvm
|
||||||
|
#
|
||||||
|
# This image uses the `run-java.sh` script to run the application.
|
||||||
|
# This scripts computes the command line to execute your Java application, and
|
||||||
|
# includes memory/GC tuning.
|
||||||
|
# You can configure the behavior using the following environment properties:
|
||||||
|
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
|
||||||
|
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
|
||||||
|
# in JAVA_OPTS (example: "-Dsome.property=foo")
|
||||||
|
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
|
||||||
|
# used to calculate a default maximal heap memory based on a containers restriction.
|
||||||
|
# If used in a container without any memory constraints for the container then this
|
||||||
|
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
|
||||||
|
# of the container available memory as set here. The default is `50` which means 50%
|
||||||
|
# of the available memory is used as an upper boundary. You can skip this mechanism by
|
||||||
|
# setting this value to `0` in which case no `-Xmx` option is added.
|
||||||
|
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
|
||||||
|
# is used to calculate a default initial heap memory based on the maximum heap memory.
|
||||||
|
# If used in a container without any memory constraints for the container then this
|
||||||
|
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
|
||||||
|
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
|
||||||
|
# is used as the initial heap size. You can skip this mechanism by setting this value
|
||||||
|
# to `0` in which case no `-Xms` option is added (example: "25")
|
||||||
|
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
|
||||||
|
# This is used to calculate the maximum value of the initial heap memory. If used in
|
||||||
|
# a container without any memory constraints for the container then this option has
|
||||||
|
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
|
||||||
|
# here. The default is 4096MB which means the calculated value of `-Xms` never will
|
||||||
|
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
|
||||||
|
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
|
||||||
|
# when things are happening. This option, if set to true, will set
|
||||||
|
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
|
||||||
|
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
|
||||||
|
# true").
|
||||||
|
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
|
||||||
|
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
|
||||||
|
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
|
||||||
|
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
|
||||||
|
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
|
||||||
|
# (example: "20")
|
||||||
|
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
|
||||||
|
# (example: "40")
|
||||||
|
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
|
||||||
|
# (example: "4")
|
||||||
|
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
|
||||||
|
# previous GC times. (example: "90")
|
||||||
|
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
|
||||||
|
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
|
||||||
|
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
|
||||||
|
# contain the necessary JRE command-line options to specify the required GC, which
|
||||||
|
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
|
||||||
|
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
|
||||||
|
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
|
||||||
|
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
|
||||||
|
# accessed directly. (example: "foo.example.com,bar.example.com")
|
||||||
|
#
|
||||||
|
###
|
||||||
|
FROM registry.access.redhat.com/ubi8/openjdk-21:1.18
|
||||||
|
|
||||||
|
ENV LANGUAGE='en_US:en'
|
||||||
|
|
||||||
|
|
||||||
|
# We make four distinct layers so if there are application changes the library layers can be re-used
|
||||||
|
COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
|
||||||
|
COPY --chown=185 target/quarkus-app/*.jar /deployments/
|
||||||
|
COPY --chown=185 target/quarkus-app/app/ /deployments/app/
|
||||||
|
COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 185
|
||||||
|
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||||
|
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
|
||||||
|
|
||||||
93
dhfs-parent/dhfs-fuse/src/main/docker/Dockerfile.legacy-jar
Normal file
93
dhfs-parent/dhfs-fuse/src/main/docker/Dockerfile.legacy-jar
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
####
|
||||||
|
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
|
||||||
|
#
|
||||||
|
# Before building the container image run:
|
||||||
|
#
|
||||||
|
# ./mvnw package -Dquarkus.package.jar.type=legacy-jar
|
||||||
|
#
|
||||||
|
# Then, build the image with:
|
||||||
|
#
|
||||||
|
# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/server-legacy-jar .
|
||||||
|
#
|
||||||
|
# Then run the container using:
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/server-legacy-jar
|
||||||
|
#
|
||||||
|
# If you want to include the debug port into your docker image
|
||||||
|
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
|
||||||
|
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
|
||||||
|
# when running the container
|
||||||
|
#
|
||||||
|
# Then run the container using :
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/server-legacy-jar
|
||||||
|
#
|
||||||
|
# This image uses the `run-java.sh` script to run the application.
|
||||||
|
# This scripts computes the command line to execute your Java application, and
|
||||||
|
# includes memory/GC tuning.
|
||||||
|
# You can configure the behavior using the following environment properties:
|
||||||
|
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
|
||||||
|
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
|
||||||
|
# in JAVA_OPTS (example: "-Dsome.property=foo")
|
||||||
|
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
|
||||||
|
# used to calculate a default maximal heap memory based on a containers restriction.
|
||||||
|
# If used in a container without any memory constraints for the container then this
|
||||||
|
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
|
||||||
|
# of the container available memory as set here. The default is `50` which means 50%
|
||||||
|
# of the available memory is used as an upper boundary. You can skip this mechanism by
|
||||||
|
# setting this value to `0` in which case no `-Xmx` option is added.
|
||||||
|
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
|
||||||
|
# is used to calculate a default initial heap memory based on the maximum heap memory.
|
||||||
|
# If used in a container without any memory constraints for the container then this
|
||||||
|
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
|
||||||
|
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
|
||||||
|
# is used as the initial heap size. You can skip this mechanism by setting this value
|
||||||
|
# to `0` in which case no `-Xms` option is added (example: "25")
|
||||||
|
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
|
||||||
|
# This is used to calculate the maximum value of the initial heap memory. If used in
|
||||||
|
# a container without any memory constraints for the container then this option has
|
||||||
|
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
|
||||||
|
# here. The default is 4096MB which means the calculated value of `-Xms` never will
|
||||||
|
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
|
||||||
|
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
|
||||||
|
# when things are happening. This option, if set to true, will set
|
||||||
|
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
|
||||||
|
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
|
||||||
|
# true").
|
||||||
|
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
|
||||||
|
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
|
||||||
|
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
|
||||||
|
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
|
||||||
|
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
|
||||||
|
# (example: "20")
|
||||||
|
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
|
||||||
|
# (example: "40")
|
||||||
|
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
|
||||||
|
# (example: "4")
|
||||||
|
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
|
||||||
|
# previous GC times. (example: "90")
|
||||||
|
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
|
||||||
|
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
|
||||||
|
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
|
||||||
|
# contain the necessary JRE command-line options to specify the required GC, which
|
||||||
|
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
|
||||||
|
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
|
||||||
|
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
|
||||||
|
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
|
||||||
|
# accessed directly. (example: "foo.example.com,bar.example.com")
|
||||||
|
#
|
||||||
|
###
|
||||||
|
FROM registry.access.redhat.com/ubi8/openjdk-21:1.18
|
||||||
|
|
||||||
|
ENV LANGUAGE='en_US:en'
|
||||||
|
|
||||||
|
|
||||||
|
COPY target/lib/* /deployments/lib/
|
||||||
|
COPY target/*-runner.jar /deployments/quarkus-run.jar
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 185
|
||||||
|
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||||
|
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
|
||||||
27
dhfs-parent/dhfs-fuse/src/main/docker/Dockerfile.native
Normal file
27
dhfs-parent/dhfs-fuse/src/main/docker/Dockerfile.native
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
####
|
||||||
|
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
|
||||||
|
#
|
||||||
|
# Before building the container image run:
|
||||||
|
#
|
||||||
|
# ./mvnw package -Dnative
|
||||||
|
#
|
||||||
|
# Then, build the image with:
|
||||||
|
#
|
||||||
|
# docker build -f src/main/docker/Dockerfile.native -t quarkus/server .
|
||||||
|
#
|
||||||
|
# Then run the container using:
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/server
|
||||||
|
#
|
||||||
|
###
|
||||||
|
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9
|
||||||
|
WORKDIR /work/
|
||||||
|
RUN chown 1001 /work \
|
||||||
|
&& chmod "g+rwX" /work \
|
||||||
|
&& chown 1001:root /work
|
||||||
|
COPY --chown=1001:root target/*-runner /work/application
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 1001
|
||||||
|
|
||||||
|
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
####
|
||||||
|
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
|
||||||
|
# It uses a micro base image, tuned for Quarkus native executables.
|
||||||
|
# It reduces the size of the resulting container image.
|
||||||
|
# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image.
|
||||||
|
#
|
||||||
|
# Before building the container image run:
|
||||||
|
#
|
||||||
|
# ./mvnw package -Dnative
|
||||||
|
#
|
||||||
|
# Then, build the image with:
|
||||||
|
#
|
||||||
|
# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/server .
|
||||||
|
#
|
||||||
|
# Then run the container using:
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/server
|
||||||
|
#
|
||||||
|
###
|
||||||
|
FROM quay.io/quarkus/quarkus-micro-image:2.0
|
||||||
|
WORKDIR /work/
|
||||||
|
RUN chown 1001 /work \
|
||||||
|
&& chmod "g+rwX" /work \
|
||||||
|
&& chown 1001:root /work
|
||||||
|
COPY --chown=1001:root target/*-runner /work/application
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 1001
|
||||||
|
|
||||||
|
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
package com.usatiuk.dhfs.fuse;
|
package com.usatiuk.dhfsfuse;
|
||||||
|
|
||||||
import com.google.protobuf.UnsafeByteOperations;
|
import com.google.protobuf.UnsafeByteOperations;
|
||||||
|
import com.kenai.jffi.MemoryIO;
|
||||||
import com.sun.security.auth.module.UnixSystem;
|
import com.sun.security.auth.module.UnixSystem;
|
||||||
import com.usatiuk.dhfs.files.service.DhfsFileService;
|
import com.usatiuk.dhfsfs.service.DhfsFileService;
|
||||||
import com.usatiuk.dhfs.files.service.DirectoryNotEmptyException;
|
import com.usatiuk.dhfsfs.service.DirectoryNotEmptyException;
|
||||||
import com.usatiuk.dhfs.files.service.GetattrRes;
|
import com.usatiuk.dhfsfs.service.GetattrRes;
|
||||||
import com.usatiuk.dhfs.supportlib.UninitializedByteBuffer;
|
|
||||||
import com.usatiuk.kleppmanntree.AlreadyExistsException;
|
import com.usatiuk.kleppmanntree.AlreadyExistsException;
|
||||||
|
import com.usatiuk.objects.JObjectKey;
|
||||||
|
import com.usatiuk.utils.UninitializedByteBuffer;
|
||||||
|
import com.usatiuk.utils.UnsafeAccessor;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.StatusRuntimeException;
|
import io.grpc.StatusRuntimeException;
|
||||||
import io.quarkus.logging.Log;
|
import io.quarkus.logging.Log;
|
||||||
@@ -17,19 +20,22 @@ import jakarta.enterprise.context.ApplicationScoped;
|
|||||||
import jakarta.enterprise.event.Observes;
|
import jakarta.enterprise.event.Observes;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jnr.ffi.Pointer;
|
import jnr.ffi.Pointer;
|
||||||
|
import jnr.ffi.Runtime;
|
||||||
|
import jnr.ffi.Struct;
|
||||||
|
import jnr.ffi.types.off_t;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
import ru.serce.jnrfuse.ErrorCodes;
|
import ru.serce.jnrfuse.ErrorCodes;
|
||||||
import ru.serce.jnrfuse.FuseFillDir;
|
import ru.serce.jnrfuse.FuseFillDir;
|
||||||
import ru.serce.jnrfuse.FuseStubFS;
|
import ru.serce.jnrfuse.FuseStubFS;
|
||||||
import ru.serce.jnrfuse.struct.FileStat;
|
import ru.serce.jnrfuse.struct.*;
|
||||||
import ru.serce.jnrfuse.struct.FuseFileInfo;
|
|
||||||
import ru.serce.jnrfuse.struct.Statvfs;
|
|
||||||
import ru.serce.jnrfuse.struct.Timespec;
|
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import static jnr.posix.FileStat.*;
|
import static jnr.posix.FileStat.*;
|
||||||
|
|
||||||
@@ -37,6 +43,8 @@ import static jnr.posix.FileStat.*;
|
|||||||
public class DhfsFuse extends FuseStubFS {
|
public class DhfsFuse extends FuseStubFS {
|
||||||
private static final int blksize = 1048576;
|
private static final int blksize = 1048576;
|
||||||
private static final int iosize = 1048576;
|
private static final int iosize = 1048576;
|
||||||
|
private final ConcurrentHashMap<Long, JObjectKey> _openHandles = new ConcurrentHashMap<>();
|
||||||
|
private final AtomicLong _fh = new AtomicLong(1);
|
||||||
@ConfigProperty(name = "dhfs.fuse.root")
|
@ConfigProperty(name = "dhfs.fuse.root")
|
||||||
String root;
|
String root;
|
||||||
@ConfigProperty(name = "dhfs.fuse.enabled")
|
@ConfigProperty(name = "dhfs.fuse.enabled")
|
||||||
@@ -46,42 +54,67 @@ public class DhfsFuse extends FuseStubFS {
|
|||||||
@ConfigProperty(name = "dhfs.files.target_chunk_size")
|
@ConfigProperty(name = "dhfs.files.target_chunk_size")
|
||||||
int targetChunkSize;
|
int targetChunkSize;
|
||||||
@Inject
|
@Inject
|
||||||
JnrPtrByteOutputAccessors jnrPtrByteOutputAccessors;
|
|
||||||
@Inject
|
|
||||||
DhfsFileService fileService;
|
DhfsFileService fileService;
|
||||||
|
|
||||||
|
private long allocateHandle(JObjectKey key) {
|
||||||
|
while (true) {
|
||||||
|
var newFh = _fh.getAndIncrement();
|
||||||
|
if (newFh == 0) continue;
|
||||||
|
if (_openHandles.putIfAbsent(newFh, key) == null) {
|
||||||
|
return newFh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JObjectKey getFromHandle(long handle) {
|
||||||
|
assert handle != 0;
|
||||||
|
return _openHandles.get(handle);
|
||||||
|
}
|
||||||
|
|
||||||
void init(@Observes @Priority(100000) StartupEvent event) {
|
void init(@Observes @Priority(100000) StartupEvent event) {
|
||||||
if (!enabled) return;
|
if (!enabled) return;
|
||||||
Paths.get(root).toFile().mkdirs();
|
|
||||||
Log.info("Mounting with root " + root);
|
Log.info("Mounting with root " + root);
|
||||||
|
|
||||||
var uid = new UnixSystem().getUid();
|
|
||||||
var gid = new UnixSystem().getGid();
|
|
||||||
|
|
||||||
var opts = new ArrayList<String>();
|
var opts = new ArrayList<String>();
|
||||||
|
|
||||||
// Assuming macFuse
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
if (SystemUtils.IS_OS_MAC) {
|
|
||||||
opts.add("-o");
|
opts.add("-o");
|
||||||
opts.add("iosize=" + iosize);
|
opts.add("auto_cache");
|
||||||
} else if (SystemUtils.IS_OS_LINUX) {
|
opts.add("-o");
|
||||||
// FIXME: There's something else missing: the writes still seem to be 32k max
|
opts.add("uid=-1");
|
||||||
|
opts.add("-o");
|
||||||
|
opts.add("gid=-1");
|
||||||
|
} else {
|
||||||
|
Paths.get(root).toFile().mkdirs();
|
||||||
|
|
||||||
|
if (!Paths.get(root).toFile().isDirectory())
|
||||||
|
throw new IllegalStateException("Could not create directory " + root);
|
||||||
|
|
||||||
|
var uid = new UnixSystem().getUid();
|
||||||
|
var gid = new UnixSystem().getGid();
|
||||||
|
|
||||||
|
// Assuming macFuse
|
||||||
|
if (SystemUtils.IS_OS_MAC) {
|
||||||
|
opts.add("-o");
|
||||||
|
opts.add("iosize=" + iosize);
|
||||||
|
} else if (SystemUtils.IS_OS_LINUX) {
|
||||||
|
// FIXME: There's something else missing: the writes still seem to be 32k max
|
||||||
// opts.add("-o");
|
// opts.add("-o");
|
||||||
// opts.add("large_read");
|
// opts.add("large_read");
|
||||||
|
opts.add("-o");
|
||||||
|
opts.add("big_writes");
|
||||||
|
opts.add("-o");
|
||||||
|
opts.add("max_read=" + iosize);
|
||||||
|
opts.add("-o");
|
||||||
|
opts.add("max_write=" + iosize);
|
||||||
|
}
|
||||||
opts.add("-o");
|
opts.add("-o");
|
||||||
opts.add("big_writes");
|
opts.add("auto_cache");
|
||||||
opts.add("-o");
|
opts.add("-o");
|
||||||
opts.add("max_read=" + iosize);
|
opts.add("uid=" + uid);
|
||||||
opts.add("-o");
|
opts.add("-o");
|
||||||
opts.add("max_write=" + iosize);
|
opts.add("gid=" + gid);
|
||||||
}
|
}
|
||||||
opts.add("-o");
|
|
||||||
opts.add("auto_cache");
|
|
||||||
opts.add("-o");
|
|
||||||
opts.add("uid=" + uid);
|
|
||||||
opts.add("-o");
|
|
||||||
opts.add("gid=" + gid);
|
|
||||||
|
|
||||||
mount(Paths.get(root), false, debug, opts.toArray(String[]::new));
|
mount(Paths.get(root), false, debug, opts.toArray(String[]::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +207,9 @@ public class DhfsFuse extends FuseStubFS {
|
|||||||
@Override
|
@Override
|
||||||
public int open(String path, FuseFileInfo fi) {
|
public int open(String path, FuseFileInfo fi) {
|
||||||
try {
|
try {
|
||||||
if (fileService.open(path).isEmpty()) return -ErrorCodes.ENOENT();
|
var opened = fileService.open(path);
|
||||||
|
if (opened.isEmpty()) return -ErrorCodes.ENOENT();
|
||||||
|
fi.fh.set(allocateHandle(opened.get()));
|
||||||
return 0;
|
return 0;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.error("When open " + path, e);
|
Log.error("When open " + path, e);
|
||||||
@@ -182,18 +217,23 @@ public class DhfsFuse extends FuseStubFS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int release(String path, FuseFileInfo fi) {
|
||||||
|
assert fi.fh.get() != 0;
|
||||||
|
_openHandles.remove(fi.fh.get());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(String path, Pointer buf, long size, long offset, FuseFileInfo fi) {
|
public int read(String path, Pointer buf, long size, long offset, FuseFileInfo fi) {
|
||||||
if (size < 0) return -ErrorCodes.EINVAL();
|
if (size < 0) return -ErrorCodes.EINVAL();
|
||||||
if (offset < 0) return -ErrorCodes.EINVAL();
|
if (offset < 0) return -ErrorCodes.EINVAL();
|
||||||
try {
|
try {
|
||||||
var fileOpt = fileService.open(path);
|
var fileKey = getFromHandle(fi.fh.get());
|
||||||
if (fileOpt.isEmpty()) return -ErrorCodes.ENOENT();
|
var read = fileService.read(fileKey, offset, (int) size);
|
||||||
var file = fileOpt.get();
|
|
||||||
var read = fileService.read(fileOpt.get(), offset, (int) size);
|
|
||||||
if (read.isEmpty()) return 0;
|
if (read.isEmpty()) return 0;
|
||||||
UnsafeByteOperations.unsafeWriteTo(read.get(), new JnrPtrByteOutput(jnrPtrByteOutputAccessors, buf, size));
|
UnsafeByteOperations.unsafeWriteTo(read, new JnrPtrByteOutput(buf, size));
|
||||||
return read.get().size();
|
return read.size();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.error("When reading " + path, e);
|
Log.error("When reading " + path, e);
|
||||||
return -ErrorCodes.EIO();
|
return -ErrorCodes.EIO();
|
||||||
@@ -202,25 +242,22 @@ public class DhfsFuse extends FuseStubFS {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int write(String path, Pointer buf, long size, long offset, FuseFileInfo fi) {
|
public int write(String path, Pointer buf, long size, long offset, FuseFileInfo fi) {
|
||||||
|
var buffer = UninitializedByteBuffer.allocate((int) size);
|
||||||
|
UnsafeAccessor.UNSAFE.copyMemory(
|
||||||
|
buf.address(),
|
||||||
|
UnsafeAccessor.NIO.getBufferAddress(buffer),
|
||||||
|
size
|
||||||
|
);
|
||||||
|
return write(path, buffer, offset, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int write(String path, ByteBuffer buffer, long offset, FuseFileInfo fi) {
|
||||||
if (offset < 0) return -ErrorCodes.EINVAL();
|
if (offset < 0) return -ErrorCodes.EINVAL();
|
||||||
try {
|
try {
|
||||||
var fileOpt = fileService.open(path);
|
var fileKey = getFromHandle(fi.fh.get());
|
||||||
if (fileOpt.isEmpty()) return -ErrorCodes.ENOENT();
|
var written = fileService.write(fileKey, offset, UnsafeByteOperations.unsafeWrap(buffer));
|
||||||
var buffer = UninitializedByteBuffer.allocateUninitialized((int) size);
|
|
||||||
|
|
||||||
if (buffer.isDirect()) {
|
|
||||||
jnrPtrByteOutputAccessors.getUnsafe().copyMemory(
|
|
||||||
buf.address(),
|
|
||||||
jnrPtrByteOutputAccessors.getNioAccess().getBufferAddress(buffer),
|
|
||||||
size
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
buf.get(0, buffer.array(), 0, (int) size);
|
|
||||||
}
|
|
||||||
|
|
||||||
var written = fileService.write(fileOpt.get(), offset, UnsafeByteOperations.unsafeWrap(buffer));
|
|
||||||
return written.intValue();
|
return written.intValue();
|
||||||
} catch (Throwable e) {
|
} catch (Exception e) {
|
||||||
Log.error("When writing " + path, e);
|
Log.error("When writing " + path, e);
|
||||||
return -ErrorCodes.EIO();
|
return -ErrorCodes.EIO();
|
||||||
}
|
}
|
||||||
@@ -231,7 +268,8 @@ public class DhfsFuse extends FuseStubFS {
|
|||||||
try {
|
try {
|
||||||
var ret = fileService.create(path, mode);
|
var ret = fileService.create(path, mode);
|
||||||
if (ret.isEmpty()) return -ErrorCodes.ENOSPC();
|
if (ret.isEmpty()) return -ErrorCodes.ENOSPC();
|
||||||
else return 0;
|
fi.fh.set(allocateHandle(ret.get()));
|
||||||
|
return 0;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.error("When creating " + path, e);
|
Log.error("When creating " + path, e);
|
||||||
return -ErrorCodes.EIO();
|
return -ErrorCodes.EIO();
|
||||||
@@ -355,7 +393,7 @@ public class DhfsFuse extends FuseStubFS {
|
|||||||
var file = fileOpt.get();
|
var file = fileOpt.get();
|
||||||
var read = fileService.readlinkBS(fileOpt.get());
|
var read = fileService.readlinkBS(fileOpt.get());
|
||||||
if (read.isEmpty()) return 0;
|
if (read.isEmpty()) return 0;
|
||||||
UnsafeByteOperations.unsafeWriteTo(read, new JnrPtrByteOutput(jnrPtrByteOutputAccessors, buf, size));
|
UnsafeByteOperations.unsafeWriteTo(read, new JnrPtrByteOutput(buf, size));
|
||||||
buf.putByte(Math.min(size - 1, read.size()), (byte) 0);
|
buf.putByte(Math.min(size - 1, read.size()), (byte) 0);
|
||||||
return 0;
|
return 0;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
@@ -387,4 +425,29 @@ public class DhfsFuse extends FuseStubFS {
|
|||||||
return -ErrorCodes.EIO();
|
return -ErrorCodes.EIO();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int write_buf(String path, FuseBufvec buf, @off_t long off, FuseFileInfo fi) {
|
||||||
|
int size = (int) libFuse.fuse_buf_size(buf);
|
||||||
|
FuseBufvec tmpVec = new FuseBufvec(Runtime.getSystemRuntime());
|
||||||
|
long tmpVecAddr = MemoryIO.getInstance().allocateMemory(Struct.size(tmpVec), false);
|
||||||
|
try {
|
||||||
|
tmpVec.useMemory(Pointer.wrap(Runtime.getSystemRuntime(), tmpVecAddr));
|
||||||
|
FuseBufvec.init(tmpVec, size);
|
||||||
|
var bb = UninitializedByteBuffer.allocate(size);
|
||||||
|
var mem = UninitializedByteBuffer.getAddress(bb);
|
||||||
|
tmpVec.buf.mem.set(mem);
|
||||||
|
tmpVec.buf.size.set(size);
|
||||||
|
int res = (int) libFuse.fuse_buf_copy(tmpVec, buf, 0);
|
||||||
|
if (res != size) {
|
||||||
|
Log.errorv("fuse_buf_copy failed: {0} != {1}", res, size);
|
||||||
|
return -ErrorCodes.ENOMEM();
|
||||||
|
}
|
||||||
|
return write(path, bb, off, fi);
|
||||||
|
} finally {
|
||||||
|
if (tmpVecAddr != 0) {
|
||||||
|
MemoryIO.getInstance().freeMemory(tmpVecAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.usatiuk.dhfs.fuse;
|
package com.usatiuk.dhfsfuse;
|
||||||
|
|
||||||
import com.google.protobuf.ByteOutput;
|
import com.google.protobuf.ByteOutput;
|
||||||
|
import com.usatiuk.utils.UnsafeAccessor;
|
||||||
import jnr.ffi.Pointer;
|
import jnr.ffi.Pointer;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@@ -9,14 +10,12 @@ import java.nio.MappedByteBuffer;
|
|||||||
public class JnrPtrByteOutput extends ByteOutput {
|
public class JnrPtrByteOutput extends ByteOutput {
|
||||||
private final Pointer _backing;
|
private final Pointer _backing;
|
||||||
private final long _size;
|
private final long _size;
|
||||||
private final JnrPtrByteOutputAccessors _accessors;
|
|
||||||
private long _pos;
|
private long _pos;
|
||||||
|
|
||||||
public JnrPtrByteOutput(JnrPtrByteOutputAccessors accessors, Pointer backing, long size) {
|
public JnrPtrByteOutput(Pointer backing, long size) {
|
||||||
_backing = backing;
|
_backing = backing;
|
||||||
_size = size;
|
_size = size;
|
||||||
_pos = 0;
|
_pos = 0;
|
||||||
_accessors = accessors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -47,9 +46,9 @@ public class JnrPtrByteOutput extends ByteOutput {
|
|||||||
if (value instanceof MappedByteBuffer mb) {
|
if (value instanceof MappedByteBuffer mb) {
|
||||||
mb.load();
|
mb.load();
|
||||||
}
|
}
|
||||||
long addr = _accessors.getNioAccess().getBufferAddress(value) + value.position();
|
long addr = UnsafeAccessor.NIO.getBufferAddress(value) + value.position();
|
||||||
var out = _backing.address() + _pos;
|
var out = _backing.address() + _pos;
|
||||||
_accessors.getUnsafe().copyMemory(addr, out, rem);
|
UnsafeAccessor.UNSAFE.copyMemory(addr, out, rem);
|
||||||
} else {
|
} else {
|
||||||
_backing.put(_pos, value.array(), value.arrayOffset() + value.position(), rem);
|
_backing.put(_pos, value.array(), value.arrayOffset() + value.position(), rem);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs;
|
package com.usatiuk.dhfsfuse;
|
||||||
|
|
||||||
import io.quarkus.runtime.Quarkus;
|
import io.quarkus.runtime.Quarkus;
|
||||||
import io.quarkus.runtime.QuarkusApplication;
|
import io.quarkus.runtime.QuarkusApplication;
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
quarkus.grpc.server.use-separate-server=false
|
||||||
|
dhfs.objects.peerdiscovery.port=42069
|
||||||
|
dhfs.objects.peerdiscovery.interval=4s
|
||||||
|
dhfs.objects.peerdiscovery.broadcast=true
|
||||||
|
dhfs.objects.sync.timeout=30
|
||||||
|
dhfs.objects.sync.ping.timeout=5
|
||||||
|
dhfs.objects.invalidation.threads=16
|
||||||
|
dhfs.objects.invalidation.delay=1000
|
||||||
|
dhfs.objects.reconnect_interval=5s
|
||||||
|
dhfs.objects.write_log=false
|
||||||
|
dhfs.objects.periodic-push-op-interval=5m
|
||||||
|
dhfs.fuse.root=${HOME}/dhfs_default/fuse
|
||||||
|
dhfs.objects.persistence.stuff.root=${HOME}/dhfs_default/data/stuff
|
||||||
|
dhfs.fuse.debug=false
|
||||||
|
dhfs.fuse.enabled=true
|
||||||
|
dhfs.files.allow_recursive_delete=false
|
||||||
|
dhfs.files.target_chunk_size=524288
|
||||||
|
dhfs.files.max_chunk_size=524288
|
||||||
|
dhfs.files.target_chunk_alignment=17
|
||||||
|
dhfs.objects.deletion.delay=1000
|
||||||
|
dhfs.objects.deletion.can-delete-retry-delay=10000
|
||||||
|
dhfs.objects.ref_verification=true
|
||||||
|
dhfs.files.use_hash_for_chunks=false
|
||||||
|
dhfs.objects.autosync.threads=16
|
||||||
|
dhfs.objects.autosync.download-all=false
|
||||||
|
dhfs.objects.move-processor.threads=16
|
||||||
|
dhfs.objects.ref-processor.threads=16
|
||||||
|
dhfs.objects.opsender.batch-size=100
|
||||||
|
dhfs.objects.lock_timeout_secs=2
|
||||||
|
dhfs.local-discovery=true
|
||||||
|
dhfs.peerdiscovery.timeout=10000
|
||||||
|
quarkus.log.category."com.usatiuk".min-level=TRACE
|
||||||
|
quarkus.log.category."com.usatiuk".level=TRACE
|
||||||
|
quarkus.http.insecure-requests=enabled
|
||||||
|
quarkus.http.ssl.client-auth=required
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.usatiuk.dhfs.fuse;
|
package com.usatiuk.dhfsfuse;
|
||||||
|
|
||||||
import com.usatiuk.dhfs.TempDataProfile;
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
import io.quarkus.test.junit.TestProfile;
|
import io.quarkus.test.junit.TestProfile;
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.usatiuk.dhfsfuse;
|
||||||
|
|
||||||
|
import io.quarkus.test.junit.QuarkusTestProfile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
abstract public class TempDataProfile implements QuarkusTestProfile {
|
||||||
|
protected void getConfigOverrides(Map<String, String> toPut) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final public Map<String, String> getConfigOverrides() {
|
||||||
|
Path tempDirWithPrefix;
|
||||||
|
try {
|
||||||
|
tempDirWithPrefix = Files.createTempDirectory("dhfs-test");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
var ret = new HashMap<String, String>();
|
||||||
|
ret.put("dhfs.objects.persistence.files.root", tempDirWithPrefix.resolve("dhfs_root_test").toString());
|
||||||
|
ret.put("dhfs.fuse.root", tempDirWithPrefix.resolve("dhfs_fuse_root_test").toString());
|
||||||
|
getConfigOverrides(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.usatiuk.dhfsfuse;
|
||||||
|
|
||||||
|
import io.quarkus.logging.Log;
|
||||||
|
import io.quarkus.runtime.ShutdownEvent;
|
||||||
|
import io.quarkus.runtime.StartupEvent;
|
||||||
|
import jakarta.annotation.Priority;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.enterprise.event.Observes;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
public class TestDataCleaner {
|
||||||
|
@ConfigProperty(name = "dhfs.objects.persistence.files.root")
|
||||||
|
String tempDirectory;
|
||||||
|
|
||||||
|
void init(@Observes @Priority(1) StartupEvent event) throws IOException {
|
||||||
|
try {
|
||||||
|
purgeDirectory(Path.of(tempDirectory).toFile());
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
Log.warn("Couldn't cleanup test data on init");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown(@Observes @Priority(1000000000) ShutdownEvent event) throws IOException {
|
||||||
|
purgeDirectory(Path.of(tempDirectory).toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void purgeDirectory(File dir) {
|
||||||
|
for (File file : Objects.requireNonNull(dir.listFiles())) {
|
||||||
|
if (file.isDirectory())
|
||||||
|
purgeDirectory(file);
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs.integration;
|
package com.usatiuk.dhfsfuse.integration;
|
||||||
|
|
||||||
import com.github.dockerjava.api.model.Device;
|
import com.github.dockerjava.api.model.Device;
|
||||||
import io.quarkus.logging.Log;
|
import io.quarkus.logging.Log;
|
||||||
@@ -32,9 +32,11 @@ public class DhfsFuseIT {
|
|||||||
String c1uuid;
|
String c1uuid;
|
||||||
String c2uuid;
|
String c2uuid;
|
||||||
|
|
||||||
|
Network network;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup(TestInfo testInfo) throws IOException, InterruptedException, TimeoutException {
|
void setup(TestInfo testInfo) throws IOException, InterruptedException, TimeoutException {
|
||||||
Network network = Network.newNetwork();
|
network = Network.newNetwork();
|
||||||
container1 = new GenericContainer<>(DhfsImage.getInstance())
|
container1 = new GenericContainer<>(DhfsImage.getInstance())
|
||||||
.withPrivilegedMode(true)
|
.withPrivilegedMode(true)
|
||||||
.withCreateContainerCmdModifier(cmd -> Objects.requireNonNull(cmd.getHostConfig()).withDevices(Device.parse("/dev/fuse")))
|
.withCreateContainerCmdModifier(cmd -> Objects.requireNonNull(cmd.getHostConfig()).withDevices(Device.parse("/dev/fuse")))
|
||||||
@@ -53,8 +55,8 @@ public class DhfsFuseIT {
|
|||||||
var loggingConsumer2 = new Slf4jLogConsumer(LoggerFactory.getLogger(DhfsFuseIT.class)).withPrefix("2-" + testInfo.getDisplayName());
|
var loggingConsumer2 = new Slf4jLogConsumer(LoggerFactory.getLogger(DhfsFuseIT.class)).withPrefix("2-" + testInfo.getDisplayName());
|
||||||
container2.followOutput(loggingConsumer2.andThen(waitingConsumer2));
|
container2.followOutput(loggingConsumer2.andThen(waitingConsumer2));
|
||||||
|
|
||||||
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/data/stuff/self_uuid").getStdout();
|
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/data/stuff/self_uuid").getStdout();
|
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
|
|
||||||
Assertions.assertDoesNotThrow(() -> UUID.fromString(c1uuid));
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c1uuid));
|
||||||
Assertions.assertDoesNotThrow(() -> UUID.fromString(c2uuid));
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c2uuid));
|
||||||
@@ -65,131 +67,119 @@ public class DhfsFuseIT {
|
|||||||
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c2uuid);
|
||||||
|
|
||||||
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c1uuid);
|
||||||
|
|
||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkConsistency() {
|
||||||
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
|
Log.info("Listing consistency");
|
||||||
|
var cat1 = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*/*");
|
||||||
|
var cat2 = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*/*");
|
||||||
|
var ls1 = container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/*/*");
|
||||||
|
var ls2 = container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/*/*");
|
||||||
|
Log.info(ls1);
|
||||||
|
Log.info(cat1);
|
||||||
|
Log.info(ls2);
|
||||||
|
Log.info(cat2);
|
||||||
|
|
||||||
|
return ls1.equals(ls2) && cat1.equals(cat2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void stop() {
|
void stop() {
|
||||||
Stream.of(container1, container2).parallel().forEach(GenericContainer::stop);
|
Stream.of(container1, container2).parallel().forEach(GenericContainer::stop);
|
||||||
|
network.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void readWriteFileTest() throws IOException, InterruptedException, TimeoutException {
|
void readWriteFileTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
"tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void readWriteRewriteFileTest() throws IOException, InterruptedException, TimeoutException {
|
void readWriteRewriteFileTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
"tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo rewritten > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo rewritten > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"rewritten\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
"rewritten\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createDelayedTest() throws IOException, InterruptedException, TimeoutException {
|
void createDelayedTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
"tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
"tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
|
|
||||||
var client = DockerClientFactory.instance().client();
|
var client = DockerClientFactory.instance().client();
|
||||||
client.pauseContainerCmd(container2.getContainerId()).exec();
|
client.pauseContainerCmd(container2.getContainerId()).exec();
|
||||||
|
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo newfile > /root/dhfs_default/fuse/testf2").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo newfile > /dhfs_test/fuse/testf2").getExitCode());
|
||||||
|
|
||||||
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
||||||
|
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"newfile\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf2").getStdout()));
|
"newfile\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf2").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"newfile\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf2").getStdout()));
|
"newfile\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf2").getStdout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void writeRewriteDelayedTest() throws IOException, InterruptedException, TimeoutException {
|
void writeRewriteDelayedTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
"tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
"tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
|
|
||||||
var client = DockerClientFactory.instance().client();
|
var client = DockerClientFactory.instance().client();
|
||||||
client.pauseContainerCmd(container2.getContainerId()).exec();
|
client.pauseContainerCmd(container2.getContainerId()).exec();
|
||||||
|
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo rewritten > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo rewritten > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
|
|
||||||
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
||||||
|
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"rewritten\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
"rewritten\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"rewritten\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
"rewritten\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: How this fits with the tree?
|
|
||||||
@Test
|
|
||||||
@Disabled
|
|
||||||
void deleteDelayedTest() throws IOException, InterruptedException, TimeoutException {
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
|
||||||
|
|
||||||
var client = DockerClientFactory.instance().client();
|
|
||||||
client.pauseContainerCmd(container2.getContainerId()).exec();
|
|
||||||
|
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "rm /root/dhfs_default/fuse/testf1").getExitCode());
|
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Delaying deletion check"), 60, TimeUnit.SECONDS, 1);
|
|
||||||
|
|
||||||
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
|
||||||
|
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse").getExitCode());
|
|
||||||
|
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Deleting from persistent"), 60, TimeUnit.SECONDS, 1);
|
|
||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Deleting from persistent"), 60, TimeUnit.SECONDS, 3);
|
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 1 == container2.execInContainer("/bin/sh", "-c", "test -f /root/dhfs_default/fuse/testf1").getExitCode());
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 1 == container1.execInContainer("/bin/sh", "-c", "test -f /root/dhfs_default/fuse/testf1").getExitCode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void deleteTest() throws IOException, InterruptedException, TimeoutException {
|
void deleteTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
"tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
"tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
"tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
|
|
||||||
Log.info("Deleting");
|
Log.info("Deleting");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "rm /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "rm /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
0 == container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse").getExitCode());
|
0 == container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse").getExitCode());
|
||||||
Log.info("Deleted");
|
Log.info("Deleted");
|
||||||
|
|
||||||
// FIXME?
|
// FIXME?
|
||||||
@@ -197,83 +187,105 @@ public class DhfsFuseIT {
|
|||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Deleting from persistent"), 60, TimeUnit.SECONDS, 3);
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Deleting from persistent"), 60, TimeUnit.SECONDS, 3);
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
1 == container2.execInContainer("/bin/sh", "-c", "test -f /root/dhfs_default/fuse/testf1").getExitCode());
|
1 == container2.execInContainer("/bin/sh", "-c", "test -f /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
1 == container1.execInContainer("/bin/sh", "-c", "test -f /root/dhfs_default/fuse/testf1").getExitCode());
|
1 == container1.execInContainer("/bin/sh", "-c", "test -f /dhfs_test/fuse/testf1").getExitCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteTestKickedOut() throws IOException, InterruptedException, TimeoutException {
|
||||||
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
|
"tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
|
"tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
|
|
||||||
|
container2.stop();
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("kicked"), 60, TimeUnit.SECONDS, 1);
|
||||||
|
|
||||||
|
Log.info("Deleting");
|
||||||
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "rm /dhfs_test/fuse/testf1").getExitCode());
|
||||||
|
Log.info("Deleted");
|
||||||
|
|
||||||
|
// FIXME?
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Deleting from persistent"), 60, TimeUnit.SECONDS, 3);
|
||||||
|
|
||||||
|
await().atMost(45, TimeUnit.SECONDS).until(() ->
|
||||||
|
1 == container1.execInContainer("/bin/sh", "-c", "test -f /dhfs_test/fuse/testf1").getExitCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void moveFileTest() throws IOException, InterruptedException, TimeoutException {
|
void moveFileTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
Log.info("Creating");
|
Log.info("Creating");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
Log.info("Listing");
|
Log.info("Listing");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/").getExitCode());
|
||||||
Log.info("Moving");
|
Log.info("Moving");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "mv /root/dhfs_default/fuse/testf1 /root/dhfs_default/fuse/testf2").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "mv /dhfs_test/fuse/testf1 /dhfs_test/fuse/testf2").getExitCode());
|
||||||
Log.info("Listing");
|
Log.info("Listing");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/").getExitCode());
|
||||||
Log.info("Reading");
|
Log.info("Reading");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf2").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf2").getStdout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void moveDirTest() throws IOException, InterruptedException, TimeoutException {
|
void moveDirTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
Log.info("Creating");
|
Log.info("Creating");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mkdir /root/dhfs_default/fuse/testdir").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mkdir /dhfs_test/fuse/testdir").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testdir/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testdir/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testdir/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testdir/testf1").getStdout()));
|
||||||
Log.info("Listing");
|
Log.info("Listing");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/").getExitCode());
|
||||||
Log.info("Moving");
|
Log.info("Moving");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "mkdir /root/dhfs_default/fuse/testdir2").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "mkdir /dhfs_test/fuse/testdir2").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "mv /root/dhfs_default/fuse/testdir /root/dhfs_default/fuse/testdir2/testdirm").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "mv /dhfs_test/fuse/testdir /dhfs_test/fuse/testdir2/testdirm").getExitCode());
|
||||||
Log.info("Listing");
|
Log.info("Listing");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/").getExitCode());
|
||||||
Log.info("Reading");
|
Log.info("Reading");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testdir2/testdirm/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testdir2/testdirm/testf1").getStdout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: This probably shouldn't be working right now
|
// TODO: This probably shouldn't be working right now
|
||||||
@Test
|
@Test
|
||||||
void removeAddHostTest() throws IOException, InterruptedException, TimeoutException {
|
void removeAddHostTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
|
|
||||||
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request DELETE " +
|
" --request DELETE " +
|
||||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c1uuid);
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo rewritten > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo rewritten > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo jioadsd > /root/dhfs_default/fuse/newfile1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo jioadsd > /dhfs_test/fuse/newfile1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo asvdkljm > /root/dhfs_default/fuse/newfile1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo asvdkljm > /dhfs_test/fuse/newfile1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
|
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo dfgvh > /root/dhfs_default/fuse/newfile2").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo dfgvh > /dhfs_test/fuse/newfile2").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo dscfg > /root/dhfs_default/fuse/newfile2").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo dscfg > /dhfs_test/fuse/newfile2").getExitCode());
|
||||||
|
|
||||||
Log.info("Re-adding");
|
Log.info("Re-adding");
|
||||||
container2.execInContainer("/bin/sh", "-c",
|
container2.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c1uuid);
|
||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "rewritten\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "rewritten\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "rewritten\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "rewritten\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
Log.info("Listing removeAddHostTest");
|
Log.info("Listing removeAddHostTest");
|
||||||
var cat1 = container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*");
|
var cat1 = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
var cat2 = container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*");
|
var cat2 = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
var ls1 = container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/");
|
var ls1 = container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/");
|
||||||
var ls2 = container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/");
|
var ls2 = container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/");
|
||||||
Log.info(cat1);
|
Log.info(cat1);
|
||||||
Log.info(cat2);
|
Log.info(cat2);
|
||||||
Log.info(ls1);
|
Log.info(ls1);
|
||||||
@@ -286,10 +298,10 @@ public class DhfsFuseIT {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void dirConflictTest() throws IOException, InterruptedException, TimeoutException {
|
void dirConflictTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse").getExitCode());
|
||||||
boolean createFail = Stream.of(Pair.of(container1, "echo test1 >> /root/dhfs_default/fuse/testf"),
|
boolean createFail = Stream.of(Pair.of(container1, "echo test1 >> /dhfs_test/fuse/testf"),
|
||||||
Pair.of(container2, "echo test2 >> /root/dhfs_default/fuse/testf")).parallel().map(p -> {
|
Pair.of(container2, "echo test2 >> /dhfs_test/fuse/testf")).parallel().map(p -> {
|
||||||
try {
|
try {
|
||||||
return p.getLeft().execInContainer("/bin/sh", "-c", p.getRight()).getExitCode();
|
return p.getLeft().execInContainer("/bin/sh", "-c", p.getRight()).getExitCode();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -298,48 +310,75 @@ public class DhfsFuseIT {
|
|||||||
}).anyMatch(r -> r != 0);
|
}).anyMatch(r -> r != 0);
|
||||||
Assumptions.assumeTrue(!createFail, "Failed creating one or more files");
|
Assumptions.assumeTrue(!createFail, "Failed creating one or more files");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
var ls = container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse");
|
var ls = container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
var cat = container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*");
|
var cat = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
Log.info(ls);
|
Log.info(ls);
|
||||||
Log.info(cat);
|
Log.info(cat);
|
||||||
return cat.getStdout().contains("test1") && cat.getStdout().contains("test2");
|
return cat.getStdout().contains("test1") && cat.getStdout().contains("test2");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void dirConflictTest2() throws IOException, InterruptedException, TimeoutException {
|
||||||
|
var client = DockerClientFactory.instance().client();
|
||||||
|
client.disconnectFromNetworkCmd().withContainerId(container1.getContainerId()).withNetworkId(network.getId()).exec();
|
||||||
|
client.disconnectFromNetworkCmd().withContainerId(container2.getContainerId()).withNetworkId(network.getId()).exec();
|
||||||
|
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 1);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 1);
|
||||||
|
|
||||||
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "mkdir /dhfs_test/fuse/a && echo fdsaio >> /dhfs_test/fuse/a/testf").getExitCode());
|
||||||
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mkdir /dhfs_test/fuse/a && echo exgrg >> /dhfs_test/fuse/a/testf").getExitCode());
|
||||||
|
|
||||||
|
client.connectToNetworkCmd().withContainerId(container1.getContainerId()).withNetworkId(network.getId()).exec();
|
||||||
|
client.connectToNetworkCmd().withContainerId(container2.getContainerId()).withNetworkId(network.getId()).exec();
|
||||||
|
|
||||||
|
Log.warn("Waiting for connections");
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 1);
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 1);
|
||||||
|
Log.warn("Connected");
|
||||||
|
|
||||||
|
checkConsistency();
|
||||||
|
|
||||||
|
var ls1 = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/a*/*");
|
||||||
|
Assertions.assertTrue(ls1.getStdout().contains("fdsaio"));
|
||||||
|
Assertions.assertTrue(ls1.getStdout().contains("exgrg"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void dirCycleTest() throws IOException, InterruptedException, TimeoutException {
|
void dirCycleTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mkdir /root/dhfs_default/fuse/a").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mkdir /dhfs_test/fuse/a").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mkdir /root/dhfs_default/fuse/b").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mkdir /dhfs_test/fuse/b").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo xqr489 >> /root/dhfs_default/fuse/a/testfa").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo xqr489 >> /dhfs_test/fuse/a/testfa").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo ahinou >> /root/dhfs_default/fuse/b/testfb").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo ahinou >> /dhfs_test/fuse/b/testfb").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "ls -lavh /root/dhfs_default/fuse").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "ls -lavh /dhfs_test/fuse").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
var c2ls = container2.execInContainer("/bin/sh", "-c", "find /root/dhfs_default/fuse -type f -exec cat {} \\;");
|
var c2ls = container2.execInContainer("/bin/sh", "-c", "find /dhfs_test/fuse -type f -exec cat {} \\;");
|
||||||
return c2ls.getExitCode() == 0 && c2ls.getStdout().contains("xqr489") && c2ls.getStdout().contains("ahinou");
|
return c2ls.getExitCode() == 0 && c2ls.getStdout().contains("xqr489") && c2ls.getStdout().contains("ahinou");
|
||||||
});
|
});
|
||||||
|
|
||||||
var client = DockerClientFactory.instance().client();
|
var client = DockerClientFactory.instance().client();
|
||||||
client.pauseContainerCmd(container1.getContainerId()).exec();
|
client.pauseContainerCmd(container1.getContainerId()).exec();
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "mv /root/dhfs_default/fuse/a /root/dhfs_default/fuse/b").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "mv /dhfs_test/fuse/a /dhfs_test/fuse/b").getExitCode());
|
||||||
client.pauseContainerCmd(container2.getContainerId()).exec();
|
client.pauseContainerCmd(container2.getContainerId()).exec();
|
||||||
client.unpauseContainerCmd(container1.getContainerId()).exec();
|
client.unpauseContainerCmd(container1.getContainerId()).exec();
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mv /root/dhfs_default/fuse/b /root/dhfs_default/fuse/a").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mv /dhfs_test/fuse/b /dhfs_test/fuse/a").getExitCode());
|
||||||
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
||||||
|
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
Log.info("Listing dirCycleTest");
|
Log.info("Listing dirCycleTest");
|
||||||
Log.info(container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse"));
|
Log.info(container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse"));
|
||||||
Log.info(container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/a"));
|
Log.info(container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/a"));
|
||||||
Log.info(container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/b"));
|
Log.info(container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/b"));
|
||||||
Log.info(container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse"));
|
Log.info(container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse"));
|
||||||
Log.info(container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/a"));
|
Log.info(container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/a"));
|
||||||
Log.info(container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/b"));
|
Log.info(container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/b"));
|
||||||
|
|
||||||
var c1ls2 = container1.execInContainer("/bin/sh", "-c", "find /root/dhfs_default/fuse -maxdepth 3 -type f -exec cat {} \\;");
|
var c1ls2 = container1.execInContainer("/bin/sh", "-c", "find /dhfs_test/fuse -maxdepth 3 -type f -exec cat {} \\;");
|
||||||
Log.info(c1ls2);
|
Log.info(c1ls2);
|
||||||
var c2ls2 = container1.execInContainer("/bin/sh", "-c", "find /root/dhfs_default/fuse -maxdepth 3 -type f -exec cat {} \\;");
|
var c2ls2 = container1.execInContainer("/bin/sh", "-c", "find /dhfs_test/fuse -maxdepth 3 -type f -exec cat {} \\;");
|
||||||
Log.info(c2ls2);
|
Log.info(c2ls2);
|
||||||
|
|
||||||
return c1ls2.getStdout().contains("xqr489") && c1ls2.getStdout().contains("ahinou")
|
return c1ls2.getStdout().contains("xqr489") && c1ls2.getStdout().contains("ahinou")
|
||||||
@@ -353,27 +392,27 @@ public class DhfsFuseIT {
|
|||||||
void removeAndMove() throws IOException, InterruptedException, TimeoutException {
|
void removeAndMove() throws IOException, InterruptedException, TimeoutException {
|
||||||
var client = DockerClientFactory.instance().client();
|
var client = DockerClientFactory.instance().client();
|
||||||
Log.info("Creating");
|
Log.info("Creating");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
Log.info("Listing");
|
Log.info("Listing");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
|
|
||||||
client.pauseContainerCmd(container1.getContainerId()).exec();
|
client.pauseContainerCmd(container1.getContainerId()).exec();
|
||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 1);
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 1);
|
||||||
|
|
||||||
Log.info("Removing");
|
Log.info("Removing");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "rm /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "rm /dhfs_test/fuse/testf1").getExitCode());
|
||||||
|
|
||||||
client.pauseContainerCmd(container2.getContainerId()).exec();
|
client.pauseContainerCmd(container2.getContainerId()).exec();
|
||||||
client.unpauseContainerCmd(container1.getContainerId()).exec();
|
client.unpauseContainerCmd(container1.getContainerId()).exec();
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 1);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 1);
|
||||||
Log.info("Moving");
|
Log.info("Moving");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mv /root/dhfs_default/fuse/testf1 /root/dhfs_default/fuse/testf2").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mv /dhfs_test/fuse/testf1 /dhfs_test/fuse/testf2").getExitCode());
|
||||||
Log.info("Listing");
|
Log.info("Listing");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/").getExitCode());
|
||||||
Log.info("Reading");
|
Log.info("Reading");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf2").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf2").getStdout()));
|
||||||
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
||||||
|
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 1);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 1);
|
||||||
@@ -382,10 +421,10 @@ public class DhfsFuseIT {
|
|||||||
// TODO: it always seems to be removed?
|
// TODO: it always seems to be removed?
|
||||||
Log.info("Reading both");
|
Log.info("Reading both");
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
var ls1 = container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/");
|
var ls1 = container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/");
|
||||||
var ls2 = container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse/");
|
var ls2 = container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse/");
|
||||||
var cat1 = container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*");
|
var cat1 = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
var cat2 = container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*");
|
var cat2 = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
Log.info("cat1: " + cat1);
|
Log.info("cat1: " + cat1);
|
||||||
Log.info("cat2: " + cat2);
|
Log.info("cat2: " + cat2);
|
||||||
Log.info("ls1: " + ls1);
|
Log.info("ls1: " + ls1);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs.integration;
|
package com.usatiuk.dhfsfuse.integration;
|
||||||
|
|
||||||
import com.github.dockerjava.api.model.Device;
|
import com.github.dockerjava.api.model.Device;
|
||||||
import io.quarkus.logging.Log;
|
import io.quarkus.logging.Log;
|
||||||
@@ -35,13 +35,15 @@ public class DhfsFusex3IT {
|
|||||||
String c2uuid;
|
String c2uuid;
|
||||||
String c3uuid;
|
String c3uuid;
|
||||||
|
|
||||||
|
Network network;
|
||||||
|
|
||||||
// This calculation is somewhat racy, so keep it hardcoded for now
|
// This calculation is somewhat racy, so keep it hardcoded for now
|
||||||
long emptyFileCount = 9;
|
long emptyFileCount = 9;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup(TestInfo testInfo) throws IOException, InterruptedException, TimeoutException {
|
void setup(TestInfo testInfo) throws IOException, InterruptedException, TimeoutException {
|
||||||
// TODO: Dedup
|
// TODO: Dedup
|
||||||
Network network = Network.newNetwork();
|
network = Network.newNetwork();
|
||||||
|
|
||||||
container1 = new GenericContainer<>(DhfsImage.getInstance())
|
container1 = new GenericContainer<>(DhfsImage.getInstance())
|
||||||
.withPrivilegedMode(true)
|
.withPrivilegedMode(true)
|
||||||
@@ -59,9 +61,9 @@ public class DhfsFusex3IT {
|
|||||||
|
|
||||||
Stream.of(container1, container2, container3).parallel().forEach(GenericContainer::start);
|
Stream.of(container1, container2, container3).parallel().forEach(GenericContainer::start);
|
||||||
|
|
||||||
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/data/stuff/self_uuid").getStdout();
|
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/data/stuff/self_uuid").getStdout();
|
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
c3uuid = container3.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/data/stuff/self_uuid").getStdout();
|
c3uuid = container3.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
|
|
||||||
Log.info(container1.getContainerId() + "=" + c1uuid);
|
Log.info(container1.getContainerId() + "=" + c1uuid);
|
||||||
Log.info(container2.getContainerId() + "=" + c2uuid);
|
Log.info(container2.getContainerId() + "=" + c2uuid);
|
||||||
@@ -91,26 +93,26 @@ public class DhfsFusex3IT {
|
|||||||
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c2uuid);
|
||||||
|
|
||||||
var c2curl1 = container2.execInContainer("/bin/sh", "-c",
|
var c2curl1 = container2.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c1uuid);
|
||||||
|
|
||||||
var c2curl3 = container2.execInContainer("/bin/sh", "-c",
|
var c2curl3 = container2.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c3uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c3uuid);
|
||||||
|
|
||||||
var c3curl = container3.execInContainer("/bin/sh", "-c",
|
var c3curl = container3.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c2uuid);
|
||||||
|
|
||||||
waitingConsumer3.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
waitingConsumer3.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
||||||
@@ -119,8 +121,8 @@ public class DhfsFusex3IT {
|
|||||||
|
|
||||||
private boolean checkEmpty() throws IOException, InterruptedException {
|
private boolean checkEmpty() throws IOException, InterruptedException {
|
||||||
for (var container : List.of(container1, container2, container3)) {
|
for (var container : List.of(container1, container2, container3)) {
|
||||||
var found = container.execInContainer("/bin/sh", "-c", "find /root/dhfs_default/data/objs -type f");
|
var found = container.execInContainer("/bin/sh", "-c", "find /dhfs_test/data/objs -type f");
|
||||||
var foundWc = container.execInContainer("/bin/sh", "-c", "find /root/dhfs_default/data/objs -type f | wc -l");
|
var foundWc = container.execInContainer("/bin/sh", "-c", "find /dhfs_test/data/objs -type f | wc -l");
|
||||||
Log.info("Remaining objects in " + container.getContainerId() + ": " + found.toString() + " " + foundWc.toString());
|
Log.info("Remaining objects in " + container.getContainerId() + ": " + found.toString() + " " + foundWc.toString());
|
||||||
if (!(found.getExitCode() == 0 && foundWc.getExitCode() == 0 && Integer.parseInt(foundWc.getStdout().strip()) == emptyFileCount))
|
if (!(found.getExitCode() == 0 && foundWc.getExitCode() == 0 && Integer.parseInt(foundWc.getStdout().strip()) == emptyFileCount))
|
||||||
return false;
|
return false;
|
||||||
@@ -131,51 +133,52 @@ public class DhfsFusex3IT {
|
|||||||
@AfterEach
|
@AfterEach
|
||||||
void stop() {
|
void stop() {
|
||||||
Stream.of(container1, container2, container3).parallel().forEach(GenericContainer::stop);
|
Stream.of(container1, container2, container3).parallel().forEach(GenericContainer::stop);
|
||||||
|
network.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void readWriteFileTest() throws IOException, InterruptedException, TimeoutException {
|
void readWriteFileTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container3.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container3.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME:
|
// FIXME:
|
||||||
@Test
|
@Test
|
||||||
@Disabled
|
@Disabled
|
||||||
void largerFileDeleteTest() throws IOException, InterruptedException, TimeoutException {
|
void largerFileDeleteTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "cd /root/dhfs_default/fuse && dd if=/dev/urandom of=10MB.bin bs=1M count=10").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "cd /dhfs_test/fuse && dd if=/dev/urandom of=10MB.bin bs=1M count=10").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "head -c 10 /root/dhfs_default/fuse/10MB.bin").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "head -c 10 /dhfs_test/fuse/10MB.bin").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container3.execInContainer("/bin/sh", "-c", "rm /root/dhfs_default/fuse/10MB.bin").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container3.execInContainer("/bin/sh", "-c", "rm /dhfs_test/fuse/10MB.bin").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> checkEmpty());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> checkEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Disabled
|
@Disabled
|
||||||
void largerFileDeleteTestNoDelays() throws IOException, InterruptedException, TimeoutException {
|
void largerFileDeleteTestNoDelays() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "cd /root/dhfs_default/fuse && dd if=/dev/urandom of=10MB.bin bs=1M count=10").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "cd /dhfs_test/fuse && dd if=/dev/urandom of=10MB.bin bs=1M count=10").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "head -c 10 /root/dhfs_default/fuse/10MB.bin").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "head -c 10 /dhfs_test/fuse/10MB.bin").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container3.execInContainer("/bin/sh", "-c", "rm /root/dhfs_default/fuse/10MB.bin").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container3.execInContainer("/bin/sh", "-c", "rm /dhfs_test/fuse/10MB.bin").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> checkEmpty());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> checkEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void gccHelloWorldTest() throws IOException, InterruptedException, TimeoutException {
|
void gccHelloWorldTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo '#include<stdio.h>\nint main(){printf(\"hello world\"); return 0;}' > /root/dhfs_default/fuse/hello.c").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo '#include<stdio.h>\nint main(){printf(\"hello world\"); return 0;}' > /dhfs_test/fuse/hello.c").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "cd /root/dhfs_default/fuse && gcc hello.c").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "cd /dhfs_test/fuse && gcc hello.c").getExitCode());
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
var helloOut = container1.execInContainer("/bin/sh", "-c", "/root/dhfs_default/fuse/a.out");
|
var helloOut = container1.execInContainer("/bin/sh", "-c", "/dhfs_test/fuse/a.out");
|
||||||
Log.info(helloOut);
|
Log.info(helloOut);
|
||||||
return helloOut.getExitCode() == 0 && helloOut.getStdout().equals("hello world");
|
return helloOut.getExitCode() == 0 && helloOut.getStdout().equals("hello world");
|
||||||
});
|
});
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
var helloOut = container2.execInContainer("/bin/sh", "-c", "/root/dhfs_default/fuse/a.out");
|
var helloOut = container2.execInContainer("/bin/sh", "-c", "/dhfs_test/fuse/a.out");
|
||||||
Log.info(helloOut);
|
Log.info(helloOut);
|
||||||
return helloOut.getExitCode() == 0 && helloOut.getStdout().equals("hello world");
|
return helloOut.getExitCode() == 0 && helloOut.getStdout().equals("hello world");
|
||||||
});
|
});
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
var helloOut = container3.execInContainer("/bin/sh", "-c", "/root/dhfs_default/fuse/a.out");
|
var helloOut = container3.execInContainer("/bin/sh", "-c", "/dhfs_test/fuse/a.out");
|
||||||
Log.info(helloOut);
|
Log.info(helloOut);
|
||||||
return helloOut.getExitCode() == 0 && helloOut.getStdout().equals("hello world");
|
return helloOut.getExitCode() == 0 && helloOut.getStdout().equals("hello world");
|
||||||
});
|
});
|
||||||
@@ -183,22 +186,22 @@ public class DhfsFusex3IT {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void removeHostTest() throws IOException, InterruptedException, TimeoutException {
|
void removeHostTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container3.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container3.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
|
|
||||||
var c3curl = container3.execInContainer("/bin/sh", "-c",
|
var c3curl = container3.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request DELETE " +
|
" --request DELETE " +
|
||||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c2uuid);
|
||||||
|
|
||||||
Thread.sleep(10000);
|
Thread.sleep(10000);
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo rewritten > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo rewritten > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "rewritten\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "rewritten\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container3.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container3.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -208,15 +211,15 @@ public class DhfsFusex3IT {
|
|||||||
client.pauseContainerCmd(container2.getContainerId()).exec();
|
client.pauseContainerCmd(container2.getContainerId()).exec();
|
||||||
// Pauses needed as otherwise docker buffers some incoming packets
|
// Pauses needed as otherwise docker buffers some incoming packets
|
||||||
waitingConsumer3.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
waitingConsumer3.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container3.execInContainer("/bin/sh", "-c", "echo test3 >> /root/dhfs_default/fuse/testf").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container3.execInContainer("/bin/sh", "-c", "echo test3 >> /dhfs_test/fuse/testf").getExitCode());
|
||||||
client.pauseContainerCmd(container3.getContainerId()).exec();
|
client.pauseContainerCmd(container3.getContainerId()).exec();
|
||||||
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo test2 >> /root/dhfs_default/fuse/testf").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo test2 >> /dhfs_test/fuse/testf").getExitCode());
|
||||||
client.pauseContainerCmd(container2.getContainerId()).exec();
|
client.pauseContainerCmd(container2.getContainerId()).exec();
|
||||||
client.unpauseContainerCmd(container1.getContainerId()).exec();
|
client.unpauseContainerCmd(container1.getContainerId()).exec();
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo test1 >> /root/dhfs_default/fuse/testf").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo test1 >> /dhfs_test/fuse/testf").getExitCode());
|
||||||
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
||||||
client.unpauseContainerCmd(container3.getContainerId()).exec();
|
client.unpauseContainerCmd(container3.getContainerId()).exec();
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
||||||
@@ -225,8 +228,8 @@ public class DhfsFusex3IT {
|
|||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
for (var c : List.of(container1, container2, container3)) {
|
for (var c : List.of(container1, container2, container3)) {
|
||||||
var ls = c.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse");
|
var ls = c.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
var cat = c.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*");
|
var cat = c.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
Log.info(ls);
|
Log.info(ls);
|
||||||
Log.info(cat);
|
Log.info(cat);
|
||||||
if (!(cat.getStdout().contains("test1") && cat.getStdout().contains("test2") && cat.getStdout().contains("test3")))
|
if (!(cat.getStdout().contains("test1") && cat.getStdout().contains("test2") && cat.getStdout().contains("test3")))
|
||||||
@@ -236,37 +239,38 @@ public class DhfsFusex3IT {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
return container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse").getStdout().equals(
|
return container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse").getStdout().equals(
|
||||||
container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse").getStdout()) &&
|
container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse").getStdout()) &&
|
||||||
container3.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse").getStdout().equals(
|
container3.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse").getStdout().equals(
|
||||||
container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse").getStdout()) &&
|
container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse").getStdout()) &&
|
||||||
container3.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*").getStdout().equals(
|
container3.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*").getStdout().equals(
|
||||||
container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*").getStdout());
|
container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*").getStdout());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void fileConflictTest() throws IOException, InterruptedException, TimeoutException {
|
void fileConflictTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container3.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container3.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf").getStdout()));
|
||||||
|
|
||||||
var client = DockerClientFactory.instance().client();
|
var client = DockerClientFactory.instance().client();
|
||||||
client.pauseContainerCmd(container1.getContainerId()).exec();
|
client.disconnectFromNetworkCmd().withContainerId(container1.getContainerId()).withNetworkId(network.getId()).exec();
|
||||||
client.pauseContainerCmd(container2.getContainerId()).exec();
|
client.disconnectFromNetworkCmd().withContainerId(container2.getContainerId()).withNetworkId(network.getId()).exec();
|
||||||
// Pauses needed as otherwise docker buffers some incoming packets
|
client.disconnectFromNetworkCmd().withContainerId(container3.getContainerId()).withNetworkId(network.getId()).exec();
|
||||||
|
|
||||||
waitingConsumer3.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
waitingConsumer3.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container3.execInContainer("/bin/sh", "-c", "echo test3 >> /root/dhfs_default/fuse/testf").getExitCode());
|
|
||||||
client.pauseContainerCmd(container3.getContainerId()).exec();
|
|
||||||
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
|
||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo test2 >> /root/dhfs_default/fuse/testf").getExitCode());
|
|
||||||
client.pauseContainerCmd(container2.getContainerId()).exec();
|
|
||||||
client.unpauseContainerCmd(container1.getContainerId()).exec();
|
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS, 2);
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo test1 >> /root/dhfs_default/fuse/testf").getExitCode());
|
|
||||||
client.unpauseContainerCmd(container2.getContainerId()).exec();
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container3.execInContainer("/bin/sh", "-c", "echo test3 >> /dhfs_test/fuse/testf").getExitCode());
|
||||||
client.unpauseContainerCmd(container3.getContainerId()).exec();
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container2.execInContainer("/bin/sh", "-c", "echo test2 >> /dhfs_test/fuse/testf").getExitCode());
|
||||||
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo test1 >> /dhfs_test/fuse/testf").getExitCode());
|
||||||
|
|
||||||
|
client.connectToNetworkCmd().withContainerId(container1.getContainerId()).withNetworkId(network.getId()).exec();
|
||||||
|
client.connectToNetworkCmd().withContainerId(container2.getContainerId()).withNetworkId(network.getId()).exec();
|
||||||
|
client.connectToNetworkCmd().withContainerId(container3.getContainerId()).withNetworkId(network.getId()).exec();
|
||||||
|
|
||||||
Log.warn("Waiting for connections");
|
Log.warn("Waiting for connections");
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS, 2);
|
||||||
@@ -276,9 +280,9 @@ public class DhfsFusex3IT {
|
|||||||
// TODO: There's some issue with cache, so avoid file reads
|
// TODO: There's some issue with cache, so avoid file reads
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
Log.info("Listing consistency 1");
|
Log.info("Listing consistency 1");
|
||||||
var ls1 = container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse");
|
var ls1 = container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
var ls2 = container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse");
|
var ls2 = container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
var ls3 = container3.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse");
|
var ls3 = container3.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
Log.info(ls1);
|
Log.info(ls1);
|
||||||
Log.info(ls2);
|
Log.info(ls2);
|
||||||
Log.info(ls3);
|
Log.info(ls3);
|
||||||
@@ -290,8 +294,8 @@ public class DhfsFusex3IT {
|
|||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
Log.info("Listing");
|
Log.info("Listing");
|
||||||
for (var c : List.of(container1, container2, container3)) {
|
for (var c : List.of(container1, container2, container3)) {
|
||||||
var ls = c.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse");
|
var ls = c.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
var cat = c.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*");
|
var cat = c.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
Log.info(ls);
|
Log.info(ls);
|
||||||
Log.info(cat);
|
Log.info(cat);
|
||||||
if (!(cat.getExitCode() == 0 && ls.getExitCode() == 0))
|
if (!(cat.getExitCode() == 0 && ls.getExitCode() == 0))
|
||||||
@@ -304,12 +308,12 @@ public class DhfsFusex3IT {
|
|||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
Log.info("Listing consistency");
|
Log.info("Listing consistency");
|
||||||
var ls1 = container1.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse");
|
var ls1 = container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
var cat1 = container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*");
|
var cat1 = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
var ls2 = container2.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse");
|
var ls2 = container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
var cat2 = container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*");
|
var cat2 = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
var ls3 = container3.execInContainer("/bin/sh", "-c", "ls /root/dhfs_default/fuse");
|
var ls3 = container3.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
var cat3 = container3.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/*");
|
var cat3 = container3.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
Log.info(ls1);
|
Log.info(ls1);
|
||||||
Log.info(cat1);
|
Log.info(cat1);
|
||||||
Log.info(ls2);
|
Log.info(ls2);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs.integration;
|
package com.usatiuk.dhfsfuse.integration;
|
||||||
|
|
||||||
import io.quarkus.logging.Log;
|
import io.quarkus.logging.Log;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -66,24 +66,31 @@ public class DhfsImage implements Future<String> {
|
|||||||
.run("apt update && apt install -y libfuse2 curl gcc")
|
.run("apt update && apt install -y libfuse2 curl gcc")
|
||||||
.copy("/app", "/app")
|
.copy("/app", "/app")
|
||||||
.copy("/libs", "/libs")
|
.copy("/libs", "/libs")
|
||||||
.cmd("java", "-ea", "-Xmx128M",
|
.cmd("java", "-ea", "-Xmx256M", "-XX:TieredStopAtLevel=1", "-XX:+UseParallelGC",
|
||||||
"--add-exports", "java.base/sun.nio.ch=ALL-UNNAMED",
|
"--add-exports", "java.base/sun.nio.ch=ALL-UNNAMED",
|
||||||
"--add-exports", "java.base/jdk.internal.access=ALL-UNNAMED",
|
"--add-exports", "java.base/jdk.internal.access=ALL-UNNAMED",
|
||||||
"--add-opens=java.base/java.nio=ALL-UNNAMED",
|
"--add-opens=java.base/java.nio=ALL-UNNAMED",
|
||||||
|
"--enable-preview",
|
||||||
"-Ddhfs.objects.peerdiscovery.interval=1s",
|
"-Ddhfs.objects.peerdiscovery.interval=1s",
|
||||||
"-Ddhfs.objects.invalidation.delay=100",
|
"-Ddhfs.objects.invalidation.delay=100",
|
||||||
"-Ddhfs.objects.deletion.delay=0",
|
"-Ddhfs.objects.deletion.delay=0",
|
||||||
"-Ddhfs.objects.deletion.can-delete-retry-delay=1000",
|
"-Ddhfs.objects.deletion.can-delete-retry-delay=1000",
|
||||||
"-Ddhfs.objects.ref_verification=true",
|
"-Ddhfs.objects.ref_verification=true",
|
||||||
"-Ddhfs.objects.write_log=true",
|
"-Ddhfs.objects.sync.timeout=30",
|
||||||
"-Ddhfs.objects.sync.timeout=10",
|
|
||||||
"-Ddhfs.objects.sync.ping.timeout=5",
|
"-Ddhfs.objects.sync.ping.timeout=5",
|
||||||
"-Ddhfs.objects.reconnect_interval=1s",
|
"-Ddhfs.objects.reconnect_interval=1s",
|
||||||
"-Dcom.usatiuk.dhfs.supportlib.native-path=/libs",
|
"-Ddhfs.objects.last-seen.timeout=30",
|
||||||
|
"-Ddhfs.objects.last-seen.update=10",
|
||||||
|
"-Ddhfs.sync.cert-check=false",
|
||||||
"-Dquarkus.log.category.\"com.usatiuk\".level=TRACE",
|
"-Dquarkus.log.category.\"com.usatiuk\".level=TRACE",
|
||||||
"-Dquarkus.log.category.\"com.usatiuk.dhfs\".level=TRACE",
|
"-Dquarkus.log.category.\"com.usatiuk.dhfs\".level=TRACE",
|
||||||
|
"-Dquarkus.log.category.\"com.usatiuk.objects.transaction\".level=INFO",
|
||||||
"-Ddhfs.objects.periodic-push-op-interval=5s",
|
"-Ddhfs.objects.periodic-push-op-interval=5s",
|
||||||
|
"-Ddhfs.fuse.root=/dhfs_test/fuse",
|
||||||
|
"-Ddhfs.objects.persistence.files.root=/dhfs_test/data",
|
||||||
|
"-Ddhfs.objects.persistence.stuff.root=/dhfs_test/data/stuff",
|
||||||
"-jar", "/app/quarkus-run.jar")
|
"-jar", "/app/quarkus-run.jar")
|
||||||
|
.run("mkdir /dhfs_test && chmod 777 /dhfs_test")
|
||||||
.build())
|
.build())
|
||||||
.withFileFromPath("/app", Paths.get(buildPath, "quarkus-app"))
|
.withFileFromPath("/app", Paths.get(buildPath, "quarkus-app"))
|
||||||
.withFileFromPath("/libs", Paths.get(nativeLibsDirectory));
|
.withFileFromPath("/libs", Paths.get(nativeLibsDirectory));
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
package com.usatiuk.dhfsfuse.integration;
|
||||||
|
|
||||||
|
import com.github.dockerjava.api.model.Device;
|
||||||
|
import com.usatiuk.dhfsfuse.TestDataCleaner;
|
||||||
|
import io.quarkus.logging.Log;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.testcontainers.DockerClientFactory;
|
||||||
|
import org.testcontainers.containers.GenericContainer;
|
||||||
|
import org.testcontainers.containers.Network;
|
||||||
|
import org.testcontainers.containers.output.Slf4jLogConsumer;
|
||||||
|
import org.testcontainers.containers.output.WaitingConsumer;
|
||||||
|
import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
|
|
||||||
|
public class KillIT {
|
||||||
|
GenericContainer<?> container1;
|
||||||
|
GenericContainer<?> container2;
|
||||||
|
|
||||||
|
WaitingConsumer waitingConsumer1;
|
||||||
|
WaitingConsumer waitingConsumer2;
|
||||||
|
|
||||||
|
String c1uuid;
|
||||||
|
String c2uuid;
|
||||||
|
|
||||||
|
File data1;
|
||||||
|
File data2;
|
||||||
|
|
||||||
|
Network network;
|
||||||
|
|
||||||
|
ExecutorService executor;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup(TestInfo testInfo) throws IOException, InterruptedException, TimeoutException {
|
||||||
|
executor = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
|
data1 = Files.createTempDirectory("").toFile();
|
||||||
|
data2 = Files.createTempDirectory("").toFile();
|
||||||
|
|
||||||
|
network = Network.newNetwork();
|
||||||
|
|
||||||
|
container1 = new GenericContainer<>(DhfsImage.getInstance())
|
||||||
|
.withPrivilegedMode(true)
|
||||||
|
.withCreateContainerCmdModifier(cmd -> Objects.requireNonNull(cmd.getHostConfig()).withDevices(Device.parse("/dev/fuse")))
|
||||||
|
.waitingFor(Wait.forLogMessage(".*Listening.*", 1).withStartupTimeout(Duration.ofSeconds(60))).withNetwork(network)
|
||||||
|
.withFileSystemBind(data1.getAbsolutePath(), "/dhfs_test/data");
|
||||||
|
container2 = new GenericContainer<>(DhfsImage.getInstance())
|
||||||
|
.withPrivilegedMode(true)
|
||||||
|
.withCreateContainerCmdModifier(cmd -> Objects.requireNonNull(cmd.getHostConfig()).withDevices(Device.parse("/dev/fuse")))
|
||||||
|
.waitingFor(Wait.forLogMessage(".*Listening.*", 1).withStartupTimeout(Duration.ofSeconds(60))).withNetwork(network)
|
||||||
|
.withFileSystemBind(data2.getAbsolutePath(), "/dhfs_test/data");
|
||||||
|
|
||||||
|
Stream.of(container1, container2).parallel().forEach(GenericContainer::start);
|
||||||
|
|
||||||
|
waitingConsumer1 = new WaitingConsumer();
|
||||||
|
var loggingConsumer1 = new Slf4jLogConsumer(LoggerFactory.getLogger(KillIT.class)).withPrefix("1-" + testInfo.getDisplayName());
|
||||||
|
container1.followOutput(loggingConsumer1.andThen(waitingConsumer1));
|
||||||
|
waitingConsumer2 = new WaitingConsumer();
|
||||||
|
var loggingConsumer2 = new Slf4jLogConsumer(LoggerFactory.getLogger(KillIT.class)).withPrefix("2-" + testInfo.getDisplayName());
|
||||||
|
container2.followOutput(loggingConsumer2.andThen(waitingConsumer2));
|
||||||
|
|
||||||
|
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
|
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
|
|
||||||
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c1uuid));
|
||||||
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c2uuid));
|
||||||
|
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("New address"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("New address"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
||||||
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
|
" --request PUT " +
|
||||||
|
" --data '{}' " +
|
||||||
|
" http://localhost:8080/peers-manage/known-peers/" + c2uuid);
|
||||||
|
|
||||||
|
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
||||||
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
|
" --request PUT " +
|
||||||
|
" --data '{}' " +
|
||||||
|
" http://localhost:8080/peers-manage/known-peers/" + c1uuid);
|
||||||
|
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void stop() {
|
||||||
|
Stream.of(container1, container2).parallel().forEach(GenericContainer::stop);
|
||||||
|
TestDataCleaner.purgeDirectory(data1);
|
||||||
|
TestDataCleaner.purgeDirectory(data2);
|
||||||
|
executor.close();
|
||||||
|
network.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkConsistency() {
|
||||||
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
|
Log.info("Listing consistency");
|
||||||
|
var ls1 = container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
|
var cat1 = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
|
var ls2 = container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
|
var cat2 = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
|
Log.info(ls1);
|
||||||
|
Log.info(cat1);
|
||||||
|
Log.info(ls2);
|
||||||
|
Log.info(cat2);
|
||||||
|
|
||||||
|
return ls1.equals(ls2) && cat1.equals(cat2) && ls1.getExitCode() == 0 && ls2.getExitCode() == 0 && cat1.getExitCode() == 0 && cat2.getExitCode() == 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void killTest(TestInfo testInfo) throws Exception {
|
||||||
|
var barrier = new CyclicBarrier(2);
|
||||||
|
var ret1 = executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier.await();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while true; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/test1; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
barrier.await();
|
||||||
|
Thread.sleep(10000);
|
||||||
|
var client = DockerClientFactory.instance().client();
|
||||||
|
client.killContainerCmd(container1.getContainerId()).exec();
|
||||||
|
container1.stop();
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
container1.start();
|
||||||
|
waitingConsumer1 = new WaitingConsumer();
|
||||||
|
var loggingConsumer1 = new Slf4jLogConsumer(LoggerFactory.getLogger(KillIT.class)).withPrefix("1-" + testInfo.getDisplayName());
|
||||||
|
container1.followOutput(loggingConsumer1.andThen(waitingConsumer1));
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
checkConsistency();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void killTestDirs(TestInfo testInfo) throws Exception {
|
||||||
|
var barrier = new CyclicBarrier(2);
|
||||||
|
var ret1 = executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier.await();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while true; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/test$counter; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
barrier.await();
|
||||||
|
Thread.sleep(10000);
|
||||||
|
var client = DockerClientFactory.instance().client();
|
||||||
|
client.killContainerCmd(container1.getContainerId()).exec();
|
||||||
|
container1.stop();
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
container1.start();
|
||||||
|
waitingConsumer1 = new WaitingConsumer();
|
||||||
|
var loggingConsumer1 = new Slf4jLogConsumer(LoggerFactory.getLogger(KillIT.class)).withPrefix("1-" + testInfo.getDisplayName());
|
||||||
|
container1.followOutput(loggingConsumer1.andThen(waitingConsumer1));
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
checkConsistency();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void killTest2(TestInfo testInfo) throws Exception {
|
||||||
|
var barrier = new CyclicBarrier(2);
|
||||||
|
var ret1 = executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier.await();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while [ ! -f /tmp/stopprinting ]; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/test1; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
barrier.await();
|
||||||
|
Thread.sleep(10000);
|
||||||
|
var client = DockerClientFactory.instance().client();
|
||||||
|
client.killContainerCmd(container2.getContainerId()).exec();
|
||||||
|
container2.stop();
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "touch /tmp/stopprinting");
|
||||||
|
container2.start();
|
||||||
|
waitingConsumer2 = new WaitingConsumer();
|
||||||
|
var loggingConsumer2 = new Slf4jLogConsumer(LoggerFactory.getLogger(KillIT.class)).withPrefix("2-" + testInfo.getDisplayName());
|
||||||
|
container2.followOutput(loggingConsumer2.andThen(waitingConsumer2));
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
checkConsistency();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void killTestDirs2(TestInfo testInfo) throws Exception {
|
||||||
|
var barrier = new CyclicBarrier(2);
|
||||||
|
var ret1 = executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier.await();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while [ ! -f /tmp/stopprinting ]; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/test$counter; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
barrier.await();
|
||||||
|
Thread.sleep(10000);
|
||||||
|
var client = DockerClientFactory.instance().client();
|
||||||
|
client.killContainerCmd(container2.getContainerId()).exec();
|
||||||
|
container2.stop();
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "touch /tmp/stopprinting");
|
||||||
|
container2.start();
|
||||||
|
waitingConsumer2 = new WaitingConsumer();
|
||||||
|
var loggingConsumer2 = new Slf4jLogConsumer(LoggerFactory.getLogger(KillIT.class)).withPrefix("2-" + testInfo.getDisplayName());
|
||||||
|
container2.followOutput(loggingConsumer2.andThen(waitingConsumer2));
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
checkConsistency();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
package com.usatiuk.dhfsfuse.integration;
|
||||||
|
|
||||||
|
import io.quarkus.logging.Log;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class LazyFs {
|
||||||
|
private static final String lazyFsPath;
|
||||||
|
|
||||||
|
static {
|
||||||
|
lazyFsPath = System.getProperty("lazyFsPath");
|
||||||
|
System.out.println("LazyFs Path: " + lazyFsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String mountRoot;
|
||||||
|
private final String dataRoot;
|
||||||
|
private final String name;
|
||||||
|
private final File configFile;
|
||||||
|
private final File fifoFile;
|
||||||
|
private Thread errPiper;
|
||||||
|
private Thread outPiper;
|
||||||
|
private CountDownLatch startLatch;
|
||||||
|
private Process fs;
|
||||||
|
public LazyFs(String name, String mountRoot, String dataRoot) {
|
||||||
|
this.name = name;
|
||||||
|
this.mountRoot = mountRoot;
|
||||||
|
this.dataRoot = dataRoot;
|
||||||
|
|
||||||
|
try {
|
||||||
|
configFile = File.createTempFile("lazyfs", ".conf");
|
||||||
|
configFile.deleteOnExit();
|
||||||
|
|
||||||
|
fifoFile = new File("/tmp/" + ThreadLocalRandom.current().nextLong() + ".faultsfifo");
|
||||||
|
fifoFile.deleteOnExit();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(this::stop));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String fifoPath() {
|
||||||
|
return fifoFile.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(String extraOpts) {
|
||||||
|
var lfsPath = Path.of(lazyFsPath).resolve("build").resolve("lazyfs");
|
||||||
|
if (!lfsPath.toFile().isFile())
|
||||||
|
throw new IllegalStateException("LazyFs binary does not exist: " + lfsPath.toAbsolutePath());
|
||||||
|
if (!lfsPath.toFile().canExecute())
|
||||||
|
throw new IllegalStateException("LazyFs binary is not executable: " + lfsPath.toAbsolutePath());
|
||||||
|
|
||||||
|
try (var rwFile = new RandomAccessFile(configFile, "rw");
|
||||||
|
var channel = rwFile.getChannel()) {
|
||||||
|
channel.truncate(0);
|
||||||
|
var config = "[faults]\n" +
|
||||||
|
"fifo_path=\"" + fifoPath() + "\"\n" +
|
||||||
|
"[cache]\n" +
|
||||||
|
"apply_eviction=false\n" +
|
||||||
|
"[cache.simple]\n" +
|
||||||
|
"custom_size=\"1gb\"\n" +
|
||||||
|
"blocks_per_page=1\n" +
|
||||||
|
"[filesystem]\n" +
|
||||||
|
"log_all_operations=false\n" +
|
||||||
|
"logfile=\"\"\n" + extraOpts;
|
||||||
|
rwFile.write(config.getBytes());
|
||||||
|
Log.info("LazyFs config: \n" + config);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
var argList = new ArrayList<String>();
|
||||||
|
|
||||||
|
argList.add(lfsPath.toString());
|
||||||
|
argList.add(Path.of(mountRoot).toString());
|
||||||
|
argList.add("--config-path");
|
||||||
|
argList.add(configFile.getAbsolutePath());
|
||||||
|
argList.add("-o");
|
||||||
|
argList.add("allow_other");
|
||||||
|
argList.add("-o");
|
||||||
|
argList.add("modules=subdir");
|
||||||
|
argList.add("-o");
|
||||||
|
argList.add("subdir=" + Path.of(dataRoot).toAbsolutePath().toString());
|
||||||
|
try {
|
||||||
|
Log.info("Starting LazyFs " + argList);
|
||||||
|
fs = Runtime.getRuntime().exec(argList.toArray(String[]::new));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
startLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
outPiper = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
try (BufferedReader input = new BufferedReader(new InputStreamReader(fs.getInputStream()))) {
|
||||||
|
String line;
|
||||||
|
|
||||||
|
while ((line = input.readLine()) != null) {
|
||||||
|
if (line.contains("running LazyFS"))
|
||||||
|
startLatch.countDown();
|
||||||
|
System.out.println(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.info("Exception in LazyFs piper", e);
|
||||||
|
}
|
||||||
|
Log.info("LazyFs out piper finished");
|
||||||
|
});
|
||||||
|
outPiper.start();
|
||||||
|
errPiper = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
try (BufferedReader input = new BufferedReader(new InputStreamReader(fs.getErrorStream()))) {
|
||||||
|
String line;
|
||||||
|
|
||||||
|
while ((line = input.readLine()) != null) {
|
||||||
|
System.out.println(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.info("Exception in LazyFs piper", e);
|
||||||
|
}
|
||||||
|
Log.info("LazyFs err piper finished");
|
||||||
|
});
|
||||||
|
errPiper.start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!startLatch.await(30, TimeUnit.SECONDS))
|
||||||
|
throw new RuntimeException("StartLatch timed out");
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
Log.info("LazyFs started");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
start("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String mdbPath() {
|
||||||
|
return Path.of(dataRoot).resolve("objects").resolve("data.mdb").toAbsolutePath().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startTornOp() {
|
||||||
|
start("\n" +
|
||||||
|
"[[injection]]\n" +
|
||||||
|
"type=\"torn-seq\"\n" +
|
||||||
|
"op=\"write\"\n" +
|
||||||
|
"file=\"" + mdbPath() + "\"\n" +
|
||||||
|
"persist=[1,4]\n" +
|
||||||
|
"occurrence=3");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startTornSeq() {
|
||||||
|
start("[[injection]]\n" +
|
||||||
|
"type=\"torn-op\"\n" +
|
||||||
|
"file=\"" + mdbPath() + "\"\n" +
|
||||||
|
"occurrence=3\n" +
|
||||||
|
"parts=3 #or parts_bytes=[4096,3600,1260]\n" +
|
||||||
|
"persist=[1,3]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void crash() {
|
||||||
|
try {
|
||||||
|
var cmd = "echo \"lazyfs::crash::timing=after::op=write::from_rgx=*\" > " + fifoPath();
|
||||||
|
Log.info("Running command: " + cmd);
|
||||||
|
Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmd}).waitFor();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
try {
|
||||||
|
synchronized (this) {
|
||||||
|
Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "fusermount3 -u " + mountRoot}).waitFor();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doesn't actually work?
|
||||||
|
//
|
||||||
|
// public void crashop() {
|
||||||
|
// try {
|
||||||
|
// var cmd = "echo \"lazyfs::torn-op::file=" + Path.of(lazyFsDataPath).toAbsolutePath().toString() + "/objects/data.mdb::persist=1,3::parts=3::occurrence=5\" > /tmp/faults.fifo";
|
||||||
|
// System.out.println("Running command: " + cmd);
|
||||||
|
// Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmd});
|
||||||
|
// Thread.sleep(1000);
|
||||||
|
// Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "fusermount3 -u " + dataRoot});
|
||||||
|
// Thread.sleep(1000);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// throw new RuntimeException(e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void crashseq() {
|
||||||
|
// try {
|
||||||
|
// var cmd = "echo \"lazyfs::torn-seq::op=write::file=" + Path.of(lazyFsDataPath).toAbsolutePath().toString() + "/objects/data.mdb::persist=1,4::occurrence=2\" > /tmp/faults.fifo";
|
||||||
|
// System.out.println("Running command: " + cmd);
|
||||||
|
// Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmd});
|
||||||
|
// Thread.sleep(1000);
|
||||||
|
// Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "fusermount3 -u " + dataRoot});
|
||||||
|
// Thread.sleep(1000);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// throw new RuntimeException(e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,489 @@
|
|||||||
|
package com.usatiuk.dhfsfuse.integration;
|
||||||
|
|
||||||
|
import com.github.dockerjava.api.model.Device;
|
||||||
|
import com.usatiuk.dhfsfuse.TestDataCleaner;
|
||||||
|
import io.quarkus.logging.Log;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.EnumSource;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.testcontainers.DockerClientFactory;
|
||||||
|
import org.testcontainers.containers.GenericContainer;
|
||||||
|
import org.testcontainers.containers.Network;
|
||||||
|
import org.testcontainers.containers.output.Slf4jLogConsumer;
|
||||||
|
import org.testcontainers.containers.output.WaitingConsumer;
|
||||||
|
import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
|
|
||||||
|
public class LazyFsIT {
|
||||||
|
GenericContainer<?> container1;
|
||||||
|
GenericContainer<?> container2;
|
||||||
|
|
||||||
|
WaitingConsumer waitingConsumer1;
|
||||||
|
WaitingConsumer waitingConsumer2;
|
||||||
|
|
||||||
|
String c1uuid;
|
||||||
|
String c2uuid;
|
||||||
|
|
||||||
|
File data1;
|
||||||
|
File data2;
|
||||||
|
File data1Lazy;
|
||||||
|
File data2Lazy;
|
||||||
|
|
||||||
|
LazyFs lazyFs1;
|
||||||
|
LazyFs lazyFs2;
|
||||||
|
|
||||||
|
ExecutorService executor;
|
||||||
|
Network network;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup(TestInfo testInfo) throws IOException, InterruptedException, TimeoutException {
|
||||||
|
executor = Executors.newCachedThreadPool();
|
||||||
|
data1 = Files.createTempDirectory("dhfsdata").toFile();
|
||||||
|
data2 = Files.createTempDirectory("dhfsdata").toFile();
|
||||||
|
data1Lazy = Files.createTempDirectory("lazyfsroot").toFile();
|
||||||
|
data2Lazy = Files.createTempDirectory("lazyfsroot").toFile();
|
||||||
|
|
||||||
|
network = Network.newNetwork();
|
||||||
|
|
||||||
|
lazyFs1 = new LazyFs(testInfo.getDisplayName(), data1.toString(), data1Lazy.toString());
|
||||||
|
lazyFs1.start();
|
||||||
|
lazyFs2 = new LazyFs(testInfo.getDisplayName(), data2.toString(), data2Lazy.toString());
|
||||||
|
lazyFs2.start();
|
||||||
|
|
||||||
|
container1 = new GenericContainer<>(DhfsImage.getInstance())
|
||||||
|
.withPrivilegedMode(true)
|
||||||
|
.withCreateContainerCmdModifier(cmd -> Objects.requireNonNull(cmd.getHostConfig()).withDevices(Device.parse("/dev/fuse")))
|
||||||
|
.waitingFor(Wait.forLogMessage(".*Listening.*", 1).withStartupTimeout(Duration.ofSeconds(60))).withNetwork(network)
|
||||||
|
.withFileSystemBind(data1.getAbsolutePath(), "/dhfs_test/data");
|
||||||
|
container2 = new GenericContainer<>(DhfsImage.getInstance())
|
||||||
|
.withPrivilegedMode(true)
|
||||||
|
.withCreateContainerCmdModifier(cmd -> Objects.requireNonNull(cmd.getHostConfig()).withDevices(Device.parse("/dev/fuse")))
|
||||||
|
.waitingFor(Wait.forLogMessage(".*Listening.*", 1).withStartupTimeout(Duration.ofSeconds(60))).withNetwork(network)
|
||||||
|
.withFileSystemBind(data2.getAbsolutePath(), "/dhfs_test/data");
|
||||||
|
|
||||||
|
Stream.of(container1, container2).parallel().forEach(GenericContainer::start);
|
||||||
|
|
||||||
|
waitingConsumer1 = new WaitingConsumer();
|
||||||
|
var loggingConsumer1 = new Slf4jLogConsumer(LoggerFactory.getLogger(LazyFsIT.class)).withPrefix("1-" + testInfo.getDisplayName());
|
||||||
|
container1.followOutput(loggingConsumer1.andThen(waitingConsumer1));
|
||||||
|
waitingConsumer2 = new WaitingConsumer();
|
||||||
|
var loggingConsumer2 = new Slf4jLogConsumer(LoggerFactory.getLogger(LazyFsIT.class)).withPrefix("2-" + testInfo.getDisplayName());
|
||||||
|
container2.followOutput(loggingConsumer2.andThen(waitingConsumer2));
|
||||||
|
|
||||||
|
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
|
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
|
|
||||||
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c1uuid));
|
||||||
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c2uuid));
|
||||||
|
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("New address"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("New address"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
||||||
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
|
" --request PUT " +
|
||||||
|
" --data '{}' " +
|
||||||
|
" http://localhost:8080/peers-manage/known-peers/" + c2uuid);
|
||||||
|
|
||||||
|
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
||||||
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
|
" --request PUT " +
|
||||||
|
" --data '{}' " +
|
||||||
|
" http://localhost:8080/peers-manage/known-peers/" + c1uuid);
|
||||||
|
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void stop() {
|
||||||
|
lazyFs1.stop();
|
||||||
|
lazyFs2.stop();
|
||||||
|
|
||||||
|
Stream.of(container1, container2).parallel().forEach(GenericContainer::stop);
|
||||||
|
TestDataCleaner.purgeDirectory(data1);
|
||||||
|
TestDataCleaner.purgeDirectory(data1Lazy);
|
||||||
|
TestDataCleaner.purgeDirectory(data2);
|
||||||
|
TestDataCleaner.purgeDirectory(data2Lazy);
|
||||||
|
|
||||||
|
executor.close();
|
||||||
|
network.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkConsistency(String testName) {
|
||||||
|
await().atMost(45, TimeUnit.SECONDS).until(() -> {
|
||||||
|
var ls1 = container1.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
|
var cat1 = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
|
var ls2 = container2.execInContainer("/bin/sh", "-c", "ls /dhfs_test/fuse");
|
||||||
|
var cat2 = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/*");
|
||||||
|
Log.info("Listing consistency " + testName + "\n"
|
||||||
|
+ ls1 + "\n"
|
||||||
|
+ cat1 + "\n"
|
||||||
|
+ ls2 + "\n"
|
||||||
|
+ cat2 + "\n");
|
||||||
|
|
||||||
|
return ls1.equals(ls2) && cat1.equals(cat2) && ls1.getExitCode() == 0 && ls2.getExitCode() == 0 && cat1.getExitCode() == 0 && cat2.getExitCode() == 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@EnumSource(CrashType.class)
|
||||||
|
void killTest(CrashType crashType, TestInfo testInfo) throws Exception {
|
||||||
|
var barrier = new CountDownLatch(1);
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier.countDown();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while true; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/test1; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
barrier.await();
|
||||||
|
Thread.sleep(3000);
|
||||||
|
Log.info("Killing");
|
||||||
|
lazyFs1.crash();
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Caused by: org.lmdbjava"), 60, TimeUnit.SECONDS);
|
||||||
|
var client = DockerClientFactory.instance().client();
|
||||||
|
client.killContainerCmd(container1.getContainerId()).exec();
|
||||||
|
container1.stop();
|
||||||
|
lazyFs1.stop();
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
Log.info("Restart");
|
||||||
|
switch (crashType) {
|
||||||
|
case CRASH -> lazyFs1.start();
|
||||||
|
case TORN_OP -> lazyFs1.startTornOp();
|
||||||
|
case TORN_SEQ -> lazyFs1.startTornSeq();
|
||||||
|
}
|
||||||
|
container1.start();
|
||||||
|
|
||||||
|
waitingConsumer1 = new WaitingConsumer();
|
||||||
|
var loggingConsumer1 = new Slf4jLogConsumer(LoggerFactory.getLogger(LazyFsIT.class)).withPrefix("1-" + testInfo.getDisplayName());
|
||||||
|
container1.followOutput(loggingConsumer1.andThen(waitingConsumer1));
|
||||||
|
try {
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
Log.info("Failed to connect: " + testInfo.getDisplayName());
|
||||||
|
// Sometimes it doesn't get mounted properly for some reason
|
||||||
|
Assumptions.assumeTrue(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier.countDown();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while true; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/test2; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Log.info("Killing");
|
||||||
|
if (crashType.equals(CrashType.CRASH)) {
|
||||||
|
Thread.sleep(3000);
|
||||||
|
lazyFs1.crash();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Caused by: org.lmdbjava"), 60, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
// Sometimes crash doesn't work
|
||||||
|
Log.info("Failed to crash: " + testInfo.getDisplayName());
|
||||||
|
if (crashType.equals(CrashType.CRASH))
|
||||||
|
throw e;
|
||||||
|
Assumptions.assumeTrue(false);
|
||||||
|
}
|
||||||
|
client.killContainerCmd(container1.getContainerId()).exec();
|
||||||
|
container1.stop();
|
||||||
|
lazyFs1.stop();
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
Log.info("Restart");
|
||||||
|
lazyFs1.start();
|
||||||
|
container1.start();
|
||||||
|
|
||||||
|
waitingConsumer1 = new WaitingConsumer();
|
||||||
|
loggingConsumer1 = new Slf4jLogConsumer(LoggerFactory.getLogger(LazyFsIT.class)).withPrefix("1-" + testInfo.getDisplayName());
|
||||||
|
container1.followOutput(loggingConsumer1.andThen(waitingConsumer1));
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
checkConsistency(testInfo.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@EnumSource(CrashType.class)
|
||||||
|
void killTestDirs(CrashType crashType, TestInfo testInfo) throws Exception {
|
||||||
|
var barrier = new CountDownLatch(1);
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier.countDown();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while true; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/test$counter; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
barrier.await();
|
||||||
|
Thread.sleep(3000);
|
||||||
|
Log.info("Killing");
|
||||||
|
lazyFs1.crash();
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Caused by: org.lmdbjava"), 60, TimeUnit.SECONDS);
|
||||||
|
var client = DockerClientFactory.instance().client();
|
||||||
|
client.killContainerCmd(container1.getContainerId()).exec();
|
||||||
|
container1.stop();
|
||||||
|
lazyFs1.stop();
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
Log.info("Restart");
|
||||||
|
switch (crashType) {
|
||||||
|
case CRASH -> lazyFs1.start();
|
||||||
|
case TORN_OP -> lazyFs1.startTornOp();
|
||||||
|
case TORN_SEQ -> lazyFs1.startTornSeq();
|
||||||
|
}
|
||||||
|
container1.start();
|
||||||
|
|
||||||
|
waitingConsumer1 = new WaitingConsumer();
|
||||||
|
var loggingConsumer1 = new Slf4jLogConsumer(LoggerFactory.getLogger(LazyFsIT.class)).withPrefix("1-" + testInfo.getDisplayName());
|
||||||
|
container1.followOutput(loggingConsumer1.andThen(waitingConsumer1));
|
||||||
|
try {
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
Log.info("Failed to connect: " + testInfo.getDisplayName());
|
||||||
|
// Sometimes it doesn't get mounted properly for some reason
|
||||||
|
|
||||||
|
Assumptions.assumeTrue(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier.countDown();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while true; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/2test$counter; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Log.info("Killing");
|
||||||
|
if (crashType.equals(CrashType.CRASH)) {
|
||||||
|
Thread.sleep(3000);
|
||||||
|
lazyFs1.crash();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Caused by: org.lmdbjava"), 60, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
// Sometimes crash doesn't work
|
||||||
|
Log.info("Failed to crash: " + testInfo.getDisplayName());
|
||||||
|
if (crashType.equals(CrashType.CRASH))
|
||||||
|
throw e;
|
||||||
|
Assumptions.assumeTrue(false);
|
||||||
|
}
|
||||||
|
client.killContainerCmd(container1.getContainerId()).exec();
|
||||||
|
container1.stop();
|
||||||
|
lazyFs1.stop();
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
Log.info("Restart");
|
||||||
|
lazyFs1.start();
|
||||||
|
container1.start();
|
||||||
|
|
||||||
|
waitingConsumer1 = new WaitingConsumer();
|
||||||
|
loggingConsumer1 = new Slf4jLogConsumer(LoggerFactory.getLogger(LazyFsIT.class)).withPrefix("1-" + testInfo.getDisplayName());
|
||||||
|
container1.followOutput(loggingConsumer1.andThen(waitingConsumer1));
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
checkConsistency(testInfo.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@EnumSource(CrashType.class)
|
||||||
|
void killTest2(CrashType crashType, TestInfo testInfo) throws Exception {
|
||||||
|
var barrier = new CountDownLatch(1);
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier.countDown();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while [ ! -f /tmp/stopprinting1 ]; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/test1; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
barrier.await();
|
||||||
|
Thread.sleep(3000);
|
||||||
|
Log.info("Killing");
|
||||||
|
lazyFs2.crash();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "touch /tmp/stopprinting1");
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Caused by: org.lmdbjava"), 60, TimeUnit.SECONDS);
|
||||||
|
var client = DockerClientFactory.instance().client();
|
||||||
|
client.killContainerCmd(container2.getContainerId()).exec();
|
||||||
|
container2.stop();
|
||||||
|
lazyFs2.stop();
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
Log.info("Restart");
|
||||||
|
switch (crashType) {
|
||||||
|
case CRASH -> lazyFs2.start();
|
||||||
|
case TORN_OP -> lazyFs2.startTornOp();
|
||||||
|
case TORN_SEQ -> lazyFs2.startTornSeq();
|
||||||
|
}
|
||||||
|
container2.start();
|
||||||
|
|
||||||
|
waitingConsumer2 = new WaitingConsumer();
|
||||||
|
var loggingConsumer2 = new Slf4jLogConsumer(LoggerFactory.getLogger(LazyFsIT.class)).withPrefix("2-" + testInfo.getDisplayName());
|
||||||
|
container2.followOutput(loggingConsumer2.andThen(waitingConsumer2));
|
||||||
|
try {
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
Log.info("Failed to connect: " + testInfo.getDisplayName());
|
||||||
|
// Sometimes it doesn't get mounted properly for some reason
|
||||||
|
|
||||||
|
Assumptions.assumeTrue(false);
|
||||||
|
}
|
||||||
|
var barrier2 = new CountDownLatch(1);
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier2.countDown();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while [ ! -f /tmp/stopprinting2 ]; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/test2; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
barrier2.await();
|
||||||
|
Log.info("Killing");
|
||||||
|
Thread.sleep(3000);
|
||||||
|
if (crashType.equals(CrashType.CRASH)) {
|
||||||
|
lazyFs2.crash();
|
||||||
|
}
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "touch /tmp/stopprinting2");
|
||||||
|
try {
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Caused by: org.lmdbjava"), 60, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
// Sometimes crash doesn't work
|
||||||
|
Log.info("Failed to crash: " + testInfo.getDisplayName());
|
||||||
|
if (crashType.equals(CrashType.CRASH))
|
||||||
|
throw e;
|
||||||
|
Assumptions.assumeTrue(false);
|
||||||
|
}
|
||||||
|
client.killContainerCmd(container2.getContainerId()).exec();
|
||||||
|
container2.stop();
|
||||||
|
lazyFs2.stop();
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
Log.info("Restart");
|
||||||
|
lazyFs2.start();
|
||||||
|
container2.start();
|
||||||
|
|
||||||
|
waitingConsumer2 = new WaitingConsumer();
|
||||||
|
loggingConsumer2 = new Slf4jLogConsumer(LoggerFactory.getLogger(LazyFsIT.class)).withPrefix("2-" + testInfo.getDisplayName());
|
||||||
|
container2.followOutput(loggingConsumer2.andThen(waitingConsumer2));
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
checkConsistency(testInfo.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@EnumSource(CrashType.class)
|
||||||
|
void killTestDirs2(CrashType crashType, TestInfo testInfo) throws Exception {
|
||||||
|
var barrier = new CountDownLatch(1);
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier.countDown();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while [ ! -f /tmp/stopprinting1 ]; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/test$counter; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
barrier.await();
|
||||||
|
Thread.sleep(3000);
|
||||||
|
Log.info("Killing");
|
||||||
|
lazyFs2.crash();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "touch /tmp/stopprinting1");
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Caused by: org.lmdbjava"), 60, TimeUnit.SECONDS);
|
||||||
|
var client = DockerClientFactory.instance().client();
|
||||||
|
client.killContainerCmd(container2.getContainerId()).exec();
|
||||||
|
container2.stop();
|
||||||
|
lazyFs2.stop();
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
Log.info("Restart");
|
||||||
|
switch (crashType) {
|
||||||
|
case CRASH -> lazyFs2.start();
|
||||||
|
case TORN_OP -> lazyFs2.startTornOp();
|
||||||
|
case TORN_SEQ -> lazyFs2.startTornSeq();
|
||||||
|
}
|
||||||
|
container2.start();
|
||||||
|
|
||||||
|
waitingConsumer2 = new WaitingConsumer();
|
||||||
|
var loggingConsumer2 = new Slf4jLogConsumer(LoggerFactory.getLogger(LazyFsIT.class)).withPrefix("2-" + testInfo.getDisplayName());
|
||||||
|
container2.followOutput(loggingConsumer2.andThen(waitingConsumer2));
|
||||||
|
try {
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
Log.info("Failed to connect: " + testInfo.getDisplayName());
|
||||||
|
// Sometimes it doesn't get mounted properly for some reason
|
||||||
|
Assumptions.assumeTrue(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var barrier2 = new CountDownLatch(1);
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
Log.info("Writing to container 1");
|
||||||
|
barrier2.countDown();
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "counter=0; while [ ! -f /tmp/stopprinting2 ]; do counter=`expr $counter + 1`; echo $counter >> /dhfs_test/fuse/2test$counter; done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
barrier2.await();
|
||||||
|
Thread.sleep(3000);
|
||||||
|
Log.info("Killing");
|
||||||
|
if (crashType.equals(CrashType.CRASH)) {
|
||||||
|
lazyFs2.crash();
|
||||||
|
}
|
||||||
|
container1.execInContainer("/bin/sh", "-c", "touch /tmp/stopprinting2");
|
||||||
|
try {
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Caused by: org.lmdbjava"), 60, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
// Sometimes crash doesn't work
|
||||||
|
Log.info("Failed to crash: " + testInfo.getDisplayName());
|
||||||
|
if (crashType.equals(CrashType.CRASH))
|
||||||
|
throw e;
|
||||||
|
Assumptions.assumeTrue(false);
|
||||||
|
}
|
||||||
|
client.killContainerCmd(container2.getContainerId()).exec();
|
||||||
|
container2.stop();
|
||||||
|
lazyFs2.stop();
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Lost connection to"), 60, TimeUnit.SECONDS);
|
||||||
|
Log.info("Restart");
|
||||||
|
lazyFs2.start();
|
||||||
|
container2.start();
|
||||||
|
|
||||||
|
waitingConsumer2 = new WaitingConsumer();
|
||||||
|
loggingConsumer2 = new Slf4jLogConsumer(LoggerFactory.getLogger(LazyFsIT.class)).withPrefix("2-" + testInfo.getDisplayName());
|
||||||
|
container2.followOutput(loggingConsumer2.andThen(waitingConsumer2));
|
||||||
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
checkConsistency(testInfo.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static enum CrashType {
|
||||||
|
CRASH,
|
||||||
|
TORN_OP,
|
||||||
|
TORN_SEQ
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.dhfs.integration;
|
package com.usatiuk.dhfsfuse.integration;
|
||||||
|
|
||||||
import com.github.dockerjava.api.model.Device;
|
import com.github.dockerjava.api.model.Device;
|
||||||
import org.junit.jupiter.api.*;
|
import org.junit.jupiter.api.*;
|
||||||
@@ -29,9 +29,11 @@ public class ResyncIT {
|
|||||||
String c1uuid;
|
String c1uuid;
|
||||||
String c2uuid;
|
String c2uuid;
|
||||||
|
|
||||||
|
Network network;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup(TestInfo testInfo) throws IOException, InterruptedException, TimeoutException {
|
void setup(TestInfo testInfo) throws IOException, InterruptedException, TimeoutException {
|
||||||
Network network = Network.newNetwork();
|
network = Network.newNetwork();
|
||||||
|
|
||||||
container1 = new GenericContainer<>(DhfsImage.getInstance())
|
container1 = new GenericContainer<>(DhfsImage.getInstance())
|
||||||
.withPrivilegedMode(true)
|
.withPrivilegedMode(true)
|
||||||
@@ -55,13 +57,14 @@ public class ResyncIT {
|
|||||||
@AfterEach
|
@AfterEach
|
||||||
void stop() {
|
void stop() {
|
||||||
Stream.of(container1, container2).parallel().forEach(GenericContainer::stop);
|
Stream.of(container1, container2).parallel().forEach(GenericContainer::stop);
|
||||||
|
network.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void readWriteFileTest() throws IOException, InterruptedException, TimeoutException {
|
void readWriteFileTest() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /root/dhfs_default/fuse/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty > /dhfs_test/fuse/testf1").getExitCode());
|
||||||
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/data/stuff/self_uuid").getStdout();
|
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/data/stuff/self_uuid").getStdout();
|
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
|
|
||||||
Assertions.assertDoesNotThrow(() -> UUID.fromString(c1uuid));
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c1uuid));
|
||||||
Assertions.assertDoesNotThrow(() -> UUID.fromString(c2uuid));
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c2uuid));
|
||||||
@@ -72,36 +75,36 @@ public class ResyncIT {
|
|||||||
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c2uuid);
|
||||||
|
|
||||||
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c1uuid);
|
||||||
|
|
||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testf1").getStdout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void manyFiles() throws IOException, InterruptedException, TimeoutException {
|
void manyFiles() throws IOException, InterruptedException, TimeoutException {
|
||||||
var ret = container1.execInContainer("/bin/sh", "-c", "for i in $(seq 1 200); do echo $i > /root/dhfs_default/fuse/test$i; done");
|
var ret = container1.execInContainer("/bin/sh", "-c", "for i in $(seq 1 200); do echo $i > /dhfs_test/fuse/test$i; done");
|
||||||
Assertions.assertEquals(0, ret.getExitCode());
|
Assertions.assertEquals(0, ret.getExitCode());
|
||||||
var foundWc = container1.execInContainer("/bin/sh", "-c", "find /root/dhfs_default/fuse -type f | wc -l");
|
var foundWc = container1.execInContainer("/bin/sh", "-c", "find /dhfs_test/fuse -type f | wc -l");
|
||||||
Assertions.assertEquals(200, Integer.valueOf(foundWc.getStdout().strip()));
|
Assertions.assertEquals(200, Integer.valueOf(foundWc.getStdout().strip()));
|
||||||
|
|
||||||
ret = container2.execInContainer("/bin/sh", "-c", "for i in $(seq 1 200); do echo $i > /root/dhfs_default/fuse/test-2-$i; done");
|
ret = container2.execInContainer("/bin/sh", "-c", "for i in $(seq 1 200); do echo $i > /dhfs_test/fuse/test-2-$i; done");
|
||||||
Assertions.assertEquals(0, ret.getExitCode());
|
Assertions.assertEquals(0, ret.getExitCode());
|
||||||
foundWc = container2.execInContainer("/bin/sh", "-c", "find /root/dhfs_default/fuse -type f | wc -l");
|
foundWc = container2.execInContainer("/bin/sh", "-c", "find /dhfs_test/fuse -type f | wc -l");
|
||||||
Assertions.assertEquals(200, Integer.valueOf(foundWc.getStdout().strip()));
|
Assertions.assertEquals(200, Integer.valueOf(foundWc.getStdout().strip()));
|
||||||
|
|
||||||
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/data/stuff/self_uuid").getStdout();
|
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/data/stuff/self_uuid").getStdout();
|
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
|
|
||||||
Assertions.assertDoesNotThrow(() -> UUID.fromString(c1uuid));
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c1uuid));
|
||||||
Assertions.assertDoesNotThrow(() -> UUID.fromString(c2uuid));
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c2uuid));
|
||||||
@@ -112,36 +115,36 @@ public class ResyncIT {
|
|||||||
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c2uuid);
|
||||||
|
|
||||||
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c1uuid);
|
||||||
|
|
||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
await().atMost(120, TimeUnit.SECONDS).until(() -> {
|
await().atMost(120, TimeUnit.SECONDS).until(() -> {
|
||||||
var foundWc2 = container2.execInContainer("/bin/sh", "-c", "find /root/dhfs_default/fuse -type f | wc -l");
|
var foundWc2 = container2.execInContainer("/bin/sh", "-c", "find /dhfs_test/fuse -type f | wc -l");
|
||||||
return 400 == Integer.valueOf(foundWc2.getStdout().strip());
|
return 400 == Integer.valueOf(foundWc2.getStdout().strip());
|
||||||
});
|
});
|
||||||
await().atMost(120, TimeUnit.SECONDS).until(() -> {
|
await().atMost(120, TimeUnit.SECONDS).until(() -> {
|
||||||
var foundWc2 = container1.execInContainer("/bin/sh", "-c", "find /root/dhfs_default/fuse -type f | wc -l");
|
var foundWc2 = container1.execInContainer("/bin/sh", "-c", "find /dhfs_test/fuse -type f | wc -l");
|
||||||
return 400 == Integer.valueOf(foundWc2.getStdout().strip());
|
return 400 == Integer.valueOf(foundWc2.getStdout().strip());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void folderAfterMove() throws IOException, InterruptedException, TimeoutException {
|
void folderAfterMove() throws IOException, InterruptedException, TimeoutException {
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mkdir /root/dhfs_default/fuse/testd1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mkdir /dhfs_test/fuse/testd1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty1 > /root/dhfs_default/fuse/testd1/testf1").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty1 > /dhfs_test/fuse/testd1/testf1").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mv /root/dhfs_default/fuse/testd1 /root/dhfs_default/fuse/testd2").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "mv /dhfs_test/fuse/testd1 /dhfs_test/fuse/testd2").getExitCode());
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty2 > /root/dhfs_default/fuse/testd2/testf2").getExitCode());
|
await().atMost(45, TimeUnit.SECONDS).until(() -> 0 == container1.execInContainer("/bin/sh", "-c", "echo tesempty2 > /dhfs_test/fuse/testd2/testf2").getExitCode());
|
||||||
|
|
||||||
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/data/stuff/self_uuid").getStdout();
|
c1uuid = container1.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/data/stuff/self_uuid").getStdout();
|
c2uuid = container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/data/stuff/self_uuid").getStdout();
|
||||||
|
|
||||||
Assertions.assertDoesNotThrow(() -> UUID.fromString(c1uuid));
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c1uuid));
|
||||||
Assertions.assertDoesNotThrow(() -> UUID.fromString(c2uuid));
|
Assertions.assertDoesNotThrow(() -> UUID.fromString(c2uuid));
|
||||||
@@ -152,20 +155,20 @@ public class ResyncIT {
|
|||||||
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
var c1curl = container1.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c2uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c2uuid);
|
||||||
|
|
||||||
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
var c2curl = container2.execInContainer("/bin/sh", "-c",
|
||||||
"curl --header \"Content-Type: application/json\" " +
|
"curl --header \"Content-Type: application/json\" " +
|
||||||
" --request PUT " +
|
" --request PUT " +
|
||||||
" --data '{\"uuid\":\"" + c1uuid + "\"}' " +
|
" --data '{}' " +
|
||||||
" http://localhost:8080/peers-manage/known-peers");
|
" http://localhost:8080/peers-manage/known-peers/" + c1uuid);
|
||||||
|
|
||||||
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer2.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
waitingConsumer1.waitUntil(frame -> frame.getUtf8String().contains("Connected"), 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty1\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testd2/testf1").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty1\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testd2/testf1").getStdout()));
|
||||||
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty2\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /root/dhfs_default/fuse/testd2/testf2").getStdout()));
|
await().atMost(45, TimeUnit.SECONDS).until(() -> "tesempty2\n".equals(container2.execInContainer("/bin/sh", "-c", "cat /dhfs_test/fuse/testd2/testf2").getStdout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
dhfs.objects.persistence.files.root=${HOME}/dhfs_data/dhfs_root_test
|
||||||
|
dhfs.objects.root=${HOME}/dhfs_data/dhfs_root_d_test
|
||||||
|
dhfs.fuse.root=${HOME}/dhfs_data/dhfs_fuse_root_test
|
||||||
|
dhfs.objects.ref_verification=true
|
||||||
|
dhfs.objects.deletion.delay=0
|
||||||
|
quarkus.log.category."com.usatiuk.dhfs".level=TRACE
|
||||||
|
quarkus.log.category."com.usatiuk.dhfs".min-level=TRACE
|
||||||
|
quarkus.http.test-port=0
|
||||||
|
quarkus.http.test-ssl-port=0
|
||||||
|
dhfs.local-discovery=false
|
||||||
|
dhfs.objects.persistence.snapshot-extra-checks=true
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.usatiuk.kleppmanntree;
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
import jakarta.annotation.Nonnull;
|
|
||||||
import jakarta.annotation.Nullable;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -17,7 +15,6 @@ public class KleppmannTree<TimestampT extends Comparable<TimestampT>, PeerIdT ex
|
|||||||
private final PeerInterface<PeerIdT> _peers;
|
private final PeerInterface<PeerIdT> _peers;
|
||||||
private final Clock<TimestampT> _clock;
|
private final Clock<TimestampT> _clock;
|
||||||
private final OpRecorder<TimestampT, PeerIdT, MetaT, NodeIdT> _opRecorder;
|
private final OpRecorder<TimestampT, PeerIdT, MetaT, NodeIdT> _opRecorder;
|
||||||
private HashMap<NodeIdT, TreeNode<TimestampT, PeerIdT, MetaT, NodeIdT>> _undoCtx = null;
|
|
||||||
|
|
||||||
public KleppmannTree(StorageInterface<TimestampT, PeerIdT, MetaT, NodeIdT> storage,
|
public KleppmannTree(StorageInterface<TimestampT, PeerIdT, MetaT, NodeIdT> storage,
|
||||||
PeerInterface<PeerIdT> peers,
|
PeerInterface<PeerIdT> peers,
|
||||||
@@ -89,7 +86,6 @@ public class KleppmannTree<TimestampT extends Comparable<TimestampT>, PeerIdT ex
|
|||||||
node.withParent(null)
|
node.withParent(null)
|
||||||
.withLastEffectiveOp(null)
|
.withLastEffectiveOp(null)
|
||||||
);
|
);
|
||||||
_undoCtx.put(node.key(), node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,31 +213,16 @@ public class KleppmannTree<TimestampT extends Comparable<TimestampT>, PeerIdT ex
|
|||||||
}
|
}
|
||||||
assert cmp != 0;
|
assert cmp != 0;
|
||||||
if (cmp < 0) {
|
if (cmp < 0) {
|
||||||
try {
|
if (log.containsKey(op.timestamp())) return;
|
||||||
if (log.containsKey(op.timestamp())) return;
|
var toUndo = log.newestSlice(op.timestamp(), false);
|
||||||
var toUndo = log.newestSlice(op.timestamp(), false);
|
for (var entry : toUndo.reversed()) {
|
||||||
_undoCtx = new HashMap<>();
|
undoOp(entry.getValue());
|
||||||
for (var entry : toUndo.reversed()) {
|
|
||||||
undoOp(entry.getValue());
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
doAndPut(op, failCreatingIfExists);
|
|
||||||
} finally {
|
|
||||||
for (var entry : toUndo) {
|
|
||||||
redoOp(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_undoCtx.isEmpty()) {
|
|
||||||
for (var e : _undoCtx.entrySet()) {
|
|
||||||
LOGGER.log(Level.FINE, "Dropping node " + e.getKey());
|
|
||||||
_storage.removeNode(e.getKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_undoCtx = null;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
tryTrimLog();
|
|
||||||
}
|
}
|
||||||
|
doAndPut(op, failCreatingIfExists);
|
||||||
|
for (var entry : toUndo) {
|
||||||
|
redoOp(entry);
|
||||||
|
}
|
||||||
|
tryTrimLog();
|
||||||
} else {
|
} else {
|
||||||
doAndPut(op, failCreatingIfExists);
|
doAndPut(op, failCreatingIfExists);
|
||||||
tryTrimLog();
|
tryTrimLog();
|
||||||
@@ -264,8 +245,7 @@ public class KleppmannTree<TimestampT extends Comparable<TimestampT>, PeerIdT ex
|
|||||||
} catch (AlreadyExistsException aex) {
|
} catch (AlreadyExistsException aex) {
|
||||||
throw aex;
|
throw aex;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.log(Level.SEVERE, "Error computing effects for op" + op.toString(), e);
|
throw new RuntimeException("Error computing effects for op " + op.toString(), e);
|
||||||
computed = new LogRecord<>(op, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (computed.effects() != null)
|
if (computed.effects() != null)
|
||||||
@@ -274,24 +254,6 @@ public class KleppmannTree<TimestampT extends Comparable<TimestampT>, PeerIdT ex
|
|||||||
}
|
}
|
||||||
|
|
||||||
private TreeNode<TimestampT, PeerIdT, MetaT, NodeIdT> getNewNode(NodeIdT key, NodeIdT parent, MetaT meta) {
|
private TreeNode<TimestampT, PeerIdT, MetaT, NodeIdT> getNewNode(NodeIdT key, NodeIdT parent, MetaT meta) {
|
||||||
if (_undoCtx != null) {
|
|
||||||
var node = _undoCtx.get(key);
|
|
||||||
if (node != null) {
|
|
||||||
try {
|
|
||||||
if (!node.children().isEmpty()) {
|
|
||||||
LOGGER.log(Level.WARNING, "Not empty children for undone node " + key);
|
|
||||||
}
|
|
||||||
node = node.withParent(parent).withMeta(meta);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Error while fixing up node " + key, e);
|
|
||||||
node = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (node != null) {
|
|
||||||
_undoCtx.remove(key);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _storage.createNewNode(key, parent, meta);
|
return _storage.createNewNode(key, parent, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,10 +334,6 @@ public class KleppmannTree<TimestampT extends Comparable<TimestampT>, PeerIdT ex
|
|||||||
var conflictNode = _storage.getById(conflictNodeId);
|
var conflictNode = _storage.getById(conflictNodeId);
|
||||||
MetaT conflictNodeMeta = conflictNode.meta();
|
MetaT conflictNodeMeta = conflictNode.meta();
|
||||||
|
|
||||||
if (Objects.equals(conflictNodeMeta, op.newMeta())) {
|
|
||||||
return new LogRecord<>(op, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGER.finer(() -> "Node creation conflict: " + conflictNode);
|
LOGGER.finer(() -> "Node creation conflict: " + conflictNode);
|
||||||
|
|
||||||
String newConflictNodeName = op.newName() + ".conflict." + conflictNode.key();
|
String newConflictNodeName = op.newName() + ".conflict." + conflictNode.key();
|
||||||
@@ -400,18 +358,14 @@ public class KleppmannTree<TimestampT extends Comparable<TimestampT>, PeerIdT ex
|
|||||||
if (oldMeta != null
|
if (oldMeta != null
|
||||||
&& op.newMeta() != null
|
&& op.newMeta() != null
|
||||||
&& !oldMeta.getClass().equals(op.newMeta().getClass())) {
|
&& !oldMeta.getClass().equals(op.newMeta().getClass())) {
|
||||||
LOGGER.log(Level.SEVERE, "Class mismatch for meta for node " + node.key());
|
throw new RuntimeException("Class mismatch for meta for node " + node.key());
|
||||||
return new LogRecord<>(op, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var replaceNodeId = newParent.children().get(op.newName());
|
var replaceNodeId = newParent.children().get(op.newName());
|
||||||
if (replaceNodeId != null) {
|
if (replaceNodeId != null) {
|
||||||
var replaceNode = _storage.getById(replaceNodeId);
|
var replaceNode = _storage.getById(replaceNodeId);
|
||||||
var replaceNodeMeta = replaceNode.meta();
|
var replaceNodeMeta = replaceNode.meta();
|
||||||
|
|
||||||
if (Objects.equals(replaceNodeMeta, op.newMeta())) {
|
|
||||||
return new LogRecord<>(op, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGER.finer(() -> "Node replacement: " + replaceNode);
|
LOGGER.finer(() -> "Node replacement: " + replaceNode);
|
||||||
|
|
||||||
return new LogRecord<>(op, List.of(
|
return new LogRecord<>(op, List.of(
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ public record LogEffect<TimestampT extends Comparable<TimestampT>, PeerIdT exten
|
|||||||
NodeIdT childId) implements Serializable {
|
NodeIdT childId) implements Serializable {
|
||||||
public String oldName() {
|
public String oldName() {
|
||||||
if (oldInfo.oldMeta() != null) {
|
if (oldInfo.oldMeta() != null) {
|
||||||
return oldInfo.oldMeta().getName();
|
return oldInfo.oldMeta().name();
|
||||||
}
|
}
|
||||||
return childId.toString();
|
return childId.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String newName() {
|
public String newName() {
|
||||||
if (newMeta != null) {
|
if (newMeta != null) {
|
||||||
return newMeta.getName();
|
return newMeta.name();
|
||||||
}
|
}
|
||||||
return childId.toString();
|
return childId.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.usatiuk.kleppmanntree;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
public interface NodeMeta extends Serializable {
|
public interface NodeMeta extends Serializable {
|
||||||
String getName();
|
String name();
|
||||||
|
|
||||||
NodeMeta withName(String name);
|
NodeMeta withName(String name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public record OpMove<TimestampT extends Comparable<TimestampT>, PeerIdT extends
|
|||||||
NodeIdT childId) implements Serializable {
|
NodeIdT childId) implements Serializable {
|
||||||
public String newName() {
|
public String newName() {
|
||||||
if (newMeta != null)
|
if (newMeta != null)
|
||||||
return newMeta.getName();
|
return newMeta.name();
|
||||||
return childId.toString();
|
return childId.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public interface TreeNode<TimestampT extends Comparable<TimestampT>, PeerIdT ext
|
|||||||
|
|
||||||
default String name() {
|
default String name() {
|
||||||
var meta = meta();
|
var meta = meta();
|
||||||
if (meta != null) return meta.getName();
|
if (meta != null) return meta.name();
|
||||||
return key().toString();
|
return key().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public abstract class TestNodeMeta implements NodeMeta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String name() {
|
||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,11 @@
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.jqwik</groupId>
|
||||||
|
<artifactId>jqwik</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-junit5</artifactId>
|
<artifactId>quarkus-junit5</artifactId>
|
||||||
@@ -54,11 +59,6 @@
|
|||||||
<artifactId>utils</artifactId>
|
<artifactId>utils</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.usatiuk.dhfs</groupId>
|
|
||||||
<artifactId>supportlib</artifactId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-junit5-mockito</artifactId>
|
<artifactId>quarkus-junit5-mockito</artifactId>
|
||||||
@@ -88,6 +88,11 @@
|
|||||||
<forkCount>1C</forkCount>
|
<forkCount>1C</forkCount>
|
||||||
<reuseForks>false</reuseForks>
|
<reuseForks>false</reuseForks>
|
||||||
<parallel>classes</parallel>
|
<parallel>classes</parallel>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<junit.jupiter.execution.parallel.enabled>
|
||||||
|
false
|
||||||
|
</junit.jupiter.execution.parallel.enabled>
|
||||||
|
</systemPropertyVariables>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
@@ -99,7 +104,6 @@
|
|||||||
<execution>
|
<execution>
|
||||||
<id>quarkus-plugin</id>
|
<id>quarkus-plugin</id>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>build</goal>
|
|
||||||
<goal>generate-code</goal>
|
<goal>generate-code</goal>
|
||||||
<goal>generate-code-tests</goal>
|
<goal>generate-code-tests</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
|||||||
@@ -2,11 +2,6 @@ package com.usatiuk.objects;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
// TODO: This could be maybe moved to a separate module?
|
|
||||||
// The base class for JObject data
|
|
||||||
// Only one instance of this "exists" per key, the instance in the manager is canonical
|
|
||||||
// When committing a transaction, the instance is checked against it, if it isn't the same, a race occurred.
|
|
||||||
// It is immutable, its version is filled in by the allocator from the AllocVersionProvider
|
|
||||||
public interface JData extends Serializable {
|
public interface JData extends Serializable {
|
||||||
JObjectKey key();
|
JObjectKey key();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
package com.usatiuk.objects;
|
package com.usatiuk.objects;
|
||||||
|
|
||||||
public interface JDataVersionedWrapper {
|
import com.usatiuk.objects.iterators.Data;
|
||||||
|
|
||||||
|
public sealed interface JDataVersionedWrapper extends Data<JDataVersionedWrapper> permits JDataVersionedWrapperLazy, JDataVersionedWrapperImpl {
|
||||||
|
@Override
|
||||||
|
default JDataVersionedWrapper value() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
JData data();
|
JData data();
|
||||||
|
|
||||||
long version();
|
long version();
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package com.usatiuk.objects;
|
|||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class JDataVersionedWrapperLazy implements JDataVersionedWrapper {
|
public final class JDataVersionedWrapperLazy implements JDataVersionedWrapper {
|
||||||
private final long _version;
|
private final long _version;
|
||||||
private final int _estimatedSize;
|
private final int _estimatedSize;
|
||||||
private Supplier<JData> _producer;
|
|
||||||
private JData _data;
|
private JData _data;
|
||||||
|
private Supplier<JData> _producer;
|
||||||
|
|
||||||
public JDataVersionedWrapperLazy(long version, int estimatedSize, Supplier<JData> producer) {
|
public JDataVersionedWrapperLazy(long version, int estimatedSize, Supplier<JData> producer) {
|
||||||
_version = version;
|
_version = version;
|
||||||
@@ -14,6 +14,18 @@ public class JDataVersionedWrapperLazy implements JDataVersionedWrapper {
|
|||||||
_producer = producer;
|
_producer = producer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCacheCallback(Runnable cacheCallback) {
|
||||||
|
if (_data != null) {
|
||||||
|
throw new IllegalStateException("Cache callback can be set only before data is loaded");
|
||||||
|
}
|
||||||
|
var oldProducer = _producer;
|
||||||
|
_producer = () -> {
|
||||||
|
var ret = oldProducer.get();
|
||||||
|
cacheCallback.run();
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public JData data() {
|
public JData data() {
|
||||||
if (_data != null)
|
if (_data != null)
|
||||||
return _data;
|
return _data;
|
||||||
|
|||||||
@@ -2,17 +2,16 @@ package com.usatiuk.objects;
|
|||||||
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.inject.Singleton;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
@ApplicationScoped
|
@Singleton
|
||||||
public class JDataVersionedWrapperSerializer implements ObjectSerializer<JDataVersionedWrapper> {
|
public class JDataVersionedWrapperSerializer {
|
||||||
@Inject
|
@Inject
|
||||||
ObjectSerializer<JData> dataSerializer;
|
ObjectSerializer<JData> dataSerializer;
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteString serialize(JDataVersionedWrapper obj) {
|
public ByteString serialize(JDataVersionedWrapper obj) {
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
|
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
|
||||||
buffer.putLong(obj.version());
|
buffer.putLong(obj.version());
|
||||||
@@ -20,10 +19,10 @@ public class JDataVersionedWrapperSerializer implements ObjectSerializer<JDataVe
|
|||||||
return ByteString.copyFrom(buffer).concat(dataSerializer.serialize(obj.data()));
|
return ByteString.copyFrom(buffer).concat(dataSerializer.serialize(obj.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public JDataVersionedWrapper deserialize(ByteBuffer data) {
|
||||||
public JDataVersionedWrapper deserialize(ByteString data) {
|
var version = data.getLong();
|
||||||
var version = data.substring(0, Long.BYTES).asReadOnlyByteBuffer().getLong();
|
return new JDataVersionedWrapperLazy(version, data.remaining(),
|
||||||
var rawData = data.substring(Long.BYTES);
|
() -> dataSerializer.deserialize(data)
|
||||||
return new JDataVersionedWrapperLazy(version, rawData.size(), () -> dataSerializer.deserialize(rawData));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user