feat(boxlite): make box disk size configurable (#4072)
The boxlite SDK's BoxOptions already supports disk_size_gb, but the omnigent wrapper never threaded it through — every box got the SDK's own default disk size with no way to override it. Add sandbox.boxlite.disk_size_gb to the server config, alongside the existing image/env knobs. Signed-off-by: Avri Chen-Roth <11185446+the-mentor@users.noreply.github.com> Co-authored-by: Avri Chen-Roth <11185446+the-mentor@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -83,6 +83,7 @@ sandbox:
|
||||
boxlite:
|
||||
image: docker.io/me/omnigent-host:latest # optional, shared; default: official
|
||||
env: [OPENAI_API_KEY, GIT_TOKEN] # optional, shared; SERVER env var NAMES
|
||||
disk_size_gb: 100 # optional, shared; default: SDK default
|
||||
cloud:
|
||||
endpoint: https://boxlite.example.com:8100 # selects CLOUD mode
|
||||
```
|
||||
|
||||
@@ -215,6 +215,7 @@ class BoxliteSandboxLauncher(SandboxLauncher):
|
||||
env: Sequence[str] | None = None,
|
||||
home_dir: str | None = None,
|
||||
registry: Mapping[str, object] | None = None,
|
||||
disk_size_gb: int | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize the launcher.
|
||||
@@ -244,6 +245,9 @@ class BoxliteSandboxLauncher(SandboxLauncher):
|
||||
``password_env`` / ``token_env``. The ``*_env`` keys NAME server
|
||||
environment variables holding the credentials (12-factor; values
|
||||
never live in config). ``None`` uses anonymous pulls.
|
||||
:param disk_size_gb: Box disk size in GB — the server's
|
||||
``sandbox.boxlite.disk_size_gb`` config. ``None`` uses the SDK's
|
||||
own default.
|
||||
|
||||
When ``home_dir`` or ``registry`` is set the launcher builds a
|
||||
customized ``Boxlite(Options(...))`` runtime; otherwise it uses the
|
||||
@@ -254,6 +258,7 @@ class BoxliteSandboxLauncher(SandboxLauncher):
|
||||
self._env_names = tuple(env) if env is not None else None
|
||||
self._home_dir = home_dir
|
||||
self._registry = dict(registry) if registry is not None else None
|
||||
self._disk_size_gb = disk_size_gb
|
||||
self._runtime: boxlite_sdk.Boxlite | None = None
|
||||
|
||||
async def _aruntime(self) -> boxlite_sdk.Boxlite:
|
||||
@@ -411,6 +416,7 @@ class BoxliteSandboxLauncher(SandboxLauncher):
|
||||
image=resolved_ref,
|
||||
cpus=_SANDBOX_CPU,
|
||||
memory_mib=_SANDBOX_MEMORY_MIB,
|
||||
disk_size_gb=self._disk_size_gb,
|
||||
env=env,
|
||||
auto_remove=False,
|
||||
detach=True,
|
||||
|
||||
@@ -60,6 +60,7 @@ stores into ``create_app``):
|
||||
boxlite: # optional block (provider: boxlite)
|
||||
image: docker.io/me/omnigent-host:latest # shared; default: official
|
||||
env: [OPENAI_API_KEY, GIT_TOKEN] # shared; SERVER env var NAMES
|
||||
disk_size_gb: 100 # shared; default: SDK default
|
||||
# exactly one mode (mutually exclusive):
|
||||
cloud: {endpoint: https://boxlite.example.com:8100} # CLOUD; key: BOXLITE_API_KEY env
|
||||
# local: {home_dir: /data/boxlite, registry: {...}} # LOCAL (default if omitted)
|
||||
@@ -770,7 +771,9 @@ def parse_sandbox_config(raw: object) -> ManagedSandboxConfig | None:
|
||||
token_ttl_s = DAYTONA_MANAGED_TOKEN_TTL_S
|
||||
elif provider == "boxlite":
|
||||
section = _boxlite_section(raw)
|
||||
_reject_unknown_keys(section, {"image", "env", "local", "cloud"}, "sandbox.boxlite")
|
||||
_reject_unknown_keys(
|
||||
section, {"image", "env", "local", "cloud", "disk_size_gb"}, "sandbox.boxlite"
|
||||
)
|
||||
endpoint, home_dir, registry = _parse_boxlite_mode(section)
|
||||
launcher_factory = _boxlite_launcher_factory(
|
||||
endpoint,
|
||||
@@ -778,6 +781,7 @@ def parse_sandbox_config(raw: object) -> ManagedSandboxConfig | None:
|
||||
_parse_boxlite_env(section),
|
||||
home_dir,
|
||||
registry,
|
||||
_parse_provider_positive_int(raw, "boxlite", "disk_size_gb"),
|
||||
)
|
||||
token_ttl_s = BOXLITE_MANAGED_TOKEN_TTL_S
|
||||
elif provider == "cwsandbox":
|
||||
@@ -1055,6 +1059,7 @@ def _boxlite_launcher_factory(
|
||||
env: list[str] | None,
|
||||
home_dir: str | None,
|
||||
registry: dict[str, object] | None,
|
||||
disk_size_gb: int | None,
|
||||
) -> Callable[[], SandboxHostLauncher]:
|
||||
"""
|
||||
Build the launcher factory for the YAML ``provider: boxlite`` path.
|
||||
@@ -1073,6 +1078,7 @@ def _boxlite_launcher_factory(
|
||||
:param registry: LOCAL-mode private-registry config for the host image
|
||||
(``host`` + optional ``transport`` / ``skip_verify`` / ``*_env``
|
||||
credential names), or ``None`` for anonymous pulls.
|
||||
:param disk_size_gb: Box disk size in GB, or ``None`` for the SDK default.
|
||||
:returns: A factory producing parameterized boxlite launchers.
|
||||
"""
|
||||
|
||||
@@ -1081,7 +1087,12 @@ def _boxlite_launcher_factory(
|
||||
from omnigent.onboarding.sandboxes.boxlite import BoxliteSandboxLauncher
|
||||
|
||||
return BoxliteSandboxLauncher(
|
||||
endpoint=endpoint, image=image, env=env, home_dir=home_dir, registry=registry
|
||||
endpoint=endpoint,
|
||||
image=image,
|
||||
env=env,
|
||||
home_dir=home_dir,
|
||||
registry=registry,
|
||||
disk_size_gb=disk_size_gb,
|
||||
)
|
||||
|
||||
return _build
|
||||
|
||||
@@ -149,6 +149,7 @@ class _FakeBoxOptions:
|
||||
image: str | None = None,
|
||||
cpus: int | None = None,
|
||||
memory_mib: int | None = None,
|
||||
disk_size_gb: int | None = None,
|
||||
env: object = None,
|
||||
auto_remove: bool | None = None,
|
||||
detach: bool | None = None,
|
||||
@@ -157,6 +158,7 @@ class _FakeBoxOptions:
|
||||
self.image = image
|
||||
self.cpus = cpus
|
||||
self.memory_mib = memory_mib
|
||||
self.disk_size_gb = disk_size_gb
|
||||
self.env = env if env is not None else []
|
||||
self.auto_remove = auto_remove
|
||||
self.detach = detach
|
||||
@@ -372,11 +374,22 @@ def test_provision_defaults_official_image_and_persists(
|
||||
assert create.options.detach is True
|
||||
assert create.options.cpus == 2
|
||||
assert create.options.memory_mib == 4096
|
||||
assert create.options.disk_size_gb is None
|
||||
assert create.options.env == []
|
||||
assert create.name == "managed-abc"
|
||||
assert fake_boxlite.mode == "local"
|
||||
|
||||
|
||||
def test_provision_disk_size_gb_reaches_box_options(
|
||||
fake_boxlite: _FakeBoxliteState,
|
||||
) -> None:
|
||||
"""``disk_size_gb`` passed to the launcher reaches ``BoxOptions`` verbatim."""
|
||||
BoxliteSandboxLauncher(disk_size_gb=100).provision("managed-abc")
|
||||
|
||||
[create] = fake_boxlite.create_calls
|
||||
assert create.options.disk_size_gb == 100
|
||||
|
||||
|
||||
def test_provision_image_resolution_order(
|
||||
fake_boxlite: _FakeBoxliteState, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
|
||||
@@ -194,6 +194,7 @@ class FakeSandboxLauncher(SandboxLauncher):
|
||||
self.endpoint: str | None = None
|
||||
self.home_dir: str | None = None
|
||||
self.registry: dict[str, object] | None = None
|
||||
self.disk_size_gb: int | None = None
|
||||
self.base_url: str | None = None
|
||||
self.gateway_profile: str | None = None
|
||||
self.snapshot_name: str | None = None
|
||||
@@ -392,6 +393,7 @@ def install_fake_boxlite_launcher(
|
||||
env: list[str] | None = None,
|
||||
home_dir: str | None = None,
|
||||
registry: dict[str, object] | None = None,
|
||||
disk_size_gb: int | None = None,
|
||||
) -> FakeSandboxLauncher:
|
||||
"""Stand-in constructor recording the construction wiring."""
|
||||
fake.endpoint = endpoint
|
||||
@@ -399,6 +401,7 @@ def install_fake_boxlite_launcher(
|
||||
fake.env = env
|
||||
fake.home_dir = home_dir
|
||||
fake.registry = registry
|
||||
fake.disk_size_gb = disk_size_gb
|
||||
return fake
|
||||
|
||||
monkeypatch.setattr(boxlite_mod, "BoxliteSandboxLauncher", _ctor)
|
||||
|
||||
@@ -228,6 +228,7 @@ def test_parse_valid_boxlite_cloud_config_builds_parameterized_factory(
|
||||
"image": "docker.io/me/omnigent-host:latest",
|
||||
"env": ["OPENAI_API_KEY", "GIT_TOKEN"],
|
||||
"cloud": {"endpoint": "https://boxlite.example.com:8100"},
|
||||
"disk_size_gb": 100,
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -242,6 +243,7 @@ def test_parse_valid_boxlite_cloud_config_builds_parameterized_factory(
|
||||
assert fake.endpoint == "https://boxlite.example.com:8100"
|
||||
assert fake.image == "docker.io/me/omnigent-host:latest"
|
||||
assert fake.env == ["OPENAI_API_KEY", "GIT_TOKEN"]
|
||||
assert fake.disk_size_gb == 100
|
||||
|
||||
|
||||
def test_parse_boxlite_without_section_defaults_local(
|
||||
@@ -260,6 +262,7 @@ def test_parse_boxlite_without_section_defaults_local(
|
||||
assert fake.endpoint is None
|
||||
assert fake.image is None
|
||||
assert fake.env is None
|
||||
assert fake.disk_size_gb is None
|
||||
|
||||
|
||||
def test_parse_boxlite_local_customization_reaches_launcher(
|
||||
|
||||
Reference in New Issue
Block a user