Skip to content

Commit f203ef5

Browse files
committed
Refactor TriggerExecutor to make InstancePre an associated type
Moves the `EitherInstance*` logic into just the `spin-trigger-http` crate where the redis trigger, for example, only has to deal with components. The intention of this is to open up future customization of instances within a trigger that don't necessarily need to affect all other triggers. The new associated type is bound itself by a new trait `TriggerInstancePre` which encapsulates the required functionality. This trait has a default implementation for `spin_core::InstancePre<T>` which serves for triggers that only work with components. Signed-off-by: Alex Crichton <[email protected]>
1 parent 17515f7 commit f203ef5

File tree

8 files changed

+96
-57
lines changed

8 files changed

+96
-57
lines changed

crates/trigger-http/src/handler.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{net::SocketAddr, str, str::FromStr};
22

3-
use crate::{Body, HttpExecutor, HttpTrigger, Store};
3+
use crate::{Body, HttpExecutor, HttpInstance, HttpTrigger, Store};
44
use anyhow::bail;
55
use anyhow::{anyhow, Context, Result};
66
use futures::TryFutureExt;
@@ -13,7 +13,7 @@ use spin_core::wasi_2023_10_18::exports::wasi::http::incoming_handler::Guest as
1313
use spin_core::wasi_2023_11_10::exports::wasi::http::incoming_handler::Guest as IncomingHandler2023_11_10;
1414
use spin_core::Instance;
1515
use spin_http::body;
16-
use spin_trigger::{EitherInstance, TriggerAppEngine};
16+
use spin_trigger::TriggerAppEngine;
1717
use spin_world::v1::http_types;
1818
use std::sync::Arc;
1919
use tokio::{sync::oneshot, task};
@@ -39,7 +39,7 @@ impl HttpExecutor for HttpHandlerExecutor {
3939
);
4040

