Class: Microsandbox::Sandbox
- Inherits:
-
Object
- Object
- Microsandbox::Sandbox
- Defined in:
- lib/microsandbox/sandbox.rb
Overview
A running sandbox (microVM) — the primary entry point of the SDK.
Constant Summary collapse
- DISK_IMAGE_EXTENSIONS =
Recognized disk-image rootfs extensions, mirroring the upstream ‘DiskImageFormat::from_extension`/`FromStr` set. Used by disk_image_rootfs? to gate the `fstype:`-vs-OCI check; keep in sync on a runtime-tag bump.
%w[raw qcow2 vmdk].freeze
Class Method Summary collapse
-
.build_create_opts(image: nil, cpus: nil, memory: nil, env: nil, workdir: nil, shell: nil, user: nil, hostname: nil, labels: nil, scripts: nil, entrypoint: nil, ports: nil, ports_udp: nil, volumes: nil, network: nil, dns: nil, tls: nil, ipv4_pool: nil, ipv6_pool: nil, max_connections: nil, trust_host_cas: nil, patches: nil, from_snapshot: nil, fstype: nil, init: nil, ephemeral: false, log_level: nil, quiet_logs: false, security: nil, oci_upper_size: nil, max_duration: nil, idle_timeout: nil, rlimits: nil, pull_policy: nil, registry_auth: nil, registry_insecure: false, registry_ca_certs: nil, secrets: nil, on_secret_violation: nil, detached: false, replace: false, replace_with_timeout: nil) ⇒ Object
private
Shared keyword-option builder for Sandbox.create/Sandbox.create_with_progress.
-
.create(name, **kwargs) {|sandbox| ... } ⇒ Sandbox, Object
Create and boot a sandbox.
-
.create_with_progress(name, **kwargs) ⇒ PullSession
Create a sandbox while streaming image-pull progress.
-
.get(name) ⇒ SandboxHandle
Fetch a controllable handle for a sandbox by name (running or not).
-
.list ⇒ Array<SandboxHandle>
List all sandboxes as controllable handles.
-
.list_with(labels: {}) ⇒ Array<SandboxHandle>
List sandboxes carrying all of the given labels (AND-matched).
-
.remove(name) ⇒ nil
Remove a (stopped) sandbox by name.
-
.start(name, detached: false) ⇒ Sandbox
Restart a previously-defined sandbox by name.
Instance Method Summary collapse
-
#attach(command, args = [], cwd: nil, user: nil, env: nil, detach_keys: nil, rlimits: nil) ⇒ Integer
Attach an interactive terminal to a command in the sandbox.
-
#attach_shell ⇒ Integer
Attach an interactive terminal running the sandbox’s default shell.
-
#detach ⇒ nil
Detach this handle: disarm the stop-on-drop safety net so the sandbox keeps running after this handle is gone (and after this process exits).
-
#drain ⇒ nil
Trigger a graceful drain (SIGUSR1).
-
#exec(command, args = [], cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ⇒ ExecOutput
Run a command (no shell interpretation) and collect its output.
-
#exec_stream(command, args = [], cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ⇒ ExecHandle
Run a command and stream its output as it arrives.
-
#fs ⇒ FS
Guest filesystem operations.
-
#initialize(native) ⇒ Sandbox
constructor
A new instance of Sandbox.
- #inspect ⇒ Object
-
#kill ⇒ nil
Force-kill the sandbox (SIGKILL).
-
#log_stream(sources: nil, since_ms: nil, from_cursor: nil, until_ms: nil, follow: false) ⇒ LogStream
Stream captured logs as they appear.
-
#logs(tail: nil, since_ms: nil, until_ms: nil, sources: nil) ⇒ Array<LogEntry>
Read captured logs.
-
#metrics ⇒ Metrics
Latest resource-usage snapshot.
-
#metrics_stream(interval: 1.0) ⇒ MetricsStream
Stream resource-usage snapshots, one per interval tick, until the sandbox stops.
-
#name ⇒ String
The sandbox name.
-
#owns_lifecycle? ⇒ Boolean
Whether this handle owns the sandbox process lifecycle (i.e. stopping it or dropping the handle terminates the sandbox).
-
#shell(script, cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ⇒ ExecOutput
Run a shell script (pipes, redirects, etc. allowed) and collect output.
-
#shell_stream(script, cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ⇒ ExecHandle
Run a shell script and stream its output as it arrives.
-
#ssh ⇒ SshOps
SSH access to the sandbox — open a native in-process SSH client or prepare a reusable server endpoint.
-
#status ⇒ Symbol
The live status, fetched from the backend (a round-trip per call).
-
#stop ⇒ nil
Gracefully stop the sandbox (SIGTERM→SIGKILL escalation, 10s default) and wait for it to terminate.
-
#stop_and_wait ⇒ ExitStatus
Gracefully stop, then wait for the process to exit.
-
#wait ⇒ ExitStatus
Wait for the sandbox process to exit.
Constructor Details
#initialize(native) ⇒ Sandbox
Returns a new instance of Sandbox.
807 808 809 |
# File 'lib/microsandbox/sandbox.rb', line 807 def initialize(native) @native = native end |
Class Method Details
.build_create_opts(image: nil, cpus: nil, memory: nil, env: nil, workdir: nil, shell: nil, user: nil, hostname: nil, labels: nil, scripts: nil, entrypoint: nil, ports: nil, ports_udp: nil, volumes: nil, network: nil, dns: nil, tls: nil, ipv4_pool: nil, ipv6_pool: nil, max_connections: nil, trust_host_cas: nil, patches: nil, from_snapshot: nil, fstype: nil, init: nil, ephemeral: false, log_level: nil, quiet_logs: false, security: nil, oci_upper_size: nil, max_duration: nil, idle_timeout: nil, rlimits: nil, pull_policy: nil, registry_auth: nil, registry_insecure: false, registry_ca_certs: nil, secrets: nil, on_secret_violation: nil, detached: false, replace: false, replace_with_timeout: nil) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Shared keyword-option builder for create/create_with_progress.
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
# File 'lib/microsandbox/sandbox.rb', line 337 def build_create_opts(image: nil, cpus: nil, memory: nil, env: nil, workdir: nil, shell: nil, user: nil, hostname: nil, labels: nil, scripts: nil, entrypoint: nil, ports: nil, ports_udp: nil, volumes: nil, network: nil, dns: nil, tls: nil, ipv4_pool: nil, ipv6_pool: nil, max_connections: nil, trust_host_cas: nil, patches: nil, from_snapshot: nil, fstype: nil, init: nil, ephemeral: false, log_level: nil, quiet_logs: false, security: nil, oci_upper_size: nil, max_duration: nil, idle_timeout: nil, rlimits: nil, pull_policy: nil, registry_auth: nil, registry_insecure: false, registry_ca_certs: nil, secrets: nil, on_secret_violation: nil, detached: false, replace: false, replace_with_timeout: nil) # A sandbox boots from exactly one rootfs source. The core would reject a # contradictory pair, but only after a runtime round-trip; fail fast and # clearly here (the Python SDK validates this the same way). if image && from_snapshot raise ArgumentError, "provide either image: or from_snapshot:, not both" end Microsandbox.ensure_runtime! # `fstype:` names the inner filesystem of a disk-image rootfs, so it only # applies when `image:` is a disk-image path (a local path ending in # .raw/.qcow2/.vmdk). Routing an OCI ref (e.g. "python") through the # disk-image builder would make the core treat it as a host disk path and # fail at boot, so reject the combination up front instead of forwarding a # value the native layer can't honour. if fstype && !disk_image_rootfs?(image) raise ArgumentError, "fstype: only applies to a disk-image rootfs; image: must be a local " \ "path ending in .raw, .qcow2, or .vmdk (got #{image.inspect}). " \ "OCI references auto-detect their filesystem — drop fstype:." end opts = {} opts["image"] = image.to_s if image opts["from_snapshot"] = from_snapshot.to_s if from_snapshot opts["fstype"] = fstype.to_s if fstype opts["cpus"] = Integer(cpus) if cpus opts["memory"] = Integer(memory) if memory opts["workdir"] = workdir.to_s if workdir opts["shell"] = shell.to_s if shell opts["user"] = user.to_s if user opts["hostname"] = hostname.to_s if hostname opts["env"] = stringify(env) if env opts["labels"] = stringify(labels) if labels opts["scripts"] = stringify(scripts) if scripts opts["entrypoint"] = Array(entrypoint).map(&:to_s) if entrypoint opts["ports"] = intify_ports(ports) if ports opts["ports_udp"] = intify_ports(ports_udp) if ports_udp opts["volumes"] = normalize_volumes(volumes) if volumes opts["patches"] = normalize_patches(patches) if patches apply_network_opts(opts, network) unless network.nil? opts["dns"] = normalize_dns(dns) if dns opts["tls"] = normalize_tls(tls) if tls opts["ipv4_pool"] = ipv4_pool.to_s if ipv4_pool opts["ipv6_pool"] = ipv6_pool.to_s if ipv6_pool opts["max_connections"] = Integer(max_connections) if max_connections set_bool(opts, "trust_host_cas", trust_host_cas) opts["log_level"] = log_level.to_s if log_level opts["quiet_logs"] = true if quiet_logs opts["security"] = security.to_s if security opts["oci_upper_size"] = Integer(oci_upper_size) if oci_upper_size opts["max_duration"] = Integer(max_duration) if max_duration opts["idle_timeout"] = Integer(idle_timeout) if idle_timeout opts["rlimits"] = normalize_rlimits(rlimits) if rlimits opts["pull_policy"] = pull_policy.to_s if pull_policy apply_registry_opts(opts, registry_auth, registry_insecure, registry_ca_certs) opts["secrets"] = normalize_secrets(secrets) if secrets opts["on_secret_violation"] = normalize_violation(on_secret_violation) if on_secret_violation opts["init"] = normalize_init(init) unless init.nil? opts["ephemeral"] = true if ephemeral opts["detached"] = true if detached if replace_with_timeout opts["replace_with_timeout"] = coerce_duration(replace_with_timeout, "replace_with_timeout") elsif replace opts["replace"] = true end opts end |
.create(name, **kwargs) {|sandbox| ... } ⇒ Sandbox, Object
Create and boot a sandbox.
When a block is given the sandbox is yielded and stopped automatically when the block returns (the block’s value is returned); otherwise the live Microsandbox::Sandbox is returned and you are responsible for calling #stop.
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/microsandbox/sandbox.rb', line 299 def create(name, **kwargs, &block) opts = build_create_opts(**kwargs) sandbox = new(Native::Sandbox.create(name.to_s, opts)) return sandbox unless block_given? begin yield sandbox ensure begin sandbox.stop rescue Microsandbox::Error # best-effort cleanup; ignore stop failures during teardown end end end |
.create_with_progress(name, **kwargs) ⇒ PullSession
Create a sandbox while streaming image-pull progress. Accepts the same options as create; returns a PullSession — iterate it (an Enumerable of progress-event Hashes, each with a “kind”), then call PullSession#sandbox for the booted Microsandbox::Sandbox. Mirrors the Python ‘create_with_progress` / Node `createWithPullProgress`.
321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/microsandbox/sandbox.rb', line 321 def create_with_progress(name, **kwargs) # Unlike {create}, this has no block form: the booted sandbox is reached # via {PullSession#sandbox} (after iterating progress) and stopped by the # caller. A block would be silently dropped — and the sandbox leaked — so # reject it loudly rather than let a `create`-style block call misfire. if block_given? raise ArgumentError, "create_with_progress takes no block; iterate the returned PullSession " \ "for progress, then call #sandbox and stop it when done" end opts = build_create_opts(**kwargs) PullSession.new(Native::Sandbox.create_with_progress(name.to_s, opts)) end |
.get(name) ⇒ SandboxHandle
Fetch a controllable handle for a sandbox by name (running or not).
425 426 427 |
# File 'lib/microsandbox/sandbox.rb', line 425 def get(name) SandboxHandle.new(Native::Sandbox.get(name.to_s)) end |
.list ⇒ Array<SandboxHandle>
List all sandboxes as controllable handles.
431 432 433 |
# File 'lib/microsandbox/sandbox.rb', line 431 def list Native::Sandbox.list.map { |h| SandboxHandle.new(h) } end |
.list_with(labels: {}) ⇒ Array<SandboxHandle>
List sandboxes carrying all of the given labels (AND-matched).
438 439 440 441 |
# File 'lib/microsandbox/sandbox.rb', line 438 def list_with(labels: {}) opts = {"labels" => stringify(labels)} Native::Sandbox.list_with(opts).map { |h| SandboxHandle.new(h) } end |
.remove(name) ⇒ nil
Remove a (stopped) sandbox by name.
445 446 447 448 |
# File 'lib/microsandbox/sandbox.rb', line 445 def remove(name) Native::Sandbox.remove(name.to_s) nil end |
.start(name, detached: false) ⇒ Sandbox
Restart a previously-defined sandbox by name.
418 419 420 421 |
# File 'lib/microsandbox/sandbox.rb', line 418 def start(name, detached: false) Microsandbox.ensure_runtime! new(Native::Sandbox.start(name.to_s, {"detached" => detached})) end |
Instance Method Details
#attach(command, args = [], cwd: nil, user: nil, env: nil, detach_keys: nil, rlimits: nil) ⇒ Integer
Attach an interactive terminal to a command in the sandbox.
Puts the host terminal into raw mode and forwards keystrokes (and SIGWINCH resizes) to the guest until the command exits or the detach sequence is typed. Requires a real TTY on stdin/stdout, so it is for CLI use, not library/automation code (use #exec/#exec_stream there). Blocks until the session ends. Mirrors the official SDKs’ ‘attach`.
877 878 879 880 881 882 883 884 885 886 887 888 889 890 |
# File 'lib/microsandbox/sandbox.rb', line 877 def attach(command, args = [], cwd: nil, user: nil, env: nil, detach_keys: nil, rlimits: nil) opts = {} opts["cwd"] = cwd.to_s if cwd opts["user"] = user.to_s if user opts["env"] = env.each_with_object({}) { |(k, v), a| a[k.to_s] = v.to_s } if env opts["detach_keys"] = detach_keys.to_s if detach_keys if rlimits opts["rlimits"] = rlimits.map do |resource, limit| soft, hard = limit.is_a?(Array) ? [limit[0], limit[1]] : [limit, limit] [resource.to_s, Integer(soft), Integer(hard)] end end @native.attach(command.to_s, Array(args).map(&:to_s), opts) end |
#attach_shell ⇒ Integer
Attach an interactive terminal running the sandbox’s default shell. See #attach for the host-TTY requirements.
895 896 897 |
# File 'lib/microsandbox/sandbox.rb', line 895 def attach_shell @native.attach_shell end |
#detach ⇒ nil
Detach this handle: disarm the stop-on-drop safety net so the sandbox keeps running after this handle is gone (and after this process exits).
1016 1017 1018 1019 |
# File 'lib/microsandbox/sandbox.rb', line 1016 def detach @native.detach nil end |
#drain ⇒ nil
Trigger a graceful drain (SIGUSR1).
989 990 991 992 |
# File 'lib/microsandbox/sandbox.rb', line 989 def drain @native.drain nil end |
#exec(command, args = [], cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ⇒ ExecOutput
Run a command (no shell interpretation) and collect its output.
829 830 831 832 |
# File 'lib/microsandbox/sandbox.rb', line 829 def exec(command, args = [], cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ExecOutput.new(@native.exec(command.to_s, Array(args).map(&:to_s), exec_opts(cwd:, user:, env:, timeout:, tty:, stdin:, rlimits:))) end |
#exec_stream(command, args = [], cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ⇒ ExecHandle
Run a command and stream its output as it arrives.
Pass stdin: :pipe to feed the process interactively: ExecHandle#stdin then returns a writable sink; close it to send EOF (a process like cat that reads until EOF will otherwise block forever).
848 849 850 851 |
# File 'lib/microsandbox/sandbox.rb', line 848 def exec_stream(command, args = [], cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ExecHandle.new(@native.exec_stream(command.to_s, Array(args).map(&:to_s), exec_opts(cwd:, user:, env:, timeout:, tty:, stdin:, rlimits:, pipe_ok: true))) end |
#fs ⇒ FS
Guest filesystem operations.
901 902 903 |
# File 'lib/microsandbox/sandbox.rb', line 901 def fs @fs ||= FS.new(@native) end |
#inspect ⇒ Object
1021 1022 1023 |
# File 'lib/microsandbox/sandbox.rb', line 1021 def inspect "#<Microsandbox::Sandbox name=#{name.inspect}>" end |
#kill ⇒ nil
Force-kill the sandbox (SIGKILL).
982 983 984 985 |
# File 'lib/microsandbox/sandbox.rb', line 982 def kill @native.kill nil end |
#log_stream(sources: nil, since_ms: nil, from_cursor: nil, until_ms: nil, follow: false) ⇒ LogStream
Stream captured logs as they appear.
955 956 957 958 959 960 961 962 963 |
# File 'lib/microsandbox/sandbox.rb', line 955 def log_stream(sources: nil, since_ms: nil, from_cursor: nil, until_ms: nil, follow: false) opts = {} opts["sources"] = Array(sources).map(&:to_s) if sources opts["since_ms"] = Float(since_ms) if since_ms opts["from_cursor"] = from_cursor.to_s if from_cursor opts["until_ms"] = Float(until_ms) if until_ms opts["follow"] = true if follow LogStream.new(@native.log_stream(opts)) end |
#logs(tail: nil, since_ms: nil, until_ms: nil, sources: nil) ⇒ Array<LogEntry>
Read captured logs.
928 929 930 931 932 933 934 935 |
# File 'lib/microsandbox/sandbox.rb', line 928 def logs(tail: nil, since_ms: nil, until_ms: nil, sources: nil) opts = {} opts["tail"] = Integer(tail) if tail opts["since_ms"] = Float(since_ms) if since_ms opts["until_ms"] = Float(until_ms) if until_ms opts["sources"] = Array(sources).map(&:to_s) if sources @native.logs(opts).map { |entry| LogEntry.new(entry) } end |
#metrics ⇒ Metrics
Latest resource-usage snapshot.
916 917 918 |
# File 'lib/microsandbox/sandbox.rb', line 916 def metrics Metrics.new(@native.metrics) end |
#metrics_stream(interval: 1.0) ⇒ MetricsStream
Stream resource-usage snapshots, one per interval tick, until the sandbox stops. Requires metrics to be enabled for the sandbox.
941 942 943 |
# File 'lib/microsandbox/sandbox.rb', line 941 def metrics_stream(interval: 1.0) MetricsStream.new(@native.metrics_stream(coerce_duration(interval, "interval"))) end |
#name ⇒ String
Returns the sandbox name.
812 813 814 |
# File 'lib/microsandbox/sandbox.rb', line 812 def name @native.name end |
#owns_lifecycle? ⇒ Boolean
Returns whether this handle owns the sandbox process lifecycle (i.e. stopping it or dropping the handle terminates the sandbox).
1009 1010 1011 |
# File 'lib/microsandbox/sandbox.rb', line 1009 def owns_lifecycle? @native.owns_lifecycle end |
#shell(script, cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ⇒ ExecOutput
Run a shell script (pipes, redirects, etc. allowed) and collect output.
836 837 838 839 |
# File 'lib/microsandbox/sandbox.rb', line 836 def shell(script, cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ExecOutput.new(@native.shell(script.to_s, exec_opts(cwd:, user:, env:, timeout:, tty:, stdin:, rlimits:))) end |
#shell_stream(script, cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ⇒ ExecHandle
Run a shell script and stream its output as it arrives.
855 856 857 858 |
# File 'lib/microsandbox/sandbox.rb', line 855 def shell_stream(script, cwd: nil, user: nil, env: nil, timeout: nil, tty: false, stdin: nil, rlimits: nil) ExecHandle.new(@native.shell_stream(script.to_s, exec_opts(cwd:, user:, env:, timeout:, tty:, stdin:, rlimits:, pipe_ok: true))) end |
#ssh ⇒ SshOps
SSH access to the sandbox — open a native in-process SSH client or prepare a reusable server endpoint.
910 911 912 |
# File 'lib/microsandbox/sandbox.rb', line 910 def ssh SshOps.new(@native) end |
#status ⇒ Symbol
The live status, fetched from the backend (a round-trip per call).
1003 1004 1005 |
# File 'lib/microsandbox/sandbox.rb', line 1003 def status @native.status.to_sym end |
#stop ⇒ nil
Gracefully stop the sandbox (SIGTERM→SIGKILL escalation, 10s default) and wait for it to terminate. For a custom timeout or fire-and-return ‘request_*` control, fetch a Microsandbox::SandboxHandle via get.
969 970 971 972 |
# File 'lib/microsandbox/sandbox.rb', line 969 def stop @native.stop nil end |
#stop_and_wait ⇒ ExitStatus
Gracefully stop, then wait for the process to exit.
976 977 978 |
# File 'lib/microsandbox/sandbox.rb', line 976 def stop_and_wait ExitStatus.new(@native.stop_and_wait) end |
#wait ⇒ ExitStatus
Wait for the sandbox process to exit.
996 997 998 |
# File 'lib/microsandbox/sandbox.rb', line 996 def wait ExitStatus.new(@native.wait) end |