fix(growing): inline CIFS creds + capture caps + storage probe timeout
Three fixes to restore growing-files (XDCAM HD422 MXF) recording: 1. capture-manager mountGrowingShare: pass username=/password= inline instead of a credentials= file. TrueNAS SMB3 rejects the creds-file form with EACCES (-13, 'cannot mount read-only') while the identical inline creds mount fine. This was causing every growing record to silently fall back to the HEVC/S3 path (producing .mov, not .mxf). 2. docker-compose capture: add cap_add SYS_ADMIN + DAC_READ_SEARCH and apparmor:unconfined so mount.cifs can run inside the container. 3. storage /overview: wrap S3 HeadBucket/ListObjects probe in a 5s timeout so the admin 'Mount health' card stops hanging on 'Probing…' forever when S3 is slow.
This commit is contained in:
parent
2812705d1c
commit
cb25711ec6
3 changed files with 25 additions and 8 deletions
|
|
@ -120,6 +120,15 @@ services:
|
|||
profiles: [capture]
|
||||
restart: unless-stopped
|
||||
runtime: nvidia
|
||||
# Growing-files mode mounts an SMB/CIFS share inside the container
|
||||
# (mount.cifs). That syscall needs CAP_SYS_ADMIN + DAC_READ_SEARCH and an
|
||||
# unconfined AppArmor profile; without these the mount fails with
|
||||
# "Unable to apply new capability set" and growing falls back to HEVC/S3.
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
- DAC_READ_SEARCH
|
||||
security_opt:
|
||||
- apparmor:unconfined
|
||||
environment:
|
||||
REDIS_URL: ${REDIS_URL}
|
||||
DATABASE_URL: ${DATABASE_URL}
|
||||
|
|
|
|||
|
|
@ -64,13 +64,14 @@ function mountGrowingShare() {
|
|||
return true;
|
||||
}
|
||||
try { mkdirSync(GROWING_PATH, { recursive: true }); } catch (_) {}
|
||||
writeFileSync(
|
||||
SMB_CREDS_FILE,
|
||||
`username=${GROWING_SMB_USERNAME}\npassword=${GROWING_SMB_PASSWORD}\n`,
|
||||
{ mode: 0o600 }
|
||||
);
|
||||
// Pass credentials inline rather than via a credentials= file. Some SMB
|
||||
// servers (notably TrueNAS SMB3) reject the credentials-file form with
|
||||
// EACCES (-13) — "cannot mount ... read-only" — even though the very same
|
||||
// username/password mount inline and smbclient lists the share fine. Inline
|
||||
// user=/password= is the reliable form here.
|
||||
const opts = [
|
||||
`credentials=${SMB_CREDS_FILE}`,
|
||||
`username=${GROWING_SMB_USERNAME}`,
|
||||
`password=${GROWING_SMB_PASSWORD}`,
|
||||
'uid=0', 'gid=0', 'file_mode=0664', 'dir_mode=0775',
|
||||
`vers=${GROWING_SMB_VERS}`,
|
||||
].join(',');
|
||||
|
|
|
|||
|
|
@ -78,14 +78,21 @@ async function probeS3Bucket() {
|
|||
if (!bucket) { out.error = 'no bucket configured'; return out; }
|
||||
|
||||
const started = Date.now();
|
||||
// Hard cap the whole probe so the admin "Mount health" card never hangs on
|
||||
// "Probing…" when S3 is slow/unreachable. Without this, the SDK's default
|
||||
// retry/backoff can block the request for tens of seconds.
|
||||
const withTimeout = (p, ms) => Promise.race([
|
||||
p,
|
||||
new Promise((_, rej) => setTimeout(() => rej(new Error('probe timed out after ' + ms + 'ms')), ms)),
|
||||
]);
|
||||
try {
|
||||
await s3Client.send(new HeadBucketCommand({ Bucket: bucket }));
|
||||
await withTimeout(s3Client.send(new HeadBucketCommand({ Bucket: bucket })), 5000);
|
||||
out.reachable = true;
|
||||
out.method = 'HeadBucket';
|
||||
} catch (headErr) {
|
||||
// Fall back to a 0-key list for stores that don't expose HeadBucket.
|
||||
try {
|
||||
await s3Client.send(new ListObjectsV2Command({ Bucket: bucket, MaxKeys: 0 }));
|
||||
await withTimeout(s3Client.send(new ListObjectsV2Command({ Bucket: bucket, MaxKeys: 0 })), 5000);
|
||||
out.reachable = true;
|
||||
out.method = 'ListObjectsV2';
|
||||
} catch (listErr) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue