diff --git a/docs.json b/docs.json index 60e17679..0d81fff0 100644 --- a/docs.json +++ b/docs.json @@ -197,7 +197,8 @@ "docs/volumes/read-write", "docs/volumes/info", "docs/volumes/upload", - "docs/volumes/download" + "docs/volumes/download", + "docs/volumes/migrate" ] }, { diff --git a/docs/volumes.mdx b/docs/volumes.mdx index 8f98d378..35e8ba49 100644 --- a/docs/volumes.mdx +++ b/docs/volumes.mdx @@ -1,5 +1,6 @@ --- title: "Volumes" +description: "Persistent storage that exists independently of sandboxes and can be mounted across multiple sandboxes." sidebarTitle: Overview --- diff --git a/docs/volumes/download.mdx b/docs/volumes/download.mdx index e2f0ef2a..aea31cc6 100644 --- a/docs/volumes/download.mdx +++ b/docs/volumes/download.mdx @@ -1,5 +1,6 @@ --- title: "Download data from volume" +description: "Download files from a volume to your local filesystem with the E2B SDK." sidebarTitle: Download data --- diff --git a/docs/volumes/info.mdx b/docs/volumes/info.mdx index 91c3c7ef..2700a43e 100644 --- a/docs/volumes/info.mdx +++ b/docs/volumes/info.mdx @@ -1,5 +1,6 @@ --- title: "Get information about a file or directory" +description: "Get metadata, check existence, and update permissions for files and directories in a volume." sidebarTitle: File & directory metadata --- diff --git a/docs/volumes/manage.mdx b/docs/volumes/manage.mdx index 08bbd6c0..6794f506 100644 --- a/docs/volumes/manage.mdx +++ b/docs/volumes/manage.mdx @@ -1,5 +1,6 @@ --- title: "Managing volumes" +description: "Create, connect to, list, inspect, and destroy volumes with the E2B SDK." --- ## Create a volume diff --git a/docs/volumes/migrate.mdx b/docs/volumes/migrate.mdx new file mode 100644 index 00000000..be401367 --- /dev/null +++ b/docs/volumes/migrate.mdx @@ -0,0 +1,168 @@ +--- +title: "Migrating data between volumes" +description: "Copy all data from one E2B volume to another by mounting both into a sandbox and using rsync." +--- + +When you need to move data from an old volume to a new one, the simplest approach is to mount both volumes into a single sandbox and copy the files across. This page walks through a script that does exactly that: it spins up a sandbox from the **base** template, mounts a source and a destination volume, and copies all data from source to destination. + +- The **source** volume must already exist. If it doesn't, the script fails. +- The **destination** volume is reused if it already exists, or created if it doesn't. + +## Prerequisites + +You need an E2B account with your API key set as `E2B_API_KEY` in your environment, and the E2B SDK installed. + + +```bash JavaScript & TypeScript +npm install e2b +``` +```bash Python +pip install e2b +``` + + +## The script + +The script first resolves both volumes by name, connecting to the source and connecting to (or creating) the destination. It then mounts both into a sandbox and uses [rsync](https://rsync.samba.org/) to copy the data across. Reading the volume type from the token is optional, but it's a handy sanity check that you're copying between the volumes you expect. + +Save this as `copy-volume.mts` (JavaScript & TypeScript) or `copy_volume.py` (Python): + + +```ts JavaScript & TypeScript +import { Sandbox, Volume } from 'e2b' + +const [sourceName, destName] = process.argv.slice(2) +if (!sourceName || !destName) { + throw new Error('Usage: copy-volume ') +} + +async function findVolumeByName(name: string) { + const volumes = await Volume.list() + return volumes.find((v) => v.name === name) ?? null +} + +const sourceInfo = await findVolumeByName(sourceName) +if (!sourceInfo) { + throw new Error(`Source volume "${sourceName}" does not exist`) +} +const sourceVolume = await Volume.connect(sourceInfo.volumeId) + +const destInfo = await findVolumeByName(destName) +const destVolume = destInfo + ? await Volume.connect(destInfo.volumeId) + : await Volume.create(destName) + +function voltype(token: string): string { + // JWT payloads are base64url and often omit padding, so normalize before decoding + let segment = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/') + segment += '='.repeat((4 - (segment.length % 4)) % 4) + const payload = JSON.parse(atob(segment)) + return payload.voltype +} + +console.log(`Source volume type: ${voltype(sourceVolume.token)}`) +console.log(`Destination volume type: ${voltype(destVolume.token)}`) + +const sandbox = await Sandbox.create({ + volumeMounts: { + '/mnt/source': sourceVolume, + '/mnt/dest': destVolume, + }, +}) + +try { + const install = await sandbox.commands.run( + 'sudo apt-get update && sudo apt-get install -y rsync', + ) + if (install.exitCode !== 0) { + throw new Error(`rsync install failed: ${install.stderr}`) + } + + const result = await sandbox.commands.run('rsync -a /mnt/source/ /mnt/dest/') + if (result.exitCode !== 0) { + throw new Error(`Copy failed: ${result.stderr}`) + } + + const listing = await sandbox.commands.run('ls -la /mnt/dest') + console.log(listing.stdout) +} finally { + await sandbox.kill() +} +``` +```python Python +import base64 +import json +import sys + +from e2b import Sandbox, Volume + +args = sys.argv[1:] +if len(args) != 2: + raise SystemExit('Usage: copy_volume ') +source_name, dest_name = args + + +def find_volume_by_name(name): + return next((v for v in Volume.list() if v.name == name), None) + + +def voltype(token): + payload = token.split('.')[1] + payload += '=' * (-len(payload) % 4) # JWT segments omit base64 padding + return json.loads(base64.urlsafe_b64decode(payload))['voltype'] + + +source_info = find_volume_by_name(source_name) +if source_info is None: + raise SystemExit(f'Source volume "{source_name}" does not exist') +source_volume = Volume.connect(source_info.volume_id) + +dest_info = find_volume_by_name(dest_name) +dest_volume = ( + Volume.connect(dest_info.volume_id) if dest_info else Volume.create(dest_name) +) + +print(f'Source volume type: {voltype(source_volume.token)}') +print(f'Destination volume type: {voltype(dest_volume.token)}') + +sandbox = Sandbox.create( + volume_mounts={ + '/mnt/source': source_volume, + '/mnt/dest': dest_volume, + }, +) + +try: + install = sandbox.commands.run( + 'sudo apt-get update && sudo apt-get install -y rsync', + ) + if install.exit_code != 0: + raise RuntimeError(f'rsync install failed: {install.stderr}') + + result = sandbox.commands.run('rsync -a /mnt/source/ /mnt/dest/') + if result.exit_code != 0: + raise RuntimeError(f'Copy failed: {result.stderr}') + + listing = sandbox.commands.run('ls -la /mnt/dest') + print(listing.stdout) +finally: + sandbox.kill() +``` + + +## Usage + +Run the script with the source and destination volume names. For example, to copy everything from `prod-data` into `prod-data-backup`: + + +```bash JavaScript & TypeScript +export E2B_API_KEY=your-api-key +npx tsx copy-volume.mts prod-data prod-data-backup +``` +```bash Python +export E2B_API_KEY=your-api-key +python copy_volume.py prod-data prod-data-backup +``` + + +On success it prints a listing of the destination so you can confirm the data landed. The sandbox is always shut down afterward, but both volumes (and their data) persist. diff --git a/docs/volumes/mount.mdx b/docs/volumes/mount.mdx index ab02a12d..8f199882 100644 --- a/docs/volumes/mount.mdx +++ b/docs/volumes/mount.mdx @@ -1,5 +1,6 @@ --- title: "Mounting volumes" +description: "Mount one or more volumes to a sandbox at custom paths when creating it." --- You can mount one or more volumes to a sandbox when creating it. The keys of the `volumeMounts` / `volume_mounts` object are the mount paths inside the sandbox. diff --git a/docs/volumes/read-write.mdx b/docs/volumes/read-write.mdx index 43838b6c..cd644e7e 100644 --- a/docs/volumes/read-write.mdx +++ b/docs/volumes/read-write.mdx @@ -1,5 +1,6 @@ --- title: "Read & write files" +description: "Read, write, list, and remove files and directories in a volume with the SDK." sidebarTitle: Read & write --- diff --git a/docs/volumes/upload.mdx b/docs/volumes/upload.mdx index dee64c68..6ac64e48 100644 --- a/docs/volumes/upload.mdx +++ b/docs/volumes/upload.mdx @@ -1,5 +1,6 @@ --- title: "Upload data to volume" +description: "Upload single files or entire directories from your local filesystem to a volume." sidebarTitle: Upload data ---