4141
let (instance, mut store) = engine.prepare_instance(component_id).await?;
42-
let EitherInstance::Component(instance) = instance else {
42+
let HttpInstance::Component(instance) = instance else {
4343
unreachable!()
4444
};
4545

crates/trigger-http/src/lib.rs

+34-6
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use spin_http::{
3232
routes::{RoutePattern, Router},
3333
};
3434
use spin_outbound_networking::{AllowedHostsConfig, OutboundUrl};
35-
use spin_trigger::{EitherInstancePre, TriggerAppEngine, TriggerExecutor};
35+
use spin_trigger::{TriggerAppEngine, TriggerExecutor, TriggerInstancePre};
3636
use tokio::{
3737
io::{AsyncRead, AsyncWrite},
3838
net::TcpListener,
@@ -86,12 +86,23 @@ impl CliArgs {
8686
}
8787
}
8888

89+
pub enum HttpInstancePre {
90+
Component(spin_core::InstancePre<RuntimeData>),
91+
Module(spin_core::ModuleInstancePre<RuntimeData>),
92+
}
93+
94+
pub enum HttpInstance {
95+
Component(spin_core::Instance),
96+
Module(spin_core::ModuleInstance),
97+
}
98+
8999
#[async_trait]
90100
impl TriggerExecutor for HttpTrigger {
91101
const TRIGGER_TYPE: &'static str = "http";
92102
type RuntimeData = RuntimeData;
93103
type TriggerConfig = HttpTriggerConfig;
94104
type RunConfig = CliArgs;
105+
type InstancePre = HttpInstancePre;
95106

96107
async fn new(engine: TriggerAppEngine<Self>) -> Result<Self> {
97108
let mut base = engine
@@ -167,20 +178,37 @@ impl TriggerExecutor for HttpTrigger {
167178
};
168179
Ok(())
169180
}
181+
}
182+
183+
#[async_trait]
184+
impl TriggerInstancePre<RuntimeData, HttpTriggerConfig> for HttpInstancePre {
185+
type Instance = HttpInstance;
170186

171187
async fn instantiate_pre(
172-
engine: &Engine<Self::RuntimeData>,
188+
engine: &Engine<RuntimeData>,
173189
component: &AppComponent,
174-
config: &Self::TriggerConfig,
175-
) -> Result<EitherInstancePre<Self::RuntimeData>> {
190+
config: &HttpTriggerConfig,
191+
) -> Result<HttpInstancePre> {
176192
if let Some(HttpExecutorType::Wagi(_)) = &config.executor {
177193
let module = component.load_module(engine).await?;
178-
Ok(EitherInstancePre::Module(
194+
Ok(HttpInstancePre::Module(
179195
engine.module_instantiate_pre(&module)?,
180196
))
181197
} else {
182198
let comp = component.load_component(engine).await?;
183-
Ok(EitherInstancePre::Component(engine.instantiate_pre(&comp)?))
199+
Ok(HttpInstancePre::Component(engine.instantiate_pre(&comp)?))
200+
}
201+
}
202+
203+
async fn instantiate(&self, store: &mut Store) -> Result<HttpInstance> {
204+
match self {
205+
HttpInstancePre::Component(pre) => pre
206+
.instantiate_async(store)
207+
.await
208+
.map(HttpInstance::Component),
209+
HttpInstancePre::Module(pre) => {
210+
pre.instantiate_async(store).await.map(HttpInstance::Module)
211+
}
184212
}
185213
}
186214
}

crates/trigger-http/src/wagi.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use std::{io::Cursor, net::SocketAddr};
22

3+
use crate::HttpInstance;
34
use anyhow::{anyhow, ensure, Context, Result};
45
use async_trait::async_trait;
56
use http_body_util::BodyExt;
67
use hyper::{Request, Response};
78
use spin_core::WasiVersion;
89
use spin_http::{config::WagiTriggerConfig, routes::RoutePattern, wagi};
9-
use spin_trigger::{EitherInstance, TriggerAppEngine};
10+
use spin_trigger::TriggerAppEngine;
1011
use wasi_common_preview1::{pipe::WritePipe, I32Exit};
1112

1213
use crate::{Body, HttpExecutor, HttpTrigger};
@@ -93,7 +94,7 @@ impl HttpExecutor for WagiHttpExecutor {
9394
.prepare_instance_with_store(component, store_builder)
9495
.await?;
9596

96-
let EitherInstance::Module(instance) = instance else {
97+
let HttpInstance::Module(instance) = instance else {
9798
unreachable!()
9899
};
99100

crates/trigger-redis/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use futures::{future::join_all, StreamExt};
77
use redis::{Client, ConnectionLike};
88
use serde::{de::IgnoredAny, Deserialize, Serialize};
99
use spin_common::url::remove_credentials;
10-
use spin_core::async_trait;
10+
use spin_core::{async_trait, InstancePre};
1111
use spin_trigger::{cli::NoArgs, TriggerAppEngine, TriggerExecutor};
1212
use std::collections::HashMap;
1313
use std::sync::Arc;
@@ -53,6 +53,7 @@ impl TriggerExecutor for RedisTrigger {
5353
type RuntimeData = RuntimeData;
5454
type TriggerConfig = RedisTriggerConfig;
5555
type RunConfig = NoArgs;
56+
type InstancePre = InstancePre<RuntimeData>;
5657

5758
async fn new(engine: TriggerAppEngine<Self>) -> Result<Self> {
5859
let default_address: String = engine

crates/trigger-redis/src/spin.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use anyhow::{anyhow, Result};
22
use async_trait::async_trait;
33
use spin_core::Instance;
4-
use spin_trigger::{EitherInstance, TriggerAppEngine};
4+
use spin_trigger::TriggerAppEngine;
55
use spin_world::v1::redis_types::{Error, Payload};
66

77
use crate::{RedisExecutor, RedisTrigger, Store};
@@ -21,9 +21,6 @@ impl RedisExecutor for SpinRedisExecutor {
2121
tracing::trace!("Executing request using the Spin executor for component {component_id}");
2222

2323
let (instance, store) = engine.prepare_instance(component_id).await?;
24-
let EitherInstance::Component(instance) = instance else {
25-
unreachable!()
26-
};
2724

2825
match Self::execute_impl(store, instance, channel, payload.to_vec()).await {
2926
Ok(()) => {

crates/trigger/src/cli.rs

+1
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ pub mod help {
291291
type RuntimeData = ();
292292
type TriggerConfig = ();
293293
type RunConfig = NoArgs;
294+
type InstancePre = spin_core::InstancePre<()>;
294295
async fn new(_: crate::TriggerAppEngine<Self>) -> Result<Self> {
295296
Ok(Self)
296297
}

crates/trigger/src/lib.rs

+48-36
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,19 @@ use serde::de::DeserializeOwned;
1313

1414
use spin_app::{App, AppComponent, AppLoader, AppTrigger, Loader, OwnedApp, APP_NAME_KEY};
1515
use spin_core::{
16-
Config, Engine, EngineBuilder, Instance, InstancePre, ModuleInstance, ModuleInstancePre,
17-
OutboundWasiHttpHandler, Store, StoreBuilder, WasiVersion,
16+
Config, Engine, EngineBuilder, Instance, InstancePre, OutboundWasiHttpHandler, Store,
17+
StoreBuilder, WasiVersion,
1818
};
1919

2020
pub use crate::runtime_config::RuntimeConfig;
2121

22-
pub enum EitherInstancePre<T> {
23-
Component(InstancePre<T>),
24-
Module(ModuleInstancePre<T>),
25-
}
26-
27-
pub enum EitherInstance {
28-
Component(Instance),
29-
Module(ModuleInstance),
30-
}
31-
3222
#[async_trait]
3323
pub trait TriggerExecutor: Sized + Send + Sync {
3424
const TRIGGER_TYPE: &'static str;
3525
type RuntimeData: OutboundWasiHttpHandler + Default + Send + Sync + 'static;
3626
type TriggerConfig;
3727
type RunConfig;
28+
type InstancePre: TriggerInstancePre<Self::RuntimeData, Self::TriggerConfig>;
3829

3930
/// Create a new trigger executor.
4031
async fn new(engine: TriggerAppEngine<Self>) -> Result<Self>;
@@ -46,18 +37,50 @@ pub trait TriggerExecutor: Sized + Send + Sync {
4637
fn configure_engine(_builder: &mut EngineBuilder<Self::RuntimeData>) -> Result<()> {
4738
Ok(())
4839
}
40+
}
41+
42+
/// Helper type alias to project the `Instance` of a given `TriggerExecutor`.
43+
pub type ExecutorInstance<T> = <<T as TriggerExecutor>::InstancePre as TriggerInstancePre<
44+
<T as TriggerExecutor>::RuntimeData,
45+
<T as TriggerExecutor>::TriggerConfig,
46+
>>::Instance;
47+
48+
#[async_trait]
49+
pub trait TriggerInstancePre<T, C>: Sized + Send + Sync
50+
where
51+
T: OutboundWasiHttpHandler + Send + Sync,
52+
{
53+
type Instance;
4954

5055
async fn instantiate_pre(
51-
engine: &Engine<Self::RuntimeData>,
56+
engine: &Engine<T>,
5257
component: &AppComponent,
53-
_config: &Self::TriggerConfig,
54-
) -> Result<EitherInstancePre<Self::RuntimeData>> {
58+
config: &C,
59+
) -> Result<Self>;
60+
61+
async fn instantiate(&self, store: &mut Store<T>) -> Result<Self::Instance>;
62+
}
63+
64+
#[async_trait]
65+
impl<T, C> TriggerInstancePre<T, C> for InstancePre<T>
66+
where
67+
T: OutboundWasiHttpHandler + Send + Sync,
68+
{
69+
type Instance = Instance;
70+
71+
async fn instantiate_pre(
72+
engine: &Engine<T>,
73+
component: &AppComponent,
74+
_config: &C,
75+
) -> Result<Self> {
5576
let comp = component.load_component(engine).await?;
56-
Ok(EitherInstancePre::Component(
57-
engine
58-
.instantiate_pre(&comp)
59-
.with_context(|| format!("Failed to instantiate component '{}'", component.id()))?,
60-
))
77+
Ok(engine
78+
.instantiate_pre(&comp)
79+
.with_context(|| format!("Failed to instantiate component '{}'", component.id()))?)
80+
}
81+
82+
async fn instantiate(&self, store: &mut Store<T>) -> Result<Self::Instance> {
83+
self.instantiate_async(store).await
6184
}
6285
}
6386

@@ -246,7 +269,7 @@ pub struct TriggerAppEngine<Executor: TriggerExecutor> {
246269
// Trigger configs for this trigger type, with order matching `app.triggers_with_type(Executor::TRIGGER_TYPE)`
247270
trigger_configs: Vec<Executor::TriggerConfig>,
248271
// Map of {Component ID -> InstancePre} for each component.
249-
component_instance_pres: HashMap<String, EitherInstancePre<Executor::RuntimeData>>,
272+
component_instance_pres: HashMap<String, Executor::InstancePre>,
250273
// Resolver for value template expressions
251274
resolver: std::sync::Arc<spin_expressions::PreparedResolver>,
252275
}
@@ -290,7 +313,7 @@ impl<Executor: TriggerExecutor> TriggerAppEngine<Executor> {
290313
if let Some(config) = trigger_config {
291314
component_instance_pres.insert(
292315
id.to_owned(),
293-
Executor::instantiate_pre(&engine, &component, config)
316+
Executor::InstancePre::instantiate_pre(&engine, &component, config)
294317
.await
295318
.with_context(|| format!("Failed to instantiate component '{id}'"))?,
296319
);
@@ -348,7 +371,7 @@ impl<Executor: TriggerExecutor> TriggerAppEngine<Executor> {
348371
pub async fn prepare_instance(
349372
&self,
350373
component_id: &str,
351-
) -> Result<(EitherInstance, Store<Executor::RuntimeData>)> {
374+
) -> Result<(ExecutorInstance<Executor>, Store<Executor::RuntimeData>)> {
352375
let store_builder = self.store_builder(component_id, WasiVersion::Preview2)?;
353376
self.prepare_instance_with_store(component_id, store_builder)
354377
.await
@@ -359,7 +382,7 @@ impl<Executor: TriggerExecutor> TriggerAppEngine<Executor> {
359382
&self,
360383
component_id: &str,
361384
mut store_builder: StoreBuilder,
362-
) -> Result<(EitherInstance, Store<Executor::RuntimeData>)> {
385+
) -> Result<(ExecutorInstance<Executor>, Store<Executor::RuntimeData>)> {
363386
let component = self.get_component(component_id)?;
364387

365388
// Build Store
@@ -372,18 +395,7 @@ impl<Executor: TriggerExecutor> TriggerAppEngine<Executor> {
372395
.get(component_id)
373396
.expect("component_instance_pres missing valid component_id");
374397

375-
let instance = match pre {
376-
EitherInstancePre::Component(pre) => pre
377-
.instantiate_async(&mut store)
378-
.await
379-
.map(EitherInstance::Component),
380-
381-
EitherInstancePre::Module(pre) => pre
382-
.instantiate_async(&mut store)
383-
.await
384-
.map(EitherInstance::Module),
385-
}
386-
.with_context(|| {
398+
let instance = pre.instantiate(&mut store).await.with_context(|| {
387399
format!(
388400
"app {:?} component {:?} instantiation failed",
389401
self.app_name, component_id

examples/spin-timer/src/lib.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use std::collections::HashMap;
33
use clap::Args;
44
use serde::{Deserialize, Serialize};
55
use spin_app::MetadataKey;
6-
use spin_core::async_trait;
7-
use spin_trigger::{EitherInstance, TriggerAppEngine, TriggerExecutor};
6+
use spin_core::{async_trait, InstancePre};
7+
use spin_trigger::{TriggerAppEngine, TriggerExecutor};
88

99
wasmtime::component::bindgen!({
1010
path: ".",
@@ -62,6 +62,8 @@ impl TriggerExecutor for TimerTrigger {
6262

6363
type RunConfig = CliArgs;
6464

65+
type InstancePre = InstancePre<RuntimeData>;
66+
6567
async fn new(engine: spin_trigger::TriggerAppEngine<Self>) -> anyhow::Result<Self> {
6668
let speedup = engine
6769
.app()
@@ -119,9 +121,6 @@ impl TimerTrigger {
119121
async fn handle_timer_event(&self, component_id: &str) -> anyhow::Result<()> {
120122
// Load the guest...
121123
let (instance, mut store) = self.engine.prepare_instance(component_id).await?;
122-
let EitherInstance::Component(instance) = instance else {
123-
unreachable!()
124-
};
125124
let instance = SpinTimer::new(&mut store, &instance)?;
126125
// ...and call the entry point
127126
instance.call_handle_timer_request(&mut store).await

0 commit comments

Comments
 (0)