akernel-sdk 0.2.0rc2__tar.gz → 0.2.0rc4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/PKG-INFO +515 -28
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/README.md +513 -26
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/__init__.py +28 -0
- akernel_sdk-0.2.0rc4/akernel_sdk/_backends/adx.py +1535 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_backends/base.py +114 -4
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_backends/errors.py +64 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_backends/registry.py +10 -2
- akernel_sdk-0.2.0rc4/akernel_sdk/_client.py +104 -0
- akernel_sdk-0.2.0rc4/akernel_sdk/_dualmethod.py +42 -0
- akernel_sdk-0.2.0rc4/akernel_sdk/_fork.py +91 -0
- akernel_sdk-0.2.0rc4/akernel_sdk/_sandboxes.py +86 -0
- akernel_sdk-0.2.0rc4/akernel_sdk/_snapshots.py +74 -0
- akernel_sdk-0.2.0rc4/akernel_sdk/commands.py +173 -0
- akernel_sdk-0.2.0rc4/akernel_sdk/sandbox.py +1350 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/types.py +65 -6
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk.egg-info/PKG-INFO +515 -28
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk.egg-info/SOURCES.txt +5 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk.egg-info/requires.txt +1 -1
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/pyproject.toml +2 -2
- akernel_sdk-0.2.0rc2/akernel_sdk/_backends/adx.py +0 -691
- akernel_sdk-0.2.0rc2/akernel_sdk/commands.py +0 -126
- akernel_sdk-0.2.0rc2/akernel_sdk/sandbox.py +0 -664
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_addresses.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_backends/__init__.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_dockercontext.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_dockerfile.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_dockerfile_launch.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_dockerfile_runner.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_instance.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_pty_transport.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_resource_api.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_resources.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_sandbox_resources.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/_tls.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/cli.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/filesystem.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/pty.py +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk/py.typed +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk.egg-info/dependency_links.txt +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk.egg-info/entry_points.txt +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/akernel_sdk.egg-info/top_level.txt +0 -0
- {akernel_sdk-0.2.0rc2 → akernel_sdk-0.2.0rc4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: akernel-sdk
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.0rc4
|
|
4
4
|
Summary: Python SDK and CLI for AKernel remote sandboxes
|
|
5
5
|
Author: AKernel Authors
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -23,7 +23,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
23
23
|
Classifier: Topic :: System :: Distributed Computing
|
|
24
24
|
Requires-Python: >=3.10
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
|
-
Requires-Dist: adx-environment==0.1.
|
|
26
|
+
Requires-Dist: adx-environment==0.1.0rc3
|
|
27
27
|
Requires-Dist: idna<4,>=3
|
|
28
28
|
Requires-Dist: websockets>=10.0
|
|
29
29
|
Requires-Dist: dockerfile-parse>=2.0.1
|
|
@@ -53,6 +53,8 @@ PTYs, port forwarding, and reverse tunnels.
|
|
|
53
53
|
- [Filesystem](#filesystem)
|
|
54
54
|
- [Interactive PTYs](#interactive-ptys)
|
|
55
55
|
- [Port forwarding](#port-forwarding)
|
|
56
|
+
- [Lifecycle compatibility](#lifecycle-compatibility)
|
|
57
|
+
- [Reusable snapshots](#reusable-snapshots)
|
|
56
58
|
- [Local failover and reload](#local-failover-and-reload)
|
|
57
59
|
- [Reverse tunnels](#reverse-tunnels)
|
|
58
60
|
- [Rootfs and mounts](#rootfs-and-mounts)
|
|
@@ -413,10 +415,11 @@ prerequisites are available.
|
|
|
413
415
|
Kata and Firecracker require at least one cluster node whose sandboxd instance
|
|
414
416
|
successfully initialized the requested runtime with a usable `/dev/kvm`
|
|
415
417
|
device. Nodes without KVM remain available for runsc workloads and do not
|
|
416
|
-
advertise either VM runtime. The bundled Firecracker configuration
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
418
|
+
advertise either VM runtime. The bundled Firecracker configuration disables
|
|
419
|
+
virtio-fs and uses EROFS image roots and mounts. OCI/Nydus image roots and
|
|
420
|
+
HostPath directories require an operator-enabled compatible virtio-fs profile;
|
|
421
|
+
see [enabling Firecracker directory access](#enabling-firecracker-directory-access).
|
|
422
|
+
OCI image mounts, GPUs, and nested KVM remain unsupported.
|
|
420
423
|
|
|
421
424
|
The all-in-one image can optionally package a native Linux `runc` backend.
|
|
422
425
|
Operators build it with `AKERNEL_ENABLE_RUNC=true` and enable it explicitly
|
|
@@ -482,6 +485,28 @@ handle.close_stdin()
|
|
|
482
485
|
result = handle.wait(timeout=15)
|
|
483
486
|
```
|
|
484
487
|
|
|
488
|
+
Commands can be reattached by stable `command_id` (preferred) or an observed
|
|
489
|
+
PID without submitting them again. A returned handle binds its stable ID once;
|
|
490
|
+
reusing the same PID for another command cannot redirect its identity, wait,
|
|
491
|
+
stdin or kill operations. Direct PID operations remain convenience lookups:
|
|
492
|
+
|
|
493
|
+
```python
|
|
494
|
+
handle = sandbox.commands.run("sleep 2; printf done", background=True)
|
|
495
|
+
command_id = handle.command_id
|
|
496
|
+
sandbox.close()
|
|
497
|
+
attached = Sandbox.connect(sandbox.sandbox_id)
|
|
498
|
+
try:
|
|
499
|
+
result = attached.commands.connect(command_id).wait(timeout=30, check=True)
|
|
500
|
+
finally:
|
|
501
|
+
attached.close()
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
`check=True` on run/wait opts into `CommandExitException` for nonzero exit,
|
|
505
|
+
retaining stdout, stderr and exit_code, and `TimeoutException` for an unfinished
|
|
506
|
+
observation. The default continues returning `CommandResult`. Authentication,
|
|
507
|
+
permission, missing-resource, timeout, rate-limit and service-busy exceptions
|
|
508
|
+
subclass `BackendOperationError` and retain structured service context.
|
|
509
|
+
|
|
485
510
|
Foreground commands return a backend-neutral `CommandResult`. Background
|
|
486
511
|
commands return an AKernel `CommandHandle`; its lifecycle operations are
|
|
487
512
|
delegated to the selected backend.
|
|
@@ -568,6 +593,363 @@ with Sandbox(port_forwardings=[8080]) as sandbox:
|
|
|
568
593
|
deployment operator explicitly wants the direct ADX Ingress address instead of the
|
|
569
594
|
public gateway.
|
|
570
595
|
|
|
596
|
+
## Lifecycle compatibility
|
|
597
|
+
|
|
598
|
+
These source APIs follow E2B's Python naming while preserving AKernel's existing
|
|
599
|
+
constructor, ID, detached cleanup, command-result and creation-budget contracts.
|
|
600
|
+
They are not present in an older published wheel merely because its package
|
|
601
|
+
version matches this checkout. Pin a tested source revision when installing.
|
|
602
|
+
|
|
603
|
+
```python
|
|
604
|
+
from akernel_sdk import Client, SandboxQuery, SandboxState
|
|
605
|
+
|
|
606
|
+
client = Client(api_url="https://akernel.example.com", api_key="<tenant-key>",
|
|
607
|
+
verify_tls=True)
|
|
608
|
+
# An explicit Client captures its own API/gateway/token. It neither changes
|
|
609
|
+
# os.environ nor redirects top-level Sandbox operations. Specify gateway_address
|
|
610
|
+
# when the data gateway differs. Addresses must be HTTP(S) origins without paths.
|
|
611
|
+
source = client.Sandbox.create(image="python:3.13-slim", idle_timeout=600)
|
|
612
|
+
identity = source.sandbox_id # source.id remains supported
|
|
613
|
+
try:
|
|
614
|
+
source.files.write("/tmp/progress", "saved")
|
|
615
|
+
assert source.pause() # beta_pause() is an alias; already paused returns False
|
|
616
|
+
source.close() # closes local transports, retaining the remote sandbox
|
|
617
|
+
restored = client.Sandbox.connect(identity)
|
|
618
|
+
try:
|
|
619
|
+
assert restored.sandbox_id == identity
|
|
620
|
+
assert restored.files.read("/tmp/progress") == "saved"
|
|
621
|
+
finally:
|
|
622
|
+
restored.close()
|
|
623
|
+
print(client.Sandbox.get_info(identity)) # get_by_id(identity) also supported
|
|
624
|
+
pages = client.Sandbox.list(limit=20,
|
|
625
|
+
query=SandboxQuery(state=[SandboxState.RUNNING, SandboxState.PAUSED]))
|
|
626
|
+
while pages.has_next:
|
|
627
|
+
for info in pages.next_items():
|
|
628
|
+
print(info.sandbox_id, info.state)
|
|
629
|
+
finally:
|
|
630
|
+
source.close()
|
|
631
|
+
client.Sandbox.kill(identity) # bool: False means already absent
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
`connect` attaches to Running or resumes Paused under the **same logical ID**.
|
|
635
|
+
It does not create a replacement, rerun a Dockerfile, restart a command, or create
|
|
636
|
+
`cwd`. It waits for Pausing/Resuming to settle and errors on other states. Its
|
|
637
|
+
`timeout` is a logical connection/resume wait budget; transport retry margins and
|
|
638
|
+
tunnel initialization can add elapsed time. Supply `port_forwardings` and
|
|
639
|
+
`reverse_tunnel` again to reconstruct local routing; do not reuse old PTYs or
|
|
640
|
+
streams. A failed attachment closes only local resources and never deletes the
|
|
641
|
+
existing sandbox. As with fresh sandboxes, a connected handle's context manager
|
|
642
|
+
owns remote deletion unless `detached=True`; use `close()` to retain it.
|
|
643
|
+
|
|
644
|
+
Pause creates an anonymous in-place checkpoint with permanent retention until
|
|
645
|
+
explicit deletion. It is separate from `create_snapshot()`'s reusable catalog
|
|
646
|
+
snapshot and `from_snapshot()`'s new ID. The SDK stops its own reverse tunnel
|
|
647
|
+
before pause so that its reconnect loop cannot immediately demand-resume the
|
|
648
|
+
sandbox. Other connected clients or inbound traffic may still resume it.
|
|
649
|
+
Pause errors and unknown outcomes propagate; False means authoritatively paused.
|
|
650
|
+
|
|
651
|
+
| Operation | Ownership and result |
|
|
652
|
+
| --- | --- |
|
|
653
|
+
| `sandbox.close()` | Idempotent local disconnect; does not delete the remote sandbox. Closing PTYs terminates their interactive processes. |
|
|
654
|
+
| `sandbox.kill()` / context exit | Existing behavior: owned handles delete remote state and close locally; detached handles only close locally. Returns None. |
|
|
655
|
+
| `sandbox.kill(delete_remote=True)` | Explicit remote deletion, including detached or previously closed handles. Failures remain retryable. |
|
|
656
|
+
| `Sandbox.kill(id)` / `Sandbox.kill_by_id(id)` | Canonical by-ID deletion, returns bool. False means already absent; permission/network/unknown errors propagate. |
|
|
657
|
+
| `Sandbox.delete(sandbox_id=id)` | Explicit by-ID deletion returning None; existing positional `Sandbox.delete(name)` continues to delete a named detached sandbox. |
|
|
658
|
+
| `sandbox.get_info()` / `Sandbox.get_info(id)` | Handle or canonical directory query; `get_by_id(id)` is an explicit class alias. |
|
|
659
|
+
| `Sandbox.list(query=..., limit=20, next_token=None)` | Lazy paginator. `next_items()` fetches one filtered page, `has_next` and `next_token` retain progress. Errors do not advance the cursor. Default states are Running and Paused. |
|
|
660
|
+
|
|
661
|
+
### Timeouts and supported backend surface
|
|
662
|
+
|
|
663
|
+
`create_timeout` on the constructor and factory/restore `timeout` are logical
|
|
664
|
+
creation budgets, not maximum lifetimes. `schedule_timeout` bounds scheduling;
|
|
665
|
+
`idle_timeout` measures inactivity. These meanings remain unchanged.
|
|
666
|
+
|
|
667
|
+
This SDK change uses the released `adx-environment==0.1.0rc3` and existing
|
|
668
|
+
cluster operations. It does not require a new backend or bundle rollout.
|
|
669
|
+
Custom user metadata and filtering, a running-lifetime deadline/set_timeout,
|
|
670
|
+
live command-output callbacks, and per-command user selection are outside this
|
|
671
|
+
API change. They require separately reviewed backend capabilities; the SDK does
|
|
672
|
+
not emulate them with scheduler labels, idle timers or output replay.
|
|
673
|
+
|
|
674
|
+
## Reusable snapshots
|
|
675
|
+
|
|
676
|
+
These APIs require `akernel-sdk>=0.2.0rc4`; `0.2.0rc3` and earlier wheels do
|
|
677
|
+
not contain them. Install a compatible release candidate with `--pre` or install
|
|
678
|
+
this revision from source. The cluster/runtime must support reusable
|
|
679
|
+
memory/filesystem snapshots. Unsupported operations fail with the backend's
|
|
680
|
+
structured error.
|
|
681
|
+
|
|
682
|
+
```python
|
|
683
|
+
from akernel_sdk import Sandbox
|
|
684
|
+
|
|
685
|
+
snapshot = None
|
|
686
|
+
try:
|
|
687
|
+
with Sandbox(image="python:3.12-slim") as source:
|
|
688
|
+
source.files.write("/tmp/progress", "turn 10")
|
|
689
|
+
snapshot = source.create_snapshot(name="turn-10", timeout=300)
|
|
690
|
+
print(Sandbox.get_snapshot(snapshot.snapshot_id))
|
|
691
|
+
# Source resumes after capture; it can also be deleted independently.
|
|
692
|
+
|
|
693
|
+
with Sandbox.create(snapshot.snapshot_id) as restored:
|
|
694
|
+
assert restored.files.read("/tmp/progress") == "turn 10"
|
|
695
|
+
finally:
|
|
696
|
+
if snapshot is not None:
|
|
697
|
+
Sandbox.delete_snapshot(snapshot.snapshot_id)
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
| API | Result / semantics |
|
|
701
|
+
| --- | --- |
|
|
702
|
+
| `sandbox.create_snapshot(name=None, *, timeout=300)` | `SnapshotInfo(snapshot_id, names)`; captures runtime memory and filesystem, then the source continues. Timeout is 1..3600 seconds. |
|
|
703
|
+
| `Sandbox.get_snapshot(snapshot_id)` | Query catalog metadata by opaque ID; pending deletion may still return metadata. |
|
|
704
|
+
| `Sandbox.list_snapshots(name=None, limit=None, next_token=None)` | Lazy `SnapshotPaginator`; `limit` is the page size. Returns the accessible catalog, optionally filtered by name. |
|
|
705
|
+
| `Sandbox.delete_snapshot(snapshot_id)` | Idempotent when absent, returns `None` after acceptance; active references can reject deletion with `SNAPSHOT_IN_USE`. |
|
|
706
|
+
| `Sandbox.create(snapshot_id, ...)` | New `Sandbox`, new ID and fresh commands/files/PTY facades; does not delete or change the source. Accepts an ID or `SnapshotInfo`. |
|
|
707
|
+
| `Sandbox.from_snapshot(snapshot_id, ...)` | Retained compatibility entry point with the same options, validation, errors and ownership as `create`. |
|
|
708
|
+
|
|
709
|
+
The naming and paginator pattern follow E2B, including
|
|
710
|
+
`Sandbox.create(snapshot.snapshot_id)`. Without a snapshot ID,
|
|
711
|
+
`Sandbox.create(image=...)` creates a fresh sandbox, and `Sandbox.create()`
|
|
712
|
+
uses the cluster default rootfs. Existing constructors and
|
|
713
|
+
`Sandbox.from_snapshot(...)` remain supported. AKernel does not resolve E2B
|
|
714
|
+
or agentenv template aliases or another platform's snapshot IDs.
|
|
715
|
+
Listing is a class-level catalog operation, including when accessed through an
|
|
716
|
+
instance; delete returns `None` because the backend does not distinguish absent from
|
|
717
|
+
newly deleted snapshots. Only memory/filesystem capture is exposed; no
|
|
718
|
+
unsupported filesystem-only mode or fabricated timestamps/storage metrics.
|
|
719
|
+
|
|
720
|
+
```python
|
|
721
|
+
pages = Sandbox.list_snapshots(name="turn-10", limit=20)
|
|
722
|
+
while pages.has_next:
|
|
723
|
+
for snapshot in pages.next_items():
|
|
724
|
+
print(snapshot.snapshot_id, snapshot.names)
|
|
725
|
+
# Save pages.next_token between pages to resume a listing later.
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
Restore inherits captured runtime and CPU/memory geometry rather than applying
|
|
729
|
+
constructor defaults. It does not replay a Dockerfile, image ENTRYPOINT, CMD or
|
|
730
|
+
working-directory creation. `cwd` only sets the command default. `idle_timeout`
|
|
731
|
+
(default 300), `schedule_timeout` (30), `timeout` (schedule timeout + 60), `name`,
|
|
732
|
+
`detached`, `failover`, `node_id`, `port_forwardings`, `mounts`, `reverse_tunnel`
|
|
733
|
+
and `network_policy` configure the new environment/client. Rebind the local
|
|
734
|
+
`HttpReverseTunnel` target explicitly; source client sockets/PTYs/Python handles
|
|
735
|
+
are not restored. Supply `network_policy` explicitly to preserve restrictions:
|
|
736
|
+
the pinned ADX backend does not inherit captured execution policy on restore.
|
|
737
|
+
An omitted or empty policy uses the backend default. A supplied policy is
|
|
738
|
+
applied during creation, before the restored workload is made available.
|
|
739
|
+
For mounted external data, consistency and availability remain the caller's
|
|
740
|
+
responsibility; restore does not copy node-local HostPath storage.
|
|
741
|
+
|
|
742
|
+
Snapshots outlive their source and do not expire automatically. Delete them
|
|
743
|
+
explicitly. Current ADX rejects deletion with `BackendOperationError` code
|
|
744
|
+
`SNAPSHOT_IN_USE` while environments or templates retain references. Release
|
|
745
|
+
those users and retry; a rejected delete has not marked the snapshot for deletion.
|
|
746
|
+
Older deployments can accept deletion before references are released. On either
|
|
747
|
+
version, an acknowledged delete prevents new restores and never kills existing
|
|
748
|
+
sandboxes; physical collection is asynchronous. Catalog records may remain
|
|
749
|
+
queryable while Deleting, so a successful
|
|
750
|
+
`get_snapshot()` is not a readiness or physical-deletion check. Once collection
|
|
751
|
+
marks the snapshot Deleted, the backend returns not-found. The
|
|
752
|
+
current backend descriptor exposes only ID and names, not catalog state; the
|
|
753
|
+
service validates readiness on `create()` / `from_snapshot()`. Storage locality
|
|
754
|
+
and cross-node restore depend on cluster configuration. Capturing complete
|
|
755
|
+
process state does not imply a full physical copy: incremental/reflink storage
|
|
756
|
+
is a runtime/deployment concern, not selected
|
|
757
|
+
by this API. These APIs are separate from anonymous failover/reload checkpoints.
|
|
758
|
+
|
|
759
|
+
A remote snapshot does not save the host harness conversation or cursor, model
|
|
760
|
+
requests, or external service state. Quiesce tools and model streams and pair
|
|
761
|
+
local harness state with the returned ID. A restore can execute concurrently
|
|
762
|
+
with its source; the harness must fence the old instance before allowing a
|
|
763
|
+
recovered agent to proceed. It must not also relaunch an agent whose process
|
|
764
|
+
memory has been restored.
|
|
765
|
+
|
|
766
|
+
`BackendOperationError` preserves status, code, retry/outcome, request ID and
|
|
767
|
+
operation ID when supplied. Snapshot creation makes one request attempt. Restore
|
|
768
|
+
uses the native transport's same-operation retries (stable request ID); the
|
|
769
|
+
public SDK does not initiate a second logical restore. On unknown outcome,
|
|
770
|
+
reconcile using the supplied identity instead of blindly calling create/restore
|
|
771
|
+
again. Before creation is confirmed, native unknown-outcome context can contain
|
|
772
|
+
a lookup name rather than a canonical ID; do not pass it blindly to `ak delete`.
|
|
773
|
+
After creation is confirmed, failure context retains the canonical ID. Read-only
|
|
774
|
+
route checks retry temporary 409/503 responses within a 10-second retry window,
|
|
775
|
+
without recreating the sandbox. A blocking transport call is separately bounded.
|
|
776
|
+
Initialization failures, including interruption during tunnel startup, attempt
|
|
777
|
+
to delete the known-created clone and preserve the original error. Failed
|
|
778
|
+
rollback errors remain in `cleanup_errors` for diagnosis and explicit retry.
|
|
779
|
+
See [`examples/snapshots.py`](./examples/snapshots.py).
|
|
780
|
+
|
|
781
|
+
`timeout` must be at least `schedule_timeout + 60` seconds; shorter budgets raise
|
|
782
|
+
`ValueError` before a create request. This prevents the pinned native SDK from
|
|
783
|
+
silently increasing the supplied budget. It is the frontend's logical creation
|
|
784
|
+
budget, not a wall-clock deadline for the entire Python call: transport margins,
|
|
785
|
+
tunnel/readiness setup, facade binding and cleanup can add elapsed time.
|
|
786
|
+
|
|
787
|
+
## Fork a running sandbox
|
|
788
|
+
|
|
789
|
+
`sandbox.fork(count=N)` captures one reusable memory/filesystem snapshot and
|
|
790
|
+
derives N new running sandboxes. It returns an ordered `list[Sandbox | Exception]`
|
|
791
|
+
with exactly one result per requested child. A child failure does not kill
|
|
792
|
+
successful siblings; a capture failure raises for the whole call. The source
|
|
793
|
+
keeps its ID and idle policy and continues after capture.
|
|
794
|
+
|
|
795
|
+
```python
|
|
796
|
+
from akernel_sdk import ForkCleanupError, Sandbox
|
|
797
|
+
|
|
798
|
+
children = []
|
|
799
|
+
pending_snapshot = None
|
|
800
|
+
try:
|
|
801
|
+
with Sandbox(image="python:3.13-slim-bookworm") as source:
|
|
802
|
+
# Explicitly acknowledge this fresh example's unrestricted policy;
|
|
803
|
+
# older API versions omit creation ownership from their responses.
|
|
804
|
+
source.update_network_policy(None)
|
|
805
|
+
source.files.write("/tmp/state", "captured")
|
|
806
|
+
try:
|
|
807
|
+
results = source.fork(count=3)
|
|
808
|
+
except ForkCleanupError as error:
|
|
809
|
+
# Preserve handles even when resource cleanup was not acknowledged.
|
|
810
|
+
children.extend(r for r in error.results if isinstance(r, Sandbox))
|
|
811
|
+
children.extend(error.pending_sandboxes)
|
|
812
|
+
pending_snapshot = error.snapshot_id
|
|
813
|
+
raise
|
|
814
|
+
for result in results:
|
|
815
|
+
if isinstance(result, Exception):
|
|
816
|
+
print("child failed:", result)
|
|
817
|
+
else:
|
|
818
|
+
children.append(result)
|
|
819
|
+
print(result.id, result.files.read("/tmp/state"))
|
|
820
|
+
finally:
|
|
821
|
+
# Production callers should attempt every deletion and retain failed IDs.
|
|
822
|
+
for child in children:
|
|
823
|
+
child.kill()
|
|
824
|
+
if pending_snapshot is not None:
|
|
825
|
+
Sandbox.delete_snapshot(pending_snapshot)
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
The maintained [`examples/fork.py`](./examples/fork.py) attempts cleanup for all
|
|
829
|
+
known children even when one deletion fails, and preserves workload exceptions.
|
|
830
|
+
|
|
831
|
+
| Parameter | Meaning |
|
|
832
|
+
| --- | --- |
|
|
833
|
+
| `count=1` | Integer 1..20; booleans are rejected. |
|
|
834
|
+
| `idle_timeout=None` | Child idle timeout in seconds; omitted inherits the source's configured idle timeout. Zero disables idle expiry. |
|
|
835
|
+
| `snapshot_timeout=300` | Capture operation deadline in seconds, 1..3600. |
|
|
836
|
+
| `restore_timeout=None` | Logical creation budget per child in seconds; minimum/default is the source's schedule timeout + 60. Checked before capture. |
|
|
837
|
+
|
|
838
|
+
Children inherit captured resources. Fork explicitly supplies the source
|
|
839
|
+
client's last acknowledged network policy with each create request, including
|
|
840
|
+
successful policy updates since source creation. After an uncertain/interrupted
|
|
841
|
+
policy update it refuses to capture until a policy is successfully reapplied.
|
|
842
|
+
Serialize policy changes and captures in the application. This differs from a
|
|
843
|
+
bare `from_snapshot()` call, which requires an explicit policy for restrictions.
|
|
844
|
+
As with restore, the logical create budget excludes client/transport overhead;
|
|
845
|
+
the client's cwd, declared ports, mounts and reverse tunnel are rebound with
|
|
846
|
+
fresh handles. Children are non-detached owned handles even if the source is
|
|
847
|
+
detached. Writes to each child's captured root filesystem and process memory
|
|
848
|
+
are independent. External services and writable HostPath mounts remain shared
|
|
849
|
+
external state. Quiesce model/tool streams before capture, reconnect affected
|
|
850
|
+
connections afterwards, and do not relaunch processes already restored from
|
|
851
|
+
memory. Deleting the source or one child does not delete its siblings.
|
|
852
|
+
|
|
853
|
+
The count/ordered partial-result convention follows
|
|
854
|
+
[E2B fork](https://docs.e2b.dev/sandbox/fork). Deliberate differences:
|
|
855
|
+
|
|
856
|
+
- This release provides an instance method, not E2B's additional fork-by-ID
|
|
857
|
+
class variant. There is no separate all-or-nothing `clone()` operation.
|
|
858
|
+
- The pinned `adx-environment==0.1.0rc3` and AKernel-pinned ADX API have
|
|
859
|
+
snapshot/create/delete primitives but no native batch fork method/route.
|
|
860
|
+
The ADX adapter captures once and then creates children **sequentially**;
|
|
861
|
+
this is multiple requests, not E2B's single server-side fork operation.
|
|
862
|
+
- E2B fork's `timeout` is an absolute child lifetime. AKernel exposes
|
|
863
|
+
`idle_timeout` and named operation deadlines instead of promising a lifetime
|
|
864
|
+
its backend does not implement. There is no ambiguous `timeout` alias.
|
|
865
|
+
- Only a confirmed new environment's creation policy is trusted. Named reuse
|
|
866
|
+
can retain an older specification even when the constructor succeeds; missing
|
|
867
|
+
ownership confirmation and connected handles are unconfirmed too. Fork fails
|
|
868
|
+
with `NETWORK_POLICY_UNCONFIRMED` before capture until the caller successfully
|
|
869
|
+
reapplies the intended policy with `update_network_policy(policy)`. Passing
|
|
870
|
+
constructor arguments again does not prove they changed an existing sandbox.
|
|
871
|
+
- Temporary snapshot deletion is requested after all creates settle. When the
|
|
872
|
+
backend explicitly rejects it with `SNAPSHOT_IN_USE`/`not_started`, each child
|
|
873
|
+
exposes `fork_snapshot_id`; the SDK retains the snapshot and deletes it after
|
|
874
|
+
the last owned child's confirmed `kill()`. `close()` does not release a live
|
|
875
|
+
child's reference. Save the ID if deleting children by ID, from another client,
|
|
876
|
+
or after a client restart; then delete the snapshot explicitly after its users
|
|
877
|
+
are released. No background or garbage-collection deletion is performed.
|
|
878
|
+
Other initial cleanup failures raise `ForkCleanupError`: `results` retains
|
|
879
|
+
successful child handles, `snapshot_id` supports retrying deletion, and
|
|
880
|
+
`cleanup_error` preserves the original error. A failed client binding whose
|
|
881
|
+
child deletion fails or is unconfirmed is retained in `pending_sandboxes`;
|
|
882
|
+
retry `kill()` after deciding that remote deletion is intended.
|
|
883
|
+
Actual cleanup exceptions are in `child_cleanup_errors`; local-only closure
|
|
884
|
+
need not raise one. With no other cleanup error, `cleanup_error.code` is the
|
|
885
|
+
SDK-generated `CLEANUP_UNCONFIRMED` (`outcome="not_started"` for remote
|
|
886
|
+
deletion). If snapshot cleanup
|
|
887
|
+
fails on the last child kill, that call raises `ForkCleanupError` with the
|
|
888
|
+
snapshot ID; retry that child's `kill()` or delete the snapshot explicitly.
|
|
889
|
+
Confirmed child deletion is not repeated during that retry.
|
|
890
|
+
|
|
891
|
+
Unknown capture/create outcomes preserve `BackendOperationError` request and
|
|
892
|
+
lookup identity; the SDK does not initiate an extra logical attempt. An
|
|
893
|
+
unconfirmed create may still exist remotely. Distinguish native lookup names
|
|
894
|
+
from canonical IDs when reconciling. Confirmed-created IDs, including failures
|
|
895
|
+
during native bootstrap or public binding, remain in `environment_id`; rollback
|
|
896
|
+
failures remain in `cleanup_errors`. Interruption, including during final
|
|
897
|
+
snapshot deletion, attempts ownership-aware rollback of every known child and
|
|
898
|
+
deletion of the temporary snapshot, and re-raises the original exception
|
|
899
|
+
object/type. Automatic rollback deletes only confirmed newly created children;
|
|
900
|
+
false or missing ownership closes local resources only. Local closure is not
|
|
901
|
+
remote deletion: these children stay pending with retryable handles, even when
|
|
902
|
+
local closure succeeds. An explicit `kill()` can delete such a child by ID after
|
|
903
|
+
the local client has closed. Reconciliation data
|
|
904
|
+
on that exception includes `snapshot_id`, `created_environment_ids`,
|
|
905
|
+
`pending_environment_ids`, `pending_sandboxes`, `cleanup_errors`, and
|
|
906
|
+
`creation_errors`. Pending sandboxes are cleanup-only public handles: retry
|
|
907
|
+
`kill()` without rebinding commands, files or tunnels. Query or connect separately
|
|
908
|
+
if inspection is needed. Errors from a second interruption during cleanup are
|
|
909
|
+
retained alongside other cleanup errors rather than replacing the first one.
|
|
910
|
+
After releasing unresolved users, explicitly retry snapshot deletion. Unknown
|
|
911
|
+
creation errors retain their lookup/request context; do not blindly delete a
|
|
912
|
+
lookup name as though it were a confirmed-created ID.
|
|
913
|
+
Restore readiness and native session/tunnel initialization can fail before a
|
|
914
|
+
child reaches the fork result list. Secondary interruptions while closing or
|
|
915
|
+
rolling back are retained on the original exception. Known unresolved bootstrap
|
|
916
|
+
children still appear in the fork's pending IDs and cleanup-only handles; their
|
|
917
|
+
failed automatic rollback is not repeated before returning the cancellation.
|
|
918
|
+
`creation_errors` combines the complete backend result set with earlier public
|
|
919
|
+
binding errors, deduplicated by exception identity. It preserves nested request,
|
|
920
|
+
operation, lookup and rollback context even if binding an earlier child is
|
|
921
|
+
interrupted. Uncertain creation errors do not grant ownership or trigger deletion
|
|
922
|
+
of their lookup names.
|
|
923
|
+
|
|
924
|
+
Run real isolation, partial-failure and cleanup acceptance on a supporting
|
|
925
|
+
deployment using a complete image that contains Python 3 (not a CLI mount image):
|
|
926
|
+
|
|
927
|
+
```bash
|
|
928
|
+
AKERNEL_RUN_INTEGRATION=1 \
|
|
929
|
+
AKERNEL_TEST_IMAGE=python:3.13-slim-bookworm \
|
|
930
|
+
PYTHONPATH=sdk/python \
|
|
931
|
+
python sdk/python/tests/integration/test_fork.py
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
The companion `tests/integration/test_fork_network.py` covers two independent
|
|
935
|
+
tunnels (default and custom listen ports), private published-port responses,
|
|
936
|
+
creation-time and updated deny policy, and isolated child policy clearing.
|
|
937
|
+
It first verifies an allowed TCP baseline against `AKERNEL_TEST_EGRESS_HOST`
|
|
938
|
+
(default `1.1.1.1`) and `AKERNEL_TEST_EGRESS_PORT` (443); an unreachable baseline
|
|
939
|
+
fails instead of producing a false policy pass. Run both suites with:
|
|
940
|
+
|
|
941
|
+
```bash
|
|
942
|
+
AKERNEL_RUN_INTEGRATION=1 AKERNEL_TEST_IMAGE=python:3.13-slim-bookworm \
|
|
943
|
+
PYTHONPATH=sdk/python python -m unittest \
|
|
944
|
+
tests.integration.test_fork tests.integration.test_fork_network -v
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
The partial-failure case injects one client-side error before its child create;
|
|
948
|
+
the other two children are real. The cleanup case injects one deletion failure
|
|
949
|
+
and verifies explicit retry. The suite checks real process memory/PID/open-file
|
|
950
|
+
offsets, private writes, source/sibling independence, and snapshot collection.
|
|
951
|
+
It never changes cluster configuration or induces a platform capacity failure.
|
|
952
|
+
|
|
571
953
|
## Local failover and reload
|
|
572
954
|
|
|
573
955
|
`Sandbox(failover=True)` opts into same-node recovery of the same logical
|
|
@@ -586,8 +968,9 @@ created by sandbox workloads through Execd's internal `POST /checkpoint`
|
|
|
586
968
|
endpoint on `/run/akernel/execd.sock`. A successful request returns
|
|
587
969
|
`{"status":"completed"}`; a concurrent checkpoint request returns HTTP 409.
|
|
588
970
|
This Unix-socket protocol is experimental and is not a stable public AKernel
|
|
589
|
-
SDK interface.
|
|
590
|
-
|
|
971
|
+
SDK interface. These anonymous checkpoints have no public catalog; use the
|
|
972
|
+
separate reusable snapshot APIs above when you need an independent snapshot ID
|
|
973
|
+
and restoration to a new sandbox.
|
|
591
974
|
|
|
592
975
|
The bundled runsc and Firecracker runtimes support this recovery flow. The
|
|
593
976
|
maintained example validates it with runsc and installs curl in the sandbox
|
|
@@ -708,15 +1091,37 @@ runtime inside an S3 object. When neither source is supplied, AKernel sends
|
|
|
708
1091
|
only the selected isolation runtime and openYuanRong overlays it onto the
|
|
709
1092
|
rootfs configured by the deployed service.
|
|
710
1093
|
|
|
711
|
-
Firecracker
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
1094
|
+
The default Firecracker profile uses the deployed EROFS root when `image`
|
|
1095
|
+
and `rootfs` are omitted; explicit `rootfs` S3 objects must contain raw EROFS
|
|
1096
|
+
images. `Sandbox(runtime="firecracker", image="ubuntu:24.04")` requires the
|
|
1097
|
+
operator-enabled virtio-fs profile described below. With that profile, OCI and
|
|
1098
|
+
Nydus roots use the image manager's directory directly. The shared root stays
|
|
1099
|
+
read-only; sandbox writes use its private node-local ext4 overlay. The bundled
|
|
1100
|
+
distill-fs supports Nydus RAFS v5; select `--fs-version 5` when preparing images
|
|
1101
|
+
with `nydusify convert`.
|
|
1102
|
+
|
|
1103
|
+
### Enabling Firecracker directory access
|
|
1104
|
+
|
|
1105
|
+
This is a node deployment setting, not an SDK `extra_config` option. Helm and
|
|
1106
|
+
standalone keep `plugin.runtime.firecracker.virtiofs_enabled=false` by default.
|
|
1107
|
+
Operators who need OCI/Nydus roots or HostPath directories must:
|
|
1108
|
+
|
|
1109
|
+
1. Qualify a KVM-capable image containing the compatible Firecracker VMM,
|
|
1110
|
+
guest kernel/agent and virtiofsd. Include the Firecracker build payload.
|
|
1111
|
+
2. Drain existing Firecracker sandboxes before changing the runtime profile.
|
|
1112
|
+
Recreate recovery points when the rootfs or mount layout changes.
|
|
1113
|
+
3. In Helm, update the `[plugin.runtime.firecracker]` section of
|
|
1114
|
+
`node.config.sandboxd.config` to `virtiofs_enabled=true` (`core.node...` in
|
|
1115
|
+
the umbrella chart), preserving the rest of the TOML configuration. For
|
|
1116
|
+
standalone, update the same section in the sandboxd configuration used by
|
|
1117
|
+
the launcher. Restart the drained node services with the updated profile.
|
|
1118
|
+
4. Verify a fresh Sandbox using the required root or HostPath directory,
|
|
1119
|
+
including permissions, private writes and checkpoint/reload, before
|
|
1120
|
+
admitting workloads. Keep runtime state and private writable layers on
|
|
1121
|
+
node-local disk, separate from shared checkpoints.
|
|
1122
|
+
|
|
1123
|
+
An incompatible runtime or filesystem profile requires new recovery points;
|
|
1124
|
+
do not restore old checkpoints across that migration.
|
|
720
1125
|
|
|
721
1126
|
The same `S3Config` type can be used as a read-only mount source:
|
|
722
1127
|
|
|
@@ -734,8 +1139,64 @@ OCI images can also be mounted read-only:
|
|
|
734
1139
|
mount = Mount(target="/opt/tools", image_url="ubuntu:24.04")
|
|
735
1140
|
```
|
|
736
1141
|
|
|
737
|
-
Firecracker mounts
|
|
738
|
-
|
|
1142
|
+
Firecracker S3 mounts use `type="erofs"` with a raw EROFS image. It rejects
|
|
1143
|
+
`image_url` mounts. HostPath directories require the enabled virtio-fs profile
|
|
1144
|
+
above; the default profile rejects them.
|
|
1145
|
+
|
|
1146
|
+
### Node HostPath bind mounts
|
|
1147
|
+
|
|
1148
|
+
Trusted management clients can bind an existing node-visible path into a
|
|
1149
|
+
sandbox. For example, expose a prepared log directory on a selected runsc node:
|
|
1150
|
+
|
|
1151
|
+
```python
|
|
1152
|
+
from akernel_sdk import Mount, Sandbox
|
|
1153
|
+
|
|
1154
|
+
logs = Mount(
|
|
1155
|
+
target="/apps/logs", # Path inside the sandbox.
|
|
1156
|
+
host_path="/apps/logs", # Path visible to sandboxd on the selected node.
|
|
1157
|
+
read_only=False, # Explicitly allow application log writes.
|
|
1158
|
+
)
|
|
1159
|
+
with Sandbox(node_id="<prepared-node-id>", mounts=[logs]) as sandbox:
|
|
1160
|
+
print(sandbox.commands.run("ls -ld /apps/logs").stdout)
|
|
1161
|
+
```
|
|
1162
|
+
|
|
1163
|
+
`read_only=True` is the default. HostPath supports `type="bind"` only;
|
|
1164
|
+
`read_only=False` is rejected for Image/S3 mounts, which remain read-only.
|
|
1165
|
+
Exactly one of `image_url`, `s3_config`, or `host_path` is required. The source
|
|
1166
|
+
must be an absolute POSIX path without traversal components or control
|
|
1167
|
+
characters. The SDK does not inspect the client machine's filesystem or create
|
|
1168
|
+
the source directory; it sends `create_if_missing=false`. An absent source or
|
|
1169
|
+
insufficient filesystem permissions is a node-side error.
|
|
1170
|
+
|
|
1171
|
+
In Kubernetes, the node Pod must first see the source, for example through an
|
|
1172
|
+
operator-managed `hostPath` volume and matching `volumeMount`. An ECS directory
|
|
1173
|
+
does not become visible to sandboxd merely because its path is in the SDK
|
|
1174
|
+
request. Directory ownership and permissions must allow the sandbox's process
|
|
1175
|
+
UID/GID to use it. Separate workers' local directories are separate data;
|
|
1176
|
+
`node_id` selects the prepared node but does not replicate storage. Data written
|
|
1177
|
+
through a bind can outlive the sandbox. Runtime-specific mount restrictions
|
|
1178
|
+
are enforced by the deployed backend. Firecracker HostPath directories,
|
|
1179
|
+
including read-only directories, require the operator-enabled virtio-fs profile.
|
|
1180
|
+
Read-only regular-file injection can use the default Firecracker profile;
|
|
1181
|
+
writable regular-file mounts are unsupported.
|
|
1182
|
+
|
|
1183
|
+
For operator-managed Kubernetes preparation and the retention/C/R contract, see
|
|
1184
|
+
[node-local application directories](../../deploy/docs/node-local-directories.md).
|
|
1185
|
+
|
|
1186
|
+
Restrict HostPath creation to trusted management clients and source paths
|
|
1187
|
+
controlled by the operator. These mounts do not provide per-tenant source-path
|
|
1188
|
+
isolation; SDK path validation and read-only defaults do not authorize access
|
|
1189
|
+
to the node's filesystem. Server permission errors are returned through the
|
|
1190
|
+
normal `BackendOperationError` fields.
|
|
1191
|
+
|
|
1192
|
+
The maintained example requires an explicit node and source. It lists the
|
|
1193
|
+
read-only mount by default; `--write` creates one uniquely named log file and
|
|
1194
|
+
leaves that file on the node:
|
|
1195
|
+
|
|
1196
|
+
```bash
|
|
1197
|
+
python sdk/python/examples/hostpath_mounts.py \
|
|
1198
|
+
--node-id <prepared-node-id> --host-path /apps/logs --write
|
|
1199
|
+
```
|
|
739
1200
|
|
|
740
1201
|
## Launch from a Dockerfile
|
|
741
1202
|
|
|
@@ -809,7 +1270,19 @@ sandbox.kill() # closes local clients; remote sandbox remains
|
|
|
809
1270
|
Sandbox.delete("worker") # terminates the named remote sandbox
|
|
810
1271
|
```
|
|
811
1272
|
|
|
812
|
-
|
|
1273
|
+
When the backend accepts creation with an existing successful name, it returns
|
|
1274
|
+
that sandbox and its original resource allocation; changed arguments never
|
|
1275
|
+
resize it. Some older deployments reject changed specifications with CONFLICT
|
|
1276
|
+
instead of reusing them. If local initialization fails while reusing a result,
|
|
1277
|
+
the SDK closes
|
|
1278
|
+
its local handle and preserves the original workload. Automatic rollback
|
|
1279
|
+
removes a remote instance only when the server confirms this create owns it.
|
|
1280
|
+
This applies to backend readiness and cwd setup, public facade construction,
|
|
1281
|
+
and Dockerfile COPY, RUN or startup-dispatch failures.
|
|
1282
|
+
Use `get_info()` to inspect the retained allocation, or delete the named
|
|
1283
|
+
sandbox before creating a replacement with a different specification.
|
|
1284
|
+
|
|
1285
|
+
`sandbox.id` (also `sandbox.sandbox_id`) is the logical ID shown by `ak list`. `get_info()` returns a
|
|
813
1286
|
`SandboxInfo` containing `id`, state, requested CPU, memory, XPU and storage,
|
|
814
1287
|
and the OCI image when one was configured.
|
|
815
1288
|
|
|
@@ -850,8 +1323,11 @@ Maintained examples are under [`examples/`](./examples):
|
|
|
850
1323
|
- `command_stdin.py`
|
|
851
1324
|
- `custom_image.py`
|
|
852
1325
|
- `dockerfile_launch.py`
|
|
1326
|
+
- `snapshots.py`
|
|
1327
|
+
- `lifecycle.py`
|
|
853
1328
|
- `failover_reload.py`
|
|
854
1329
|
- `gpu_sandbox.py`
|
|
1330
|
+
- `hostpath_mounts.py`
|
|
855
1331
|
- `named_sandbox.py`
|
|
856
1332
|
- `network_policy.py`
|
|
857
1333
|
- `pty.py`
|
|
@@ -875,12 +1351,21 @@ PYTHONPATH=sdk/python \
|
|
|
875
1351
|
python -m unittest discover -s sdk/python/tests/integration -t sdk/python -v
|
|
876
1352
|
```
|
|
877
1353
|
|
|
878
|
-
Set `AKERNEL_TEST_RUNTIME=firecracker`
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
1354
|
+
Set `AKERNEL_TEST_RUNTIME=firecracker` or `firecracker-pvm` and
|
|
1355
|
+
`AKERNEL_TEST_IMAGE=ubuntu:24.04` after explicitly enabling a compatible
|
|
1356
|
+
virtio-fs profile on the test node to exercise the same suite, including
|
|
1357
|
+
checkpoint/reload, against a directory image root. `AKERNEL_TEST_IMAGE` also
|
|
1358
|
+
accepts a Nydus image reference and enables checks for private writes and
|
|
1359
|
+
network policy replacement. Use immutable digest references when qualifying a
|
|
1360
|
+
runtime release, and run both OCI and Nydus images. The pinned distill-fs
|
|
1361
|
+
requires Nydus RAFS v5.
|
|
1362
|
+
|
|
1363
|
+
The network checks use `https://example.com/` by default. Set
|
|
1364
|
+
`AKERNEL_TEST_NETWORK_URL` to an HTTP endpoint reachable from the sandbox to
|
|
1365
|
+
verify an isolated deployment without external network access. The suite checks
|
|
1366
|
+
access before blocking new flows, SDK command execution while blocked, and
|
|
1367
|
+
access after removing the policy. Images can include `curl` and CA certificates
|
|
1368
|
+
in advance; otherwise the suite installs them using `apt-get`. Omit
|
|
884
1369
|
`AKERNEL_TEST_IMAGE` to test the deployed default EROFS root.
|
|
885
1370
|
|
|
886
1371
|
Load and transfer benchmarks live under [`benchmarks/`](./benchmarks) and are
|
|
@@ -896,7 +1381,7 @@ not part of the default test suite.
|
|
|
896
1381
|
| `SandboxInfo` | `id`, `state`, `cpu`, `memory`, `image`, `xpu`, `storage_mb` |
|
|
897
1382
|
| `NodeInfo` | `id`, `status`, `capacity`, `allocatable`, `labels` |
|
|
898
1383
|
| `S3Config` | `endpoint`, `bucket`, `object`, optional credentials |
|
|
899
|
-
| `Mount` | `target`, one
|
|
1384
|
+
| `Mount` | `target`, exactly one of `image_url` / `s3_config` / `host_path`, `type`, `read_only` (default `True`; writable only for HostPath) |
|
|
900
1385
|
| `HttpReverseTunnel` | `target`, `reverse_port`, `listen_port`, `connect_timeout` |
|
|
901
1386
|
| `PortRange` | `first`, `last` |
|
|
902
1387
|
| `NetworkRule` | action, direction, protocol, peer and sandbox port selectors, priority |
|
|
@@ -915,7 +1400,9 @@ not part of the default test suite.
|
|
|
915
1400
|
public exception: `code`, `retry`, `outcome`, `request_id`, `operation_id`,
|
|
916
1401
|
`environment_id`, `status_code`, `scheduling_failure`, `failure`,
|
|
917
1402
|
`probe_failure`, and `request_forwarded`. Fields absent from the backend response
|
|
918
|
-
are `None`; the original exception remains in `__cause__`.
|
|
1403
|
+
are `None`; the original exception remains in `__cause__`. `cleanup_errors` is a
|
|
1404
|
+
tuple of rollback failures (empty when absent). Known-created IDs and these
|
|
1405
|
+
errors are also attached to interruption exceptions without changing their type.
|
|
919
1406
|
|
|
920
1407
|
```python
|
|
921
1408
|
from akernel_sdk import BackendOperationError, Sandbox
|