mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-02 19:55:18 +02:00
rewrite snapshot script: use run_commands, dotenv, idempotent delete
This commit is contained in:
parent
c4a5ac4144
commit
a71338e368
1 changed files with 62 additions and 26 deletions
|
|
@ -1,16 +1,32 @@
|
||||||
"""Create the Daytona snapshot used by SurfSense sandboxes.
|
"""Create the Daytona snapshot used by SurfSense code-execution sandboxes.
|
||||||
|
|
||||||
Usage:
|
Run from the backend directory:
|
||||||
|
cd surfsense_backend
|
||||||
uv run python scripts/create_sandbox_snapshot.py
|
uv run python scripts/create_sandbox_snapshot.py
|
||||||
|
|
||||||
Requires DAYTONA_API_KEY (and optionally DAYTONA_API_URL / DAYTONA_TARGET)
|
Prerequisites:
|
||||||
to be set in the environment or in a .env file.
|
- DAYTONA_API_KEY set in surfsense_backend/.env (or exported in shell)
|
||||||
|
- DAYTONA_API_URL=https://app.daytona.io/api
|
||||||
|
- DAYTONA_TARGET=us (or eu)
|
||||||
|
|
||||||
|
After this script succeeds, add to surfsense_backend/.env:
|
||||||
|
DAYTONA_SNAPSHOT_ID=surfsense-sandbox
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from daytona import CreateSnapshotParams, Daytona, DaytonaConfig, Image
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
_here = Path(__file__).parent
|
||||||
|
for candidate in [_here / "../surfsense_backend/.env", _here / ".env", _here / "../.env"]:
|
||||||
|
if candidate.exists():
|
||||||
|
load_dotenv(candidate)
|
||||||
|
break
|
||||||
|
|
||||||
|
from daytona import CreateSnapshotParams, Daytona, Image # noqa: E402
|
||||||
|
|
||||||
SNAPSHOT_NAME = "surfsense-sandbox"
|
SNAPSHOT_NAME = "surfsense-sandbox"
|
||||||
|
|
||||||
|
|
@ -23,38 +39,58 @@ PACKAGES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def build_image() -> Image:
|
||||||
config = DaytonaConfig(
|
"""Build the sandbox image with data-science packages and a /documents symlink."""
|
||||||
api_key=os.environ.get("DAYTONA_API_KEY", ""),
|
return (
|
||||||
api_url=os.environ.get("DAYTONA_API_URL", "https://app.daytona.io/api"),
|
|
||||||
target=os.environ.get("DAYTONA_TARGET", "us"),
|
|
||||||
)
|
|
||||||
daytona = Daytona(config)
|
|
||||||
|
|
||||||
image = (
|
|
||||||
Image.debian_slim("3.12")
|
Image.debian_slim("3.12")
|
||||||
.pip_install(*PACKAGES)
|
.pip_install(*PACKAGES)
|
||||||
# The agent's virtual filesystem serves documents at /documents/.
|
# Symlink /documents → /home/daytona/documents so the LLM can use
|
||||||
# This symlink lets code inside the sandbox use the same path.
|
# the same /documents/ path it sees in the virtual filesystem.
|
||||||
.run("mkdir -p /home/daytona/documents && ln -sf /home/daytona/documents /documents")
|
.run_commands(
|
||||||
|
"mkdir -p /home/daytona/documents",
|
||||||
|
"ln -sfn /home/daytona/documents /documents",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
api_key = os.environ.get("DAYTONA_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
print("ERROR: DAYTONA_API_KEY is not set.", file=sys.stderr)
|
||||||
|
print("Add it to surfsense_backend/.env or export it in your shell.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
daytona = Daytona()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
existing = daytona.snapshot.get(SNAPSHOT_NAME)
|
existing = daytona.snapshot.get(SNAPSHOT_NAME)
|
||||||
print(f"Deleting existing snapshot '{SNAPSHOT_NAME}'...")
|
print(f"Deleting existing snapshot '{SNAPSHOT_NAME}' …")
|
||||||
daytona.snapshot.delete(existing)
|
daytona.snapshot.delete(existing)
|
||||||
print("Deleted.")
|
print(f"Deleted '{SNAPSHOT_NAME}'. Waiting for removal to propagate …")
|
||||||
|
for attempt in range(30):
|
||||||
|
time.sleep(2)
|
||||||
|
try:
|
||||||
|
daytona.snapshot.get(SNAPSHOT_NAME)
|
||||||
|
except Exception:
|
||||||
|
print(f"Confirmed '{SNAPSHOT_NAME}' is gone.\n")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(f"WARNING: '{SNAPSHOT_NAME}' may still exist after 60s. Proceeding anyway.\n")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print(f"Creating snapshot '{SNAPSHOT_NAME}' with packages: {', '.join(PACKAGES)}")
|
print(f"Building snapshot '{SNAPSHOT_NAME}' …")
|
||||||
snapshot = daytona.snapshot.create(
|
print(f"Packages: {', '.join(PACKAGES)}\n")
|
||||||
CreateSnapshotParams(name=SNAPSHOT_NAME, image=image),
|
|
||||||
on_logs=lambda chunk: print(chunk, end=""),
|
daytona.snapshot.create(
|
||||||
|
CreateSnapshotParams(name=SNAPSHOT_NAME, image=build_image()),
|
||||||
|
on_logs=lambda chunk: print(chunk, end="", flush=True),
|
||||||
)
|
)
|
||||||
print(f"\nSnapshot created: {snapshot.name}")
|
|
||||||
print(f"Set DAYTONA_SNAPSHOT_ID={snapshot.name} in your .env")
|
print(f"\n\nSnapshot '{SNAPSHOT_NAME}' is ready.")
|
||||||
|
print("\nAdd this to surfsense_backend/.env:")
|
||||||
|
print(f" DAYTONA_SNAPSHOT_ID={SNAPSHOT_NAME}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(main() or 0)
|
main()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue