d11e3940fa
smolvm pack create --from-vm requires the VM to be stopped. Add machine_is_running() to smolvm.py (via machine ls --json state field), and add the same confirm-stop flow to SmolmachinesFreezer that was originally designed for macos-container: if running, prompt the user, stop the VM, then pack. Already-stopped VMs are packed directly.
53 lines
1.9 KiB
Python
53 lines
1.9 KiB
Python
"""SmolmachinesFreezer — snapshot a smolmachines bottle via smolvm pack."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
|
|
from .. import ActiveAgent
|
|
from ..freeze import CommitCancelled, Freezer
|
|
from .smolvm import machine_is_running, machine_stop, pack_create_from_vm
|
|
from ...bottle_state import bottle_state_dir
|
|
from ...log import info
|
|
|
|
|
|
def _read_tty_line() -> str:
|
|
"""Read one line from /dev/tty, falling back to stdin."""
|
|
try:
|
|
with open("/dev/tty", "r", encoding="utf-8") as tty:
|
|
return tty.readline().rstrip("\n")
|
|
except OSError:
|
|
return sys.stdin.readline().rstrip("\n")
|
|
|
|
|
|
class SmolmachinesFreezer(Freezer):
|
|
"""Freezes a smolmachines bottle via `smolvm pack create --from-vm`.
|
|
|
|
`smolvm pack create --from-vm` requires the VM to be stopped first.
|
|
If the VM is running the freezer prompts the user to confirm the stop
|
|
before proceeding. The VM remains stopped after commit; use
|
|
`./cli.py resume` to restart."""
|
|
|
|
backend_name = "smolmachines"
|
|
|
|
def _freeze(self, agent: ActiveAgent) -> str:
|
|
machine = f"bot-bottle-{agent.slug}"
|
|
output = bottle_state_dir(agent.slug) / "committed-smolmachine"
|
|
output.parent.mkdir(parents=True, exist_ok=True)
|
|
if machine_is_running(machine):
|
|
sys.stderr.write(
|
|
f"bot-bottle: bottle {agent.slug!r} is running; "
|
|
"commit will stop it. Continue? [y/N] "
|
|
)
|
|
sys.stderr.flush()
|
|
reply = _read_tty_line().strip().lower()
|
|
if reply not in ("y", "yes"):
|
|
raise CommitCancelled
|
|
machine_stop(machine)
|
|
pack_create_from_vm(machine, output)
|
|
artifact = output.with_name(f"{output.name}.smolmachine")
|
|
return str(artifact)
|
|
|
|
def _export_hint(self, slug: str, image_ref: str) -> None:
|
|
info(f"to export for migration: cp {image_ref} {slug}.smolmachine")
|