Class: Bsdkrun::Sandbox
- Inherits:
-
Object
- Object
- Bsdkrun::Sandbox
- Defined in:
- lib/bsdkrun/sandbox.rb
Overview
A handle to a running (or stopped) bsdkrun microVM.
Create one with Sandbox.create, reconnect with Sandbox.get, or enumerate with Sandbox.list.
Instance Attribute Summary collapse
-
#id ⇒ String
readonly
The machine's Docker-style short id.
-
#ssh_port ⇒ Integer?
readonly
Host port forwarded to the guest's SSH, if the boot banner reported one.
Class Method Summary collapse
-
.create(opts = {}, **kwargs) ⇒ Sandbox
Boot a new microVM and return a handle to it.
-
.get(id) ⇒ Sandbox
Reconnect to an existing machine by id (a unique prefix is enough).
-
.list(all: false) ⇒ Array<SandboxInfo>
List machines.
Instance Method Summary collapse
-
#connect_network(network) ⇒ void
Join or switch this machine to a global network.
-
#disconnect_network ⇒ void
Detach this machine from its network.
-
#exec(command, args: [], env: {}, tty: false, stdin: nil, cwd: nil, throw_on_error: false, log_level: 0) ⇒ Result
Run a command in the guest through its exec agent.
-
#initialize(id, ssh_port: nil) ⇒ Sandbox
constructor
A new instance of Sandbox.
-
#logs(boot: false) ⇒ String
Read the machine's console log.
-
#remove(force: false) ⇒ void
Remove the machine and its state.
-
#run_command(command, args = [], **opts) ⇒ Result
Vercel-Sandbox-style alias for #exec: a program plus its args.
-
#running? ⇒ Boolean
Whether the machine is currently running.
-
#shell ⇒ Boolean
Attach an interactive shell to the machine (inherits the terminal).
-
#ssh_setup(user: nil, key: nil) ⇒ Result
Install SSH keys in the guest via the agent (+ssh setup+).
-
#start ⇒ void
Restart a stopped machine in place (same id, disk/rootfs).
-
#status ⇒ SandboxInfo?
Fetch this machine's current status row, or nil if it's gone.
-
#stop ⇒ void
Stop the machine.
-
#tailscale_up(authkey: nil, hostname: nil, args: []) ⇒ Result
Put the guest on your tailnet (+tailscale setup+).
-
#update(cpus: nil, mem: nil) ⇒ void
Change the recorded vCPU / RAM.
Constructor Details
#initialize(id, ssh_port: nil) ⇒ Sandbox
Returns a new instance of Sandbox.
30 31 32 33 |
# File 'lib/bsdkrun/sandbox.rb', line 30 def initialize(id, ssh_port: nil) @id = id @ssh_port = ssh_port end |
Instance Attribute Details
#id ⇒ String (readonly)
The machine's Docker-style short id.
22 23 24 |
# File 'lib/bsdkrun/sandbox.rb', line 22 def id @id end |
#ssh_port ⇒ Integer? (readonly)
Host port forwarded to the guest's SSH, if the boot banner reported one.
26 27 28 |
# File 'lib/bsdkrun/sandbox.rb', line 26 def ssh_port @ssh_port end |
Class Method Details
.create(opts = {}, **kwargs) ⇒ Sandbox
Boot a new microVM and return a handle to it.
Accepts create options as a keyword list or a Hash; discriminated on
:os ("linux", "freebsd", "netbsd", "firmware", "kernel").
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/bsdkrun/sandbox.rb', line 44 def create(opts = {}, **kwargs) opts = normalize(opts.merge(kwargs)) args = Args.build_create_args(opts) res = Process.run(args, log_level: opts.fetch(:log_level, 1)) if res.exit_code != 0 raise CommandFailed.new( exit_code: res.exit_code, stdout: res.stdout, stderr: res.stderr, command: "bsdkrun create" ) end # Detached runs print just the machine id on stdout. id = res.stdout.split("\n").map(&:strip).select { |l| l.match?(ID_RE) }.last unless id raise CommandFailed.new( exit_code: res.exit_code, stdout: res.stdout, stderr: res.stderr, command: "bsdkrun create (no machine id in output)" ) end match = res.stderr.match(SSH_PORT_RE) new(id, ssh_port: match && match[1].to_i) end |
.get(id) ⇒ Sandbox
Reconnect to an existing machine by id (a unique prefix is enough).
71 72 73 74 75 76 |
# File 'lib/bsdkrun/sandbox.rb', line 71 def get(id) row = list(all: true).find { |m| m.id == id || m.id.start_with?(id) } raise SandboxNotFound, id unless row new(row.id) end |
.list(all: false) ⇒ Array<SandboxInfo>
List machines. all: true includes exited ones (default running only).
82 83 84 85 86 87 88 |
# File 'lib/bsdkrun/sandbox.rb', line 82 def list(all: false) args = ["ps", "--json"] args << "--all" if all res = Process.run!(args, label: "bsdkrun ps") rows = JSON.parse(res.stdout.empty? ? "[]" : res.stdout) rows.map { |row| SandboxInfo.from_row(row) } end |
Instance Method Details
#connect_network(network) ⇒ void
This method returns an undefined value.
Join or switch this machine to a global network. Applies on next #start.
211 212 213 |
# File 'lib/bsdkrun/sandbox.rb', line 211 def connect_network(network) lifecycle(["network", "connect", @id, network], "bsdkrun network connect") end |
#disconnect_network ⇒ void
This method returns an undefined value.
Detach this machine from its network. Applies on next #start.
217 218 219 |
# File 'lib/bsdkrun/sandbox.rb', line 217 def disconnect_network lifecycle(["network", "disconnect", @id], "bsdkrun network disconnect") end |
#exec(command, args: [], env: {}, tty: false, stdin: nil, cwd: nil, throw_on_error: false, log_level: 0) ⇒ Result
Run a command in the guest through its exec agent.
command may be an Array (argv, no shell parsing) or a String program
name; with a String, args: supplies its arguments.
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/bsdkrun/sandbox.rb', line 113 def exec(command, args: [], env: {}, tty: false, stdin: nil, cwd: nil, throw_on_error: false, log_level: 0) argv = command.is_a?(Array) ? command.dup : [command, *args] if cwd # Emulate a working directory: cd, drop it, then exec the real argv. argv = ["/bin/sh", "-c", 'cd "$1" && shift && exec "$@"', "sh", cwd, *argv] end cli = ["exec"] cli << "-t" if tty env.each { |k, v| cli.push("-e", "#{k}=#{v}") } cli.push(@id, *argv) res = Process.run(cli, stdin: stdin, log_level: log_level) result = Result.new( stdout: res.stdout, stderr: res.stderr, exit_code: res.exit_code, command: "exec #{argv.join(' ')}" ) result.throw_if_failed! if throw_on_error result end |
#logs(boot: false) ⇒ String
Read the machine's console log.
149 150 151 152 153 154 |
# File 'lib/bsdkrun/sandbox.rb', line 149 def logs(boot: false) args = ["logs"] args << "--boot" if boot args << @id Process.run(args).stdout end |
#remove(force: false) ⇒ void
This method returns an undefined value.
Remove the machine and its state. force stops it first if running.
190 191 192 193 194 195 |
# File 'lib/bsdkrun/sandbox.rb', line 190 def remove(force: false) args = ["rm"] args << "--force" if force args << @id lifecycle(args, "bsdkrun rm") end |
#run_command(command, args = [], **opts) ⇒ Result
Vercel-Sandbox-style alias for #exec: a program plus its args.
141 142 143 |
# File 'lib/bsdkrun/sandbox.rb', line 141 def run_command(command, args = [], **opts) exec(command, args: args, **opts) end |
#running? ⇒ Boolean
Whether the machine is currently running.
170 171 172 173 |
# File 'lib/bsdkrun/sandbox.rb', line 170 def running? s = status s ? s.running : false end |
#shell ⇒ Boolean
Attach an interactive shell to the machine (inherits the terminal).
158 159 160 |
# File 'lib/bsdkrun/sandbox.rb', line 158 def shell Process.spawn_interactive(["shell", @id]) end |
#ssh_setup(user: nil, key: nil) ⇒ Result
Install SSH keys in the guest via the agent (+ssh setup+). With no keys,
the CLI installs your local ~/.ssh/*.pub.
227 228 229 230 231 232 |
# File 'lib/bsdkrun/sandbox.rb', line 227 def ssh_setup(user: nil, key: nil) action = ["setup"] action.push("--user", user) if user Array(key).each { |k| action.push("--key", k) } agent("ssh", action) end |
#start ⇒ void
This method returns an undefined value.
Restart a stopped machine in place (same id, disk/rootfs). Boots detached.
183 184 185 |
# File 'lib/bsdkrun/sandbox.rb', line 183 def start lifecycle(["start", @id], "bsdkrun start") end |
#status ⇒ SandboxInfo?
Fetch this machine's current status row, or nil if it's gone.
164 165 166 |
# File 'lib/bsdkrun/sandbox.rb', line 164 def status Sandbox.list(all: true).find { |m| m.id == @id } end |
#stop ⇒ void
This method returns an undefined value.
Stop the machine. BSD guests are cleanly powered off; Linux is SIGTERM'd.
177 178 179 |
# File 'lib/bsdkrun/sandbox.rb', line 177 def stop lifecycle(["stop", @id], "bsdkrun stop") end |
#tailscale_up(authkey: nil, hostname: nil, args: []) ⇒ Result
Put the guest on your tailnet (+tailscale setup+).
240 241 242 243 244 245 |
# File 'lib/bsdkrun/sandbox.rb', line 240 def tailscale_up(authkey: nil, hostname: nil, args: []) action = ["setup"] action.push("--hostname", hostname) if hostname action.concat(args) agent("tailscale", action, env: authkey ? { "TS_AUTHKEY" => authkey } : {}) end |
#update(cpus: nil, mem: nil) ⇒ void
This method returns an undefined value.
Change the recorded vCPU / RAM. Applies on the next #start.
201 202 203 204 205 206 |
# File 'lib/bsdkrun/sandbox.rb', line 201 def update(cpus: nil, mem: nil) args = ["update", @id] args.push("--cpus", cpus.to_s) unless cpus.nil? args.push("--mem", mem.to_s) unless mem.nil? lifecycle(args, "bsdkrun update") end |