bsdkrun 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3cc7fd1dcbcb90243855217c4fe7852f6e98baf235fc6e0875ca3f7aeafeb7b
4
- data.tar.gz: 92aaa16edb2d6970912f64840835dbd78611cf9f30bc4c5830c1c4f0f537fa71
3
+ metadata.gz: b4ab49439569d91cb726f6aba3a2aff97bcd5ebae0fcc08daff49386d37493a6
4
+ data.tar.gz: 8e5e860d49fab4b63b29254f5e886e16db71e031bd1e35b0f24602e4c619c858
5
5
  SHA512:
6
- metadata.gz: 1505af9b58a1397e6de294f06d76b2a8ca81ae0a46ba80f61c12baa6e5de99d8e9fba3c973bee7157c663233e575468bbe175e435fe23a01258855a4278a00fa
7
- data.tar.gz: 38b533ac4692529528ce0c5c5739b1f62f8012f7ec6a2ac2814f866c99d7ec47a13ab26f1639e102012ca4e8161483cfe0731c8d987c23432da42133dfc61167
6
+ metadata.gz: 6e88557a8e9eaa412bd43bcf67d25b8ed63970fbdefb42edfe81ff07b94b4df0013870c8fa1cf98ea1797ca607eb9481839934fea65eb34d0a34be507c1ee267
7
+ data.tar.gz: 5980d80cdbc6d908ce8c489a5728699f7ae9fcabeeac325c7043aad8d6f11051091be4631047f1d69c4870546eeda3f902f79e7c2b8ffe1ce06805c509673c71
data/README.md CHANGED
@@ -1,9 +1,7 @@
1
1
  # bsdkrun (Ruby SDK)
2
2
 
