1use std::{path::PathBuf, sync::Arc};
4
5#[cfg(feature = "napi-v8")]
6use std::borrow::Cow;
7
8use anyhow::{Context, Error};
9use tracing::Instrument;
10use virtual_fs::{ArcBoxFile, FileSystem, TmpFileSystem, VirtualFile};
11use wasmer::{Engine, Module};
12use wasmer_types::ModuleHash;
13use webc::metadata::{Command, annotations::Wasi};
14
15use crate::{
16 Runtime, WasiEnvBuilder, WasiError, WasiRuntimeError,
17 bin_factory::BinaryPackage,
18 capabilities::Capabilities,
19 journal::{DynJournal, DynReadableJournal, SnapshotTrigger},
20 runners::{MappedDirectory, MountedDirectory, wasi_common::CommonWasiOptions},
21 runtime::task_manager::VirtualTaskManagerExt,
22};
23
24use super::wasi_common::{MAPPED_CURRENT_DIR_DEFAULT_PATH, MappedCommand};
25
26#[cfg(feature = "napi-v8")]
27use wasmer_napi::NapiCtx;
28
29#[derive(Debug, Default, Clone)]
30pub struct WasiRunner {
31 wasi: CommonWasiOptions,
32 stdin: Option<ArcBoxFile>,
33 stdout: Option<ArcBoxFile>,
34 stderr: Option<ArcBoxFile>,
35}
36
37pub enum PackageOrHash<'a> {
38 Package(&'a BinaryPackage),
39 Hash(ModuleHash),
40}
41
42pub enum RuntimeOrEngine {
43 Runtime(Arc<dyn Runtime + Send + Sync>),
44 Engine(Engine),
45}
46
47impl WasiRunner {
48 pub fn new() -> Self {
50 WasiRunner::default()
51 }
52
53 pub fn entry_function(&self) -> Option<String> {
55 self.wasi.entry_function.clone()
56 }
57
58 pub fn with_entry_function<S>(&mut self, entry_function: S) -> &mut Self
60 where
61 S: Into<String>,
62 {
63 self.wasi.entry_function = Some(entry_function.into());
64 self
65 }
66
67 pub fn get_args(&self) -> Vec<String> {
69 self.wasi.args.clone()
70 }
71
72 pub fn with_args<A, S>(&mut self, args: A) -> &mut Self
74 where
75 A: IntoIterator<Item = S>,
76 S: Into<String>,
77 {
78 self.wasi.args = args.into_iter().map(|s| s.into()).collect();
79 self
80 }
81
82 pub fn with_env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
84 self.wasi.env.insert(key.into(), value.into());
85 self
86 }
87
88 pub fn with_envs<I, K, V>(&mut self, envs: I) -> &mut Self
89 where
90 I: IntoIterator<Item = (K, V)>,
91 K: Into<String>,
92 V: Into<String>,
93 {
94 for (key, value) in envs {
95 self.wasi.env.insert(key.into(), value.into());
96 }
97 self
98 }
99
100 pub fn with_forward_host_env(&mut self, forward: bool) -> &mut Self {
101 self.wasi.forward_host_env = forward;
102 self
103 }
104
105 pub fn with_mapped_directories<I, D>(&mut self, dirs: I) -> &mut Self
106 where
107 I: IntoIterator<Item = D>,
108 D: Into<MappedDirectory>,
109 {
110 self.with_mounted_directories(dirs.into_iter().map(Into::into).map(MountedDirectory::from))
111 }
112
113 pub fn with_home_mapped(&mut self, is_home_mapped: bool) -> &mut Self {
114 self.wasi.is_home_mapped = is_home_mapped;
115 self
116 }
117
118 pub fn with_mounted_directories<I, D>(&mut self, dirs: I) -> &mut Self
119 where
120 I: IntoIterator<Item = D>,
121 D: Into<MountedDirectory>,
122 {
123 self.wasi.mounts.extend(dirs.into_iter().map(Into::into));
124 self
125 }
126
127 pub fn with_mount(&mut self, dest: String, fs: Arc<dyn FileSystem + Send + Sync>) -> &mut Self {
129 self.wasi.mounts.push(MountedDirectory { guest: dest, fs });
130 self
131 }
132
133 pub fn with_current_dir(&mut self, dir: impl Into<PathBuf>) -> &mut Self {
135 self.wasi.current_dir = Some(dir.into());
136 self
137 }
138
139 pub fn with_injected_package(&mut self, pkg: BinaryPackage) -> &mut Self {
141 self.wasi.injected_packages.push(pkg);
142 self
143 }
144
145 pub fn with_injected_packages(
147 &mut self,
148 packages: impl IntoIterator<Item = BinaryPackage>,
149 ) -> &mut Self {
150 self.wasi.injected_packages.extend(packages);
151 self
152 }
153
154 pub fn with_mapped_host_command(
155 &mut self,
156 alias: impl Into<String>,
157 target: impl Into<String>,
158 ) -> &mut Self {
159 self.wasi.mapped_host_commands.push(MappedCommand {
160 alias: alias.into(),
161 target: target.into(),
162 });
163 self
164 }
165
166 pub fn with_mapped_host_commands(
167 &mut self,
168 commands: impl IntoIterator<Item = MappedCommand>,
169 ) -> &mut Self {
170 self.wasi.mapped_host_commands.extend(commands);
171 self
172 }
173
174 pub fn capabilities_mut(&mut self) -> &mut Capabilities {
175 &mut self.wasi.capabilities
176 }
177
178 pub fn with_capabilities(&mut self, capabilities: Capabilities) -> &mut Self {
179 self.wasi.capabilities = capabilities;
180 self
181 }
182
183 #[cfg(feature = "napi-v8")]
184 pub fn with_napi_ctx(
185 &mut self,
186 runtime: &mut crate::PluggableRuntime,
187 napi: &NapiCtx,
188 ) -> &mut Self {
189 let hooks = napi.runtime_hooks();
190 runtime.with_additional_imports({
191 let hooks = hooks.clone();
192 move |module, store| hooks.additional_imports(module, store)
193 });
194 runtime.with_instance_setup(move |module, store, instance, imported_memory| {
195 hooks.configure_instance(module, store, instance, imported_memory)
196 });
197
198 self
199 }
200
201 #[cfg(feature = "journal")]
202 pub fn with_snapshot_trigger(&mut self, on: SnapshotTrigger) -> &mut Self {
203 self.wasi.snapshot_on.push(on);
204 self
205 }
206
207 #[cfg(feature = "journal")]
208 pub fn with_default_snapshot_triggers(&mut self) -> &mut Self {
209 for on in crate::journal::DEFAULT_SNAPSHOT_TRIGGERS {
210 if !self.has_snapshot_trigger(on) {
211 self.with_snapshot_trigger(on);
212 }
213 }
214 self
215 }
216
217 #[cfg(feature = "journal")]
218 pub fn has_snapshot_trigger(&self, on: SnapshotTrigger) -> bool {
219 self.wasi.snapshot_on.contains(&on)
220 }
221
222 #[cfg(feature = "journal")]
223 pub fn with_snapshot_interval(&mut self, period: std::time::Duration) -> &mut Self {
224 if !self.has_snapshot_trigger(SnapshotTrigger::PeriodicInterval) {
225 self.with_snapshot_trigger(SnapshotTrigger::PeriodicInterval);
226 }
227 self.wasi.snapshot_interval.replace(period);
228 self
229 }
230
231 #[cfg(feature = "journal")]
232 pub fn with_stop_running_after_snapshot(&mut self, stop_running: bool) -> &mut Self {
233 self.wasi.stop_running_after_snapshot = stop_running;
234 self
235 }
236
237 #[cfg(feature = "journal")]
238 pub fn with_read_only_journal(&mut self, journal: Arc<DynReadableJournal>) -> &mut Self {
239 self.wasi.read_only_journals.push(journal);
240 self
241 }
242
243 #[cfg(feature = "journal")]
244 pub fn with_writable_journal(&mut self, journal: Arc<DynJournal>) -> &mut Self {
245 self.wasi.writable_journals.push(journal);
246 self
247 }
248
249 pub fn with_skip_stdio_during_bootstrap(&mut self, skip: bool) -> &mut Self {
250 self.wasi.skip_stdio_during_bootstrap = skip;
251 self
252 }
253
254 pub fn with_stdin(&mut self, stdin: Box<dyn VirtualFile + Send + Sync>) -> &mut Self {
255 self.stdin = Some(ArcBoxFile::new(stdin));
256 self
257 }
258
259 pub fn with_stdout(&mut self, stdout: Box<dyn VirtualFile + Send + Sync>) -> &mut Self {
260 self.stdout = Some(ArcBoxFile::new(stdout));
261 self
262 }
263
264 pub fn with_stderr(&mut self, stderr: Box<dyn VirtualFile + Send + Sync>) -> &mut Self {
265 self.stderr = Some(ArcBoxFile::new(stderr));
266 self
267 }
268
269 fn ensure_tokio_runtime() -> Option<tokio::runtime::Runtime> {
270 #[cfg(feature = "sys-thread")]
271 {
272 if tokio::runtime::Handle::try_current().is_ok() {
273 return None;
274 }
275
276 let rt = tokio::runtime::Builder::new_multi_thread()
277 .enable_all()
278 .build()
279 .expect(
280 "Failed to build a multi-threaded tokio runtime. This is necessary \
281 for WASIX to work. You can provide a tokio runtime by building one \
282 yourself and entering it before using WasiRunner.",
283 );
284 Some(rt)
285 }
286
287 #[cfg(not(feature = "sys-thread"))]
288 {
289 None
290 }
291 }
292
293 #[cfg(feature = "napi-v8")]
294 fn maybe_disable_async_threading_for_module(&mut self, module: &Module) {
295 if crate::module_needs_napi(module) {
296 self.wasi
297 .capabilities
298 .threading
299 .enable_asynchronous_threading = false;
300 }
301 }
302
303 #[cfg(feature = "napi-v8")]
304 fn maybe_disable_async_threading_for_command(
305 &mut self,
306 cmd: &crate::bin_factory::BinaryPackageCommand,
307 runtime_or_engine: &RuntimeOrEngine,
308 ) -> Result<(), anyhow::Error> {
309 let module = match runtime_or_engine {
310 RuntimeOrEngine::Runtime(runtime) => runtime.resolve_module_sync(
311 crate::runtime::ModuleInput::Command(Cow::Borrowed(cmd)),
312 None,
313 None,
314 )?,
315 RuntimeOrEngine::Engine(engine) => Module::new(engine, cmd.atom_ref())?,
316 };
317 self.maybe_disable_async_threading_for_module(&module);
318 Ok(())
319 }
320
321 #[tracing::instrument(level = "debug", skip_all)]
322 pub fn prepare_webc_env(
323 &self,
324 program_name: &str,
325 wasi: &Wasi,
326 pkg_or_hash: PackageOrHash,
327 runtime_or_engine: RuntimeOrEngine,
328 root_fs: Option<TmpFileSystem>,
329 ) -> Result<WasiEnvBuilder, anyhow::Error> {
330 let mut builder = WasiEnvBuilder::new(program_name);
331
332 match runtime_or_engine {
333 RuntimeOrEngine::Runtime(runtime) => {
334 builder.set_runtime(runtime);
335 }
336 RuntimeOrEngine::Engine(engine) => {
337 builder.set_engine(engine);
338 }
339 }
340
341 let container_fs = match pkg_or_hash {
342 PackageOrHash::Package(pkg) => {
343 builder.add_webc(pkg.clone());
344 builder.set_module_hash(pkg.hash());
345 builder.include_packages(pkg.package_ids.clone());
346
347 pkg.webc_fs.as_deref().map(|fs| fs.duplicate())
348 }
349 PackageOrHash::Hash(hash) => {
350 builder.set_module_hash(hash);
351 None
352 }
353 };
354
355 if self.wasi.is_home_mapped {
356 builder.set_current_dir(MAPPED_CURRENT_DIR_DEFAULT_PATH);
357 }
358
359 if let Some(current_dir) = &self.wasi.current_dir {
360 builder.set_current_dir(current_dir.clone());
361 }
362
363 if let Some(cwd) = &wasi.cwd {
364 builder.set_current_dir(cwd);
365 }
366
367 self.wasi
368 .prepare_webc_env(&mut builder, container_fs, wasi, root_fs)?;
369
370 if let Some(stdin) = &self.stdin {
371 builder.set_stdin(Box::new(stdin.clone()));
372 }
373 if let Some(stdout) = &self.stdout {
374 builder.set_stdout(Box::new(stdout.clone()));
375 }
376 if let Some(stderr) = &self.stderr {
377 builder.set_stderr(Box::new(stderr.clone()));
378 }
379
380 Ok(builder)
381 }
382
383 pub fn run_wasm(
384 &self,
385 runtime_or_engine: RuntimeOrEngine,
386 program_name: &str,
387 module: Module,
388 module_hash: ModuleHash,
389 ) -> Result<(), Error> {
390 let tokio_runtime = Self::ensure_tokio_runtime();
392 let _guard = tokio_runtime.as_ref().map(|rt| rt.enter());
393
394 #[cfg(feature = "napi-v8")]
395 let mut runner = self.clone();
396 #[cfg(not(feature = "napi-v8"))]
397 let runner = self.clone();
398 #[cfg(feature = "napi-v8")]
399 runner.maybe_disable_async_threading_for_module(&module);
400
401 let wasi = webc::metadata::annotations::Wasi::new(program_name);
402
403 let mut builder = runner.prepare_webc_env(
404 program_name,
405 &wasi,
406 PackageOrHash::Hash(module_hash),
407 runtime_or_engine,
408 None,
409 )?;
410
411 #[cfg(feature = "ctrlc")]
412 {
413 builder = builder.attach_ctrl_c();
414 }
415
416 #[cfg(feature = "journal")]
417 {
418 for journal in runner.wasi.read_only_journals.iter().cloned() {
419 builder.add_read_only_journal(journal);
420 }
421 for journal in runner.wasi.writable_journals.iter().cloned() {
422 builder.add_writable_journal(journal);
423 }
424
425 if !runner.wasi.snapshot_on.is_empty() {
426 for trigger in runner.wasi.snapshot_on.iter().cloned() {
427 builder.add_snapshot_trigger(trigger);
428 }
429 } else if !runner.wasi.writable_journals.is_empty() {
430 for on in crate::journal::DEFAULT_SNAPSHOT_TRIGGERS {
431 builder.add_snapshot_trigger(on);
432 }
433 }
434
435 if let Some(period) = runner.wasi.snapshot_interval {
436 if runner.wasi.writable_journals.is_empty() {
437 return Err(anyhow::format_err!(
438 "If you specify a snapshot interval then you must also specify a writable journal file"
439 ));
440 }
441 builder.with_snapshot_interval(period);
442 }
443
444 builder.with_stop_running_after_snapshot(runner.wasi.stop_running_after_snapshot);
445 builder.with_skip_stdio_during_bootstrap(runner.wasi.skip_stdio_during_bootstrap);
446 }
447
448 let env = builder.build()?;
449 let runtime = env.runtime.clone();
450 let tasks = runtime.task_manager().clone();
451
452 let mut task_handle =
453 crate::bin_factory::spawn_exec_module(module, env, &runtime).context("Spawn failed")?;
454
455 #[cfg(feature = "ctrlc")]
456 task_handle.install_ctrlc_handler();
457 let task_handle = async move { task_handle.wait_finished().await }.in_current_span();
458
459 let result = tasks.spawn_and_block_on(task_handle)?;
460 let exit_code = result
461 .map_err(|err| {
462 let msg = err.to_string();
464 let weak = Arc::downgrade(&err);
465 Arc::into_inner(err).unwrap_or_else(|| {
466 weak.upgrade()
467 .map(|err| wasi_runtime_error_to_owned(&err))
468 .unwrap_or_else(|| {
469 WasiRuntimeError::Anyhow(Arc::new(anyhow::format_err!("{msg}")))
470 })
471 })
472 })
473 .context("Unable to wait for the process to exit")?;
474
475 if exit_code.raw() == 0 {
476 Ok(())
477 } else {
478 Err(WasiRuntimeError::Wasi(crate::WasiError::Exit(exit_code)).into())
479 }
480 }
481
482 pub fn run_command(
483 &mut self,
484 command_name: &str,
485 pkg: &BinaryPackage,
486 runtime_or_engine: RuntimeOrEngine,
487 ) -> Result<(), Error> {
488 let tokio_runtime = Self::ensure_tokio_runtime();
490 let _guard = tokio_runtime.as_ref().map(|rt| rt.enter());
491
492 let cmd = pkg
493 .get_command(command_name)
494 .with_context(|| format!("The package doesn't contain a \"{command_name}\" command"))?;
495
496 #[cfg(feature = "napi-v8")]
497 self.maybe_disable_async_threading_for_command(cmd, &runtime_or_engine)?;
498
499 let wasi = cmd
500 .metadata()
501 .annotation("wasi")?
502 .unwrap_or_else(|| Wasi::new(command_name));
503
504 let exec_name = if let Some(exec_name) = wasi.exec_name.as_ref() {
505 exec_name
506 } else {
507 command_name
508 };
509
510 #[allow(unused_mut)]
511 let mut builder = self
512 .prepare_webc_env(
513 exec_name,
514 &wasi,
515 PackageOrHash::Package(pkg),
516 runtime_or_engine,
517 None,
518 )
519 .context("Unable to prepare the WASI environment")?;
520
521 #[cfg(feature = "journal")]
522 {
523 for journal in self.wasi.read_only_journals.iter().cloned() {
524 builder.add_read_only_journal(journal);
525 }
526 for journal in self.wasi.writable_journals.iter().cloned() {
527 builder.add_writable_journal(journal);
528 }
529
530 if !self.wasi.snapshot_on.is_empty() {
531 for trigger in self.wasi.snapshot_on.iter().cloned() {
532 builder.add_snapshot_trigger(trigger);
533 }
534 } else if !self.wasi.writable_journals.is_empty() {
535 for on in crate::journal::DEFAULT_SNAPSHOT_TRIGGERS {
536 builder.add_snapshot_trigger(on);
537 }
538 }
539
540 if let Some(period) = self.wasi.snapshot_interval {
541 if self.wasi.writable_journals.is_empty() {
542 return Err(anyhow::format_err!(
543 "If you specify a snapshot interval then you must also specify a journal file"
544 ));
545 }
546 builder.with_snapshot_interval(period);
547 }
548
549 builder.with_stop_running_after_snapshot(self.wasi.stop_running_after_snapshot);
550 }
551
552 let env = builder.build()?;
553 let runtime = env.runtime.clone();
554 let command_name = command_name.to_string();
555 let tasks = runtime.task_manager().clone();
556 let pkg = pkg.clone();
557
558 let exit_code = tasks.spawn_and_block_on(
563 async move {
564 let mut task_handle =
565 crate::bin_factory::spawn_exec(pkg, &command_name, env, &runtime)
566 .await
567 .context("Spawn failed")?;
568
569 #[cfg(feature = "ctrlc")]
570 task_handle.install_ctrlc_handler();
571
572 task_handle
573 .wait_finished()
574 .await
575 .map_err(|err| {
576 let msg = err.to_string();
578 let weak = Arc::downgrade(&err);
579 Arc::into_inner(err).unwrap_or_else(|| {
580 weak.upgrade()
581 .map(|err| wasi_runtime_error_to_owned(&err))
582 .unwrap_or_else(|| {
583 WasiRuntimeError::Anyhow(Arc::new(anyhow::format_err!("{msg}")))
584 })
585 })
586 })
587 .context("Unable to wait for the process to exit")
588 }
589 .in_current_span(),
590 )??;
591
592 if exit_code.raw() == 0 {
593 Ok(())
594 } else {
595 Err(WasiRuntimeError::Wasi(crate::WasiError::Exit(exit_code)).into())
596 }
597 }
598}
599
600impl crate::runners::Runner for WasiRunner {
601 fn can_run_command(command: &Command) -> Result<bool, Error> {
602 Ok(command
603 .runner
604 .starts_with(webc::metadata::annotations::WASI_RUNNER_URI))
605 }
606
607 #[tracing::instrument(skip_all)]
608 fn run_command(
609 &mut self,
610 command_name: &str,
611 pkg: &BinaryPackage,
612 runtime: Arc<dyn Runtime + Send + Sync>,
613 ) -> Result<(), Error> {
614 self.run_command(command_name, pkg, RuntimeOrEngine::Runtime(runtime))
615 }
616}
617
618fn wasi_runtime_error_to_owned(err: &WasiRuntimeError) -> WasiRuntimeError {
619 match err {
620 WasiRuntimeError::Init(a) => WasiRuntimeError::Init(a.clone()),
621 WasiRuntimeError::Export(a) => WasiRuntimeError::Export(a.clone()),
622 WasiRuntimeError::Instantiation(a) => WasiRuntimeError::Instantiation(a.clone()),
623 WasiRuntimeError::Wasi(WasiError::Exit(a)) => WasiRuntimeError::Wasi(WasiError::Exit(*a)),
624 WasiRuntimeError::Wasi(WasiError::ThreadExit) => {
625 WasiRuntimeError::Wasi(WasiError::ThreadExit)
626 }
627 WasiRuntimeError::Wasi(WasiError::UnknownWasiVersion) => {
628 WasiRuntimeError::Wasi(WasiError::UnknownWasiVersion)
629 }
630 WasiRuntimeError::Wasi(WasiError::DeepSleep(_)) => {
631 WasiRuntimeError::Anyhow(Arc::new(anyhow::format_err!("deep-sleep")))
632 }
633 WasiRuntimeError::Wasi(WasiError::DlSymbolResolutionFailed(symbol)) => {
634 WasiRuntimeError::Wasi(WasiError::DlSymbolResolutionFailed(symbol.clone()))
635 }
636 WasiRuntimeError::ControlPlane(a) => WasiRuntimeError::ControlPlane(a.clone()),
637 WasiRuntimeError::Runtime(a) => WasiRuntimeError::Runtime(a.clone()),
638 WasiRuntimeError::Thread(a) => WasiRuntimeError::Thread(a.clone()),
639 WasiRuntimeError::Anyhow(a) => WasiRuntimeError::Anyhow(a.clone()),
640 }
641}
642
643#[cfg(test)]
644mod tests {
645 use super::*;
646
647 #[test]
648 fn send_and_sync() {
649 fn assert_send<T: Send>() {}
650 fn assert_sync<T: Sync>() {}
651
652 assert_send::<WasiRunner>();
653 assert_sync::<WasiRunner>();
654 }
655
656 #[cfg(all(feature = "host-fs", feature = "sys"))]
657 #[tokio::test]
658 async fn test_volume_mount_without_webcs() {
659 use std::sync::Arc;
660
661 let root_fs = virtual_fs::RootFileSystemBuilder::new().build();
662
663 let tokrt = tokio::runtime::Handle::current();
664
665 let hostdir = virtual_fs::host_fs::FileSystem::new(tokrt.clone(), "/").unwrap();
666 let hostdir_dyn: Arc<dyn virtual_fs::FileSystem + Send + Sync> = Arc::new(hostdir);
667
668 root_fs
669 .mount("/host".into(), &hostdir_dyn, "/".into())
670 .unwrap();
671
672 let envb = crate::runners::wasi::WasiRunner::new();
673
674 let annotations = webc::metadata::annotations::Wasi::new("test");
675
676 let tm = Arc::new(crate::runtime::task_manager::tokio::TokioTaskManager::new(
677 tokrt.clone(),
678 ));
679 let rt = crate::PluggableRuntime::new(tm);
680
681 let envb = envb
682 .prepare_webc_env(
683 "test",
684 &annotations,
685 PackageOrHash::Hash(ModuleHash::random()),
686 RuntimeOrEngine::Runtime(Arc::new(rt)),
687 Some(root_fs),
688 )
689 .unwrap();
690
691 let init = envb.build_init().unwrap();
692
693 let fs = &init.state.fs.root_fs;
694
695 fs.read_dir(std::path::Path::new("/host")).unwrap();
696 }
697
698 #[cfg(all(feature = "host-fs", feature = "sys"))]
699 #[tokio::test]
700 async fn test_volume_mount_with_webcs() {
701 use std::sync::Arc;
702
703 use wasmer_package::utils::from_bytes;
704
705 let root_fs = virtual_fs::RootFileSystemBuilder::new().build();
706
707 let tokrt = tokio::runtime::Handle::current();
708
709 let hostdir = virtual_fs::host_fs::FileSystem::new(tokrt.clone(), "/").unwrap();
710 let hostdir_dyn: Arc<dyn virtual_fs::FileSystem + Send + Sync> = Arc::new(hostdir);
711
712 root_fs
713 .mount("/host".into(), &hostdir_dyn, "/".into())
714 .unwrap();
715
716 let envb = crate::runners::wasi::WasiRunner::new();
717
718 let annotations = webc::metadata::annotations::Wasi::new("test");
719
720 let tm = Arc::new(crate::runtime::task_manager::tokio::TokioTaskManager::new(
721 tokrt.clone(),
722 ));
723 let mut rt = crate::PluggableRuntime::new(tm);
724 rt.set_package_loader(crate::runtime::package_loader::BuiltinPackageLoader::new());
725
726 let webc_path = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("../../tests/integration/cli/tests/webc/wasmer-tests--volume-static-webserver@0.1.0.webc");
727 let webc_data = std::fs::read(webc_path).unwrap();
728 let container = from_bytes(webc_data).unwrap();
729
730 let binpkg = crate::bin_factory::BinaryPackage::from_webc(&container, &rt)
731 .await
732 .unwrap();
733
734 let mut envb = envb
735 .prepare_webc_env(
736 "test",
737 &annotations,
738 PackageOrHash::Package(&binpkg),
739 RuntimeOrEngine::Runtime(Arc::new(rt)),
740 Some(root_fs),
741 )
742 .unwrap();
743
744 envb = envb.preopen_dir("/host").unwrap();
745
746 let init = envb.build_init().unwrap();
747
748 let fs = &init.state.fs.root_fs;
749
750 fs.read_dir(std::path::Path::new("/host")).unwrap();
751 fs.read_dir(std::path::Path::new("/settings")).unwrap();
752 }
753}