From 47f3b7e0674e4a9dc80597de00b53bba21cb848a Mon Sep 17 00:00:00 2001 From: chenpei Date: Sat, 15 Aug 2026 18:31:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=85=BC=E5=AE=B9=E6=97=A7=E5=AE=B9?= =?UTF-8?q?=E5=99=A8=E4=B8=8A=E4=BC=A0=E6=8C=82=E8=BD=BD=E5=88=AB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video-transcode-deploy-drain.test.ts | 25 ++++++++++++++++++- scripts/git-incremental-deploy.ps1 | 24 +++++++++++++++--- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/lib/__tests__/video-transcode-deploy-drain.test.ts b/lib/__tests__/video-transcode-deploy-drain.test.ts index d1e95bd..d345fd7 100644 --- a/lib/__tests__/video-transcode-deploy-drain.test.ts +++ b/lib/__tests__/video-transcode-deploy-drain.test.ts @@ -155,7 +155,7 @@ describe('production video transcode drain gate', () => { ).status).not.toBe(0) }) - test('Docker Mounts 模拟拒绝 volume、错 source、只读和重复 /app/uploads', () => { + test('Docker Mounts 模拟拒绝 volume、错 source、只读和重复 /app/uploads;旧容器仅可用已解析到同一数据盘的别名', () => { const bash = findBash() expect(bash).not.toBeNull() if (!bash) return @@ -177,6 +177,29 @@ describe('production video transcode drain gate', () => { expect(run( `/app/uploads\tbind\t${expectedSource}\ttrue\n/app/uploads\tbind\t${expectedSource}\ttrue`, ).status).not.toBe(0) + + const legacySource = '/mnt/PoolOne/apps/youyidian-3d/uploads' + const aliasHelper = ` +readlink() { + if [ "$3" = "${legacySource}" ]; then + printf '%s\\n' "${expectedSource}" + else + printf '%s\\n' "$3" + fi +} +${helper}` + const runWithResolvedAlias = (allowAlias: string) => runBashGate( + bash, + aliasHelper, + 'uploads_container_mount_metadata_is_trusted "$EXPECTED_SOURCE" "$CASE_METADATA" "$ALLOW_ALIAS"', + { + EXPECTED_SOURCE: expectedSource, + CASE_METADATA: `/app/uploads\tbind\t${legacySource}\ttrue`, + ALLOW_ALIAS: allowAlias, + }, + ) + expect(runWithResolvedAlias('0').status).not.toBe(0) + expect(runWithResolvedAlias('1').status).toBe(0) }) test('statfs 模拟必须拒绝宿主根盘签名和宿主/容器不一致', () => { diff --git a/scripts/git-incremental-deploy.ps1 b/scripts/git-incremental-deploy.ps1 index 6219cfe..cf307db 100644 --- a/scripts/git-incremental-deploy.ps1 +++ b/scripts/git-incremental-deploy.ps1 @@ -1212,6 +1212,7 @@ statfs_signatures_match() { uploads_container_mount_metadata_is_trusted() { local expected_source="$1" local metadata="$2" + local allow_resolved_source_alias="${3:-0}" local matching_metadata="" local matching_count="" local destination="" @@ -1219,6 +1220,8 @@ uploads_container_mount_metadata_is_trusted() { local source="" local read_write="" local extra="" + local resolved_source="" + local resolved_expected_source="" [ -n "$metadata" ] || return 1 case "$metadata" in *$'\r'*) return 1 ;; esac @@ -1229,7 +1232,17 @@ uploads_container_mount_metadata_is_trusted() { IFS=$'\t' read -r destination mount_type source read_write extra <<< "$matching_metadata" [ "$destination" = "/app/uploads" ] || return 1 [ "$mount_type" = "bind" ] || return 1 - [ "$source" = "$expected_source" ] || return 1 + if [ "$source" != "$expected_source" ]; then + # Old production containers were created before UPLOADS_MOUNT was + # canonicalised. They may retain the former path spelling in Docker + # metadata, while resolving to exactly the same verified ZFS dataset. + # This compatibility is for draining an existing container only; newly + # created candidates always use the canonical string above. + [ "$allow_resolved_source_alias" = "1" ] || return 1 + resolved_source="$(readlink -f -- "$source")" || return 1 + resolved_expected_source="$(readlink -f -- "$expected_source")" || return 1 + [ "$resolved_source" = "$resolved_expected_source" ] || return 1 + fi [ "$read_write" = "true" ] || return 1 [ -z "$extra" ] } @@ -1280,13 +1293,14 @@ container_uploads_mount_metadata() { verify_container_uploads_mount_metadata() { local name="$1" + local allow_resolved_source_alias="${2:-0}" local metadata="" if ! metadata="$(container_uploads_mount_metadata "$name")"; then warn "Cannot inspect uploads mount metadata for container ${name}" return 1 fi - if ! uploads_container_mount_metadata_is_trusted "$UPLOADS_MOUNT" "$metadata"; then + if ! uploads_container_mount_metadata_is_trusted "$UPLOADS_MOUNT" "$metadata" "$allow_resolved_source_alias"; then warn "Container ${name} does not have exactly one trusted RW bind mount at /app/uploads" return 1 fi @@ -1315,7 +1329,7 @@ verify_container_upload_dir_config() { if [ "$count" = "0" ]; then return 0 fi - if [ "$count" = "1" ] && [ "$value" = "/app/uploads" ]; then + if [ "$count" = "1" ] && { [ "$value" = "/app/uploads" ] || [ "$value" = "./uploads" ]; }; then return 0 fi warn "Existing container ${name} has an unsafe UPLOAD_DIR override" @@ -1354,7 +1368,9 @@ verify_container_uploads_statfs() { verify_container_uploads_storage() { local name="$1" local require_exact="${2:-0}" - verify_container_uploads_mount_metadata "$name" || return 1 + local allow_resolved_source_alias="0" + [ "$require_exact" = "1" ] || allow_resolved_source_alias="1" + verify_container_uploads_mount_metadata "$name" "$allow_resolved_source_alias" || return 1 verify_container_uploads_statfs "$name" || return 1 verify_container_upload_dir_runtime "$name" "$require_exact" }