3
- A Ruby SDK for [**bsdkrun**](https://github.com/tsirysndr/bsdkrun) — a
4
- Firecracker-style microVM launcher for **BSD and Linux** guests on macOS and
5
- Linux, built on [libkrun](https://github.com/containers/libkrun). Boot and drive
6
- microVMs programmatically, inspired by the **Vercel** and **Deno** Sandbox SDKs.
3
+ A Ruby SDK for [**bsdkrun**](https://github.com/tsirysndr/bsdkrun) — a Firecracker-style microVM launcher for **BSD and Linux** guests on macOS and
4
+ Linux, built on [libkrun](https://github.com/containers/libkrun). Boot and drive microVMs programmatically, inspired by the **Vercel** and **Deno** Sandbox SDKs.
7
5
 
8
6
  The SDK shells out to the `bsdkrun` binary, so it has **zero runtime
9
7
  dependencies** — just the Ruby standard library (`open3`, `json`, `pathname`).
@@ -191,6 +189,68 @@ Names resolve on Linux and FreeBSD via the network's DNS; **NetBSD** resolves
191
189
  via a synced `/etc/hosts` block — joins auto-sync, and `Networks.sync` refreshes
192
190
  an existing network without restarting members.
193
191
 
192
+ ## Connecting to a remote daemon
193
+
194
+ Everything above talks to a local `bsdkrun` binary. `Bsdkrun::Client` is the
195
+ network sibling: it drives the same operations against a remote
196
+ [`bsdkrund`](../../daemon/README.md) over its GraphQL API — no local binary
197
+ needed, just a URL and a bearer token.
198
+
199
+ ```ruby
200
+ require "bsdkrun"
201
+
202
+ client = Bsdkrun::Client.new(url: "http://vps.example.com:50052", token: "9f2c...")
203
+ # or, from BSDKRUN_URL / BSDKRUN_TOKEN:
204
+ client = Bsdkrun::Client.from_env
205
+
206
+ machines = client.list(all: true) # Array<SandboxInfo> — same type Sandbox.list returns
207
+ id = client.run_linux(image: "alpine", cpus: 2, mem: 1024, command: ["sleep", "300"])
208
+
209
+ result = client.exec(id, ["uname", "-a"])
210
+ puts result.output, result.exit_code
211
+
212
+ client.stop(id)
213
+ client.remove([id])
214
+ ```
215
+
216
+ `client.run_linux`/`run_bsd`/`run_nanos`/`run_unikraft`/`run_osv`/`run_flavor`
217
+ each take the same options as the corresponding GraphQL mutation
218
+ (`daemon/src/graphql.rs`) — `run_bsd(os: "freebsd", ...)`, etc. — and return
219
+ the new machine's id. `stop`/`start`/`remove`/`update`/`commit` return a
220
+ `CommandResult` (`exit_code`, `stdout`, `stderr`).
221
+
222
+ For a live terminal instead of a one-shot `exec`, use `shell`:
223
+
224
+ ```ruby
225
+ session = client.shell(id) # or shell(id, command: [...]) for a non-login command
226
+ session.on_output { |bytes| $stdout.write(bytes) }
227
+ session.on_exit { |code| puts "\nexited #{code}" }
228
+ session.write("ls -la\n")
229
+ session.resize(50, 120)
230
+ session.close
231
+ ```
232
+
233
+ `follow_logs(id) { |bytes| ... }` streams a machine's console live instead of
234
+ the one-shot `logs(id)`. Both `exec`/`shell` and `follow_logs` are built on
235
+ the same `openShell`/`shellOutput` shell-session protocol the daemon uses for
236
+ every interactive terminal — see [`daemon/README.md`](../../daemon/README.md#interactive-shells-over-graphql)
237
+ for the wire-level story.
238
+
239
+ Not every GraphQL operation has a typed method yet (flavor/network/volume
240
+ management, for instance) — `client.request(query, variables)` runs any raw
241
+ query or mutation, and `client.subscribe(query, variables, on_next: ...)` runs
242
+ any raw subscription, for anything not wrapped above.
243
+
244
+ Like the rest of this gem, `Client` uses **only the Ruby standard library** —
245
+ the HTTP transport is `Net::HTTP`, and subscriptions (used by `exec`/`shell`/
246
+ `follow_logs`) run over a hand-rolled `graphql-transport-ws` client on top of
247
+ `Socket`/`OpenSSL`, since Ruby's standard library has no WebSocket client of
248
+ its own.
249
+
250
+ `Client.new(url:, token:)` and `.from_env` both reject a URL configured
251
+ without a token rather than silently making an unauthenticated request — set
252
+ both `BSDKRUN_URL` and `BSDKRUN_TOKEN`, or pass both explicitly.
253
+
194
254
  ## Errors
195
255
 
196
256
  All errors extend `Bsdkrun::Error`:
@@ -200,6 +260,9 @@ All errors extend `Bsdkrun::Error`:
200
260
  `stdout`, `stderr`, `command`). Raised by `exec` with `throw_on_error: true`,
201
261
  by the lifecycle/namespace helpers, and by the agent helpers.
202
262
  - `Bsdkrun::SandboxNotFound` — `Sandbox.get` matched no machine.
263
+ - `Bsdkrun::GraphQLError` — a `Client` request failed (carries `code`, the
264
+ daemon's `extensions.code`, when there is one).
265
+ - `Bsdkrun::AuthError` (a `GraphQLError`) — the daemon rejected the bearer token.
203
266
 
204
267
  ## Try it interactively
205
268
 
data/lib/bsdkrun/args.rb CHANGED
@@ -65,6 +65,9 @@ module Bsdkrun
65
65
  when "netbsd" then netbsd_args(opts)
66
66
  when "firmware" then firmware_args(opts)
67
67
  when "kernel" then kernel_args(opts)
68
+ when "unikraft" then unikraft_args(opts)
69
+ when "nanos" then nanos_args(opts)
70
+ when "osv" then osv_args(opts)
68
71
  else raise ArgumentError, "unknown os: #{opts[:os].inspect}"
69
72
  end
70
73
  end
@@ -121,5 +124,48 @@ module Bsdkrun
121
124
  a.concat(disk_args(opts)).concat(net_args(opts[:net]))
122
125
  .concat(name_args(opts)).concat(vm_args(opts))
123
126
  end
127
+
128
+ # @!visibility private
129
+ #
130
+ # Nanos: no agent (like unikraft), but it has a root disk, so +persist+
131
+ # is honored. +:image+ is a path or a ~/.ops/images name.
132
+ def nanos_args(opts)
133
+ a = ["nanos", "-d"]
134
+ a.push("--kernel", opts[:kernel]) if opts[:kernel]
135
+ a.push("--cmdline", opts[:cmdline]) if opts[:cmdline]
136
+ a.push("--persist") if opts[:persist]
137
+ a.concat(net_args(opts[:net])).concat(name_args(opts)).concat(vm_args(opts))
138
+ a.push(opts.fetch(:image).to_s)
139
+ end
140
+
141
+ # @!visibility private
142
+ #
143
+ # OSv: like nanos, no agent (no exec/shell/snapshot), but it does have a
144
+ # root filesystem, so +persist+ is honored. +:image+ is a loader.img, or on
145
+ # x86_64 the loader ELF plus a +:disk+.
146
+ def osv_args(opts)
147
+ a = ["osv", "-d"]
148
+ a.push("--cmdline", opts[:cmdline]) if opts[:cmdline]
149
+ a.push("--disk", opts[:disk]) if opts[:disk]
150
+ a.push("--gic", opts[:gic].to_s) if opts[:gic]
151
+ a.push("--persist") if opts[:persist]
152
+ a.concat(net_args(opts[:net])).concat(name_args(opts)).concat(vm_args(opts))
153
+ a.push(opts.fetch(:image).to_s)
154
+ end
155
+
156
+ # @!visibility private
157
+ #
158
+ # No +disk_args+: a unikernel has no disk, so there is nothing to persist,
159
+ # attach or clone. +:path+ is a kraft project dir or an image; default +.+.
160
+ def unikraft_args(opts)
161
+ a = ["unikraft", "-d"]
162
+ a.push("--cmdline", opts[:cmdline]) if opts[:cmdline]
163
+ a.push("--initramfs", opts[:initramfs]) if opts[:initramfs]
164
+ # Volumes are the exception to "no disk options": virtio-fs shares,
165
+ # which need neither a disk nor an agent.
166
+ Array(opts[:mounts]).each { |m| a.push("--mount", m) }
167
+ a.concat(net_args(opts[:net])).concat(name_args(opts)).concat(vm_args(opts))
168
+ a.push((opts[:path] || ".").to_s)
169
+ end
124
170
  end
125
171
  end