diff --git a/crates/trigger-http/src/handler.rs b/crates/trigger-http/src/handler.rs index 516a4f893b..2fbdd95f83 100644 --- a/crates/trigger-http/src/handler.rs +++ b/crates/trigger-http/src/handler.rs @@ -1,6 +1,6 @@ use std::{net::SocketAddr, str, str::FromStr}; -use crate::{Body, HttpExecutor, HttpTrigger, Store}; +use crate::{Body, HttpExecutor, HttpInstance, HttpTrigger, Store}; use anyhow::bail; use anyhow::{anyhow, Context, Result}; use futures::TryFutureExt; @@ -13,7 +13,7 @@ use spin_core::wasi_2023_10_18::exports::wasi::http::incoming_handler::Guest as use spin_core::wasi_2023_11_10::exports::wasi::http::incoming_handler::Guest as IncomingHandler2023_11_10; use spin_core::Instance; use spin_http::body; -use spin_trigger::{EitherInstance, TriggerAppEngine}; +use spin_trigger::TriggerAppEngine; use spin_world::v1::http_types; use std::sync::Arc; use tokio::{sync::oneshot, task}; @@ -39,7 +39,7 @@ impl HttpExecutor for HttpHandlerExecutor { ); let (instance, mut store) = engine.prepare_instance(component_id).await?; - let EitherInstance::Component(instance) = instance else { + let HttpInstance::Component(instance) = instance else { unreachable!() }; diff --git a/crates/trigger-http/src/lib.rs b/crates/trigger-http/src/lib.rs index 590daf4066..6f6a33e52a 100644 --- a/crates/trigger-http/src/lib.rs +++ b/crates/trigger-http/src/lib.rs @@ -32,7 +32,7 @@ use spin_http::{ routes::{RoutePattern, Router}, }; use spin_outbound_networking::{AllowedHostsConfig, OutboundUrl}; -use spin_trigger::{EitherInstancePre, TriggerAppEngine, TriggerExecutor}; +use spin_trigger::{TriggerAppEngine, TriggerExecutor, TriggerInstancePre}; use tokio::{ io::{AsyncRead, AsyncWrite}, net::TcpListener, @@ -86,12 +86,23 @@ impl CliArgs { } } +pub enum HttpInstancePre { + Component(spin_core::InstancePre), + Module(spin_core::ModuleInstancePre), +} + +pub enum HttpInstance { + Component(spin_core::Instance), + Module(spin_core::ModuleInstance), +} + #[async_trait] impl TriggerExecutor for HttpTrigger { const TRIGGER_TYPE: &'static str = "http"; type RuntimeData = RuntimeData; type TriggerConfig = HttpTriggerConfig; type RunConfig = CliArgs; + type InstancePre = HttpInstancePre; async fn new(engine: TriggerAppEngine) -> Result { let mut base = engine @@ -167,20 +178,37 @@ impl TriggerExecutor for HttpTrigger { }; Ok(()) } +} + +#[async_trait] +impl TriggerInstancePre for HttpInstancePre { + type Instance = HttpInstance; async fn instantiate_pre( - engine: &Engine, + engine: &Engine, component: &AppComponent, - config: &Self::TriggerConfig, - ) -> Result> { + config: &HttpTriggerConfig, + ) -> Result { if let Some(HttpExecutorType::Wagi(_)) = &config.executor { let module = component.load_module(engine).await?; - Ok(EitherInstancePre::Module( + Ok(HttpInstancePre::Module( engine.module_instantiate_pre(&module)?, )) } else { let comp = component.load_component(engine).await?; - Ok(EitherInstancePre::Component(engine.instantiate_pre(&comp)?)) + Ok(HttpInstancePre::Component(engine.instantiate_pre(&comp)?)) + } + } + + async fn instantiate(&self, store: &mut Store) -> Result { + match self { + HttpInstancePre::Component(pre) => pre + .instantiate_async(store) + .await + .map(HttpInstance::Component), + HttpInstancePre::Module(pre) => { + pre.instantiate_async(store).await.map(HttpInstance::Module) + } } } } diff --git a/crates/trigger-http/src/wagi.rs b/crates/trigger-http/src/wagi.rs index f92763bac3..eccde970f2 100644 --- a/crates/trigger-http/src/wagi.rs +++ b/crates/trigger-http/src/wagi.rs @@ -1,12 +1,13 @@ use std::{io::Cursor, net::SocketAddr}; +use crate::HttpInstance; use anyhow::{anyhow, ensure, Context, Result}; use async_trait::async_trait; use http_body_util::BodyExt; use hyper::{Request, Response}; use spin_core::WasiVersion; use spin_http::{config::WagiTriggerConfig, routes::RoutePattern, wagi}; -use spin_trigger::{EitherInstance, TriggerAppEngine}; +use spin_trigger::TriggerAppEngine; use wasi_common_preview1::{pipe::WritePipe, I32Exit}; use crate::{Body, HttpExecutor, HttpTrigger}; @@ -93,7 +94,7 @@ impl HttpExecutor for WagiHttpExecutor { .prepare_instance_with_store(component, store_builder) .await?; - let EitherInstance::Module(instance) = instance else { + let HttpInstance::Module(instance) = instance else { unreachable!() }; diff --git a/crates/trigger-redis/src/lib.rs b/crates/trigger-redis/src/lib.rs index 9c3f49b4d7..30e5f6db1f 100644 --- a/crates/trigger-redis/src/lib.rs +++ b/crates/trigger-redis/src/lib.rs @@ -7,7 +7,7 @@ use futures::{future::join_all, StreamExt}; use redis::{Client, ConnectionLike}; use serde::{de::IgnoredAny, Deserialize, Serialize}; use spin_common::url::remove_credentials; -use spin_core::async_trait; +use spin_core::{async_trait, InstancePre}; use spin_trigger::{cli::NoArgs, TriggerAppEngine, TriggerExecutor}; use std::collections::HashMap; use std::sync::Arc; @@ -53,6 +53,7 @@ impl TriggerExecutor for RedisTrigger { type RuntimeData = RuntimeData; type TriggerConfig = RedisTriggerConfig; type RunConfig = NoArgs; + type InstancePre = InstancePre; async fn new(engine: TriggerAppEngine) -> Result { let default_address: String = engine diff --git a/crates/trigger-redis/src/spin.rs b/crates/trigger-redis/src/spin.rs index 70270dd301..38045ac8c6 100644 --- a/crates/trigger-redis/src/spin.rs +++ b/crates/trigger-redis/src/spin.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use spin_core::Instance; -use spin_trigger::{EitherInstance, TriggerAppEngine}; +use spin_trigger::TriggerAppEngine; use spin_world::v1::redis_types::{Error, Payload}; use crate::{RedisExecutor, RedisTrigger, Store}; @@ -21,9 +21,6 @@ impl RedisExecutor for SpinRedisExecutor { tracing::trace!("Executing request using the Spin executor for component {component_id}"); let (instance, store) = engine.prepare_instance(component_id).await?; - let EitherInstance::Component(instance) = instance else { - unreachable!() - }; match Self::execute_impl(store, instance, channel, payload.to_vec()).await { Ok(()) => { diff --git a/crates/trigger/src/cli.rs b/crates/trigger/src/cli.rs index 499e9542ad..727dba8efc 100644 --- a/crates/trigger/src/cli.rs +++ b/crates/trigger/src/cli.rs @@ -291,6 +291,7 @@ pub mod help { type RuntimeData = (); type TriggerConfig = (); type RunConfig = NoArgs; + type InstancePre = spin_core::InstancePre; async fn new(_: crate::TriggerAppEngine) -> Result { Ok(Self) } diff --git a/crates/trigger/src/lib.rs b/crates/trigger/src/lib.rs index 604fdd6a51..aeefe638e1 100644 --- a/crates/trigger/src/lib.rs +++ b/crates/trigger/src/lib.rs @@ -13,28 +13,19 @@ use serde::de::DeserializeOwned; use spin_app::{App, AppComponent, AppLoader, AppTrigger, Loader, OwnedApp, APP_NAME_KEY}; use spin_core::{ - Config, Engine, EngineBuilder, Instance, InstancePre, ModuleInstance, ModuleInstancePre, - OutboundWasiHttpHandler, Store, StoreBuilder, WasiVersion, + Config, Engine, EngineBuilder, Instance, InstancePre, OutboundWasiHttpHandler, Store, + StoreBuilder, WasiVersion, }; pub use crate::runtime_config::RuntimeConfig; -pub enum EitherInstancePre { - Component(InstancePre), - Module(ModuleInstancePre), -} - -pub enum EitherInstance { - Component(Instance), - Module(ModuleInstance), -} - #[async_trait] pub trait TriggerExecutor: Sized + Send + Sync { const TRIGGER_TYPE: &'static str; type RuntimeData: OutboundWasiHttpHandler + Default + Send + Sync + 'static; type TriggerConfig; type RunConfig; + type InstancePre: TriggerInstancePre; /// Create a new trigger executor. async fn new(engine: TriggerAppEngine) -> Result; @@ -46,18 +37,50 @@ pub trait TriggerExecutor: Sized + Send + Sync { fn configure_engine(_builder: &mut EngineBuilder) -> Result<()> { Ok(()) } +} + +/// Helper type alias to project the `Instance` of a given `TriggerExecutor`. +pub type ExecutorInstance = <::InstancePre as TriggerInstancePre< + ::RuntimeData, + ::TriggerConfig, +>>::Instance; + +#[async_trait] +pub trait TriggerInstancePre: Sized + Send + Sync +where + T: OutboundWasiHttpHandler + Send + Sync, +{ + type Instance; async fn instantiate_pre( - engine: &Engine, + engine: &Engine, component: &AppComponent, - _config: &Self::TriggerConfig, - ) -> Result> { + config: &C, + ) -> Result; + + async fn instantiate(&self, store: &mut Store) -> Result; +} + +#[async_trait] +impl TriggerInstancePre for InstancePre +where + T: OutboundWasiHttpHandler + Send + Sync, +{ + type Instance = Instance; + + async fn instantiate_pre( + engine: &Engine, + component: &AppComponent, + _config: &C, + ) -> Result { let comp = component.load_component(engine).await?; - Ok(EitherInstancePre::Component( - engine - .instantiate_pre(&comp) - .with_context(|| format!("Failed to instantiate component '{}'", component.id()))?, - )) + Ok(engine + .instantiate_pre(&comp) + .with_context(|| format!("Failed to instantiate component '{}'", component.id()))?) + } + + async fn instantiate(&self, store: &mut Store) -> Result { + self.instantiate_async(store).await } } @@ -246,7 +269,7 @@ pub struct TriggerAppEngine { // Trigger configs for this trigger type, with order matching `app.triggers_with_type(Executor::TRIGGER_TYPE)` trigger_configs: Vec, // Map of {Component ID -> InstancePre} for each component. - component_instance_pres: HashMap>, + component_instance_pres: HashMap, // Resolver for value template expressions resolver: std::sync::Arc, } @@ -290,7 +313,7 @@ impl TriggerAppEngine { if let Some(config) = trigger_config { component_instance_pres.insert( id.to_owned(), - Executor::instantiate_pre(&engine, &component, config) + Executor::InstancePre::instantiate_pre(&engine, &component, config) .await .with_context(|| format!("Failed to instantiate component '{id}'"))?, ); @@ -348,7 +371,7 @@ impl TriggerAppEngine { pub async fn prepare_instance( &self, component_id: &str, - ) -> Result<(EitherInstance, Store)> { + ) -> Result<(ExecutorInstance, Store)> { let store_builder = self.store_builder(component_id, WasiVersion::Preview2)?; self.prepare_instance_with_store(component_id, store_builder) .await @@ -359,7 +382,7 @@ impl TriggerAppEngine { &self, component_id: &str, mut store_builder: StoreBuilder, - ) -> Result<(EitherInstance, Store)> { + ) -> Result<(ExecutorInstance, Store)> { let component = self.get_component(component_id)?; // Build Store @@ -372,18 +395,7 @@ impl TriggerAppEngine { .get(component_id) .expect("component_instance_pres missing valid component_id"); - let instance = match pre { - EitherInstancePre::Component(pre) => pre - .instantiate_async(&mut store) - .await - .map(EitherInstance::Component), - - EitherInstancePre::Module(pre) => pre - .instantiate_async(&mut store) - .await - .map(EitherInstance::Module), - } - .with_context(|| { + let instance = pre.instantiate(&mut store).await.with_context(|| { format!( "app {:?} component {:?} instantiation failed", self.app_name, component_id diff --git a/examples/spin-timer/src/lib.rs b/examples/spin-timer/src/lib.rs index b7750d0ab0..a9e9be54e3 100644 --- a/examples/spin-timer/src/lib.rs +++ b/examples/spin-timer/src/lib.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; use clap::Args; use serde::{Deserialize, Serialize}; use spin_app::MetadataKey; -use spin_core::async_trait; -use spin_trigger::{EitherInstance, TriggerAppEngine, TriggerExecutor}; +use spin_core::{async_trait, InstancePre}; +use spin_trigger::{TriggerAppEngine, TriggerExecutor}; wasmtime::component::bindgen!({ path: ".", @@ -62,6 +62,8 @@ impl TriggerExecutor for TimerTrigger { type RunConfig = CliArgs; + type InstancePre = InstancePre; + async fn new(engine: spin_trigger::TriggerAppEngine) -> anyhow::Result { let speedup = engine .app() @@ -119,9 +121,6 @@ impl TimerTrigger { async fn handle_timer_event(&self, component_id: &str) -> anyhow::Result<()> { // Load the guest... let (instance, mut store) = self.engine.prepare_instance(component_id).await?; - let EitherInstance::Component(instance) = instance else { - unreachable!() - }; let instance = SpinTimer::new(&mut store, &instance)?; // ...and call the entry point instance.call_handle_timer_request(&mut store).await