mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-20 05:22:34 +00:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51a905600d | ||
|
|
40bb169544 | ||
|
|
51ef39fc76 | ||
|
|
6542d1e0aa | ||
|
|
e86e65e5ab | ||
|
|
8e9c12e2c7 | ||
|
|
4f5f1f6be7 | ||
|
|
20952aa514 |
@@ -15,15 +15,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [amd64, arm64, arm, 386]
|
||||
include:
|
||||
- platform: amd64
|
||||
qemu: false
|
||||
- platform: arm64
|
||||
qemu: true
|
||||
- platform: arm
|
||||
qemu: true
|
||||
- platform: 386
|
||||
qemu: true
|
||||
variant: [standard, large_disk]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -43,6 +36,18 @@ jobs:
|
||||
[ -d /go/pkg ] && rm -rf /go/pkg || true
|
||||
echo "Available disk space after cleanup:"
|
||||
df -h
|
||||
|
||||
- name: Configure variant
|
||||
id: config
|
||||
run: |
|
||||
if [ "${{ matrix.variant }}" == "large_disk" ]; then
|
||||
echo "tag_suffix=_large_disk" >> $GITHUB_OUTPUT
|
||||
echo "build_args=TAGS=5BytesOffset" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "tag_suffix=" >> $GITHUB_OUTPUT
|
||||
echo "build_args=" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@v5
|
||||
@@ -50,13 +55,13 @@ jobs:
|
||||
images: |
|
||||
chrislusf/seaweedfs
|
||||
ghcr.io/chrislusf/seaweedfs
|
||||
tags: type=raw,value=latest
|
||||
tags: type=raw,value=latest,suffix=${{ steps.config.outputs.tag_suffix }}
|
||||
labels: |
|
||||
org.opencontainers.image.title=seaweedfs
|
||||
org.opencontainers.image.description=SeaweedFS is a distributed storage system for blobs, objects, files, and data lake, to store and serve billions of files fast!
|
||||
org.opencontainers.image.vendor=Chris Lu
|
||||
- name: Set up QEMU
|
||||
if: matrix.qemu
|
||||
if: matrix.platform != 'amd64'
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Create BuildKit config
|
||||
run: |
|
||||
@@ -82,7 +87,7 @@ jobs:
|
||||
registry: ghcr.io
|
||||
username: ${{ secrets.GHCR_USERNAME }}
|
||||
password: ${{ secrets.GHCR_TOKEN }}
|
||||
- name: Build ${{ matrix.platform }}
|
||||
- name: Build ${{ matrix.platform }} ${{ matrix.variant }}
|
||||
uses: docker/build-push-action@v6
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
@@ -92,13 +97,14 @@ jobs:
|
||||
file: ./docker/Dockerfile.go_build
|
||||
platforms: linux/${{ matrix.platform }}
|
||||
# Push to GHCR only during build to avoid Docker Hub rate limits
|
||||
tags: ghcr.io/chrislusf/seaweedfs:latest-${{ matrix.platform }}
|
||||
tags: ghcr.io/chrislusf/seaweedfs:latest${{ steps.config.outputs.tag_suffix }}-${{ matrix.platform }}
|
||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
cache-from: type=gha,scope=${{ matrix.variant }}-${{ matrix.platform }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.variant }}-${{ matrix.platform }}
|
||||
build-args: |
|
||||
BUILDKIT_INLINE_CACHE=1
|
||||
BRANCH=${{ github.sha }}
|
||||
${{ steps.config.outputs.build_args }}
|
||||
- name: Clean up build artifacts
|
||||
if: always()
|
||||
run: |
|
||||
@@ -111,9 +117,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
if: github.event_name != 'pull_request'
|
||||
strategy:
|
||||
matrix:
|
||||
variant: [standard, large_disk]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure variant
|
||||
id: config
|
||||
run: |
|
||||
if [ "${{ matrix.variant }}" == "large_disk" ]; then
|
||||
echo "tag_suffix=_large_disk" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "tag_suffix=" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@v5
|
||||
@@ -121,7 +140,7 @@ jobs:
|
||||
images: |
|
||||
chrislusf/seaweedfs
|
||||
ghcr.io/chrislusf/seaweedfs
|
||||
tags: type=raw,value=latest
|
||||
tags: type=raw,value=latest,suffix=${{ steps.config.outputs.tag_suffix }}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -142,13 +161,15 @@ jobs:
|
||||
crane version
|
||||
- name: Create and push manifest
|
||||
run: |
|
||||
SUFFIX="${{ steps.config.outputs.tag_suffix }}"
|
||||
|
||||
# Create manifest on GHCR first (no rate limits)
|
||||
echo "Creating GHCR manifest (no rate limits)..."
|
||||
docker buildx imagetools create -t ghcr.io/chrislusf/seaweedfs:latest \
|
||||
ghcr.io/chrislusf/seaweedfs:latest-amd64 \
|
||||
ghcr.io/chrislusf/seaweedfs:latest-arm64 \
|
||||
ghcr.io/chrislusf/seaweedfs:latest-arm \
|
||||
ghcr.io/chrislusf/seaweedfs:latest-386
|
||||
docker buildx imagetools create -t ghcr.io/chrislusf/seaweedfs:latest${SUFFIX} \
|
||||
ghcr.io/chrislusf/seaweedfs:latest${SUFFIX}-amd64 \
|
||||
ghcr.io/chrislusf/seaweedfs:latest${SUFFIX}-arm64 \
|
||||
ghcr.io/chrislusf/seaweedfs:latest${SUFFIX}-arm \
|
||||
ghcr.io/chrislusf/seaweedfs:latest${SUFFIX}-386
|
||||
|
||||
# Copy the complete multi-arch image from GHCR to Docker Hub
|
||||
# This only requires one pull from GHCR (no rate limit) and one push to Docker Hub
|
||||
@@ -184,16 +205,16 @@ jobs:
|
||||
# Use crane or skopeo to copy, fallback to docker if not available
|
||||
if command -v crane &> /dev/null; then
|
||||
echo "Using crane to copy..."
|
||||
retry_with_backoff crane copy ghcr.io/chrislusf/seaweedfs:latest chrislusf/seaweedfs:latest
|
||||
retry_with_backoff crane copy ghcr.io/chrislusf/seaweedfs:latest${SUFFIX} chrislusf/seaweedfs:latest${SUFFIX}
|
||||
elif command -v skopeo &> /dev/null; then
|
||||
echo "Using skopeo to copy..."
|
||||
retry_with_backoff skopeo copy --all docker://ghcr.io/chrislusf/seaweedfs:latest docker://chrislusf/seaweedfs:latest
|
||||
retry_with_backoff skopeo copy --all docker://ghcr.io/chrislusf/seaweedfs:latest${SUFFIX} docker://chrislusf/seaweedfs:latest${SUFFIX}
|
||||
else
|
||||
echo "Using docker buildx imagetools (pulling 4 images from Docker Hub)..."
|
||||
# Fallback: create manifest directly on Docker Hub (pulls from Docker Hub - rate limited)
|
||||
retry_with_backoff docker buildx imagetools create -t chrislusf/seaweedfs:latest \
|
||||
ghcr.io/chrislusf/seaweedfs:latest-amd64 \
|
||||
ghcr.io/chrislusf/seaweedfs:latest-arm64 \
|
||||
ghcr.io/chrislusf/seaweedfs:latest-arm \
|
||||
ghcr.io/chrislusf/seaweedfs:latest-386
|
||||
retry_with_backoff docker buildx imagetools create -t chrislusf/seaweedfs:latest${SUFFIX} \
|
||||
ghcr.io/chrislusf/seaweedfs:latest${SUFFIX}-amd64 \
|
||||
ghcr.io/chrislusf/seaweedfs:latest${SUFFIX}-arm64 \
|
||||
ghcr.io/chrislusf/seaweedfs:latest${SUFFIX}-arm \
|
||||
ghcr.io/chrislusf/seaweedfs:latest${SUFFIX}-386
|
||||
fi
|
||||
|
||||
@@ -141,3 +141,4 @@ test/s3/iam/.test_env
|
||||
/test/erasure_coding/admin_dockertest/tmp
|
||||
/test/erasure_coding/admin_dockertest/task_logs
|
||||
weed_bin
|
||||
.aider*
|
||||
|
||||
@@ -4,6 +4,7 @@ COPY ./weed /usr/bin/weed
|
||||
RUN chmod +x /usr/bin/weed && ls -la /usr/bin/weed
|
||||
RUN mkdir -p /etc/seaweedfs
|
||||
COPY ./filer.toml /etc/seaweedfs/filer.toml
|
||||
COPY ./security.toml.example /etc/seaweedfs/security.toml
|
||||
COPY ./entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Install dependencies and create non-root user
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Put this file to one of the location, with descending priority
|
||||
# ./security.toml
|
||||
# $HOME/.seaweedfs/security.toml
|
||||
# /etc/seaweedfs/security.toml
|
||||
# this file is read by master, volume server, filer, and worker
|
||||
|
||||
# comma separated origins allowed to make requests to the filer and s3 gateway.
|
||||
# enter in this format: https://domain.com, or http://localhost:port
|
||||
[cors.allowed_origins]
|
||||
values = "*"
|
||||
|
||||
# this jwt signing key is read by master and volume server, and it is used for write operations:
|
||||
# - the Master server generates the JWT, which can be used to write a certain file on a volume server
|
||||
# - the Volume server validates the JWT on writing
|
||||
# the jwt defaults to expire after 10 seconds.
|
||||
[jwt.signing]
|
||||
key = "V1JJVEVTRUNSRVRFWEFNUExFMTIzNDU2Nzg5MDEy" # Example: WRITESECRETEXAMPLE123456789012
|
||||
# this jwt signing key is read by master and volume server, and it is used for read operations:
|
||||
# - the Master server generates the JWT, which can be used to read a certain file on a volume server
|
||||
# - the Volume server validates the JWT on reading
|
||||
[jwt.signing.read]
|
||||
key = "UkVBRFNFQ1JFVUVYQU1QTEUxMjM0NTY3ODkwMTI=" # Example: READSECRETEXAMPLE123456789012
|
||||
# If this JWT key is configured, Filer only accepts writes over HTTP if they are signed with this JWT:
|
||||
# - f.e. the S3 API Shim generates the JWT
|
||||
# - the Filer server validates the JWT on writing
|
||||
# the jwt defaults to expire after 10 seconds.
|
||||
[jwt.filer_signing]
|
||||
key = "RklMRVJXUklURVNFQ1JFVEVYQU1QTEUxMjM0NTY3OA==" # Example: FILERWRITESECRETEXAMPLE12345678
|
||||
# If this JWT key is configured, Filer only accepts reads over HTTP if they are signed with this JWT:
|
||||
# - f.e. the S3 API Shim generates the JWT
|
||||
# - the Filer server validates the JWT on reading
|
||||
# the jwt defaults to expire after 10 seconds.
|
||||
[jwt.filer_signing.read]
|
||||
key = "RklMRVJSRUFEU0VDUkVURVhBTVBMRTEyMzQ1Njc4OQ==" # Example: FILERREADSECRETEXAMPLE123456789
|
||||
@@ -1,6 +1,5 @@
|
||||
# SEAWEEDFS - helm chart (2.x+)
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Add the helm repo
|
||||
|
||||
|
||||
@@ -42,11 +42,11 @@ spec:
|
||||
backend:
|
||||
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }}
|
||||
service:
|
||||
name: {{ include "seaweedfs.fullname" . }}-admin
|
||||
name: {{ include "seaweedfs.componentName" (list . "admin") }}
|
||||
port:
|
||||
number: {{ .Values.admin.port }}
|
||||
{{- else }}
|
||||
serviceName: {{ include "seaweedfs.fullname" . }}-admin
|
||||
serviceName: {{ include "seaweedfs.componentName" (list . "admin") }}
|
||||
servicePort: {{ .Values.admin.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ include "seaweedfs.fullname" . }}-admin
|
||||
name: {{ include "seaweedfs.componentName" (list . "admin") }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "seaweedfs.name" . }}
|
||||
@@ -21,7 +21,7 @@ metadata:
|
||||
{{- toYaml .Values.admin.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
serviceName: {{ include "seaweedfs.fullname" . }}-admin
|
||||
serviceName: {{ include "seaweedfs.componentName" (list . "admin") }}
|
||||
podManagementPolicy: {{ .Values.admin.podManagementPolicy }}
|
||||
replicas: {{ .Values.admin.replicas }}
|
||||
selector:
|
||||
|
||||
@@ -57,7 +57,7 @@ spec:
|
||||
priorityClassName: {{ .Values.cosi.priorityClassName | quote }}
|
||||
{{- end }}
|
||||
enableServiceLinks: false
|
||||
serviceAccountName: {{ .Values.global.serviceAccountName }}-objectstorage-provisioner
|
||||
serviceAccountName: {{ include "seaweedfs.componentName" (list . "objectstorage-provisioner") }}
|
||||
{{- if .Values.cosi.initContainers }}
|
||||
initContainers:
|
||||
{{ tpl .Values.cosi.initContainers . | nindent 8 | trim }}
|
||||
@@ -78,16 +78,16 @@ spec:
|
||||
{{- else if .Values.s3.ingress.enabled }}
|
||||
value: "{{ printf "https://%s" .Values.s3.ingress.host }}"
|
||||
{{- else if .Values.s3.enabled }}
|
||||
value: "{{ printf "https://%s-s3.%s.svc" (include "seaweedfs.fullname" .) .Release.Namespace }}"
|
||||
value: "{{ printf "https://%s.%s.svc" (include "seaweedfs.componentName" (list . "s3")) .Release.Namespace }}"
|
||||
{{- else }}
|
||||
value: "{{ printf "https://%s-filer.%s.svc" (include "seaweedfs.fullname" .) .Release.Namespace }}"
|
||||
value: "{{ printf "https://%s.%s.svc" (include "seaweedfs.componentName" (list . "filer")) .Release.Namespace }}"
|
||||
{{- end }}
|
||||
{{- with .Values.cosi.region }}
|
||||
- name: REGION
|
||||
value: "{{ . }}"
|
||||
{{- end }}
|
||||
- name: SEAWEEDFS_FILER
|
||||
value: "{{ include "seaweedfs.fullname" . }}-filer:{{ .Values.filer.grpcPort }}"
|
||||
value: "{{ include "seaweedfs.componentName" (list . "filer") }}:{{ .Values.filer.grpcPort }}"
|
||||
{{- if .Values.global.enableSecurity }}
|
||||
- name: WEED_GRPC_CLIENT_KEY
|
||||
value: /usr/local/share/ca-certificates/client/tls.key
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{{- $filerEnabled := or .Values.filer.enabled .Values.allInOne.enabled }}
|
||||
{{- if and $filerEnabled .Values.filer.ingress.enabled }}
|
||||
{{- /* Determine service name based on deployment mode */}}
|
||||
{{- $serviceName := ternary (printf "%s-all-in-one" (include "seaweedfs.fullname" .)) (printf "%s-filer" (include "seaweedfs.fullname" .)) .Values.allInOne.enabled }}
|
||||
{{- $serviceName := ternary (include "seaweedfs.componentName" (list . "all-in-one")) (include "seaweedfs.componentName" (list . "filer")) .Values.allInOne.enabled }}
|
||||
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ include "seaweedfs.fullname" . }}-filer
|
||||
name: {{ include "seaweedfs.componentName" (list . "filer") }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "seaweedfs.name" . }}
|
||||
@@ -15,7 +15,7 @@ metadata:
|
||||
{{- toYaml .Values.filer.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
serviceName: {{ include "seaweedfs.fullname" . }}-filer
|
||||
serviceName: {{ include "seaweedfs.componentName" (list . "filer") }}
|
||||
podManagementPolicy: {{ .Values.filer.podManagementPolicy }}
|
||||
replicas: {{ .Values.filer.replicas }}
|
||||
{{- if (gt (int .Values.filer.updatePartition) 0) }}
|
||||
|
||||
@@ -33,15 +33,15 @@ spec:
|
||||
backend:
|
||||
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }}
|
||||
service:
|
||||
name: {{ include "seaweedfs.fullname" . }}-master
|
||||
name: {{ include "seaweedfs.componentName" (list . "master") }}
|
||||
port:
|
||||
number: {{ .Values.master.port }}
|
||||
#name:
|
||||
{{- else }}
|
||||
serviceName: {{ include "seaweedfs.fullname" . }}-master
|
||||
serviceName: {{ include "seaweedfs.componentName" (list . "master") }}
|
||||
servicePort: {{ .Values.master.port }}
|
||||
{{- end }}
|
||||
{{- if .Values.filer.ingress.host }}
|
||||
{{- if .Values.master.ingress.host }}
|
||||
host: {{ .Values.master.ingress.host }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -9,6 +9,7 @@ metadata:
|
||||
app.kubernetes.io/component: master
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
annotations:
|
||||
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
|
||||
{{- if .Values.master.annotations }}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ include "seaweedfs.fullname" . }}-master
|
||||
name: {{ include "seaweedfs.componentName" (list . "master") }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "seaweedfs.name" . }}
|
||||
@@ -15,7 +15,7 @@ metadata:
|
||||
{{- toYaml .Values.master.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
serviceName: {{ include "seaweedfs.fullname" . }}-master
|
||||
serviceName: {{ include "seaweedfs.componentName" (list . "master") }}
|
||||
podManagementPolicy: {{ .Values.master.podManagementPolicy }}
|
||||
replicas: {{ .Values.master.replicas }}
|
||||
{{- if (gt (int .Values.master.updatePartition) 0) }}
|
||||
@@ -183,7 +183,7 @@ spec:
|
||||
{{- if .Values.master.garbageThreshold }}
|
||||
-garbageThreshold={{ .Values.master.garbageThreshold }} \
|
||||
{{- end }}
|
||||
-ip=${POD_NAME}.${SEAWEEDFS_FULLNAME}-master.{{ .Release.Namespace }} \
|
||||
-ip=${POD_NAME}.{{ include "seaweedfs.componentName" (list . "master") }}.{{ .Release.Namespace }} \
|
||||
-peers={{ include "seaweedfs.masterServers" . }} \
|
||||
{{- range .Values.master.extraArgs }}
|
||||
{{ . }} \
|
||||
|
||||
@@ -149,7 +149,7 @@ spec:
|
||||
{{- if .Values.s3.auditLogConfig }}
|
||||
-auditLogConfig=/etc/sw/s3_auditLogConfig.json \
|
||||
{{- end }}
|
||||
-filer={{ include "seaweedfs.fullname" . }}-filer-client.{{ .Release.Namespace }}:{{ .Values.filer.port }} \
|
||||
-filer={{ include "seaweedfs.componentName" (list . "filer-client") }}.{{ .Release.Namespace }}:{{ .Values.filer.port }} \
|
||||
{{- range .Values.s3.extraArgs }}
|
||||
{{ . }} \
|
||||
{{- end }}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{{- $s3Enabled := or .Values.s3.enabled (and .Values.filer.s3.enabled (not .Values.allInOne.enabled)) (and .Values.allInOne.enabled .Values.allInOne.s3.enabled) }}
|
||||
{{- if and $s3Enabled .Values.s3.ingress.enabled }}
|
||||
{{- /* Determine service name based on deployment mode */}}
|
||||
{{- $serviceName := ternary (printf "%s-all-in-one" (include "seaweedfs.fullname" .)) (printf "%s-s3" (include "seaweedfs.fullname" .)) .Values.allInOne.enabled }}
|
||||
{{- $serviceName := ternary (include "seaweedfs.componentName" (list . "all-in-one")) (include "seaweedfs.componentName" (list . "s3")) .Values.allInOne.enabled }}
|
||||
{{- $s3Port := .Values.allInOne.s3.port | default .Values.s3.port }}
|
||||
{{- /* Build hosts list - support both legacy .host (string) and new .hosts (array) for backwards compatibility */}}
|
||||
{{- $hosts := list }}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
{{- if or (and (or .Values.s3.enabled .Values.allInOne.enabled) .Values.s3.enableAuth (not .Values.s3.existingConfigSecret)) (and .Values.filer.s3.enabled .Values.filer.s3.enableAuth (not .Values.filer.s3.existingConfigSecret)) }}
|
||||
{{- $secretName := printf "%s-s3-secret" (include "seaweedfs.fullname" .) }}
|
||||
{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace (default $secretName .Values.s3.legacySecretName) }}
|
||||
{{- $legacySecretName := "seaweedfs-s3-secret" }}
|
||||
{{- $lookupName := $secretName }}
|
||||
{{- if .Values.s3.reuseLegacySecret }}
|
||||
{{- $lookupName = default $legacySecretName .Values.s3.legacySecretName }}
|
||||
{{- end }}
|
||||
{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace $lookupName }}
|
||||
{{- $reuse := false }}
|
||||
{{- if and .Values.s3.reuseLegacySecret $existingSecret }}
|
||||
{{- $reuse = true }}
|
||||
|
||||
@@ -9,6 +9,7 @@ metadata:
|
||||
app.kubernetes.io/component: s3
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- if .Values.s3.annotations }}
|
||||
annotations:
|
||||
{{- toYaml .Values.s3.annotations | nindent 4 }}
|
||||
|
||||
@@ -171,7 +171,7 @@ spec:
|
||||
-key.file=/usr/local/share/ca-certificates/client/tls.key \
|
||||
{{- end }}
|
||||
-userStoreFile=/etc/sw/seaweedfs_sftp_config \
|
||||
-filer={{ include "seaweedfs.fullname" . }}-filer-client.{{ .Release.Namespace }}:{{ .Values.filer.port }}
|
||||
-filer={{ include "seaweedfs.componentName" (list . "filer-client") }}.{{ .Release.Namespace }}:{{ .Values.filer.port }}
|
||||
volumeMounts:
|
||||
{{- if or (eq .Values.sftp.logs.type "hostPath") (eq .Values.sftp.logs.type "emptyDir") }}
|
||||
- name: logs
|
||||
|
||||
@@ -17,6 +17,22 @@ be used as a full name.
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create a truncated component name.
|
||||
Usage: {{ include "seaweedfs.componentName" (list . "component-suffix") }}
|
||||
*/}}
|
||||
{{- define "seaweedfs.componentName" -}}
|
||||
{{- $context := index . 0 -}}
|
||||
{{- $suffix := index . 1 -}}
|
||||
{{- if gt (len $suffix) 61 -}}
|
||||
{{- fail (printf "Suffix '%s' is too long for componentName helper. Max length is 61." $suffix) -}}
|
||||
{{- end -}}
|
||||
{{- $fullname := include "seaweedfs.fullname" $context -}}
|
||||
{{- $maxLen := sub 62 (len $suffix) | int -}}
|
||||
{{- $truncatedFullname := trunc $maxLen $fullname | trimSuffix "-" -}}
|
||||
{{- printf "%s-%s" $truncatedFullname $suffix -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
@@ -261,7 +277,7 @@ If allInOne is enabled, point to the all-in-one service; otherwise, point to the
|
||||
{{- if .Values.allInOne.enabled -}}
|
||||
{{- $serviceNameSuffix = "-all-in-one" -}}
|
||||
{{- end -}}
|
||||
{{- printf "%s%s.%s:%d" (include "seaweedfs.fullname" .) $serviceNameSuffix .Release.Namespace (int .Values.master.port) -}}
|
||||
{{- printf "%s.%s:%d" (printf "%s%s" (include "seaweedfs.fullname" .) $serviceNameSuffix | trunc 63 | trimSuffix "-") .Release.Namespace (int .Values.master.port) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
@@ -273,19 +289,19 @@ If allInOne is enabled, point to the all-in-one service; otherwise, point to the
|
||||
{{- if .Values.allInOne.enabled -}}
|
||||
{{- $serviceNameSuffix = "-all-in-one" -}}
|
||||
{{- end -}}
|
||||
{{- printf "%s%s.%s:%d" (include "seaweedfs.fullname" .) $serviceNameSuffix .Release.Namespace (int .Values.filer.port) -}}
|
||||
{{- printf "%s.%s:%d" (printf "%s%s" (include "seaweedfs.fullname" .) $serviceNameSuffix | trunc 63 | trimSuffix "-") .Release.Namespace (int .Values.filer.port) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Generate comma-separated list of master server addresses.
|
||||
Usage: {{ include "seaweedfs.masterServers" . }}
|
||||
Output example: ${SEAWEEDFS_FULLNAME}-master-0.${SEAWEEDFS_FULLNAME}-master.namespace:9333,${SEAWEEDFS_FULLNAME}-master-1...
|
||||
Output example: my-release-master-0.my-release-master.namespace:9333,my-release-master-1...
|
||||
*/}}
|
||||
{{- define "seaweedfs.masterServers" -}}
|
||||
{{- $fullname := include "seaweedfs.fullname" . -}}
|
||||
{{- $masterName := include "seaweedfs.componentName" (list . "master") -}}
|
||||
{{- range $index := until (.Values.master.replicas | int) -}}
|
||||
{{- if $index }},{{ end -}}
|
||||
${SEAWEEDFS_FULLNAME}-master-{{ $index }}.${SEAWEEDFS_FULLNAME}-master.{{ $.Release.Namespace }}:{{ $.Values.master.port }}
|
||||
{{ $masterName }}-{{ $index }}.{{ $masterName }}.{{ $.Release.Namespace }}:{{ $.Values.master.port }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
|
||||
@@ -15,14 +15,18 @@
|
||||
{{- $existingConfigSecret = or .Values.allInOne.s3.existingConfigSecret .Values.s3.existingConfigSecret .Values.filer.s3.existingConfigSecret }}
|
||||
{{- end }}
|
||||
{{- else if .Values.master.enabled }}
|
||||
{{- /* Check standalone filer.s3 mode */}}
|
||||
{{- if .Values.filer.s3.enabled }}
|
||||
{{- /* Check if embedded (in filer) or standalone S3 gateway is enabled */}}
|
||||
{{- if or .Values.filer.s3.enabled .Values.s3.enabled }}
|
||||
{{- $s3Enabled = true }}
|
||||
{{- if .Values.filer.s3.createBuckets }}
|
||||
{{- if .Values.s3.createBuckets }}
|
||||
{{- $createBuckets = .Values.s3.createBuckets }}
|
||||
{{- $enableAuth = .Values.s3.enableAuth }}
|
||||
{{- $existingConfigSecret = .Values.s3.existingConfigSecret }}
|
||||
{{- else if .Values.filer.s3.createBuckets }}
|
||||
{{- $createBuckets = .Values.filer.s3.createBuckets }}
|
||||
{{- $enableAuth = .Values.filer.s3.enableAuth }}
|
||||
{{- $existingConfigSecret = .Values.filer.s3.existingConfigSecret }}
|
||||
{{- end }}
|
||||
{{- $enableAuth = .Values.filer.s3.enableAuth }}
|
||||
{{- $existingConfigSecret = .Values.filer.s3.existingConfigSecret }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -36,7 +40,7 @@ metadata:
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
annotations:
|
||||
"helm.sh/hook": post-install
|
||||
"helm.sh/hook": post-install,post-upgrade
|
||||
"helm.sh/hook-weight": "-5"
|
||||
"helm.sh/hook-delete-policy": hook-succeeded
|
||||
spec:
|
||||
@@ -105,9 +109,12 @@ spec:
|
||||
wait_for_service "http://$WEED_CLUSTER_SW_FILER{{ .Values.filer.readinessProbe.httpGet.path }}"
|
||||
{{- end }}
|
||||
{{- range $createBuckets }}
|
||||
/bin/echo \
|
||||
"s3.bucket.create --name {{ .name }}" |\
|
||||
/usr/bin/weed shell
|
||||
if /bin/echo "s3.bucket.list" | /usr/bin/weed shell | awk '{print $1}' | grep -Fxq "{{ .name }}"; then
|
||||
echo "Bucket '{{ .name }}' already exists, skipping creation."
|
||||
else
|
||||
echo "Creating bucket '{{ .name }}'..."
|
||||
/bin/echo "s3.bucket.create --name {{ .name }}" | /usr/bin/weed shell
|
||||
fi
|
||||
{{- end }}
|
||||
{{- range $createBuckets }}
|
||||
{{- if .anonymousRead }}
|
||||
|
||||
@@ -15,7 +15,6 @@ metadata:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
stringData:
|
||||
user: "YourSWUser"
|
||||
password: "HardCodedPassword"
|
||||
# better to random generate and create in DB
|
||||
# password: {{ randAlphaNum 10 | sha256sum | b64enc | trunc 32 }}
|
||||
# auto-generated password
|
||||
password: {{ randAlphaNum 10 | sha256sum | b64enc | trunc 32 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -48,7 +48,7 @@ data:
|
||||
{{- if .Values.global.securityConfig.jwtSigning.filerRead }}
|
||||
# If this JWT key is configured, Filer only accepts reads over HTTP if they are signed with this JWT:
|
||||
# - f.e. the S3 API Shim generates the JWT
|
||||
# - the Filer server validates the JWT on writing
|
||||
# - the Filer server validates the JWT on reading
|
||||
# the jwt defaults to expire after 10 seconds.
|
||||
[jwt.filer_signing.read]
|
||||
key = "{{ dig "jwt" "filer_signing" "read" "key" (randAlphaNum 10 | b64enc) $securityConfig }}"
|
||||
|
||||
@@ -42,11 +42,11 @@ spec:
|
||||
backend:
|
||||
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }}
|
||||
service:
|
||||
name: {{ include "seaweedfs.fullname" . }}-volume
|
||||
name: {{ include "seaweedfs.componentName" (list . "volume") }}
|
||||
port:
|
||||
number: {{ .Values.volume.port }}
|
||||
{{- else }}
|
||||
serviceName: {{ include "seaweedfs.fullname" . }}-volume
|
||||
serviceName: {{ include "seaweedfs.componentName" (list . "volume") }}
|
||||
servicePort: {{ .Values.volume.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ include "seaweedfs.fullname" $ }}-{{ $volumeName }}
|
||||
name: {{ include "seaweedfs.componentName" (list $ $volumeName) }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "seaweedfs.name" $ }}
|
||||
@@ -21,7 +21,7 @@ metadata:
|
||||
{{- toYaml $volume.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
serviceName: {{ include "seaweedfs.fullname" $ }}-{{ $volumeName }}
|
||||
serviceName: {{ include "seaweedfs.componentName" (list $ $volumeName) }}
|
||||
replicas: {{ $volume.replicas }}
|
||||
podManagementPolicy: {{ $volume.podManagementPolicy }}
|
||||
selector:
|
||||
@@ -197,7 +197,7 @@ spec:
|
||||
-fileSizeLimitMB={{ $volume.fileSizeLimitMB }} \
|
||||
{{- end }}
|
||||
-minFreeSpacePercent={{ $volume.minFreeSpacePercent }} \
|
||||
-ip=${POD_NAME}.${SEAWEEDFS_FULLNAME}-{{ $volumeName }}.{{ $.Release.Namespace }} \
|
||||
-ip=${POD_NAME}.{{ include "seaweedfs.componentName" (list $ $volumeName) }}.{{ $.Release.Namespace }} \
|
||||
-compactionMBps={{ $volume.compactionMBps }} \
|
||||
-master={{ include "seaweedfs.masterServerArg" $ }} \
|
||||
{{- range $volume.extraArgs }}
|
||||
|
||||
@@ -891,7 +891,7 @@ filer:
|
||||
# should have a secret key called seaweedfs_s3_config with an inline json configure
|
||||
existingConfigSecret: null
|
||||
auditLogConfig: {}
|
||||
# You may specify buckets to be created during the install process.
|
||||
# You may specify buckets to be created during the install or upgrade process.
|
||||
# Buckets may be exposed publicly by setting `anonymousRead` to `true`
|
||||
# createBuckets:
|
||||
# - name: bucket-a
|
||||
@@ -916,6 +916,13 @@ s3:
|
||||
# should have a secret key called seaweedfs_s3_config with an inline json config
|
||||
existingConfigSecret: null
|
||||
auditLogConfig: {}
|
||||
# You may specify buckets to be created during the install or upgrade process.
|
||||
# Buckets may be exposed publicly by setting `anonymousRead` to `true`
|
||||
# createBuckets:
|
||||
# - name: bucket-a
|
||||
# anonymousRead: true
|
||||
# - name: bucket-b
|
||||
# anonymousRead: false
|
||||
|
||||
# Suffix of the host name, {bucket}.{domainName}
|
||||
domainName: ""
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/seaweedfs/seaweedfs/weed/admin/view/layout"
|
||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/security"
|
||||
"github.com/seaweedfs/seaweedfs/weed/util"
|
||||
"github.com/seaweedfs/seaweedfs/weed/util/http/client"
|
||||
)
|
||||
@@ -121,7 +122,6 @@ func (h *FileBrowserHandlers) DeleteFile(c *gin.Context) {
|
||||
})
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete file: " + err.Error()})
|
||||
return
|
||||
@@ -228,7 +228,7 @@ func (h *FileBrowserHandlers) CreateFolder(c *gin.Context) {
|
||||
Name: filepath.Base(fullPath),
|
||||
IsDirectory: true,
|
||||
Attributes: &filer_pb.FuseAttributes{
|
||||
FileMode: uint32(0755 | os.ModeDir), // Directory mode
|
||||
FileMode: uint32(0o755 | os.ModeDir), // Directory mode
|
||||
Uid: filer_pb.OS_UID,
|
||||
Gid: filer_pb.OS_GID,
|
||||
Crtime: time.Now().Unix(),
|
||||
@@ -239,7 +239,6 @@ func (h *FileBrowserHandlers) CreateFolder(c *gin.Context) {
|
||||
})
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create folder: " + err.Error()})
|
||||
return
|
||||
@@ -407,6 +406,9 @@ func (h *FileBrowserHandlers) uploadFileToFiler(filePath string, fileHeader *mul
|
||||
// Set content type with boundary
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
// Add JWT Token to Authorization Header
|
||||
h.setupFilerJwtAuth(req, "jwt.filer_signing.key", "jwt.filer_signing.expires_after_seconds", "filer upload")
|
||||
|
||||
// Send request using TLS-aware HTTP client with 60s timeout for large file uploads
|
||||
// lgtm[go/ssrf]
|
||||
// Safe: filerAddress validated by validateFilerAddress() to match configured filer
|
||||
@@ -525,7 +527,12 @@ func (h *FileBrowserHandlers) fetchFileContent(filePath string, timeout time.Dur
|
||||
// Safe: filerAddress validated by validateFilerAddress() to match configured filer
|
||||
// Safe: cleanFilePath validated and cleaned by validateAndCleanFilePath() to prevent path traversal
|
||||
client := h.newClientWithTimeout(timeout)
|
||||
resp, err := client.Get(fileURL)
|
||||
req, err := http.NewRequest("GET", fileURL, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
h.addFilerJwtAuthHeader(req)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to fetch file from filer: %w", err)
|
||||
}
|
||||
@@ -595,6 +602,9 @@ func (h *FileBrowserHandlers) DownloadFile(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
client := h.newClientWithTimeout(5 * time.Minute) // Longer timeout for large file downloads
|
||||
|
||||
h.addFilerJwtAuthHeader(req)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": "Failed to fetch file from filer: " + err.Error()})
|
||||
@@ -687,7 +697,6 @@ func (h *FileBrowserHandlers) ViewFile(c *gin.Context) {
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get file metadata: " + err.Error()})
|
||||
return
|
||||
@@ -837,7 +846,6 @@ func (h *FileBrowserHandlers) GetFileProperties(c *gin.Context) {
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get file properties: " + err.Error()})
|
||||
return
|
||||
@@ -1032,7 +1040,13 @@ func (h *FileBrowserHandlers) isLikelyTextFile(filePath string, maxCheckSize int
|
||||
// Safe: filerAddress validated by validateFilerAddress() to match configured filer
|
||||
// Safe: cleanFilePath validated and cleaned by validateAndCleanFilePath() to prevent path traversal
|
||||
client := h.newClientWithTimeout(10 * time.Second)
|
||||
resp, err := client.Get(fileURL)
|
||||
req, err := http.NewRequest("GET", fileURL, nil)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to create request: %v", err)
|
||||
return false
|
||||
}
|
||||
h.addFilerJwtAuthHeader(req)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -1086,3 +1100,36 @@ func min(a, b int64) int64 {
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// setupFilerJwtAuth generates a JWT token and adds it to the request Authorization header if configured.
|
||||
func (h *FileBrowserHandlers) setupFilerJwtAuth(req *http.Request, keyPath, expiresPath, operation string) {
|
||||
// Load security configuration
|
||||
v := util.GetViper()
|
||||
|
||||
// Read Filer JWT token from security.toml
|
||||
signingKey := security.SigningKey(v.GetString(keyPath))
|
||||
expiresAfterSec := v.GetInt(expiresPath)
|
||||
|
||||
// Generate JWT token to authenticate with Filer
|
||||
var jwtToken security.EncodedJwt
|
||||
if len(signingKey) > 0 {
|
||||
jwtToken = security.GenJwtForFilerServer(signingKey, expiresAfterSec)
|
||||
glog.V(4).Infof("Generated JWT token for %s (expires in %d sec)", operation, expiresAfterSec)
|
||||
} else {
|
||||
if v.GetString("jwt.signing.key") != "" {
|
||||
glog.Warningf("JWT %s key not configured, but general JWT security is enabled. %s without authentication.", keyPath, operation)
|
||||
} else {
|
||||
glog.V(1).Infof("No JWT signing key configured, %s without authentication", operation)
|
||||
}
|
||||
}
|
||||
|
||||
// Add JWT Token to Authorization Header
|
||||
if jwtToken != "" {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", string(jwtToken)))
|
||||
glog.V(4).Infof("Added JWT authorization header for %s", operation)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *FileBrowserHandlers) addFilerJwtAuthHeader(req *http.Request) {
|
||||
h.setupFilerJwtAuth(req, "jwt.filer_signing.read.key", "jwt.filer_signing.read.expires_after_seconds", "filer request")
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:build !linux && !darwin
|
||||
// +build !linux,!darwin
|
||||
//go:build !darwin && !freebsd && !linux
|
||||
// +build !darwin,!freebsd,!linux
|
||||
|
||||
package command
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:build linux || darwin
|
||||
// +build linux darwin
|
||||
//go:build darwin || freebsd || linux
|
||||
// +build darwin freebsd linux
|
||||
|
||||
package command
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ expires_after_seconds = 10 # seconds
|
||||
|
||||
# If this JWT key is configured, Filer only accepts reads over HTTP if they are signed with this JWT:
|
||||
# - f.e. the S3 API Shim generates the JWT
|
||||
# - the Filer server validates the JWT on writing
|
||||
# - the Filer server validates the JWT on reading
|
||||
# the jwt defaults to expire after 10 seconds.
|
||||
[jwt.filer_signing.read]
|
||||
key = ""
|
||||
|
||||
@@ -306,6 +306,9 @@ func (wfs *WFS) lookupEntry(fullpath util.FullPath) (*filer.Entry, fuse.Status)
|
||||
glog.V(1).Infof("lookupEntry GetEntry %s: %v", fullpath, err)
|
||||
return nil, fuse.ENOENT
|
||||
}
|
||||
if entry != nil && entry.Attributes != nil && wfs.option.UidGidMapper != nil {
|
||||
entry.Attributes.Uid, entry.Attributes.Gid = wfs.option.UidGidMapper.FilerToLocal(entry.Attributes.Uid, entry.Attributes.Gid)
|
||||
}
|
||||
return filer.FromPbEntry(dir, entry), fuse.OK
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,13 @@ import (
|
||||
"github.com/seaweedfs/seaweedfs/weed/util"
|
||||
)
|
||||
|
||||
// closeEcVolumes closes all EC volumes in the given DiskLocation to release file handles.
|
||||
func closeEcVolumes(dl *DiskLocation) {
|
||||
for _, ecVol := range dl.ecVolumes {
|
||||
ecVol.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// TestIncompleteEcEncodingCleanup tests the cleanup logic for incomplete EC encoding scenarios
|
||||
func TestIncompleteEcEncodingCleanup(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -182,11 +189,18 @@ func TestIncompleteEcEncodingCleanup(t *testing.T) {
|
||||
t.Logf("loadAllEcShards returned error (expected in some cases): %v", loadErr)
|
||||
}
|
||||
|
||||
// Close EC volumes before idempotency test to avoid leaking file handles
|
||||
closeEcVolumes(diskLocation)
|
||||
diskLocation.ecVolumes = make(map[needle.VolumeId]*erasure_coding.EcVolume)
|
||||
|
||||
// Test idempotency - running again should not cause issues
|
||||
loadErr2 := diskLocation.loadAllEcShards(nil)
|
||||
if loadErr2 != nil {
|
||||
t.Logf("Second loadAllEcShards returned error: %v", loadErr2)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
closeEcVolumes(diskLocation)
|
||||
})
|
||||
|
||||
// Verify cleanup expectations
|
||||
if tt.expectCleanup {
|
||||
@@ -554,6 +568,9 @@ func TestEcCleanupWithSeparateIdxDirectory(t *testing.T) {
|
||||
if loadErr != nil {
|
||||
t.Logf("loadAllEcShards error: %v", loadErr)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
closeEcVolumes(diskLocation)
|
||||
})
|
||||
|
||||
// Verify cleanup occurred in data directory (shards)
|
||||
for i := 0; i < erasure_coding.TotalShardsCount; i++ {
|
||||
@@ -625,6 +642,9 @@ func TestDistributedEcVolumeNoFileDeletion(t *testing.T) {
|
||||
if loadErr != nil {
|
||||
t.Logf("loadAllEcShards returned error (expected): %v", loadErr)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
closeEcVolumes(diskLocation)
|
||||
})
|
||||
|
||||
// CRITICAL CHECK: Verify shard files still exist (should NOT be deleted)
|
||||
for i := 0; i < 5; i++ {
|
||||
|
||||
@@ -18,6 +18,7 @@ func TestFirstInvalidIndex(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("volume creation: %v", err)
|
||||
}
|
||||
defer v.Close()
|
||||
type WriteInfo struct {
|
||||
offset int64
|
||||
size int32
|
||||
|
||||
@@ -45,7 +45,10 @@ func newTestStore(t *testing.T, numDirs int) *Store {
|
||||
}
|
||||
}
|
||||
}()
|
||||
t.Cleanup(func() { close(done) })
|
||||
t.Cleanup(func() {
|
||||
store.Close()
|
||||
close(done)
|
||||
})
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ func TestReadNeedMetaWithWritesAndUpdates(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("volume creation: %v", err)
|
||||
}
|
||||
defer v.Close()
|
||||
type WriteInfo struct {
|
||||
offset int64
|
||||
size int32
|
||||
@@ -55,6 +56,7 @@ func TestReadNeedMetaWithDeletesThenWrites(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("volume creation: %v", err)
|
||||
}
|
||||
defer v.Close()
|
||||
type WriteInfo struct {
|
||||
offset int64
|
||||
size int32
|
||||
|
||||
@@ -119,6 +119,7 @@ func testCompaction(t *testing.T, needleMapKind NeedleMapKind) {
|
||||
if err != nil {
|
||||
t.Fatalf("volume reloading: %v", err)
|
||||
}
|
||||
defer v.Close()
|
||||
|
||||
for i := 1; i <= beforeCommitFileCount+afterCommitFileCount; i++ {
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ func TestSearchVolumesWithDeletedNeedles(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("volume creation: %v", err)
|
||||
}
|
||||
defer v.Close()
|
||||
|
||||
count := 20
|
||||
|
||||
@@ -119,6 +120,7 @@ func TestDestroyNonemptyVolumeWithOnlyEmpty(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("volume creation: %v", err)
|
||||
}
|
||||
defer v.Close()
|
||||
path := v.DataBackend.Name()
|
||||
|
||||
// should return "volume not empty" error and do not delete file when Destroy non-empty volume
|
||||
|
||||
Reference in New Issue
Block a user