microsandbox-rb 0.11.0 → 0.12.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +44 -0
- data/Cargo.lock +33 -25
- data/README.md +11 -6
- data/ext/microsandbox/Cargo.toml +3 -3
- data/ext/microsandbox/src/backend.rs +7 -9
- data/ext/microsandbox/src/error.rs +85 -1
- data/ext/microsandbox/src/image.rs +18 -8
- data/ext/microsandbox/src/lib.rs +9 -9
- data/ext/microsandbox/src/sandbox.rs +38 -23
- data/ext/microsandbox/src/volume.rs +5 -3
- data/lib/microsandbox/errors.rb +12 -0
- data/lib/microsandbox/sandbox.rb +66 -8
- data/lib/microsandbox/version.rb +2 -2
- data/sig/microsandbox.rbs +19 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 72468dc2fa865510c82ecc783988925460acbd7f7d92025f367964277b98e756
|
|
4
|
+
data.tar.gz: f4bca46d64a694a76a59b712b8d827e6ddff12ba4cb9708a33951f4969c8fce2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 31cace38acfb4b535aaeb5f9026acb4c63d5f1376637628c2947e1d5ff9c1492d388cde85f3ba38bd8d462bb0598d12f046467331a6507d6af2cd2720bb57137
|
|
7
|
+
data.tar.gz: 97e665bd96d8d7ae0719c34d5a7d7f7823966a70f793d251d034a519ad04da70a8925c0e17c01c854ab074de904aa9a1baa88b148f099afba33c751836f97a60
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,50 @@ All notable changes to this gem are documented here. The format is based on
|
|
|
6
6
|
microsandbox runtime it embeds; each release notes the upstream runtime tag it
|
|
7
7
|
wraps, and the README's Versioning section keeps the full gem→runtime map.
|
|
8
8
|
|
|
9
|
+
## [0.12.0] - 2026-07-30
|
|
10
|
+
|
|
11
|
+
Adopts upstream runtime **`v0.6.7` → `v0.6.8`** and mirrors its breaking SDK
|
|
12
|
+
surface (upstream #1232 "unify cloud backend and paginated listings"), keeping
|
|
13
|
+
parity with the Python/Node SDKs.
|
|
14
|
+
|
|
15
|
+
### Breaking
|
|
16
|
+
|
|
17
|
+
- **Sandbox listing is cursor-paginated** (upstream #1232). `Sandbox.list` and
|
|
18
|
+
`Sandbox.list_with` now return a {Microsandbox::SandboxPage} — an Enumerable
|
|
19
|
+
page of `SandboxHandle`s carrying `#next_cursor` — instead of a plain Array.
|
|
20
|
+
`Sandbox.list` fetches the first page (upstream default size 20);
|
|
21
|
+
`Sandbox.list_with` gains `limit:` (1..100) and `cursor:` keywords alongside
|
|
22
|
+
the existing `labels:` filter. Code that only enumerates
|
|
23
|
+
(`list.each`/`map`/`to_a`) keeps working; code relying on the return value
|
|
24
|
+
*being* an Array (e.g. `list + other`, `Array ===`) must adapt, and listings
|
|
25
|
+
of more than one page must follow `page.next_cursor` via
|
|
26
|
+
`list_with(cursor:)`.
|
|
27
|
+
- **`UnsupportedError` messages are re-keyed by structured operations**
|
|
28
|
+
(upstream #1232): the core now reports the rejected API and a remedial hint
|
|
29
|
+
(e.g. `image.list is not supported by this backend: use a local backend`)
|
|
30
|
+
instead of the old free-text `feature`/`available_when` pair. The exception
|
|
31
|
+
additionally exposes the new structured attributes below.
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- {Microsandbox::SandboxPage} — Enumerable over its `#sandboxes`, plus
|
|
36
|
+
`#next_cursor`, `#size`/`#length`, `#empty?` and `#last_page?`.
|
|
37
|
+
- `Sandbox.list_with(labels:, limit:, cursor:)` pagination keywords.
|
|
38
|
+
- `UnsupportedError#operation` / `#hint` — the rejected API in Ruby rendering
|
|
39
|
+
(`"sandbox.kill"`) and the remedial hint (`"use a local backend"`),
|
|
40
|
+
mirroring the Python SDK's enriched `UnsupportedError`.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Direct `VolumeFs` operations now route through the backend's volume trait
|
|
45
|
+
(upstream #1232) instead of a construction-time local-backend guard, so an
|
|
46
|
+
unsupported backend rejects each operation with a precise per-operation
|
|
47
|
+
error.
|
|
48
|
+
- The embedded runtime is `v0.6.8`; see the upstream release notes for
|
|
49
|
+
runtime-side changes (shared log registry for followed streams, cloud
|
|
50
|
+
sandbox reconnect fixes for `exec`/`ssh`, kernel sourced from
|
|
51
|
+
cdn.kernel.org).
|
|
52
|
+
|
|
9
53
|
## [0.11.0] - 2026-07-27
|
|
10
54
|
|
|
11
55
|
Adopts upstream runtime **`v0.6.6` → `v0.6.7`** and mirrors its breaking SDK
|
data/Cargo.lock
CHANGED
|
@@ -2954,8 +2954,8 @@ dependencies = [
|
|
|
2954
2954
|
|
|
2955
2955
|
[[package]]
|
|
2956
2956
|
name = "microsandbox"
|
|
2957
|
-
version = "0.6.
|
|
2958
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
2957
|
+
version = "0.6.8"
|
|
2958
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
2959
2959
|
dependencies = [
|
|
2960
2960
|
"anyhow",
|
|
2961
2961
|
"astral-tokio-tar",
|
|
@@ -2990,6 +2990,7 @@ dependencies = [
|
|
|
2990
2990
|
"reqwest",
|
|
2991
2991
|
"russh",
|
|
2992
2992
|
"russh-sftp",
|
|
2993
|
+
"rustls",
|
|
2993
2994
|
"scopeguard",
|
|
2994
2995
|
"sea-orm",
|
|
2995
2996
|
"serde",
|
|
@@ -3003,6 +3004,7 @@ dependencies = [
|
|
|
3003
3004
|
"tracing",
|
|
3004
3005
|
"typed-builder",
|
|
3005
3006
|
"typed-path",
|
|
3007
|
+
"webpki-roots 1.0.7",
|
|
3006
3008
|
"which",
|
|
3007
3009
|
"windows-sys 0.61.2",
|
|
3008
3010
|
"zeroize",
|
|
@@ -3010,8 +3012,8 @@ dependencies = [
|
|
|
3010
3012
|
|
|
3011
3013
|
[[package]]
|
|
3012
3014
|
name = "microsandbox-agent-client"
|
|
3013
|
-
version = "0.6.
|
|
3014
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
3015
|
+
version = "0.6.8"
|
|
3016
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
3015
3017
|
dependencies = [
|
|
3016
3018
|
"ciborium",
|
|
3017
3019
|
"microsandbox-protocol",
|
|
@@ -3023,8 +3025,8 @@ dependencies = [
|
|
|
3023
3025
|
|
|
3024
3026
|
[[package]]
|
|
3025
3027
|
name = "microsandbox-db"
|
|
3026
|
-
version = "0.6.
|
|
3027
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
3028
|
+
version = "0.6.8"
|
|
3029
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
3028
3030
|
dependencies = [
|
|
3029
3031
|
"async-trait",
|
|
3030
3032
|
"sea-orm",
|
|
@@ -3035,8 +3037,8 @@ dependencies = [
|
|
|
3035
3037
|
|
|
3036
3038
|
[[package]]
|
|
3037
3039
|
name = "microsandbox-filesystem"
|
|
3038
|
-
version = "0.6.
|
|
3039
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
3040
|
+
version = "0.6.8"
|
|
3041
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
3040
3042
|
dependencies = [
|
|
3041
3043
|
"libc",
|
|
3042
3044
|
"microsandbox-utils",
|
|
@@ -3048,8 +3050,8 @@ dependencies = [
|
|
|
3048
3050
|
|
|
3049
3051
|
[[package]]
|
|
3050
3052
|
name = "microsandbox-image"
|
|
3051
|
-
version = "0.6.
|
|
3052
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
3053
|
+
version = "0.6.8"
|
|
3054
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
3053
3055
|
dependencies = [
|
|
3054
3056
|
"astral-tokio-tar",
|
|
3055
3057
|
"async-compression",
|
|
@@ -3075,8 +3077,8 @@ dependencies = [
|
|
|
3075
3077
|
|
|
3076
3078
|
[[package]]
|
|
3077
3079
|
name = "microsandbox-metrics"
|
|
3078
|
-
version = "0.6.
|
|
3079
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
3080
|
+
version = "0.6.8"
|
|
3081
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
3080
3082
|
dependencies = [
|
|
3081
3083
|
"chrono",
|
|
3082
3084
|
"libc",
|
|
@@ -3087,8 +3089,8 @@ dependencies = [
|
|
|
3087
3089
|
|
|
3088
3090
|
[[package]]
|
|
3089
3091
|
name = "microsandbox-migration"
|
|
3090
|
-
version = "0.6.
|
|
3091
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
3092
|
+
version = "0.6.8"
|
|
3093
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
3092
3094
|
dependencies = [
|
|
3093
3095
|
"sea-orm-migration",
|
|
3094
3096
|
"serde_json",
|
|
@@ -3096,8 +3098,8 @@ dependencies = [
|
|
|
3096
3098
|
|
|
3097
3099
|
[[package]]
|
|
3098
3100
|
name = "microsandbox-network"
|
|
3099
|
-
version = "0.6.
|
|
3100
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
3101
|
+
version = "0.6.8"
|
|
3102
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
3101
3103
|
dependencies = [
|
|
3102
3104
|
"base64",
|
|
3103
3105
|
"bytes",
|
|
@@ -3140,8 +3142,8 @@ dependencies = [
|
|
|
3140
3142
|
|
|
3141
3143
|
[[package]]
|
|
3142
3144
|
name = "microsandbox-protocol"
|
|
3143
|
-
version = "0.6.
|
|
3144
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
3145
|
+
version = "0.6.8"
|
|
3146
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
3145
3147
|
dependencies = [
|
|
3146
3148
|
"chrono",
|
|
3147
3149
|
"ciborium",
|
|
@@ -3155,8 +3157,8 @@ dependencies = [
|
|
|
3155
3157
|
|
|
3156
3158
|
[[package]]
|
|
3157
3159
|
name = "microsandbox-runtime"
|
|
3158
|
-
version = "0.6.
|
|
3159
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
3160
|
+
version = "0.6.8"
|
|
3161
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
3160
3162
|
dependencies = [
|
|
3161
3163
|
"bytes",
|
|
3162
3164
|
"chrono",
|
|
@@ -3187,8 +3189,8 @@ dependencies = [
|
|
|
3187
3189
|
|
|
3188
3190
|
[[package]]
|
|
3189
3191
|
name = "microsandbox-types"
|
|
3190
|
-
version = "0.6.
|
|
3191
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
3192
|
+
version = "0.6.8"
|
|
3193
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
3192
3194
|
dependencies = [
|
|
3193
3195
|
"chrono",
|
|
3194
3196
|
"ipnetwork",
|
|
@@ -3201,8 +3203,8 @@ dependencies = [
|
|
|
3201
3203
|
|
|
3202
3204
|
[[package]]
|
|
3203
3205
|
name = "microsandbox-utils"
|
|
3204
|
-
version = "0.6.
|
|
3205
|
-
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.
|
|
3206
|
+
version = "0.6.8"
|
|
3207
|
+
source = "git+https://github.com/superradcompany/microsandbox?tag=v0.6.8#7958cddeff2fce10dbcfd0c0d6a32dec93a9d5ea"
|
|
3206
3208
|
dependencies = [
|
|
3207
3209
|
"dirs",
|
|
3208
3210
|
"libc",
|
|
@@ -3214,7 +3216,7 @@ dependencies = [
|
|
|
3214
3216
|
|
|
3215
3217
|
[[package]]
|
|
3216
3218
|
name = "microsandbox_rb"
|
|
3217
|
-
version = "0.
|
|
3219
|
+
version = "0.12.0"
|
|
3218
3220
|
dependencies = [
|
|
3219
3221
|
"chrono",
|
|
3220
3222
|
"futures",
|
|
@@ -6012,8 +6014,12 @@ checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c"
|
|
|
6012
6014
|
dependencies = [
|
|
6013
6015
|
"futures-util",
|
|
6014
6016
|
"log",
|
|
6017
|
+
"rustls",
|
|
6018
|
+
"rustls-pki-types",
|
|
6015
6019
|
"tokio",
|
|
6020
|
+
"tokio-rustls",
|
|
6016
6021
|
"tungstenite",
|
|
6022
|
+
"webpki-roots 0.26.11",
|
|
6017
6023
|
]
|
|
6018
6024
|
|
|
6019
6025
|
[[package]]
|
|
@@ -6170,6 +6176,8 @@ dependencies = [
|
|
|
6170
6176
|
"httparse",
|
|
6171
6177
|
"log",
|
|
6172
6178
|
"rand 0.9.4",
|
|
6179
|
+
"rustls",
|
|
6180
|
+
"rustls-pki-types",
|
|
6173
6181
|
"sha1 0.10.6",
|
|
6174
6182
|
"thiserror 2.0.18",
|
|
6175
6183
|
]
|
data/README.md
CHANGED
|
@@ -126,10 +126,13 @@ ensure
|
|
|
126
126
|
# sb.kill # force (SIGKILL); sb.drain for a graceful drain
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
-
# Inspect / manage existing sandboxes. `get
|
|
129
|
+
# Inspect / manage existing sandboxes. `get` returns a controllable
|
|
130
130
|
# SandboxHandle (the live `stop`/`kill`/`drain`/`wait` live on the object from
|
|
131
|
-
# `create`/`start`; fine-grained control lives on the handle).
|
|
132
|
-
|
|
131
|
+
# `create`/`start`; fine-grained control lives on the handle). `list` returns a
|
|
132
|
+
# cursor-paginated, Enumerable SandboxPage of handles (runtime v0.6.8).
|
|
133
|
+
Microsandbox::Sandbox.list # => Microsandbox::SandboxPage (first page)
|
|
134
|
+
Microsandbox::Sandbox.list.map(&:name) # enumerate the page's handles
|
|
135
|
+
# next page: Sandbox.list_with(cursor: page.next_cursor, limit: 50)
|
|
133
136
|
h = Microsandbox::Sandbox.get("box") # => Microsandbox::SandboxHandle
|
|
134
137
|
h.status # :running, :stopped, :created, ...
|
|
135
138
|
h.stop_with_timeout(5) # custom escalation timeout
|
|
@@ -442,8 +445,8 @@ change diverged the two numbers — the gem version is **not** a reliable indica
|
|
|
442
445
|
of the embedded runtime version. To learn which runtime a build wraps, ask it:
|
|
443
446
|
|
|
444
447
|
```ruby
|
|
445
|
-
Microsandbox::VERSION # => "0.
|
|
446
|
-
Microsandbox.runtime_version # => "v0.6.
|
|
448
|
+
Microsandbox::VERSION # => "0.12.0" (the gem's own version)
|
|
449
|
+
Microsandbox.runtime_version # => "v0.6.8" (the embedded upstream runtime tag)
|
|
447
450
|
```
|
|
448
451
|
|
|
449
452
|
| Gem version | Upstream runtime | Notes |
|
|
@@ -465,6 +468,7 @@ Microsandbox.runtime_version # => "v0.6.7" (the embedded upstream runtime tag
|
|
|
465
468
|
| `0.9.3` | `v0.6.6` | adopts upstream `v0.6.4`+`v0.6.6` (`v0.6.5` was yanked upstream): snapshot restore by pinned digest — fixes fatal restore-after-tag-republish bug, fragmented-UDP/PMTU relay fixes, exec kills the whole process group, ephemeral stop-wait tolerance, readdir RSS-leak fix; upstream API growth is additive-only — no Ruby surface change |
|
|
466
469
|
| `0.10.0` | `v0.6.6` | `v0.6.6` API parity: live `modify`/resize, `ping`/`touch`, create `max_cpus`/`max_memory` |
|
|
467
470
|
| `0.11.0` | `v0.6.7` | adopts upstream `v0.6.7` (**breaking**): network profiles replace `public_only`/`non_local`, structured `root_disk:` replaces `oci_upper_size:` (deprecated alias kept), snapshot descriptor contract (`create` re-keyed by name, `save`/`load` rename, `snapshot_to` removed, on-disk auto-migration), `Image.load`/`Image.save`, `follow_root_symlinks:`; runtime carries the GHSA-4vq3-cjpp-v7fg `msb copy` fix |
|
|
471
|
+
| `0.12.0` | `v0.6.8` | adopts upstream `v0.6.8` (**breaking**): `Sandbox.list`/`.list_with` return a cursor-paginated `SandboxPage` (`limit:`/`cursor:` keywords), `UnsupportedError` re-keyed by structured operations with `#operation`/`#hint`; runtime adds a shared log registry for followed streams and cloud exec/ssh reconnects |
|
|
468
472
|
|
|
469
473
|
**Going forward** — the gem version moves on its own semver track and no longer
|
|
470
474
|
mirrors the upstream tag:
|
|
@@ -544,7 +548,8 @@ lifecycle (the live `Sandbox` `stop`/`stop_and_wait`/`kill`/`drain`/`wait`/
|
|
|
544
548
|
`status`/`detach`/`owns_lifecycle?`, plus the `SandboxHandle` controls
|
|
545
549
|
`stop_with_timeout`/`request_stop`/`request_kill`/`request_drain`/
|
|
546
550
|
`wait_until_stopped`/`config`/`config_json`/`snapshot` from
|
|
547
|
-
`Sandbox.get`, and
|
|
551
|
+
`Sandbox.get`, and the cursor-paginated `list`/`list_with` with label
|
|
552
|
+
filters),
|
|
548
553
|
backend routing (`set_default_backend`/`with_backend`/`default_backend_kind`),
|
|
549
554
|
`exec`/`shell` (collected and streaming), interactive `attach`/
|
|
550
555
|
`attach_shell`, the full guest filesystem (incl. streaming `read_stream`/
|
data/ext/microsandbox/Cargo.toml
CHANGED
|
@@ -7,7 +7,7 @@ description = "Ruby SDK native extension for microsandbox — secure, fast micro
|
|
|
7
7
|
# Must equal Microsandbox::VERSION (lib/microsandbox/version.rb) — Native.version
|
|
8
8
|
# returns this via env!("CARGO_PKG_VERSION") and version_spec.rb asserts equality.
|
|
9
9
|
# The core-crate dependency below stays pinned at its own tag (v0.6.7).
|
|
10
|
-
version = "0.
|
|
10
|
+
version = "0.12.0"
|
|
11
11
|
authors = ["Super Rad Company <development@superrad.company>"]
|
|
12
12
|
repository = "https://github.com/superradcompany/microsandbox"
|
|
13
13
|
license = "Apache-2.0"
|
|
@@ -35,8 +35,8 @@ rb-sys = "0.9"
|
|
|
35
35
|
# `.cargo/config.toml.example`). "ssh" matches the feature set the Python/Node
|
|
36
36
|
# SDKs ship with; default features add "prebuilt" (provisions msb + libkrunfw at
|
|
37
37
|
# build time), "net", and "keyring".
|
|
38
|
-
microsandbox = { git = "https://github.com/superradcompany/microsandbox", tag = "v0.6.
|
|
39
|
-
microsandbox-network = { git = "https://github.com/superradcompany/microsandbox", tag = "v0.6.
|
|
38
|
+
microsandbox = { git = "https://github.com/superradcompany/microsandbox", tag = "v0.6.8", default-features = true, features = ["ssh"] }
|
|
39
|
+
microsandbox-network = { git = "https://github.com/superradcompany/microsandbox", tag = "v0.6.8" }
|
|
40
40
|
|
|
41
41
|
# Async core bridged to Ruby's synchronous API via a blocking tokio runtime.
|
|
42
42
|
tokio = { version = "1", features = ["rt-multi-thread", "sync", "time"] }
|
|
@@ -20,7 +20,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
|
|
20
20
|
use std::sync::{Arc, Mutex, OnceLock};
|
|
21
21
|
|
|
22
22
|
use magnus::{function, prelude::*, Error, RModule, Ruby};
|
|
23
|
-
use microsandbox::{Backend, MicrosandboxError};
|
|
23
|
+
use microsandbox::{Backend, MicrosandboxError, Operation};
|
|
24
24
|
|
|
25
25
|
use crate::error;
|
|
26
26
|
use crate::runtime::block_on;
|
|
@@ -31,17 +31,14 @@ use crate::runtime::block_on;
|
|
|
31
31
|
/// borrowing `&LocalBackend` from it (`as_local()` borrows the `Arc`). Pure
|
|
32
32
|
/// Rust — safe to call inside `block_on` (no Ruby C API), and it returns a raw
|
|
33
33
|
/// `MicrosandboxError` so the Ruby-exception mapping happens *after* `block_on`
|
|
34
|
-
/// re-acquires the GVL. Cloud backends yield `Unsupported
|
|
35
|
-
/// `resolve_local`.
|
|
36
|
-
pub fn local_backend() -> Result<Arc<dyn Backend>, MicrosandboxError> {
|
|
34
|
+
/// re-acquires the GVL. Cloud backends yield `Unsupported` for the given
|
|
35
|
+
/// operation (v0.6.8 `Operation`-keyed shape), mirroring pyo3's `resolve_local`.
|
|
36
|
+
pub fn local_backend(op: Operation) -> Result<Arc<dyn Backend>, MicrosandboxError> {
|
|
37
37
|
let backend = microsandbox::default_backend();
|
|
38
38
|
if backend.as_local().is_some() {
|
|
39
39
|
Ok(backend)
|
|
40
40
|
} else {
|
|
41
|
-
Err(MicrosandboxError::
|
|
42
|
-
feature: "this operation requires a local backend".into(),
|
|
43
|
-
available_when: "with the local backend (the default)".into(),
|
|
44
|
-
})
|
|
41
|
+
Err(MicrosandboxError::local_only(op))
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
44
|
|
|
@@ -53,10 +50,11 @@ pub fn local_backend() -> Result<Arc<dyn Backend>, MicrosandboxError> {
|
|
|
53
50
|
/// it and returns the same `Arc` kept alive for the borrow — so it lives here
|
|
54
51
|
/// once rather than at every call site.
|
|
55
52
|
pub fn with_local_backend<T>(
|
|
53
|
+
operation: Operation,
|
|
56
54
|
op: impl AsyncFnOnce(µsandbox::LocalBackend) -> Result<T, MicrosandboxError>,
|
|
57
55
|
) -> Result<T, Error> {
|
|
58
56
|
block_on(async move {
|
|
59
|
-
let backend = local_backend()?;
|
|
57
|
+
let backend = local_backend(operation)?;
|
|
60
58
|
let local = backend
|
|
61
59
|
.as_local()
|
|
62
60
|
.expect("local_backend() guarantees a local backend");
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//! is always the core error's `to_string()`.
|
|
7
7
|
|
|
8
8
|
use magnus::{value::ReprValue, Error, ExceptionClass, Module, RClass, RModule, Ruby};
|
|
9
|
-
use microsandbox::{AgentClientError, MicrosandboxError};
|
|
9
|
+
use microsandbox::{AgentClientError, MicrosandboxError, Operation, UnsupportedReason};
|
|
10
10
|
|
|
11
11
|
/// The Ruby class (relative to the `Microsandbox` module) for a core error.
|
|
12
12
|
/// `"Error"` is the base class; anything else is a named subclass.
|
|
@@ -83,12 +83,96 @@ pub fn to_ruby(err: MicrosandboxError) -> Error {
|
|
|
83
83
|
Err(_) => return Error::new(magnus::exception::runtime_error(), message),
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
+
// `Unsupported` gets a Ruby-idiom message (`sandbox.kill` instead of
|
|
87
|
+
// `Sandbox::kill`) plus structured `operation` / `hint` attributes on the
|
|
88
|
+
// exception instance, mirroring the Python SDK's enrichment (v0.6.8).
|
|
89
|
+
if let MicrosandboxError::Unsupported { op, reason } = &err {
|
|
90
|
+
return unsupported_error(&ruby, &ruby_api_name(*op), &ruby_hint(reason));
|
|
91
|
+
}
|
|
92
|
+
|
|
86
93
|
match exception_class(&ruby, class_name(&err)) {
|
|
87
94
|
Some(class) => Error::new(class, message),
|
|
88
95
|
None => Error::new(ruby.exception_runtime_error(), message),
|
|
89
96
|
}
|
|
90
97
|
}
|
|
91
98
|
|
|
99
|
+
/// `UnsupportedError` for shim-only entry points that require the local
|
|
100
|
+
/// backend but have no SDK [`Operation`] (Ruby-only diagnostic hooks such as
|
|
101
|
+
/// `Microsandbox.runtime_path`). `name` is the Ruby-facing API name. Mirrors
|
|
102
|
+
/// the Python SDK's name-based `local_only` helper.
|
|
103
|
+
#[allow(deprecated)]
|
|
104
|
+
pub fn local_only(name: &str) -> Error {
|
|
105
|
+
match Ruby::get() {
|
|
106
|
+
Ok(ruby) => unsupported_error(&ruby, name, "use a local backend"),
|
|
107
|
+
Err(_) => Error::new(
|
|
108
|
+
magnus::exception::runtime_error(),
|
|
109
|
+
format!("{name} is not supported by this backend: use a local backend"),
|
|
110
|
+
),
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// Build a `Microsandbox::UnsupportedError` carrying the rendered message and
|
|
115
|
+
/// the structured `@operation` / `@hint` attributes.
|
|
116
|
+
fn unsupported_error(ruby: &Ruby, operation: &str, hint: &str) -> Error {
|
|
117
|
+
let message = format!("{operation} is not supported by this backend: {hint}");
|
|
118
|
+
let Some(class) = exception_class(ruby, "UnsupportedError") else {
|
|
119
|
+
return Error::new(ruby.exception_runtime_error(), message);
|
|
120
|
+
};
|
|
121
|
+
match class
|
|
122
|
+
.as_value()
|
|
123
|
+
.funcall::<_, _, magnus::Exception>("new", (message.as_str(),))
|
|
124
|
+
{
|
|
125
|
+
Ok(exc) => {
|
|
126
|
+
// Best-effort extras; the message already carries both.
|
|
127
|
+
let _ = exc
|
|
128
|
+
.funcall::<_, _, magnus::Value>("instance_variable_set", ("@operation", operation));
|
|
129
|
+
let _ = exc.funcall::<_, _, magnus::Value>("instance_variable_set", ("@hint", hint));
|
|
130
|
+
exc.into()
|
|
131
|
+
}
|
|
132
|
+
Err(_) => Error::new(class, message),
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// Render an [`Operation`] as the Ruby API it corresponds to: `Sandbox::kill`
|
|
137
|
+
/// becomes `sandbox.kill` and `SandboxFsOps::stat_handle` becomes
|
|
138
|
+
/// `sandbox_fs_ops.stat_handle`; a parenthetical keeps its Ruby keyword shape
|
|
139
|
+
/// (`log_stream(follow=false)` becomes `log_stream(follow: false)`). Plain
|
|
140
|
+
/// phrases without a `Type::method` shape (`config`, `snapshot operations`)
|
|
141
|
+
/// pass through as-is. Mirrors the Python SDK's `py_api_name`.
|
|
142
|
+
fn ruby_api_name(op: Operation) -> String {
|
|
143
|
+
let path = op.api_path();
|
|
144
|
+
let Some((ty, method)) = path.split_once("::") else {
|
|
145
|
+
return path.to_string();
|
|
146
|
+
};
|
|
147
|
+
format!("{}.{}", camel_to_snake(ty), method.replace('=', ": "))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/// Render an [`UnsupportedReason`] with `use instead` targets pointing at the
|
|
151
|
+
/// Ruby API name rather than the Rust path.
|
|
152
|
+
fn ruby_hint(reason: &UnsupportedReason) -> String {
|
|
153
|
+
match reason {
|
|
154
|
+
UnsupportedReason::UseInstead(op) => format!("use {}", ruby_api_name(*op)),
|
|
155
|
+
other => other.hint(),
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/// Lower a `CamelCase` type name to `snake_case` (`SandboxFsOps` becomes
|
|
160
|
+
/// `sandbox_fs_ops`).
|
|
161
|
+
fn camel_to_snake(name: &str) -> String {
|
|
162
|
+
let mut out = String::with_capacity(name.len() + 4);
|
|
163
|
+
for (i, ch) in name.char_indices() {
|
|
164
|
+
if ch.is_ascii_uppercase() {
|
|
165
|
+
if i > 0 {
|
|
166
|
+
out.push('_');
|
|
167
|
+
}
|
|
168
|
+
out.push(ch.to_ascii_lowercase());
|
|
169
|
+
} else {
|
|
170
|
+
out.push(ch);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
out
|
|
174
|
+
}
|
|
175
|
+
|
|
92
176
|
/// A plain `Microsandbox::Error` (base) with a custom message — used for
|
|
93
177
|
/// binding-level validation errors that have no core counterpart.
|
|
94
178
|
#[allow(deprecated)]
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
use magnus::{function, prelude::*, Error, RArray, RHash, RModule, Ruby};
|
|
9
9
|
use microsandbox::image::{Image, ImageDetail, ImageHandle, ImagePruneReport};
|
|
10
|
-
use microsandbox::ImageArchiveFormat;
|
|
10
|
+
use microsandbox::{ImageArchiveFormat, Operation};
|
|
11
11
|
|
|
12
12
|
use crate::backend::with_local_backend;
|
|
13
13
|
use crate::conv;
|
|
@@ -84,12 +84,16 @@ fn report_to_hash(report: ImagePruneReport) -> RHash {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
fn get(reference: String) -> Result<RHash, Error> {
|
|
87
|
-
let handle = with_local_backend(async |local|
|
|
87
|
+
let handle = with_local_backend(Operation::ImageGet, async |local| {
|
|
88
|
+
Image::get_local(local, &reference).await
|
|
89
|
+
})?;
|
|
88
90
|
Ok(handle_to_hash(&handle))
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
fn list() -> Result<RArray, Error> {
|
|
92
|
-
let handles = with_local_backend(async |local|
|
|
94
|
+
let handles = with_local_backend(Operation::ImageList, async |local| {
|
|
95
|
+
Image::list_local(local).await
|
|
96
|
+
})?;
|
|
93
97
|
let arr = ruby().ary_new();
|
|
94
98
|
for h in handles.iter() {
|
|
95
99
|
arr.push(handle_to_hash(h))?;
|
|
@@ -98,16 +102,22 @@ fn list() -> Result<RArray, Error> {
|
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
fn inspect(reference: String) -> Result<RHash, Error> {
|
|
101
|
-
let detail = with_local_backend(async |local|
|
|
105
|
+
let detail = with_local_backend(Operation::ImageInspect, async |local| {
|
|
106
|
+
Image::inspect_local(local, &reference).await
|
|
107
|
+
})?;
|
|
102
108
|
Ok(detail_to_hash(detail))
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
fn remove(reference: String, force: bool) -> Result<(), Error> {
|
|
106
|
-
with_local_backend(async |local|
|
|
112
|
+
with_local_backend(Operation::ImageRemove, async |local| {
|
|
113
|
+
Image::remove_local(local, &reference, force).await
|
|
114
|
+
})
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
fn prune() -> Result<RHash, Error> {
|
|
110
|
-
let report = with_local_backend(async |local|
|
|
118
|
+
let report = with_local_backend(Operation::ImagePrune, async |local| {
|
|
119
|
+
Image::prune_local(local).await
|
|
120
|
+
})?;
|
|
111
121
|
Ok(report_to_hash(report))
|
|
112
122
|
}
|
|
113
123
|
|
|
@@ -117,7 +127,7 @@ fn prune() -> Result<RHash, Error> {
|
|
|
117
127
|
/// `imageLoad` added in v0.6.7. (The `"-"` stdin form is spooled to a temp
|
|
118
128
|
/// file by the Ruby layer — the core reads seekable files only.)
|
|
119
129
|
fn load(input_path: String, tags: Vec<String>) -> Result<RArray, Error> {
|
|
120
|
-
let handles = with_local_backend(async |local| {
|
|
130
|
+
let handles = with_local_backend(Operation::ImageLoad, async |local| {
|
|
121
131
|
Image::load_local(local, std::path::Path::new(&input_path), tags).await
|
|
122
132
|
})?;
|
|
123
133
|
let arr = ruby().ary_new();
|
|
@@ -140,7 +150,7 @@ fn save(references: Vec<String>, output_path: String, format: String) -> Result<
|
|
|
140
150
|
)))
|
|
141
151
|
}
|
|
142
152
|
};
|
|
143
|
-
with_local_backend(async |local| {
|
|
153
|
+
with_local_backend(Operation::ImageSave, async |local| {
|
|
144
154
|
Image::save_local(local, &references, std::path::Path::new(&output_path), fmt).await
|
|
145
155
|
})
|
|
146
156
|
}
|
data/ext/microsandbox/src/lib.rs
CHANGED
|
@@ -33,9 +33,10 @@ fn version() -> String {
|
|
|
33
33
|
/// Ruby Hash. Mirrors the official `all_sandbox_metrics` / `allSandboxMetrics`
|
|
34
34
|
/// helpers (Python/Node/Go).
|
|
35
35
|
fn all_sandbox_metrics() -> Result<RHash, Error> {
|
|
36
|
-
let map =
|
|
37
|
-
microsandbox::
|
|
38
|
-
|
|
36
|
+
let map =
|
|
37
|
+
backend::with_local_backend(microsandbox::Operation::AllSandboxMetrics, async |local| {
|
|
38
|
+
microsandbox::sandbox::all_sandbox_metrics_local(local).await
|
|
39
|
+
})?;
|
|
39
40
|
let hash = runtime::ruby().hash_new();
|
|
40
41
|
for (name, metrics) in &map {
|
|
41
42
|
hash.aset(name.as_str(), sandbox::metrics_to_hash(metrics))?;
|
|
@@ -107,12 +108,11 @@ fn set_runtime_libkrunfw_path(path: String) {
|
|
|
107
108
|
/// backend's config.
|
|
108
109
|
fn resolved_msb_path() -> Result<String, Error> {
|
|
109
110
|
let backend = microsandbox::default_backend();
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
})?;
|
|
111
|
+
// Shim-only entry point with no SDK `Operation` — report the Ruby-facing
|
|
112
|
+
// name (`Microsandbox.runtime_path`), like Python's name-based local_only.
|
|
113
|
+
let local = backend
|
|
114
|
+
.as_local()
|
|
115
|
+
.ok_or_else(|| error::local_only("runtime_path"))?;
|
|
116
116
|
let path = local.config().resolve_msb_path().map_err(error::to_ruby)?;
|
|
117
117
|
Ok(path.to_string_lossy().into_owned())
|
|
118
118
|
}
|
|
@@ -20,9 +20,9 @@ use microsandbox::logs::{
|
|
|
20
20
|
use microsandbox::sandbox::{
|
|
21
21
|
AttachOptionsBuilder, DiskImageFormat, EnvVar, FsEntry, FsEntryKind, FsMetadata,
|
|
22
22
|
HostPermissions, Patch, PullPolicy, PullProgress, PullProgressHandle, RlimitResource,
|
|
23
|
-
RootDiskBuilder, SandboxBuilder,
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
RootDiskBuilder, SandboxBuilder, SandboxHandle, SandboxMetrics, SandboxModificationBuilder,
|
|
24
|
+
SandboxModificationPatch, SandboxStatus, SandboxStopResult, SecretBuilder,
|
|
25
|
+
SecretModificationPatch, SecretSource, SecurityProfile, StatVirtualization,
|
|
26
26
|
};
|
|
27
27
|
use microsandbox::LogLevel;
|
|
28
28
|
use microsandbox::MicrosandboxResult;
|
|
@@ -477,29 +477,44 @@ impl Sandbox {
|
|
|
477
477
|
Ok(SbHandle::from_inner(handle))
|
|
478
478
|
}
|
|
479
479
|
|
|
480
|
-
///
|
|
481
|
-
|
|
482
|
-
|
|
480
|
+
/// Convert one `SandboxPage` into a `{ "sandboxes" => [SbHandle], "next_cursor" => String|nil }`
|
|
481
|
+
/// Ruby Hash (v0.6.8 paginated listing contract).
|
|
482
|
+
fn page_to_hash(page: microsandbox::sandbox::SandboxPage) -> Result<RHash, Error> {
|
|
483
483
|
let arr = ruby().ary_new();
|
|
484
|
-
for h in
|
|
484
|
+
for h in page.sandboxes {
|
|
485
485
|
arr.push(SbHandle::from_inner(h))?;
|
|
486
486
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
487
|
+
let hash = ruby().hash_new();
|
|
488
|
+
hash.aset("sandboxes", arr)?;
|
|
489
|
+
hash.aset("next_cursor", page.next_cursor)?;
|
|
490
|
+
Ok(hash)
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/// First page of sandboxes (default page size), as a page Hash of
|
|
494
|
+
/// controllable handles.
|
|
495
|
+
fn list() -> Result<RHash, Error> {
|
|
496
|
+
let page = block_on(microsandbox::Sandbox::list()).map_err(error::to_ruby)?;
|
|
497
|
+
Self::page_to_hash(page)
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/// One configured page of sandboxes as a page Hash. `opts` carries an
|
|
501
|
+
/// optional string→string `labels` map (AND-matched), an optional `limit`
|
|
502
|
+
/// (1..=100) and an optional opaque `cursor` from a previous page.
|
|
503
|
+
fn list_with(opts: RHash) -> Result<RHash, Error> {
|
|
504
|
+
let labels = conv::opt_string_map(opts, "labels")?;
|
|
505
|
+
let limit = conv::opt_u32(opts, "limit")?;
|
|
506
|
+
let cursor = conv::opt_string(opts, "cursor")?;
|
|
507
|
+
let page = block_on(microsandbox::Sandbox::list_with(move |mut b| {
|
|
508
|
+
if let Some(limit) = limit {
|
|
509
|
+
b = b.limit(limit);
|
|
510
|
+
}
|
|
511
|
+
if let Some(cursor) = cursor {
|
|
512
|
+
b = b.cursor(cursor);
|
|
513
|
+
}
|
|
514
|
+
b.labels(labels)
|
|
515
|
+
}))
|
|
516
|
+
.map_err(error::to_ruby)?;
|
|
517
|
+
Self::page_to_hash(page)
|
|
503
518
|
}
|
|
504
519
|
|
|
505
520
|
/// Remove a (stopped) sandbox by name.
|
|
@@ -9,7 +9,6 @@ use magnus::{function, method, prelude::*, Error, RArray, RHash, RModule, RStrin
|
|
|
9
9
|
use microsandbox::volume::VolumeHandle;
|
|
10
10
|
use microsandbox::Backend;
|
|
11
11
|
|
|
12
|
-
use crate::backend::local_backend;
|
|
13
12
|
use crate::conv;
|
|
14
13
|
use crate::error;
|
|
15
14
|
use crate::runtime::{block_on, ruby};
|
|
@@ -108,10 +107,13 @@ pub struct VolumeFs {
|
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
impl VolumeFs {
|
|
111
|
-
/// Resolve the
|
|
110
|
+
/// Resolve the ambient backend once and bind it to `name`. As of v0.6.8
|
|
111
|
+
/// every `VolumeFs` op dispatches through the backend's `VolumeBackend`
|
|
112
|
+
/// trait, which yields precise per-operation `Unsupported` errors on
|
|
113
|
+
/// backends that can't serve it — no local-only downcast needed here.
|
|
112
114
|
fn for_volume(name: String) -> Result<VolumeFs, Error> {
|
|
113
115
|
Ok(VolumeFs {
|
|
114
|
-
backend:
|
|
116
|
+
backend: microsandbox::default_backend(),
|
|
115
117
|
name,
|
|
116
118
|
})
|
|
117
119
|
}
|
data/lib/microsandbox/errors.rb
CHANGED
|
@@ -90,4 +90,16 @@ module Microsandbox
|
|
|
90
90
|
# cloud backend) is distinct from `UnsupportedOperationError` above.
|
|
91
91
|
define_error(:CloudHttpError, "cloud-http")
|
|
92
92
|
define_error(:UnsupportedError, "unsupported")
|
|
93
|
+
|
|
94
|
+
# As of runtime v0.6.8 the core keys Unsupported errors by a structured
|
|
95
|
+
# (operation, reason) pair. The native layer renders both into the message
|
|
96
|
+
# ("sandbox.kill is not supported by this backend: ...") and also attaches
|
|
97
|
+
# them here as structured attributes, mirroring the Python SDK's
|
|
98
|
+
# `UnsupportedError.operation` / `.hint`.
|
|
99
|
+
class UnsupportedError
|
|
100
|
+
# @return [String, nil] the rejected API in Ruby rendering, e.g. "sandbox.kill"
|
|
101
|
+
attr_reader :operation
|
|
102
|
+
# @return [String, nil] why it was rejected / what to use instead
|
|
103
|
+
attr_reader :hint
|
|
104
|
+
end
|
|
93
105
|
end
|
data/lib/microsandbox/sandbox.rb
CHANGED
|
@@ -202,6 +202,48 @@ module Microsandbox
|
|
|
202
202
|
end
|
|
203
203
|
end
|
|
204
204
|
|
|
205
|
+
# One page of sandbox handles, returned by {Sandbox.list} and
|
|
206
|
+
# {Sandbox.list_with} — sandbox listing is cursor-paginated as of runtime
|
|
207
|
+
# v0.6.8. Enumerable over its {SandboxHandle}s; fetch the following page by
|
|
208
|
+
# passing {#next_cursor} to {Sandbox.list_with}.
|
|
209
|
+
#
|
|
210
|
+
# @example Walk every page
|
|
211
|
+
# page = Microsandbox::Sandbox.list
|
|
212
|
+
# loop do
|
|
213
|
+
# page.each { |h| puts h.name }
|
|
214
|
+
# break if page.last_page?
|
|
215
|
+
# page = Microsandbox::Sandbox.list_with(cursor: page.next_cursor)
|
|
216
|
+
# end
|
|
217
|
+
class SandboxPage
|
|
218
|
+
include Enumerable
|
|
219
|
+
|
|
220
|
+
# @return [Array<SandboxHandle>] the sandboxes in this page
|
|
221
|
+
attr_reader :sandboxes
|
|
222
|
+
# @return [String, nil] opaque cursor for the next page; nil on the final page
|
|
223
|
+
attr_reader :next_cursor
|
|
224
|
+
|
|
225
|
+
# @api private — construct via {Sandbox.list}/{Sandbox.list_with}.
|
|
226
|
+
def initialize(sandboxes, next_cursor)
|
|
227
|
+
@sandboxes = sandboxes
|
|
228
|
+
@next_cursor = next_cursor
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def each(&) = @sandboxes.each(&)
|
|
232
|
+
|
|
233
|
+
# @return [Integer]
|
|
234
|
+
def size = @sandboxes.size
|
|
235
|
+
alias_method :length, :size
|
|
236
|
+
|
|
237
|
+
def empty? = @sandboxes.empty?
|
|
238
|
+
|
|
239
|
+
# Whether this is the final page (no cursor to continue from).
|
|
240
|
+
def last_page? = @next_cursor.nil?
|
|
241
|
+
|
|
242
|
+
def inspect
|
|
243
|
+
"#<Microsandbox::SandboxPage size=#{size} next_cursor=#{@next_cursor.inspect}>"
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
205
247
|
# A running sandbox (microVM) — the primary entry point of the SDK.
|
|
206
248
|
#
|
|
207
249
|
# @example Block form (auto-stops on exit)
|
|
@@ -478,18 +520,25 @@ module Microsandbox
|
|
|
478
520
|
SandboxHandle.new(Native::Sandbox.get(name.to_s))
|
|
479
521
|
end
|
|
480
522
|
|
|
481
|
-
# List
|
|
482
|
-
#
|
|
523
|
+
# List the first page of sandboxes (default page size 20) as controllable
|
|
524
|
+
# handles. Cursor-paginated as of runtime v0.6.8 — follow
|
|
525
|
+
# {SandboxPage#next_cursor} via {.list_with} for subsequent pages.
|
|
526
|
+
# @return [SandboxPage]
|
|
483
527
|
def list
|
|
484
|
-
Native::Sandbox.list
|
|
528
|
+
build_page(Native::Sandbox.list)
|
|
485
529
|
end
|
|
486
530
|
|
|
487
|
-
# List
|
|
488
|
-
# @param labels [Hash] required key => value labels
|
|
489
|
-
# @
|
|
490
|
-
|
|
531
|
+
# List one configured page of sandboxes.
|
|
532
|
+
# @param labels [Hash] required key => value labels (AND-matched)
|
|
533
|
+
# @param limit [Integer, nil] page size, 1..100 (upstream default 20)
|
|
534
|
+
# @param cursor [String, nil] opaque cursor from a previous page's
|
|
535
|
+
# {SandboxPage#next_cursor}
|
|
536
|
+
# @return [SandboxPage]
|
|
537
|
+
def list_with(labels: {}, limit: nil, cursor: nil)
|
|
491
538
|
opts = {"labels" => stringify(labels)}
|
|
492
|
-
|
|
539
|
+
opts["limit"] = Integer(limit) if limit
|
|
540
|
+
opts["cursor"] = cursor.to_s if cursor
|
|
541
|
+
build_page(Native::Sandbox.list_with(opts))
|
|
493
542
|
end
|
|
494
543
|
|
|
495
544
|
# Remove a (stopped) sandbox by name.
|
|
@@ -518,6 +567,15 @@ module Microsandbox
|
|
|
518
567
|
hash.each_with_object({}) { |(k, v), acc| acc[k.to_s] = v.to_s }
|
|
519
568
|
end
|
|
520
569
|
|
|
570
|
+
# Wrap a native page Hash ({"sandboxes" => [...], "next_cursor" => ...})
|
|
571
|
+
# into a {SandboxPage} of {SandboxHandle}s.
|
|
572
|
+
def build_page(data)
|
|
573
|
+
SandboxPage.new(
|
|
574
|
+
data["sandboxes"].map { |h| SandboxHandle.new(h) },
|
|
575
|
+
data["next_cursor"]
|
|
576
|
+
)
|
|
577
|
+
end
|
|
578
|
+
|
|
521
579
|
def intify_ports(ports)
|
|
522
580
|
ports.each_with_object({}) { |(k, v), acc| acc[Integer(k)] = Integer(v) }
|
|
523
581
|
end
|
data/lib/microsandbox/version.rb
CHANGED
|
@@ -8,12 +8,12 @@ module Microsandbox
|
|
|
8
8
|
# Versioning section of the README for the full gem-to-runtime map. Must equal
|
|
9
9
|
# the native ext's Cargo crate version (`Native.version`), enforced by
|
|
10
10
|
# spec/unit/version_spec.rb.
|
|
11
|
-
VERSION = "0.
|
|
11
|
+
VERSION = "0.12.0"
|
|
12
12
|
|
|
13
13
|
# The upstream microsandbox runtime release this gem build embeds — the `tag`
|
|
14
14
|
# pinned on the `microsandbox`/`microsandbox-network` git deps in
|
|
15
15
|
# ext/microsandbox/Cargo.toml. Exposed at runtime as
|
|
16
16
|
# {Microsandbox.runtime_version}. spec/unit/version_spec.rb asserts it stays in
|
|
17
17
|
# sync with the Cargo tag so it can't silently drift out of date.
|
|
18
|
-
RUNTIME_VERSION = "v0.6.
|
|
18
|
+
RUNTIME_VERSION = "v0.6.8"
|
|
19
19
|
end
|
data/sig/microsandbox.rbs
CHANGED
|
@@ -61,7 +61,10 @@ module Microsandbox
|
|
|
61
61
|
class MetricsUnavailableError < Error end
|
|
62
62
|
class UnsupportedOperationError < Error end
|
|
63
63
|
class CloudHttpError < Error end
|
|
64
|
-
class UnsupportedError < Error
|
|
64
|
+
class UnsupportedError < Error
|
|
65
|
+
def operation: () -> String?
|
|
66
|
+
def hint: () -> String?
|
|
67
|
+
end
|
|
65
68
|
|
|
66
69
|
class ExecOutput
|
|
67
70
|
def initialize: (Hash[String, untyped] data) -> void
|
|
@@ -198,6 +201,19 @@ module Microsandbox
|
|
|
198
201
|
def observed_at: () -> Time
|
|
199
202
|
end
|
|
200
203
|
|
|
204
|
+
class SandboxPage
|
|
205
|
+
include Enumerable[SandboxHandle]
|
|
206
|
+
def initialize: (Array[SandboxHandle] sandboxes, String? next_cursor) -> void
|
|
207
|
+
def sandboxes: () -> Array[SandboxHandle]
|
|
208
|
+
def next_cursor: () -> String?
|
|
209
|
+
def each: () { (SandboxHandle) -> void } -> Array[SandboxHandle]
|
|
210
|
+
| () -> Enumerator[SandboxHandle, Array[SandboxHandle]]
|
|
211
|
+
def size: () -> Integer
|
|
212
|
+
def length: () -> Integer
|
|
213
|
+
def empty?: () -> bool
|
|
214
|
+
def last_page?: () -> bool
|
|
215
|
+
end
|
|
216
|
+
|
|
201
217
|
class PingResult
|
|
202
218
|
def initialize: (Hash[String, untyped] data) -> void
|
|
203
219
|
def name: () -> String
|
|
@@ -251,8 +267,8 @@ module Microsandbox
|
|
|
251
267
|
def self.create_with_progress: (String name, **untyped) -> PullSession
|
|
252
268
|
def self.start: (String name, ?detached: bool) -> Sandbox
|
|
253
269
|
def self.get: (String name) -> SandboxHandle
|
|
254
|
-
def self.list: () ->
|
|
255
|
-
def self.list_with: (?labels: Hash[untyped, untyped]) ->
|
|
270
|
+
def self.list: () -> SandboxPage
|
|
271
|
+
def self.list_with: (?labels: Hash[untyped, untyped], ?limit: Integer?, ?cursor: String?) -> SandboxPage
|
|
256
272
|
def self.remove: (String name) -> nil
|
|
257
273
|
|
|
258
274
|
def name: () -> String
|