diff --git a/Cargo.lock b/Cargo.lock index c0d0b25bc3..eb699b28c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -280,31 +280,12 @@ version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04e142bbbe9d5d6a2dd0387f887a000b41f4c82fb1226316dfb4cc8dbc3b1a29" dependencies = [ - "cap-primitives 0.25.2", - "cap-std 0.25.2", - "io-lifetimes 0.7.3", + "cap-primitives", + "cap-std", + "io-lifetimes", "windows-sys", ] -[[package]] -name = "cap-primitives" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8fca3e81fae1d91a36e9784ca22a39ef623702b5f7904d89dc31f10184a178" -dependencies = [ - "ambient-authority", - "errno", - "fs-set-times 0.15.0", - "io-extras 0.13.2", - "io-lifetimes 0.5.3", - "ipnet", - "maybe-owned", - "rustix 0.33.7", - "winapi", - "winapi-util", - "winx 0.31.0", -] - [[package]] name = "cap-primitives" version = "0.25.2" @@ -313,15 +294,15 @@ checksum = "7f22f4975282dd4f2330ee004f001c4e22f420da9fb474ea600e9af330f1e548" dependencies = [ "ambient-authority", "errno", - "fs-set-times 0.17.1", - "io-extras 0.15.0", - "io-lifetimes 0.7.3", + "fs-set-times", + "io-extras", + "io-lifetimes", "ipnet", "maybe-owned", - "rustix 0.35.9", + "rustix", "winapi-util", "windows-sys", - "winx 0.33.0", + "winx", ] [[package]] @@ -334,30 +315,17 @@ dependencies = [ "rand 0.8.5", ] -[[package]] -name = "cap-std" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2247568946095c7765ad2b441a56caffc08027734c634a6d5edda648f04e32eb" -dependencies = [ - "cap-primitives 0.24.4", - "io-extras 0.13.2", - "io-lifetimes 0.5.3", - "ipnet", - "rustix 0.33.7", -] - [[package]] name = "cap-std" version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95624bb0abba6b6ff6fad2e02a7d3945d093d064ac5a3477a308c29fbe3bfd49" dependencies = [ - "cap-primitives 0.25.2", - "io-extras 0.15.0", - "io-lifetimes 0.7.3", + "cap-primitives", + "io-extras", + "io-lifetimes", "ipnet", - "rustix 0.35.9", + "rustix", ] [[package]] @@ -366,10 +334,10 @@ version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46a2d284862edf6e431e9ad4e109c02855157904cebaceae6f042b124a1a21e2" dependencies = [ - "cap-primitives 0.25.2", + "cap-primitives", "once_cell", - "rustix 0.35.9", - "winx 0.33.0", + "rustix", + "winx", ] [[package]] @@ -1119,25 +1087,14 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs-set-times" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df62ee66ee2d532ea8d567b5a3f0d03ecd64636b98bad5be1e93dcc918b92aa" -dependencies = [ - "io-lifetimes 0.5.3", - "rustix 0.33.7", - "winapi", -] - [[package]] name = "fs-set-times" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a267b6a9304912e018610d53fe07115d8b530b160e85db4d2d3a59f3ddde1aec" dependencies = [ - "io-lifetimes 0.7.3", - "rustix 0.35.9", + "io-lifetimes", + "rustix", "windows-sys", ] @@ -1601,32 +1558,16 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "io-extras" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c937cc9891c12eaa8c63ad347e4a288364b1328b924886970b47a14ab8f8f8" -dependencies = [ - "io-lifetimes 0.5.3", - "winapi", -] - [[package]] name = "io-extras" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5d8c2ab5becd8720e30fd25f8fa5500d8dc3fceadd8378f05859bd7b46fc49" dependencies = [ - "io-lifetimes 0.7.3", + "io-lifetimes", "windows-sys", ] -[[package]] -name = "io-lifetimes" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec58677acfea8a15352d42fc87d11d63596ade9239e0a7c9352914417515dbe6" - [[package]] name = "io-lifetimes" version = "0.7.3" @@ -1650,8 +1591,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d508111813f9af3afd2f92758f77e4ed2cc9371b642112c6a48d22eb73105c5" dependencies = [ "hermit-abi 0.2.5", - "io-lifetimes 0.7.3", - "rustix 0.35.9", + "io-lifetimes", + "rustix", "windows-sys", ] @@ -1768,12 +1709,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linux-raw-sys" -version = "0.0.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7" - [[package]] name = "linux-raw-sys" version = "0.0.46" @@ -1917,7 +1852,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "480b5a5de855d11ff13195950bdc8b98b5e942ef47afc447f6615cdcc4e15d80" dependencies = [ - "rustix 0.35.9", + "rustix", ] [[package]] @@ -2854,22 +2789,6 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" -[[package]] -name = "rustix" -version = "0.33.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938a344304321a9da4973b9ff4f9f8db9caf4597dfd9dda6a60b523340a0fff0" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes 0.5.3", - "itoa 1.0.3", - "libc", - "linux-raw-sys 0.0.42", - "once_cell", - "winapi", -] - [[package]] name = "rustix" version = "0.35.9" @@ -2878,10 +2797,10 @@ checksum = "72c825b8aa8010eb9ee99b75f05e10180b9278d161583034d7574c9d617aeada" dependencies = [ "bitflags", "errno", - "io-lifetimes 0.7.3", + "io-lifetimes", "itoa 1.0.3", "libc", - "linux-raw-sys 0.0.46", + "linux-raw-sys", "once_cell", "windows-sys", ] @@ -2937,16 +2856,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "sanitize-filename" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf18934a12018228c5b55a6dae9df5d0641e3566b3630cb46cc55564068e7c2f" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "sanitize-filename" version = "0.4.0" @@ -3330,7 +3239,6 @@ dependencies = [ "sha2 0.10.3", "spin-build", "spin-config", - "spin-engine", "spin-http", "spin-loader", "spin-manifest", @@ -3379,26 +3287,6 @@ dependencies = [ "wasmtime-wasi", ] -[[package]] -name = "spin-engine" -version = "0.2.0" -dependencies = [ - "anyhow", - "bytes", - "cap-std 0.24.4", - "dirs 4.0.0", - "sanitize-filename 0.3.0", - "spin-manifest", - "tempfile", - "tokio", - "tracing", - "wasi-cap-std-sync", - "wasi-common", - "wasmtime", - "wasmtime-wasi", - "wit-bindgen-wasmtime", -] - [[package]] name = "spin-http" version = "0.2.0" @@ -3612,7 +3500,7 @@ dependencies = [ "outbound-http", "outbound-pg", "outbound-redis", - "sanitize-filename 0.4.0", + "sanitize-filename", "serde", "serde_json", "spin-app", @@ -3723,11 +3611,11 @@ dependencies = [ "atty", "bitflags", "cap-fs-ext", - "cap-std 0.25.2", - "io-lifetimes 0.7.3", - "rustix 0.35.9", + "cap-std", + "io-lifetimes", + "rustix", "windows-sys", - "winx 0.33.0", + "winx", ] [[package]] @@ -4278,14 +4166,14 @@ dependencies = [ "async-trait", "cap-fs-ext", "cap-rand", - "cap-std 0.25.2", + "cap-std", "cap-time-ext", - "fs-set-times 0.17.1", - "io-extras 0.15.0", - "io-lifetimes 0.7.3", + "fs-set-times", + "io-extras", + "io-lifetimes", "is-terminal", "lazy_static", - "rustix 0.35.9", + "rustix", "system-interface", "tracing", "wasi-common", @@ -4301,9 +4189,9 @@ dependencies = [ "anyhow", "bitflags", "cap-rand", - "cap-std 0.25.2", - "io-extras 0.15.0", - "rustix 0.35.9", + "cap-std", + "io-extras", + "rustix", "thiserror", "tracing", "wiggle", @@ -4317,11 +4205,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab325bba31ae9286b8ebdc18d32a43d6471312c9bc4e477240be444e00ec4f4" dependencies = [ "anyhow", - "cap-std 0.25.2", - "io-extras 0.15.0", - "io-lifetimes 0.7.3", + "cap-std", + "io-extras", + "io-lifetimes", "lazy_static", - "rustix 0.35.9", + "rustix", "tokio", "wasi-cap-std-sync", "wasi-common", @@ -4458,7 +4346,7 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.35.9", + "rustix", "serde", "sha2 0.9.9", "toml", @@ -4516,7 +4404,7 @@ checksum = "2f6aba0b317746e8213d1f36a4c51974e66e69c1f05bfc09ed29b4d4bda290eb" dependencies = [ "cc", "cfg-if", - "rustix 0.35.9", + "rustix", "windows-sys", ] @@ -4537,7 +4425,7 @@ dependencies = [ "object 0.28.4", "region", "rustc-demangle", - "rustix 0.35.9", + "rustix", "serde", "target-lexicon", "thiserror", @@ -4555,7 +4443,7 @@ checksum = "55e23273fddce8cab149a0743c46932bf4910268641397ed86b46854b089f38f" dependencies = [ "lazy_static", "object 0.28.4", - "rustix 0.35.9", + "rustix", ] [[package]] @@ -4577,7 +4465,7 @@ dependencies = [ "more-asserts", "rand 0.8.5", "region", - "rustix 0.35.9", + "rustix", "thiserror", "wasmtime-environ", "wasmtime-fiber", @@ -4807,17 +4695,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "winx" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d5973cb8cd94a77d03ad7e23bbe14889cb29805da1cec0e4aff75e21aebded" -dependencies = [ - "bitflags", - "io-lifetimes 0.5.3", - "winapi", -] - [[package]] name = "winx" version = "0.33.0" @@ -4825,7 +4702,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7b01e010390eb263a4518c8cebf86cb67469d1511c00b749a47b64c39e8054d" dependencies = [ "bitflags", - "io-lifetimes 0.7.3", + "io-lifetimes", "windows-sys", ] diff --git a/Cargo.toml b/Cargo.toml index ecc19ea059..611418ec18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,6 @@ serde_json = "1.0.82" sha2 = "0.10.2" spin-build = { path = "crates/build" } spin-config = { path = "crates/config" } -spin-engine = { path = "crates/engine" } spin-http = { path = "crates/http" } spin-loader = { path = "crates/loader" } spin-manifest = { path = "crates/manifest" } @@ -73,7 +72,6 @@ members = [ "crates/build", "crates/config", "crates/core", - "crates/engine", "crates/http", "crates/loader", "crates/manifest", diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml deleted file mode 100644 index 99f1d47846..0000000000 --- a/crates/engine/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "spin-engine" -version = "0.2.0" -edition = "2021" -authors = ["Fermyon Engineering "] - -[dependencies] -anyhow = "1.0.44" -bytes = "1.1.0" -dirs = "4.0" -sanitize-filename = "0.3.0" -spin-manifest = { path = "../manifest" } -tempfile = "3.3.0" -tokio = { version = "1.10.0", features = [ "full" ] } -tracing = { version = "0.1", features = [ "log" ] } -wasi-cap-std-sync = "0.39.1" -wasi-common = "0.39.1" -wasmtime = { version = "0.39.1", features = [ "async" ] } -wasmtime-wasi = "0.39.1" -cap-std = "0.24.1" - -[dev-dependencies.wit-bindgen-wasmtime] -git = "https://github.com/bytecodealliance/wit-bindgen" -rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba" -features = [ "async" ] diff --git a/crates/engine/src/host_component.rs b/crates/engine/src/host_component.rs deleted file mode 100644 index 059fdeaad2..0000000000 --- a/crates/engine/src/host_component.rs +++ /dev/null @@ -1,100 +0,0 @@ -use std::{any::Any, marker::PhantomData}; - -use anyhow::Result; -use spin_manifest::CoreComponent; -use wasmtime::Linker; - -use crate::RuntimeContext; - -/// Represents a host implementation of a Wasm interface. -pub trait HostComponent: Send + Sync { - /// Host component runtime state. - type State: Any + Send; - - /// Add this component to the given Linker, using the given runtime state-getting handle. - fn add_to_linker( - linker: &mut Linker>, - state_handle: HostComponentsStateHandle, - ) -> Result<()>; - - /// Build a new runtime state object for the given component. - fn build_state(&self, component: &CoreComponent) -> Result; -} -type HostComponentState = Box; - -type StateBuilder = Box Result + Send + Sync>; - -#[derive(Default)] -pub(crate) struct HostComponents { - state_builders: Vec, -} - -impl HostComponents { - pub(crate) fn insert( - &mut self, - linker: &mut Linker>, - host_component: Component, - ) -> Result<()> { - let handle = HostComponentsStateHandle { - idx: self.state_builders.len(), - _phantom: PhantomData, - }; - Component::add_to_linker(linker, handle)?; - self.state_builders.push(Box::new(move |c| { - Ok(Box::new(host_component.build_state(c)?)) - })); - Ok(()) - } - - pub(crate) fn build_state(&self, c: &CoreComponent) -> Result { - Ok(HostComponentsState( - self.state_builders - .iter() - .map(|build_state| build_state(c)) - .collect::>()?, - )) - } -} - -/// A collection of host components state. -#[derive(Default)] -pub struct HostComponentsState(Vec); - -/// A handle to component state, used in HostComponent::add_to_linker. -pub struct HostComponentsStateHandle { - idx: usize, - _phantom: PhantomData T>, -} - -impl HostComponentsStateHandle { - /// Get a ref to the component state associated with this handle from the RuntimeContext. - pub fn get<'a, U>(&self, ctx: &'a RuntimeContext) -> &'a T { - ctx.host_components_state - .0 - .get(self.idx) - .unwrap() - .downcast_ref() - .unwrap() - } - - /// Get a mutable ref to the component state associated with this handle from the RuntimeContext. - pub fn get_mut<'a, U>(&self, ctx: &'a mut RuntimeContext) -> &'a mut T { - ctx.host_components_state - .0 - .get_mut(self.idx) - .unwrap() - .downcast_mut() - .unwrap() - } -} - -impl Clone for HostComponentsStateHandle { - fn clone(&self) -> Self { - Self { - idx: self.idx, - _phantom: PhantomData, - } - } -} - -impl Copy for HostComponentsStateHandle {} diff --git a/crates/engine/src/io.rs b/crates/engine/src/io.rs deleted file mode 100644 index 6553fc9c08..0000000000 --- a/crates/engine/src/io.rs +++ /dev/null @@ -1,256 +0,0 @@ -use std::{ - collections::HashSet, - fmt::Debug, - io::{LineWriter, Write}, - sync::{Arc, RwLock, RwLockReadGuard}, -}; - -use wasi_common::{ - pipe::{ReadPipe, WritePipe}, - WasiFile, -}; - -/// Prepares a WASI pipe which writes to a memory buffer, optionally -/// copying to the specified output stream. -pub fn redirect_to_mem_buffer( - follow: Follow, -) -> (WritePipe, Arc>) { - let immediate = follow.writer(); - - let buffer: Vec = vec![]; - let std_dests = WriteDestinations { buffer, immediate }; - let lock = Arc::new(RwLock::new(std_dests)); - let std_pipe = WritePipe::from_shared(lock.clone()); - - (std_pipe, lock) -} - -/// Which components should have their logs followed on stdout/stderr. -#[derive(Clone, Debug)] -pub enum FollowComponents { - /// No components should have their logs followed. - None, - /// Only the specified components should have their logs followed. - Named(HashSet), - /// All components should have their logs followed. - All, -} - -impl FollowComponents { - /// Whether a given component should have its logs followed on stdout/stderr. - pub fn should_follow(&self, component_id: &str) -> bool { - match self { - Self::None => false, - Self::All => true, - Self::Named(ids) => ids.contains(component_id), - } - } -} - -impl Default for FollowComponents { - fn default() -> Self { - Self::None - } -} - -/// The buffers in which Wasm module output has been saved. -pub trait OutputBuffers { - /// The buffer in which stdout has been saved. - fn stdout(&self) -> &[u8]; - /// The buffer in which stderr has been saved. - fn stderr(&self) -> &[u8]; -} - -/// A set of redirected standard I/O streams with which -/// a Wasm module is to be run. -pub struct ModuleIoRedirects { - /// pipes for ModuleIoRedirects - pub pipes: RedirectPipes, - /// read handles for ModuleIoRedirects - pub read_handles: RedirectReadHandles, -} - -impl Default for ModuleIoRedirects { - fn default() -> Self { - Self::new(false) - } -} - -impl ModuleIoRedirects { - /// Constructs the ModuleIoRedirects, and RedirectReadHandles instances the default way - pub fn new(follow: bool) -> Self { - let rrh = RedirectReadHandles::new(follow); - - let in_stdpipe: Box = Box::new(ReadPipe::from(vec![])); - let out_stdpipe: Box = Box::new(WritePipe::from_shared(rrh.stdout.clone())); - let err_stdpipe: Box = Box::new(WritePipe::from_shared(rrh.stderr.clone())); - - Self { - pipes: RedirectPipes { - stdin: in_stdpipe, - stdout: out_stdpipe, - stderr: err_stdpipe, - }, - read_handles: rrh, - } - } -} - -/// Pipes from `ModuleIoRedirects` -pub struct RedirectPipes { - pub(crate) stdin: Box, - pub(crate) stdout: Box, - pub(crate) stderr: Box, -} - -impl RedirectPipes { - /// Constructs an instance from a set of WasiFile objects. - pub fn new( - stdin: Box, - stdout: Box, - stderr: Box, - ) -> Self { - Self { - stdin, - stdout, - stderr, - } - } -} - -/// The destinations to which redirected module output will be written. -/// Used for subsequently reading back the output. -pub struct RedirectReadHandles { - stdout: Arc>, - stderr: Arc>, -} - -impl Default for RedirectReadHandles { - fn default() -> Self { - Self::new(false) - } -} - -impl RedirectReadHandles { - /// Creates a new RedirectReadHandles instance - pub fn new(follow: bool) -> Self { - let out_immediate = Follow::stdout(follow).writer(); - let err_immediate = Follow::stderr(follow).writer(); - - let out_buffer: Vec = vec![]; - let err_buffer: Vec = vec![]; - - let out_std_dests = WriteDestinations { - buffer: out_buffer, - immediate: out_immediate, - }; - let err_std_dests = WriteDestinations { - buffer: err_buffer, - immediate: err_immediate, - }; - - Self { - stdout: Arc::new(RwLock::new(out_std_dests)), - stderr: Arc::new(RwLock::new(err_std_dests)), - } - } - /// Acquires a read lock for the in-memory output buffers. - pub fn read(&self) -> impl OutputBuffers + '_ { - RedirectReadHandlesLock { - stdout: self.stdout.read().unwrap(), - stderr: self.stderr.read().unwrap(), - } - } -} - -struct RedirectReadHandlesLock<'a> { - stdout: RwLockReadGuard<'a, WriteDestinations>, - stderr: RwLockReadGuard<'a, WriteDestinations>, -} - -impl<'a> OutputBuffers for RedirectReadHandlesLock<'a> { - fn stdout(&self) -> &[u8] { - self.stdout.buffer() - } - fn stderr(&self) -> &[u8] { - self.stderr.buffer() - } -} - -/// Indicates whether a memory redirect should also pipe the output to -/// the console so it can be followed live. -pub enum Follow { - /// Do not pipe to console - only write to memory. - None, - /// Also pipe to stdout. - Stdout, - /// Also pipe to stderr. - Stderr, -} - -impl Follow { - pub(crate) fn writer(&self) -> Box { - match self { - Self::None => Box::new(DiscardingWriter), - Self::Stdout => Box::new(LineWriter::new(std::io::stdout())), - Self::Stderr => Box::new(LineWriter::new(std::io::stderr())), - } - } - - /// Follow on stdout if so specified. - pub fn stdout(follow_on_stdout: bool) -> Self { - if follow_on_stdout { - Self::Stdout - } else { - Self::None - } - } - - /// Follow on stderr if so specified. - pub fn stderr(follow_on_stderr: bool) -> Self { - if follow_on_stderr { - Self::Stderr - } else { - Self::None - } - } -} - -/// The destinations to which a component writes an output stream. -pub struct WriteDestinations { - buffer: Vec, - immediate: Box, -} - -impl WriteDestinations { - /// The memory buffer to which a component writes an output stream. - pub fn buffer(&self) -> &[u8] { - &self.buffer - } -} - -impl Write for WriteDestinations { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - let written = self.buffer.write(buf)?; - self.immediate.write_all(&buf[0..written])?; - Ok(written) - } - - fn flush(&mut self) -> std::io::Result<()> { - self.buffer.flush()?; - self.immediate.flush()?; - Ok(()) - } -} - -struct DiscardingWriter; - -impl Write for DiscardingWriter { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - Ok(buf.len()) - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs deleted file mode 100644 index ffbd803a10..0000000000 --- a/crates/engine/src/lib.rs +++ /dev/null @@ -1,399 +0,0 @@ -//! A Spin execution context for applications. - -#![deny(missing_docs)] - -/// Host components. -pub mod host_component; -/// Input / Output redirects. -pub mod io; - -use std::{collections::HashMap, io::Write, path::PathBuf, sync::Arc}; - -use anyhow::{bail, Context, Result}; -use host_component::{HostComponent, HostComponents, HostComponentsState}; -use io::{FollowComponents, OutputBuffers, RedirectPipes}; -use spin_manifest::{CoreComponent, DirectoryMount, ModuleSource}; -use tokio::{ - task::JoinHandle, - time::{sleep, Duration}, -}; -use tracing::{instrument, log}; -use wasi_common::WasiCtx; -use wasmtime::{Instance, InstancePre, Linker, Module, Store}; -use wasmtime_wasi::{ambient_authority, Dir, WasiCtxBuilder}; - -const SPIN_HOME: &str = ".spin"; - -/// Builder-specific configuration. -#[derive(Clone, Debug, Default)] -pub struct ExecutionContextConfiguration { - /// Component configuration. - pub components: Vec, - /// Label for logging, etc. - pub label: String, - /// Log directory on host. - pub log_dir: Option, - /// Component log following configuration. - pub follow_components: FollowComponents, -} - -/// Top-level runtime context data to be passed to a component. -#[derive(Default)] -pub struct RuntimeContext { - /// WASI context data. - pub wasi: Option, - /// Host components state. - pub host_components_state: HostComponentsState, - /// Generic runtime data that can be configured by specialized engines. - pub data: Option, -} - -/// The engine struct that encapsulate wasmtime engine -#[derive(Clone, Default)] -pub struct Engine(wasmtime::Engine); - -impl Engine { - /// Create a new engine and initialize it with the given config. - pub fn new(mut config: wasmtime::Config) -> Result { - config.async_support(true); - Ok(Self(wasmtime::Engine::new(&config)?)) - } - - /// Get a clone of the internal `wasmtime::Engine`. - /// WARNING: The configuration of this Engine is likely to change in the future, and - /// will not be covered by any future stability guarantees. - pub fn inner(&self) -> wasmtime::Engine { - self.0.clone() - } -} - -/// An execution context builder. -pub struct Builder { - config: ExecutionContextConfiguration, - linker: Linker>, - store: Store>, - engine: Engine, - host_components: HostComponents, -} - -impl Builder { - /// Creates a new instance of the execution builder. - pub fn new(config: ExecutionContextConfiguration) -> Result> { - Self::with_engine(config, Engine::new(Default::default())?) - } - - /// Creates a new instance of the execution builder with the given wasmtime::Config. - pub fn with_engine( - config: ExecutionContextConfiguration, - engine: Engine, - ) -> Result> { - let data = RuntimeContext::default(); - let linker = Linker::new(&engine.0); - let store = Store::new(&engine.0, data); - let host_components = Default::default(); - - Ok(Self { - config, - linker, - store, - engine, - host_components, - }) - } - - /// Returns the current ExecutionContextConfiguration. - pub fn config(&self) -> &ExecutionContextConfiguration { - &self.config - } - - /// Configures the WASI linker imports for the current execution context. - pub fn link_wasi(&mut self) -> Result<&mut Self> { - wasmtime_wasi::add_to_linker(&mut self.linker, |ctx| ctx.wasi.as_mut().unwrap())?; - Ok(self) - } - - /// Adds a HostComponent to the execution context. - pub fn add_host_component( - &mut self, - host_component: impl HostComponent + 'static, - ) -> Result<&mut Self> { - self.host_components - .insert(&mut self.linker, host_component)?; - Ok(self) - } - - /// Builds a new instance of the execution context. - #[instrument(skip(self))] - pub async fn build(mut self) -> Result> { - let _sloth_warning = warn_if_slothful(); - let mut components = HashMap::new(); - for c in &self.config.components { - let core = c.clone(); - let module = match c.source.clone() { - ModuleSource::FileReference(p) => { - let module = Module::from_file(&self.engine.0, &p).with_context(|| { - format!( - "Cannot create module for component {} from file {}", - &c.id, - &p.display() - ) - })?; - log::trace!("Created module for component {} from file {:?}", &c.id, &p); - module - } - ModuleSource::Buffer(bytes, info) => { - let module = - Module::from_binary(&self.engine.0, &bytes).with_context(|| { - format!("Cannot create module for component {} from {}", &c.id, info) - })?; - log::trace!( - "Created module for component {} from {} with size {}", - &c.id, - info, - bytes.len() - ); - module - } - }; - - let pre = Arc::new(self.linker.instantiate_pre(&mut self.store, &module)?); - log::trace!("Created pre-instance from module for component {}.", &c.id); - - components.insert(c.id.clone(), Component { core, pre }); - } - - log::trace!("Execution context initialized."); - - Ok(ExecutionContext { - config: self.config, - engine: self.engine, - components, - host_components: Arc::new(self.host_components), - }) - } - - /// Configures default host interface implementations. - pub fn link_defaults(&mut self) -> Result<&mut Self> { - self.link_wasi() - } - - /// Builds a new default instance of the execution context. - pub async fn build_default( - config: ExecutionContextConfiguration, - ) -> Result> { - let mut builder = Self::new(config)?; - builder.link_defaults()?; - builder.build().await - } -} - -/// Component for the execution context. -#[derive(Clone)] -pub struct Component { - /// Configuration for the component. - pub core: CoreComponent, - /// The pre-instance of the component - pub pre: Arc>>, -} - -/// A generic execution context for WebAssembly components. -#[derive(Clone)] -pub struct ExecutionContext { - /// Top-level runtime configuration. - pub config: ExecutionContextConfiguration, - /// Wasmtime engine. - pub engine: Engine, - /// Collection of pre-initialized (and already linked) components. - pub components: HashMap>, - - host_components: Arc, -} - -impl ExecutionContext { - /// Creates a store for a given component given its configuration and runtime data. - #[instrument(skip(self, data, io))] - pub async fn prepare_component( - &self, - component: &str, - data: Option, - io: Option, - env: Option>, - args: Option>, - ) -> Result<(Store>, Instance)> { - log::trace!("Preparing component {}", component); - let component = match self.components.get(component) { - Some(c) => c, - None => bail!("Cannot find component {}", component), - }; - - let mut store = self.store(component, data, io, env, args)?; - let instance = component.pre.instantiate_async(&mut store).await?; - - Ok((store, instance)) - } - - /// Save logs for a given component in the log directory on the host - pub fn save_output_to_logs( - &self, - ior: impl OutputBuffers, - component: &str, - save_stdout: bool, - save_stderr: bool, - ) -> Result<()> { - let sanitized_label = sanitize(&self.config.label); - let sanitized_component_name = sanitize(&component); - - let log_dir = match &self.config.log_dir { - Some(l) => l.clone(), - None => match dirs::home_dir() { - Some(h) => h.join(SPIN_HOME).join(&sanitized_label).join("logs"), - None => PathBuf::from(&sanitized_label).join("logs"), - }, - }; - - let stdout_filename = log_dir.join(sanitize(format!( - "{}_{}.txt", - sanitized_component_name, "stdout", - ))); - - let stderr_filename = log_dir.join(sanitize(format!( - "{}_{}.txt", - sanitized_component_name, "stderr" - ))); - - std::fs::create_dir_all(&log_dir)?; - - log::trace!("Saving logs to {:?} {:?}", stdout_filename, stderr_filename); - - if save_stdout { - let mut file = std::fs::OpenOptions::new() - .write(true) - .append(true) - .create(true) - .open(stdout_filename)?; - let contents = ior.stdout(); - file.write_all(contents)?; - } - - if save_stderr { - let mut file = std::fs::OpenOptions::new() - .write(true) - .append(true) - .create(true) - .open(stderr_filename)?; - let contents = ior.stderr(); - file.write_all(contents)?; - } - - Ok(()) - } - /// Creates a store for a given component given its configuration and runtime data. - fn store( - &self, - component: &Component, - data: Option, - io: Option, - env: Option>, - args: Option>, - ) -> Result>> { - log::trace!("Creating store."); - let (env, dirs) = Self::wasi_config(component, env)?; - let mut ctx = RuntimeContext::default(); - let mut wasi_ctx = WasiCtxBuilder::new() - .args(&args.unwrap_or_default())? - .envs(&env)?; - match io { - Some(r) => { - wasi_ctx = wasi_ctx.stderr(r.stderr).stdout(r.stdout).stdin(r.stdin); - } - None => wasi_ctx = wasi_ctx.inherit_stdio(), - }; - - for dir in dirs { - let guest = dir.guest; - let host = dir.host; - wasi_ctx = - wasi_ctx.preopened_dir(Dir::open_ambient_dir(host, ambient_authority())?, guest)?; - } - - ctx.host_components_state = self.host_components.build_state(&component.core)?; - - ctx.wasi = Some(wasi_ctx.build()); - ctx.data = data; - - let store = Store::new(&self.engine.0, ctx); - Ok(store) - } - - #[allow(clippy::type_complexity)] - fn wasi_config( - component: &Component, - env: Option>, - ) -> Result<(Vec<(String, String)>, Vec)> { - let mut res = vec![]; - - for (k, v) in &component.core.wasm.environment { - res.push((k.clone(), v.clone())); - } - - // Custom environment variables currently take precedence over component-defined - // environment variables. This might change in the future. - if let Some(envs) = env { - for (k, v) in envs { - res.push((k.clone(), v.clone())); - } - }; - - let dirs = component.core.wasm.mounts.clone(); - - Ok((res, dirs)) - } -} - -fn sanitize(name: impl AsRef) -> String { - // options block copied from sanitize_filename project readme - let options = sanitize_filename::Options { - // true by default, truncates to 255 bytes - truncate: true, - // default value depends on the OS, removes reserved names like `con` from start of strings on Windows - windows: true, - // str to replace sanitized chars/strings - replacement: "", - }; - - // filename logic defined in the project works for directory names as well - // refer to: https://github.com/kardeiz/sanitize-filename/blob/f5158746946ed81015c3a33078dedf164686da19/src/lib.rs#L76-L165 - sanitize_filename::sanitize_with_options(name, options) -} - -const SLOTH_WARNING_DELAY_MILLIS: u64 = 1250; - -struct SlothWarning { - warning: JoinHandle, -} - -impl Drop for SlothWarning { - fn drop(&mut self) { - self.warning.abort() - } -} - -fn warn_if_slothful() -> SlothWarning<()> { - let warning = tokio::spawn(warn_slow()); - SlothWarning { warning } -} - -#[cfg(debug_assertions)] -async fn warn_slow() { - sleep(Duration::from_millis(SLOTH_WARNING_DELAY_MILLIS)).await; - println!("This is a debug build - preparing Wasm modules might take a few seconds"); - println!("If you're experiencing long startup times please switch to the release build"); - println!(); -} - -#[cfg(not(debug_assertions))] -async fn warn_slow() { - sleep(Duration::from_millis(SLOTH_WARNING_DELAY_MILLIS)).await; - println!("Preparing Wasm modules is taking a few seconds..."); - println!(); -} diff --git a/docs/content/architecture.md b/docs/content/architecture.md index 77def7383d..bcc24f0dd0 100644 --- a/docs/content/architecture.md +++ b/docs/content/architecture.md @@ -34,30 +34,6 @@ application configuration ([#40](https://github.com/fermyon/spin/issues/40) explores a trigger handling multiple applications), starts an HTTP listener, and for each new request, it routes it to the component configured in the application configuration. Then, it instantiates the WebAssembly module (using a -`spin_engine::ExecutionContext`) and uses the appropriate executor (either the +`spin_core::Engine`) and uses the appropriate executor (either the `SpinHttpExecutor` or the `WagiHttpExecutor`, based on the component configuration) to handle the request and return the response. - -## The Spin execution context - -The Spin execution context (or "Spin engine") is the part of Spin that executes -WebAssembly components using the -[Wasmtime](https://github.com/bytecodealliance/wasmtime) WebAssembly runtime. It -is implemented in the `spin-engine` crate, and serves as -the part of Spin that takes a fully formed application configuration and creates -Wasm instances based on the component configurations. - -There are two important concepts in this crate: - -- `spin_engine::Builder` — the builder for creating an execution context. It is - created using an `ExecutionContextConfiguration` object (which contains a Spin - application and Wasmtime configuration), and implements the logic for - configuring WASI and the other host implementations provided by Spin. The - builder exposes the Wasmtime - [`Linker`](https://docs.rs/wasmtime/latest/wasmtime/struct.Linker.html), - [`Engine`](https://docs.rs/wasmtime/latest/wasmtime/struct.Engine.html), and - [`Store>`](https://docs.rs/wasmtime/latest/wasmtime/struct.Store.html) - (where `RuntimeContext` is the internal Spin context, which is detailed - later in the document), and it uses them to [pre-instantiate]() - -- `spin_engine::ExecutionContext` — the main execution engine in Spin. diff --git a/tests/integration.rs b/tests/integration.rs index e8e0450f51..d9df4284de 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -680,7 +680,7 @@ mod integration_tests { .args(args) .env( "RUST_LOG", - "spin=trace,spin_loader=trace,spin_engine=trace,spin_http=trace", + "spin=trace,spin_loader=trace,spin_core=trace,spin_http=trace", ) .spawn() .with_context(|| "executing Spin")?; @@ -717,7 +717,7 @@ mod integration_tests { .args(args) .env( "RUST_LOG", - "spin=trace,spin_loader=trace,spin_engine=trace,spin_http=trace", + "spin=trace,spin_loader=trace,spin_core=trace,spin_http=trace", ) .spawn() .with_context(|| "executing Spin")?;