microsandbox-rb 0.17.2 → 0.17.3
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 +45 -1
- data/Cargo.lock +1 -1
- data/DESIGN.md +98 -0
- data/README.md +3 -2
- data/docs/errors.md +1 -0
- data/docs/runtime.md +85 -0
- data/docs/surface.md +3 -0
- data/ext/microsandbox/Cargo.toml +1 -1
- data/ext/microsandbox/src/agent.rs +13 -10
- data/ext/microsandbox/src/backend.rs +290 -33
- data/ext/microsandbox/src/error.rs +18 -0
- data/ext/microsandbox/src/exec.rs +32 -18
- data/ext/microsandbox/src/fs_stream.rs +11 -8
- data/ext/microsandbox/src/host_path.rs +4 -3
- data/ext/microsandbox/src/lib.rs +1 -0
- data/ext/microsandbox/src/runtime.rs +127 -36
- data/ext/microsandbox/src/sandbox.rs +175 -91
- data/ext/microsandbox/src/snapshot.rs +7 -4
- data/ext/microsandbox/src/ssh.rs +26 -23
- data/ext/microsandbox/src/stream.rs +13 -7
- data/ext/microsandbox/src/volume.rs +22 -16
- data/lib/microsandbox/errors.rb +8 -0
- data/lib/microsandbox/version.rb +1 -1
- data/lib/microsandbox.rb +20 -1
- data/sig/microsandbox.rbs +1 -0
- metadata +1 -1
|
@@ -140,10 +140,13 @@ fn list_dir(dir: String) -> Result<RArray, Error> {
|
|
|
140
140
|
fn reindex(dir: Option<String>) -> Result<u64, Error> {
|
|
141
141
|
let dir: PathBuf = match dir {
|
|
142
142
|
Some(d) => PathBuf::from(d),
|
|
143
|
-
None =>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
143
|
+
None => {
|
|
144
|
+
crate::backend::ensure_this_process_backend()?;
|
|
145
|
+
microsandbox::default_backend()
|
|
146
|
+
.as_local()
|
|
147
|
+
.map(|l| l.snapshots_dir())
|
|
148
|
+
.unwrap_or_else(|| PathBuf::from("."))
|
|
149
|
+
}
|
|
147
150
|
};
|
|
148
151
|
let n = block_on(Snapshot::reindex(&dir))?.map_err(error::to_ruby)?;
|
|
149
152
|
Ok(n as u64)
|
data/ext/microsandbox/src/ssh.rs
CHANGED
|
@@ -21,7 +21,7 @@ use tokio::io::AsyncWriteExt;
|
|
|
21
21
|
use tokio::sync::Mutex;
|
|
22
22
|
|
|
23
23
|
use crate::error;
|
|
24
|
-
use crate::runtime::{block_on, ruby};
|
|
24
|
+
use crate::runtime::{block_on, ruby, ForkGuarded};
|
|
25
25
|
|
|
26
26
|
/// A core error for a session whose value was already taken (closed). Built
|
|
27
27
|
/// inside `block_on`, so it must be a plain Rust error, not a Ruby one.
|
|
@@ -35,19 +35,19 @@ fn consumed() -> MicrosandboxError {
|
|
|
35
35
|
|
|
36
36
|
#[magnus::wrap(class = "Microsandbox::Native::SshClient", free_immediately, size)]
|
|
37
37
|
pub struct SshClient {
|
|
38
|
-
inner: Arc<Mutex<Option<CoreSshClient
|
|
38
|
+
inner: ForkGuarded<Arc<Mutex<Option<CoreSshClient>>>>,
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
impl SshClient {
|
|
42
42
|
pub fn from_core(inner: CoreSshClient) -> Self {
|
|
43
43
|
Self {
|
|
44
|
-
inner: Arc::new(Mutex::new(Some(inner))),
|
|
44
|
+
inner: ForkGuarded::new("Microsandbox::SshClient", Arc::new(Mutex::new(Some(inner)))),
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/// Run a command over SSH and collect {status, stdout, stderr}.
|
|
49
49
|
fn exec(&self, command: String, tty: bool) -> Result<RHash, Error> {
|
|
50
|
-
let inner = Arc::clone(
|
|
50
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
51
51
|
let result: MicrosandboxResult<SshOutput> = block_on(async move {
|
|
52
52
|
let guard = inner.lock().await;
|
|
53
53
|
let client = guard.as_ref().ok_or_else(consumed)?;
|
|
@@ -59,7 +59,7 @@ impl SshClient {
|
|
|
59
59
|
/// Attach the local terminal to an interactive SSH shell; returns the exit
|
|
60
60
|
/// status. Host-TTY coupled (raw mode); not meaningful without a real tty.
|
|
61
61
|
fn attach(&self, term: Option<String>, detach_keys: Option<String>) -> Result<i32, Error> {
|
|
62
|
-
let inner = Arc::clone(
|
|
62
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
63
63
|
let result: MicrosandboxResult<i32> = block_on(async move {
|
|
64
64
|
let guard = inner.lock().await;
|
|
65
65
|
let client = guard.as_ref().ok_or_else(consumed)?;
|
|
@@ -80,7 +80,7 @@ impl SshClient {
|
|
|
80
80
|
|
|
81
81
|
/// Open an SFTP session over this SSH connection.
|
|
82
82
|
fn sftp(&self) -> Result<SftpClient, Error> {
|
|
83
|
-
let inner = Arc::clone(
|
|
83
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
84
84
|
let result: MicrosandboxResult<CoreSftp> = block_on(async move {
|
|
85
85
|
let guard = inner.lock().await;
|
|
86
86
|
let client = guard.as_ref().ok_or_else(consumed)?;
|
|
@@ -91,7 +91,7 @@ impl SshClient {
|
|
|
91
91
|
|
|
92
92
|
/// Close the SSH client session. Idempotent.
|
|
93
93
|
fn close(&self) -> Result<(), Error> {
|
|
94
|
-
let inner = Arc::clone(
|
|
94
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
95
95
|
let result: MicrosandboxResult<()> = block_on(async move {
|
|
96
96
|
let client = {
|
|
97
97
|
let mut guard = inner.lock().await;
|
|
@@ -122,7 +122,7 @@ fn ssh_output_to_hash(output: SshOutput) -> RHash {
|
|
|
122
122
|
|
|
123
123
|
#[magnus::wrap(class = "Microsandbox::Native::SftpClient", free_immediately, size)]
|
|
124
124
|
pub struct SftpClient {
|
|
125
|
-
inner: Arc<Mutex<Option<CoreSftp
|
|
125
|
+
inner: ForkGuarded<Arc<Mutex<Option<CoreSftp>>>>,
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/// Format an SFTP-layer error as a plain string inside `block_on`; the Ruby
|
|
@@ -134,12 +134,15 @@ fn sftp_str(e: impl std::fmt::Display) -> String {
|
|
|
134
134
|
impl SftpClient {
|
|
135
135
|
pub fn from_core(inner: CoreSftp) -> Self {
|
|
136
136
|
Self {
|
|
137
|
-
inner:
|
|
137
|
+
inner: ForkGuarded::new(
|
|
138
|
+
"Microsandbox::SftpClient",
|
|
139
|
+
Arc::new(Mutex::new(Some(inner))),
|
|
140
|
+
),
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
fn read(&self, path: String) -> Result<RString, Error> {
|
|
142
|
-
let inner = Arc::clone(
|
|
145
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
143
146
|
let result: Result<Vec<u8>, String> = block_on(async move {
|
|
144
147
|
let guard = inner.lock().await;
|
|
145
148
|
let sftp = guard.as_ref().ok_or_else(|| sftp_str("session closed"))?;
|
|
@@ -150,7 +153,7 @@ impl SftpClient {
|
|
|
150
153
|
|
|
151
154
|
fn write(&self, path: String, data: RString) -> Result<(), Error> {
|
|
152
155
|
let bytes = unsafe { data.as_slice() }.to_vec();
|
|
153
|
-
let inner = Arc::clone(
|
|
156
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
154
157
|
let result: Result<(), String> = block_on(async move {
|
|
155
158
|
let guard = inner.lock().await;
|
|
156
159
|
let sftp = guard.as_ref().ok_or_else(|| sftp_str("session closed"))?;
|
|
@@ -163,7 +166,7 @@ impl SftpClient {
|
|
|
163
166
|
}
|
|
164
167
|
|
|
165
168
|
fn mkdir(&self, path: String) -> Result<(), Error> {
|
|
166
|
-
let inner = Arc::clone(
|
|
169
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
167
170
|
let result: Result<(), String> = block_on(async move {
|
|
168
171
|
let guard = inner.lock().await;
|
|
169
172
|
let sftp = guard.as_ref().ok_or_else(|| sftp_str("session closed"))?;
|
|
@@ -173,7 +176,7 @@ impl SftpClient {
|
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
fn remove_file(&self, path: String) -> Result<(), Error> {
|
|
176
|
-
let inner = Arc::clone(
|
|
179
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
177
180
|
let result: Result<(), String> = block_on(async move {
|
|
178
181
|
let guard = inner.lock().await;
|
|
179
182
|
let sftp = guard.as_ref().ok_or_else(|| sftp_str("session closed"))?;
|
|
@@ -183,7 +186,7 @@ impl SftpClient {
|
|
|
183
186
|
}
|
|
184
187
|
|
|
185
188
|
fn remove_dir(&self, path: String) -> Result<(), Error> {
|
|
186
|
-
let inner = Arc::clone(
|
|
189
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
187
190
|
let result: Result<(), String> = block_on(async move {
|
|
188
191
|
let guard = inner.lock().await;
|
|
189
192
|
let sftp = guard.as_ref().ok_or_else(|| sftp_str("session closed"))?;
|
|
@@ -193,7 +196,7 @@ impl SftpClient {
|
|
|
193
196
|
}
|
|
194
197
|
|
|
195
198
|
fn rename(&self, old_path: String, new_path: String) -> Result<(), Error> {
|
|
196
|
-
let inner = Arc::clone(
|
|
199
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
197
200
|
let result: Result<(), String> = block_on(async move {
|
|
198
201
|
let guard = inner.lock().await;
|
|
199
202
|
let sftp = guard.as_ref().ok_or_else(|| sftp_str("session closed"))?;
|
|
@@ -203,7 +206,7 @@ impl SftpClient {
|
|
|
203
206
|
}
|
|
204
207
|
|
|
205
208
|
fn symlink(&self, target: String, link_path: String) -> Result<(), Error> {
|
|
206
|
-
let inner = Arc::clone(
|
|
209
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
207
210
|
let result: Result<(), String> = block_on(async move {
|
|
208
211
|
let guard = inner.lock().await;
|
|
209
212
|
let sftp = guard.as_ref().ok_or_else(|| sftp_str("session closed"))?;
|
|
@@ -213,7 +216,7 @@ impl SftpClient {
|
|
|
213
216
|
}
|
|
214
217
|
|
|
215
218
|
fn real_path(&self, path: String) -> Result<String, Error> {
|
|
216
|
-
let inner = Arc::clone(
|
|
219
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
217
220
|
let result: Result<String, String> = block_on(async move {
|
|
218
221
|
let guard = inner.lock().await;
|
|
219
222
|
let sftp = guard.as_ref().ok_or_else(|| sftp_str("session closed"))?;
|
|
@@ -223,7 +226,7 @@ impl SftpClient {
|
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
fn read_link(&self, path: String) -> Result<String, Error> {
|
|
226
|
-
let inner = Arc::clone(
|
|
229
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
227
230
|
let result: Result<String, String> = block_on(async move {
|
|
228
231
|
let guard = inner.lock().await;
|
|
229
232
|
let sftp = guard.as_ref().ok_or_else(|| sftp_str("session closed"))?;
|
|
@@ -233,7 +236,7 @@ impl SftpClient {
|
|
|
233
236
|
}
|
|
234
237
|
|
|
235
238
|
fn close(&self) -> Result<(), Error> {
|
|
236
|
-
let inner = Arc::clone(
|
|
239
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
237
240
|
let result: Result<(), String> = block_on(async move {
|
|
238
241
|
let sftp = {
|
|
239
242
|
let mut guard = inner.lock().await;
|
|
@@ -254,19 +257,19 @@ impl SftpClient {
|
|
|
254
257
|
|
|
255
258
|
#[magnus::wrap(class = "Microsandbox::Native::SshServer", free_immediately, size)]
|
|
256
259
|
pub struct SshServer {
|
|
257
|
-
inner: Arc<Mutex<Option<CoreSshServer
|
|
260
|
+
inner: ForkGuarded<Arc<Mutex<Option<CoreSshServer>>>>,
|
|
258
261
|
}
|
|
259
262
|
|
|
260
263
|
impl SshServer {
|
|
261
264
|
pub fn from_core(inner: CoreSshServer) -> Self {
|
|
262
265
|
Self {
|
|
263
|
-
inner: Arc::new(Mutex::new(Some(inner))),
|
|
266
|
+
inner: ForkGuarded::new("Microsandbox::SshServer", Arc::new(Mutex::new(Some(inner)))),
|
|
264
267
|
}
|
|
265
268
|
}
|
|
266
269
|
|
|
267
270
|
/// Serve one SSH connection over this process's stdin/stdout.
|
|
268
271
|
fn serve_connection(&self) -> Result<(), Error> {
|
|
269
|
-
let inner = Arc::clone(
|
|
272
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
270
273
|
let result: MicrosandboxResult<()> = block_on(async move {
|
|
271
274
|
let server = {
|
|
272
275
|
let guard = inner.lock().await;
|
|
@@ -282,7 +285,7 @@ impl SshServer {
|
|
|
282
285
|
|
|
283
286
|
/// Release the prepared server endpoint. Idempotent.
|
|
284
287
|
fn close(&self) -> Result<(), Error> {
|
|
285
|
-
let inner = Arc::clone(
|
|
288
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
286
289
|
block_on(async move {
|
|
287
290
|
inner.lock().await.take();
|
|
288
291
|
})?;
|
|
@@ -18,14 +18,14 @@ use microsandbox::MicrosandboxResult;
|
|
|
18
18
|
use tokio::sync::Mutex;
|
|
19
19
|
|
|
20
20
|
use crate::error;
|
|
21
|
-
use crate::runtime::block_on;
|
|
21
|
+
use crate::runtime::{block_on, ForkGuarded};
|
|
22
22
|
use crate::sandbox::{log_entry_to_hash, metrics_to_hash};
|
|
23
23
|
|
|
24
24
|
type BoxStream<T> = Pin<Box<dyn Stream<Item = MicrosandboxResult<T>> + Send>>;
|
|
25
25
|
|
|
26
26
|
#[magnus::wrap(class = "Microsandbox::Native::LogStream", free_immediately, size)]
|
|
27
27
|
pub struct LogStream {
|
|
28
|
-
inner: Arc<Mutex<BoxStream<LogEntry
|
|
28
|
+
inner: ForkGuarded<Arc<Mutex<BoxStream<LogEntry>>>>,
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
impl LogStream {
|
|
@@ -33,14 +33,17 @@ impl LogStream {
|
|
|
33
33
|
stream: impl Stream<Item = MicrosandboxResult<LogEntry>> + Send + 'static,
|
|
34
34
|
) -> Self {
|
|
35
35
|
Self {
|
|
36
|
-
inner:
|
|
36
|
+
inner: ForkGuarded::new(
|
|
37
|
+
"Microsandbox::LogStream",
|
|
38
|
+
Arc::new(Mutex::new(Box::pin(stream))),
|
|
39
|
+
),
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
/// Next log entry as a Hash, or nil when the stream ends.
|
|
41
44
|
fn recv(&self) -> Result<Option<RHash>, Error> {
|
|
42
45
|
use futures::StreamExt;
|
|
43
|
-
let inner = Arc::clone(
|
|
46
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
44
47
|
match block_on(async move { inner.lock().await.next().await })? {
|
|
45
48
|
Some(Ok(entry)) => Ok(Some(log_entry_to_hash(&entry))),
|
|
46
49
|
Some(Err(e)) => Err(error::to_ruby(e)),
|
|
@@ -51,7 +54,7 @@ impl LogStream {
|
|
|
51
54
|
|
|
52
55
|
#[magnus::wrap(class = "Microsandbox::Native::MetricsStream", free_immediately, size)]
|
|
53
56
|
pub struct MetricsStream {
|
|
54
|
-
inner: Arc<Mutex<BoxStream<SandboxMetrics
|
|
57
|
+
inner: ForkGuarded<Arc<Mutex<BoxStream<SandboxMetrics>>>>,
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
impl MetricsStream {
|
|
@@ -59,14 +62,17 @@ impl MetricsStream {
|
|
|
59
62
|
stream: impl Stream<Item = MicrosandboxResult<SandboxMetrics>> + Send + 'static,
|
|
60
63
|
) -> Self {
|
|
61
64
|
Self {
|
|
62
|
-
inner:
|
|
65
|
+
inner: ForkGuarded::new(
|
|
66
|
+
"Microsandbox::MetricsStream",
|
|
67
|
+
Arc::new(Mutex::new(Box::pin(stream))),
|
|
68
|
+
),
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
/// Next metrics snapshot as a Hash, or nil when the stream ends.
|
|
67
73
|
fn recv(&self) -> Result<Option<RHash>, Error> {
|
|
68
74
|
use futures::StreamExt;
|
|
69
|
-
let inner = Arc::clone(
|
|
75
|
+
let inner = Arc::clone(self.inner.get()?);
|
|
70
76
|
match block_on(async move { inner.lock().await.next().await })? {
|
|
71
77
|
Some(Ok(metrics)) => Ok(Some(metrics_to_hash(&metrics))),
|
|
72
78
|
Some(Err(e)) => Err(error::to_ruby(e)),
|
|
@@ -12,7 +12,7 @@ use microsandbox::{Backend, MicrosandboxError};
|
|
|
12
12
|
|
|
13
13
|
use crate::conv;
|
|
14
14
|
use crate::error;
|
|
15
|
-
use crate::runtime::{block_on, ruby};
|
|
15
|
+
use crate::runtime::{block_on, ruby, ForkGuarded};
|
|
16
16
|
use crate::sandbox::{fs_entry_to_hash, fs_metadata_to_hash};
|
|
17
17
|
|
|
18
18
|
fn handle_to_hash(h: &VolumeHandle) -> RHash {
|
|
@@ -128,7 +128,9 @@ fn contained(path: &str) -> Result<&str, Error> {
|
|
|
128
128
|
/// each operation rebuilds it from the stored backend + name.
|
|
129
129
|
#[magnus::wrap(class = "Microsandbox::Native::VolumeFs", free_immediately, size)]
|
|
130
130
|
pub struct VolumeFs {
|
|
131
|
-
backend
|
|
131
|
+
/// The backend this view is bound to — a clone of the creating process's
|
|
132
|
+
/// default backend, so it must not cross a fork.
|
|
133
|
+
backend: ForkGuarded<Arc<dyn Backend>>,
|
|
132
134
|
name: String,
|
|
133
135
|
}
|
|
134
136
|
|
|
@@ -138,40 +140,44 @@ impl VolumeFs {
|
|
|
138
140
|
/// trait, which yields precise per-operation `Unsupported` errors on
|
|
139
141
|
/// backends that can't serve it — no local-only downcast needed here.
|
|
140
142
|
fn for_volume(name: String) -> Result<VolumeFs, Error> {
|
|
143
|
+
crate::backend::ensure_this_process_backend()?;
|
|
141
144
|
Ok(VolumeFs {
|
|
142
|
-
backend: microsandbox::default_backend(),
|
|
145
|
+
backend: ForkGuarded::new("Microsandbox::VolumeFs", microsandbox::default_backend()),
|
|
143
146
|
name,
|
|
144
147
|
})
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
fn fs(&self) -> microsandbox::volume::VolumeFs<'_> {
|
|
148
|
-
|
|
150
|
+
fn fs(&self) -> Result<microsandbox::volume::VolumeFs<'_>, Error> {
|
|
151
|
+
let backend = self.backend.get()?.clone();
|
|
152
|
+
Ok(microsandbox::volume::VolumeFs::with_backend(
|
|
153
|
+
backend, &self.name,
|
|
154
|
+
))
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
fn read(&self, path: String) -> Result<RString, Error> {
|
|
158
|
+
let fs = self.fs()?;
|
|
152
159
|
let path = contained(&path)?;
|
|
153
|
-
let fs = self.fs();
|
|
154
160
|
let bytes = block_on(fs.read(path))?.map_err(error::to_ruby)?;
|
|
155
161
|
Ok(ruby().str_from_slice(bytes.as_ref()))
|
|
156
162
|
}
|
|
157
163
|
|
|
158
164
|
fn read_text(&self, path: String) -> Result<String, Error> {
|
|
165
|
+
let fs = self.fs()?;
|
|
159
166
|
let path = contained(&path)?;
|
|
160
|
-
let fs = self.fs();
|
|
161
167
|
block_on(fs.read_to_string(path))?.map_err(error::to_ruby)
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
fn write(&self, path: String, data: RString) -> Result<(), Error> {
|
|
165
171
|
// Copy the bytes out while the GVL is held (GC.compact could move them).
|
|
166
172
|
let bytes = unsafe { data.as_slice() }.to_vec();
|
|
173
|
+
let fs = self.fs()?;
|
|
167
174
|
let path = contained(&path)?;
|
|
168
|
-
let fs = self.fs();
|
|
169
175
|
block_on(fs.write(path, &bytes))?.map_err(error::to_ruby)
|
|
170
176
|
}
|
|
171
177
|
|
|
172
178
|
fn list(&self, path: String) -> Result<RArray, Error> {
|
|
179
|
+
let fs = self.fs()?;
|
|
173
180
|
let path = contained(&path)?;
|
|
174
|
-
let fs = self.fs();
|
|
175
181
|
let entries = block_on(fs.list(path))?.map_err(error::to_ruby)?;
|
|
176
182
|
let arr = ruby().ary_new();
|
|
177
183
|
for entry in &entries {
|
|
@@ -181,46 +187,46 @@ impl VolumeFs {
|
|
|
181
187
|
}
|
|
182
188
|
|
|
183
189
|
fn mkdir(&self, path: String) -> Result<(), Error> {
|
|
190
|
+
let fs = self.fs()?;
|
|
184
191
|
let path = contained(&path)?;
|
|
185
|
-
let fs = self.fs();
|
|
186
192
|
block_on(fs.mkdir(path))?.map_err(error::to_ruby)
|
|
187
193
|
}
|
|
188
194
|
|
|
189
195
|
fn remove_file(&self, path: String) -> Result<(), Error> {
|
|
196
|
+
let fs = self.fs()?;
|
|
190
197
|
let path = contained(&path)?;
|
|
191
|
-
let fs = self.fs();
|
|
192
198
|
block_on(fs.remove(path))?.map_err(error::to_ruby)
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
fn remove_dir(&self, path: String) -> Result<(), Error> {
|
|
202
|
+
let fs = self.fs()?;
|
|
196
203
|
let path = contained(&path)?;
|
|
197
|
-
let fs = self.fs();
|
|
198
204
|
block_on(fs.remove_dir(path))?.map_err(error::to_ruby)
|
|
199
205
|
}
|
|
200
206
|
|
|
201
207
|
fn exists(&self, path: String) -> Result<bool, Error> {
|
|
208
|
+
let fs = self.fs()?;
|
|
202
209
|
let path = contained(&path)?;
|
|
203
|
-
let fs = self.fs();
|
|
204
210
|
block_on(fs.exists(path))?.map_err(error::to_ruby)
|
|
205
211
|
}
|
|
206
212
|
|
|
207
213
|
fn copy(&self, from: String, to: String) -> Result<(), Error> {
|
|
214
|
+
let fs = self.fs()?;
|
|
208
215
|
let from = contained(&from)?;
|
|
209
216
|
let to = contained(&to)?;
|
|
210
|
-
let fs = self.fs();
|
|
211
217
|
block_on(fs.copy(from, to))?.map_err(error::to_ruby)
|
|
212
218
|
}
|
|
213
219
|
|
|
214
220
|
fn rename(&self, from: String, to: String) -> Result<(), Error> {
|
|
221
|
+
let fs = self.fs()?;
|
|
215
222
|
let from = contained(&from)?;
|
|
216
223
|
let to = contained(&to)?;
|
|
217
|
-
let fs = self.fs();
|
|
218
224
|
block_on(fs.rename(from, to))?.map_err(error::to_ruby)
|
|
219
225
|
}
|
|
220
226
|
|
|
221
227
|
fn stat(&self, path: String) -> Result<RHash, Error> {
|
|
228
|
+
let fs = self.fs()?;
|
|
222
229
|
let path = contained(&path)?;
|
|
223
|
-
let fs = self.fs();
|
|
224
230
|
let meta = block_on(fs.stat(path))?.map_err(error::to_ruby)?;
|
|
225
231
|
Ok(fs_metadata_to_hash(&meta))
|
|
226
232
|
}
|
data/lib/microsandbox/errors.rb
CHANGED
|
@@ -102,6 +102,14 @@ module Microsandbox
|
|
|
102
102
|
define_error(:CloudHttpError, "cloud-http")
|
|
103
103
|
define_error(:UnsupportedError, "unsupported")
|
|
104
104
|
|
|
105
|
+
# Process model ------------------------------------------------------------
|
|
106
|
+
# A handle (a Sandbox, a SandboxHandle, an ExecHandle, a stream, an SSH or
|
|
107
|
+
# agent client, a VolumeFs, ...) was used in a process forked from the one
|
|
108
|
+
# that created it. Handles are bound to their creating process; a forked
|
|
109
|
+
# child opens its own (`Sandbox.get(name).connect`, `Volume.fs(name)`, ...).
|
|
110
|
+
# The message names both pids. Ruby-only: no other SDK has a counterpart.
|
|
111
|
+
define_error(:ForkedHandleError, "forked-handle")
|
|
112
|
+
|
|
105
113
|
# As of runtime v0.6.8 the core keys Unsupported errors by a structured
|
|
106
114
|
# (operation, reason) pair. The native layer renders both into the message
|
|
107
115
|
# ("sandbox.kill is not supported by this backend: ...") and also attaches
|
data/lib/microsandbox/version.rb
CHANGED
|
@@ -8,7 +8,7 @@ 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.17.
|
|
11
|
+
VERSION = "0.17.3"
|
|
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
|
data/lib/microsandbox.rb
CHANGED
|
@@ -240,6 +240,15 @@ module Microsandbox
|
|
|
240
240
|
# avoid calling {set_default_backend} inside the block (the restore on exit
|
|
241
241
|
# would overwrite that change). Mirrors the official SDKs' scoped-backend helper.
|
|
242
242
|
#
|
|
243
|
+
# The restore can fail in one situation: the previous backend belonged to a
|
|
244
|
+
# parent process (the block is left in a child forked while it was running,
|
|
245
|
+
# or it is a forked child's first use of the SDK) and cannot be chosen
|
|
246
|
+
# again there — it came from the environment or config file, which no
|
|
247
|
+
# longer resolve. The block's temporary selection then stays in effect. If
|
|
248
|
+
# the block itself raised, its exception is the one that propagates and the
|
|
249
|
+
# restore failure is only warned about; otherwise the restore failure is
|
|
250
|
+
# raised.
|
|
251
|
+
#
|
|
243
252
|
# @param kind ["local","cloud", Symbol]
|
|
244
253
|
# @param url [String, nil]
|
|
245
254
|
# @param api_key [String, nil]
|
|
@@ -248,10 +257,20 @@ module Microsandbox
|
|
|
248
257
|
# @return [Object] the block's return value
|
|
249
258
|
def with_backend(kind, url: nil, api_key: nil, profile: nil)
|
|
250
259
|
token = Native.push_default_backend(kind.to_s, url&.to_s, api_key&.to_s, profile&.to_s)
|
|
260
|
+
raised = false
|
|
251
261
|
begin
|
|
252
262
|
yield
|
|
263
|
+
rescue Exception # standard:disable Lint/RescueException -- only noted, re-raised at once
|
|
264
|
+
raised = true
|
|
265
|
+
raise
|
|
253
266
|
ensure
|
|
254
|
-
|
|
267
|
+
begin
|
|
268
|
+
Native.pop_default_backend(token)
|
|
269
|
+
rescue Microsandbox::Error => e
|
|
270
|
+
# Never let the restore failure replace what the block raised.
|
|
271
|
+
raise unless raised
|
|
272
|
+
warn "microsandbox: with_backend could not restore the previous backend: #{e.message}"
|
|
273
|
+
end
|
|
255
274
|
end
|
|
256
275
|
end
|
|
257
276
|
|
data/sig/microsandbox.rbs
CHANGED
|
@@ -64,6 +64,7 @@ module Microsandbox
|
|
|
64
64
|
class MetricsUnavailableError < Error end
|
|
65
65
|
class UnsupportedOperationError < Error end
|
|
66
66
|
class CloudHttpError < Error end
|
|
67
|
+
class ForkedHandleError < Error end
|
|
67
68
|
class UnsupportedError < Error
|
|
68
69
|
def operation: () -> String?
|
|
69
70
|
def hint: () -> String?
|