Browse Source

Add an End-to-End workflow for FUSE mount (#3562)

* Add an e2e workflow to test FUSE mount

* Fix deadlocks during concurrent r/w
pull/3572/head
Patrick Schmidt 2 years ago
committed by GitHub
parent
commit
a73e177ecf
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .github/workflows/codeql.yml
  2. 89
      .github/workflows/e2e.yml
  3. 30
      docker/Dockerfile.e2e
  4. 1
      docker/Dockerfile.local
  5. 14
      docker/Makefile
  6. 53
      docker/compose/e2e-mount.yml
  7. 8
      weed/mount/filehandle.go
  8. 4
      weed/mount/weedfs_file_read.go

4
.github/workflows/codeql.yml

@ -3,6 +3,10 @@ name: "Code Scanning - Action"
on: on:
pull_request: pull_request:
concurrency:
group: ${{ github.head_ref }}/codeql
cancel-in-progress: true
jobs: jobs:
CodeQL-Build: CodeQL-Build:
# CodeQL runs on ubuntu-latest, windows-latest, and macos-latest # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest

89
.github/workflows/e2e.yml

@ -0,0 +1,89 @@
name: "End to End"
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
concurrency:
group: ${{ github.head_ref }}/e2e
cancel-in-progress: true
permissions:
contents: read
defaults:
run:
working-directory: docker
jobs:
e2e:
name: FUSE Mount
runs-on: ubuntu-22.04
timeout-minutes: 15
steps:
- name: Set up Go 1.x
uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f # v2
with:
go-version: ^1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v2
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y fuse
- name: Start SeaweedFS
timeout-minutes: 5
run: make build_e2e && docker compose -f ./compose/e2e-mount.yml up --wait
- name: Run FIO
timeout-minutes: 5
run: |
echo "Starting FIO at: $(date)"
# Concurrent r/w
echo 'Run randrw with size=16M bs=4k'
docker compose -f ./compose/e2e-mount.yml exec mount timeout -k5 40 fio --name=fiotest --filename=/mnt/seaweedfs/fiotest --size=16M --rw=randrw --bs=4k --direct=1 --numjobs=8 --ioengine=libaio --iodepth=32 --group_reporting --runtime=30 --time_based=1
echo 'Run randrw with size=16M bs=128k'
docker compose -f ./compose/e2e-mount.yml exec mount timeout -k5 40 fio --name=fiotest --filename=/mnt/seaweedfs/fiotest --size=16M --rw=randrw --bs=128k --direct=1 --numjobs=8 --ioengine=libaio --iodepth=32 --group_reporting --runtime=30 --time_based=1
echo 'Run randrw with size=16M bs=1m'
docker compose -f ./compose/e2e-mount.yml exec mount timeout -k5 40 fio --name=fiotest --filename=/mnt/seaweedfs/fiotest --size=16M --rw=randrw --bs=1m --direct=1 --numjobs=8 --ioengine=libaio --iodepth=32 --group_reporting --runtime=30 --time_based=1
# Verified write
echo 'Run randwrite with size=16M bs=4k'
docker compose -f ./compose/e2e-mount.yml exec mount timeout -k5 40 fio --name=fiotest --filename=/mnt/seaweedfs/fiotest --size=16M --rw=randwrite --bs=4k --direct=1 --numjobs=8 --ioengine=libaio --iodepth=32 --group_reporting --runtime=30 --time_based=1 --do_verify=0 --verify=crc32c --verify_backlog=1
echo 'Run randwrite with size=16M bs=128k'
docker compose -f ./compose/e2e-mount.yml exec mount timeout -k5 40 fio --name=fiotest --filename=/mnt/seaweedfs/fiotest --size=16M --rw=randwrite --bs=128k --direct=1 --numjobs=8 --ioengine=libaio --iodepth=32 --group_reporting --runtime=30 --time_based=1 --do_verify=0 --verify=crc32c --verify_backlog=1
echo 'Run randwrite with size=16M bs=1m'
docker compose -f ./compose/e2e-mount.yml exec mount timeout -k5 40 fio --name=fiotest --filename=/mnt/seaweedfs/fiotest --size=16M --rw=randwrite --bs=1m --direct=1 --numjobs=8 --ioengine=libaio --iodepth=32 --group_reporting --runtime=30 --time_based=1 --do_verify=0 --verify=crc32c --verify_backlog=1
- name: Save logs
if: always()
run: |
docker compose -f ./compose/e2e-mount.yml logs > output.log
echo 'Showing last 500 log lines of mount service:'
docker compose -f ./compose/e2e-mount.yml logs --tail 500 mount
- name: Check for data races
if: always()
continue-on-error: true # TODO: remove this comment to enable build failure on data races (after all are fixed)
run: grep -A50 'DATA RACE' output.log && exit 1 || exit 0
- name: Archive logs
if: always()
uses: actions/upload-artifact@v3
with:
name: output-logs
path: docker/output.log
- name: Cleanup
if: always()
run: docker compose -f ./compose/e2e-mount.yml down --volumes --remove-orphans --rmi all

30
docker/Dockerfile.e2e

@ -0,0 +1,30 @@
FROM ubuntu:22.04
LABEL author="Chris Lu"
RUN apt-get update && apt-get install -y curl fio fuse
RUN mkdir -p /etc/seaweedfs /data/filerldb2
COPY ./weed /usr/bin/
COPY ./filer.toml /etc/seaweedfs/filer.toml
COPY ./entrypoint.sh /entrypoint.sh
# volume server grpc port
EXPOSE 18080
# volume server http port
EXPOSE 8080
# filer server grpc port
EXPOSE 18888
# filer server http port
EXPOSE 8888
# master server shared grpc port
EXPOSE 19333
# master server shared http port
EXPOSE 9333
VOLUME /data
WORKDIR /data
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

1
docker/Dockerfile.local

@ -5,6 +5,7 @@ RUN mkdir -p /etc/seaweedfs
COPY ./filer.toml /etc/seaweedfs/filer.toml COPY ./filer.toml /etc/seaweedfs/filer.toml
COPY ./entrypoint.sh /entrypoint.sh COPY ./entrypoint.sh /entrypoint.sh
RUN apk add fuse # for weed mount RUN apk add fuse # for weed mount
RUN apk add curl # for health checks
# volume server grpc port # volume server grpc port
EXPOSE 18080 EXPOSE 18080

14
docker/Makefile

@ -8,11 +8,17 @@ cgo ?= 0
binary: binary:
export SWCOMMIT=$(shell git rev-parse --short HEAD) export SWCOMMIT=$(shell git rev-parse --short HEAD)
export SWLDFLAGS="-X github.com/seaweedfs/seaweedfs/weed/util.COMMIT=$(SWCOMMIT)" export SWLDFLAGS="-X github.com/seaweedfs/seaweedfs/weed/util.COMMIT=$(SWCOMMIT)"
cd ../weed; CGO_ENABLED=$(cgo) GOOS=linux go build $(options) -tags "$(tags)" -ldflags "-extldflags -static $(SWLDFLAGS)"; mv weed ../docker/
cd ../weed && CGO_ENABLED=$(cgo) GOOS=linux go build $(options) -tags "$(tags)" -ldflags "-extldflags -static $(SWLDFLAGS)" && mv weed ../docker/
binary_race: options = -race
binary_race: cgo = 1
binary_race: binary
build: binary build: binary
docker build --no-cache -t chrislusf/seaweedfs:local -f Dockerfile.local . docker build --no-cache -t chrislusf/seaweedfs:local -f Dockerfile.local .
rm ./weed
build_e2e: binary_race
docker build --no-cache -t chrislusf/seaweedfs:e2e -f Dockerfile.e2e .
go_build: # make go_build tags=elastic,ydb,gocdk,hdfs,5BytesOffset go_build: # make go_build tags=elastic,ydb,gocdk,hdfs,5BytesOffset
docker build --build-arg TAGS=$(tags) --no-cache -t chrislusf/seaweedfs:go_build -f Dockerfile.go_build . docker build --build-arg TAGS=$(tags) --no-cache -t chrislusf/seaweedfs:go_build -f Dockerfile.go_build .
@ -29,9 +35,7 @@ s3tests_build:
dev: build dev: build
docker-compose -f compose/local-dev-compose.yml -p seaweedfs up docker-compose -f compose/local-dev-compose.yml -p seaweedfs up
dev_race: options = -race
dev_race: cgo = 1
dev_race: build
dev_race: binary_race
docker-compose -f compose/local-dev-compose.yml -p seaweedfs up docker-compose -f compose/local-dev-compose.yml -p seaweedfs up
dev_tls: build certstrap dev_tls: build certstrap

53
docker/compose/e2e-mount.yml

@ -0,0 +1,53 @@
version: '3.9'
services:
master:
image: chrislusf/seaweedfs:e2e
command: "-v=4 master -ip=master -ip.bind=0.0.0.0 -raftBootstrap"
healthcheck:
test: [ "CMD", "curl", "--fail", "-I", "http://localhost:9333/cluster/healthz" ]
interval: 1s
timeout: 60s
volume:
image: chrislusf/seaweedfs:e2e
command: "-v=4 volume -mserver=master:9333 -ip=volume -ip.bind=0.0.0.0 -preStopSeconds=1"
healthcheck:
test: [ "CMD", "curl", "--fail", "-I", "http://localhost:8080/healthz" ]
interval: 1s
timeout: 30s
depends_on:
master:
condition: service_healthy
filer:
image: chrislusf/seaweedfs:e2e
command: "-v=4 filer -master=master:9333 -ip=filer -ip.bind=0.0.0.0"
healthcheck:
test: [ "CMD", "curl", "--fail", "-I", "http://localhost:8888" ]
interval: 1s
timeout: 30s
depends_on:
volume:
condition: service_healthy
mount:
image: chrislusf/seaweedfs:e2e
command: "-v=4 mount -filer=filer:8888 -filer.path=/ -dirAutoCreate -dir=/mnt/seaweedfs"
cap_add:
- SYS_ADMIN
devices:
- /dev/fuse
security_opt:
- apparmor:unconfined
deploy:
resources:
limits:
memory: 4096m
healthcheck:
test: [ "CMD", "mountpoint", "-q", "--", "/mnt/seaweedfs" ]
interval: 1s
timeout: 30s
depends_on:
filer:
condition: service_healthy

8
weed/mount/filehandle.go

@ -77,6 +77,10 @@ func (fh *FileHandle) AddChunks(chunks []*filer_pb.FileChunk) {
fh.entryLock.Lock() fh.entryLock.Lock()
defer fh.entryLock.Unlock() defer fh.entryLock.Unlock()
if fh.entry == nil {
return
}
// find the earliest incoming chunk // find the earliest incoming chunk
newChunks := chunks newChunks := chunks
earliestChunk := newChunks[0] earliestChunk := newChunks[0]
@ -86,10 +90,6 @@ func (fh *FileHandle) AddChunks(chunks []*filer_pb.FileChunk) {
} }
} }
if fh.entry == nil {
return
}
// pick out-of-order chunks from existing chunks // pick out-of-order chunks from existing chunks
for _, chunk := range fh.entry.Chunks { for _, chunk := range fh.entry.Chunks {
if lessThan(earliestChunk, chunk) { if lessThan(earliestChunk, chunk) {

4
weed/mount/weedfs_file_read.go

@ -39,8 +39,8 @@ func (wfs *WFS) Read(cancel <-chan struct{}, in *fuse.ReadIn, buff []byte) (fuse
return nil, fuse.ENOENT return nil, fuse.ENOENT
} }
fh.entryLock.Lock()
defer fh.entryLock.Unlock()
fh.Lock()
defer fh.Unlock()
offset := int64(in.Offset) offset := int64(in.Offset)
totalRead, err := readDataByFileHandle(buff, fh, offset) totalRead, err := readDataByFileHandle(buff, fh, offset)

Loading…
Cancel
Save