-
Notifications
You must be signed in to change notification settings - Fork 49
Closed
Description
Summary
Add support for mounting a specific subdirectory (prefix) within an S3 bucket, rather than only supporting mounting the entire bucket root.
s3fs-fuse Support
s3fs-fuse already supports this via the bucket path syntax:
s3fs mybucket:/path/prefix/ /mountpoint
Reference: https://github.com/s3fs-fuse/s3fs-fuse/wiki/Fuse-Over-Amazon#prefix-default-coming-soon
"For now, you can specify via
s3fs mybucket:/path/prefix/"
Proposed API
// Mount entire bucket (current behavior)
await sandbox.mountBucket('my-bucket', '/mnt/data', options);
// Mount specific subdirectory (new behavior)
await sandbox.mountBucket('my-bucket', '/mnt/data', {
...options,
prefix: '/123/data/'
});Current Workaround
I've implemented this locally via a patch, just putting the prefix in with the bucket name.
diff --git a/dist/index.js b/dist/index.js
index 6cc68fe..0e24855 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -2700,14 +2700,15 @@ var Sandbox = class extends Container {
};
}
async mountBucket(bucket, mountPath, options) {
- this.logger.info(`Mounting bucket ${bucket} to ${mountPath}`);
+ const { bucketName, s3fsSource } = this.parseBucketSource(bucket);
+ this.logger.info(`Mounting bucket ${bucketName} to ${mountPath}`);
this.validateMountOptions(bucket, mountPath, options);
const provider = options.provider || detectProviderFromUrl(options.endpoint);
this.logger.debug(`Detected provider: ${provider || "unknown"}`, { explicitProvider: options.provider });
const credentials = detectCredentials(options, this.envVars);
const passwordFilePath = this.generatePasswordFilePath();
this.activeMounts.set(mountPath, {
- bucket,
+ bucket: s3fsSource,
mountPath,
endpoint: options.endpoint,
provider,
@@ -2715,9 +2716,9 @@ var Sandbox = class extends Container {
mounted: false
});
try {
- await this.createPasswordFile(passwordFilePath, bucket, credentials);
+ await this.createPasswordFile(passwordFilePath, bucketName, credentials);
await this.exec(`mkdir -p ${shellEscape(mountPath)}`);
- await this.executeS3FSMount(bucket, mountPath, options, provider, passwordFilePath);
+ await this.executeS3FSMount(s3fsSource, mountPath, options, provider, passwordFilePath);
this.activeMounts.set(mountPath, {
bucket,
mountPath,
@@ -2752,6 +2753,14 @@ var Sandbox = class extends Container {
}
this.logger.info(`Successfully unmounted bucket from ${mountPath}`);
}
+parseBucketSource(bucket) {
+ const idx = bucket.indexOf(':/');
+ if (idx === -1) {
+ return { bucketName: bucket, s3fsSource: bucket };
+ }
+ return { bucketName: bucket.slice(0, idx), s3fsSource: bucket };
+}
+
/**
* Validate mount options
*/
@@ -2762,7 +2771,7 @@ var Sandbox = class extends Container {
} catch (error) {
throw new InvalidMountConfigError(`Invalid endpoint URL: "${options.endpoint}". Must be a valid HTTP(S) URL.`);
}
- if (!/^[a-z0-9]([a-z0-9.-]{0,61}[a-z0-9])?$/.test(bucket)) throw new InvalidMountConfigError(`Invalid bucket name: "${bucket}". Bucket names must be 3-63 characters, lowercase alphanumeric, dots, or hyphens, and cannot start/end with dots or hyphens.`);
+ if (!/^[a-z0-9]([a-z0-9.-]{0,61}[a-z0-9])?(?::\/.*\/)?$/.test(bucket)) throw new InvalidMountConfigError(`Invalid bucket name: "${bucket}". Bucket names must be 3-63 characters, lowercase alphanumeric, dots, or hyphens, and cannot start/end with dots or hyphens.`);
if (!mountPath.startsWith("/")) throw new InvalidMountConfigError(`Mount path must be absolute (start with /): "${mountPath}"`);
if (this.activeMounts.has(mountPath)) throw new InvalidMountConfigError(`Mount path "${mountPath}" is already in use by bucket "${this.activeMounts.get(mountPath)?.bucket}". Unmount the existing bucket first or use a different mount path.`);
}Metadata
Metadata
Assignees
Labels
No labels