Skip to content

Commit ebfa5eb

Browse files
committed
HTTP middleware
Signed-off-by: itowlson <ivan.towlson@fermyon.com>
1 parent 4b7e2c8 commit ebfa5eb

35 files changed

Lines changed: 1877 additions & 118 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/compose/src/lib.rs

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,17 @@ pub use spin_capabilities::InheritConfiguration;
2929
/// dependent component. Finally, the composer will export all exports from the
3030
/// dependent component to its dependents. The composer will then encode the
3131
/// composition graph into a byte array and return it.
32-
pub async fn compose<L: ComponentSourceLoader>(
32+
pub async fn compose<
33+
L: ComponentSourceLoader,
34+
Fut: std::future::Future<Output = Result<Vec<u8>, ComposeError>>,
35+
>(
3336
loader: &L,
3437
component: &L::Component,
38+
apply_trigger_deps: impl Fn(Vec<u8>) -> Fut,
3539
) -> Result<Vec<u8>, ComposeError> {
36-
Composer::new(loader).compose(component).await
40+
Composer::new(loader)
41+
.compose(component, apply_trigger_deps)
42+
.await
3743
}
3844

3945
/// A Spin component dependency. This abstracts over the metadata associated with the
@@ -90,8 +96,10 @@ impl DependencyLike for spin_app::locked::LockedComponentDependency {
9096
pub trait ComponentSourceLoader {
9197
type Component: ComponentLike<Dependency = Self::Dependency>;
9298
type Dependency: DependencyLike;
99+
type Source;
93100
async fn load_component_source(&self, source: &Self::Component) -> anyhow::Result<Vec<u8>>;
94101
async fn load_dependency_source(&self, source: &Self::Dependency) -> anyhow::Result<Vec<u8>>;
102+
async fn load_source(&self, source: &Self::Source) -> anyhow::Result<Vec<u8>>;
95103
}
96104

97105
/// A ComponentSourceLoader that loads component sources from the filesystem.
@@ -101,6 +109,7 @@ pub struct ComponentSourceLoaderFs;
101109
impl ComponentSourceLoader for ComponentSourceLoaderFs {
102110
type Component = spin_app::locked::LockedComponent;
103111
type Dependency = spin_app::locked::LockedComponentDependency;
112+
type Source = spin_app::locked::LockedComponentSource;
104113

105114
async fn load_component_source(&self, source: &Self::Component) -> anyhow::Result<Vec<u8>> {
106115
Self::load_from_locked_source(&source.source).await
@@ -109,6 +118,10 @@ impl ComponentSourceLoader for ComponentSourceLoaderFs {
109118
async fn load_dependency_source(&self, source: &Self::Dependency) -> anyhow::Result<Vec<u8>> {
110119
Self::load_from_locked_source(&source.source).await
111120
}
121+
122+
async fn load_source(&self, source: &Self::Source) -> anyhow::Result<Vec<u8>> {
123+
Self::load_from_locked_source(source).await
124+
}
112125
}
113126

114127
impl ComponentSourceLoaderFs {
@@ -195,39 +208,47 @@ struct Composer<'a, L> {
195208
}
196209

197210
impl<'a, L: ComponentSourceLoader> Composer<'a, L> {
198-
async fn compose(mut self, component: &L::Component) -> Result<Vec<u8>, ComposeError> {
211+
async fn compose<Fut: std::future::Future<Output = Result<Vec<u8>, ComposeError>>>(
212+
mut self,
213+
component: &L::Component,
214+
apply_trigger_deps: impl Fn(Vec<u8>) -> Fut,
215+
) -> Result<Vec<u8>, ComposeError> {
199216
let source = self
200217
.loader
201218
.load_component_source(component)
202219
.await
203220
.map_err(ComposeError::PrepareError)?;
204221

205-
if component.dependencies().len() == 0 {
206-
return Ok(source);
207-
}
222+
let fulfilled_source = if component.dependencies().len() == 0 {
223+
source
224+
} else {
225+
let (world_id, instantiation_id) = self
226+
.register_package(component.id(), None, source)
227+
.map_err(ComposeError::PrepareError)?;
208228

209-
let (world_id, instantiation_id) = self
210-
.register_package(component.id(), None, source)
211-
.map_err(ComposeError::PrepareError)?;
229+
let prepared = self.prepare_dependencies(world_id, component).await?;
212230

213-
let prepared = self.prepare_dependencies(world_id, component).await?;
231+
let arguments = self
232+
.build_instantiation_arguments(world_id, prepared)
233+
.await?;
214234

215-
let arguments = self
216-
.build_instantiation_arguments(world_id, prepared)
217-
.await?;
235+
for (argument_name, argument) in arguments {
236+
self.graph
237+
.set_instantiation_argument(instantiation_id, &argument_name, argument)
238+
.map_err(|e| ComposeError::PrepareError(e.into()))?;
239+
}
240+
241+
self.export_dependents_exports(world_id, instantiation_id)
242+
.map_err(ComposeError::PrepareError)?;
218243

219-
for (argument_name, argument) in arguments {
220244
self.graph
221-
.set_instantiation_argument(instantiation_id, &argument_name, argument)
222-
.map_err(|e| ComposeError::PrepareError(e.into()))?;
223-
}
245+
.encode(Default::default())
246+
.map_err(|e| ComposeError::EncodeError(e.into()))?
247+
};
224248

225-
self.export_dependents_exports(world_id, instantiation_id)
226-
.map_err(ComposeError::PrepareError)?;
249+
let with_extras = apply_trigger_deps(fulfilled_source).await?;
227250

228-
self.graph
229-
.encode(Default::default())
230-
.map_err(|e| ComposeError::EncodeError(e.into()))
251+
Ok(with_extras)
231252
}
232253

233254
fn new(loader: &'a L) -> Self {

crates/environments/src/loader.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ impl ApplicationToValidate {
147147

148148
let loader = ComponentSourceLoader::new(&self.wasm_loader);
149149

150-
let wasm = spin_compose::compose(&loader, &component).await.with_context(|| format!("Spin needed to compose dependencies for {} as part of target checking, but composition failed", component.id))?;
150+
let wasm = spin_compose::compose(&loader, &component, async |data| Ok(data)).await.with_context(|| format!("Spin needed to compose dependencies for {} as part of target checking, but composition failed", component.id))?;
151151

152152
let host_requirements = if component.requires_service_chaining {
153153
vec!["local_service_chaining".to_string()]
@@ -185,6 +185,7 @@ impl<'a> ComponentSourceLoader<'a> {
185185
impl<'a> spin_compose::ComponentSourceLoader for ComponentSourceLoader<'a> {
186186
type Component = ComponentSource<'a>;
187187
type Dependency = WrappedComponentDependency;
188+
type Source = spin_manifest::schema::v2::ComponentSource;
188189
async fn load_component_source(&self, source: &Self::Component) -> anyhow::Result<Vec<u8>> {
189190
let path = self
190191
.wasm_loader
@@ -210,6 +211,19 @@ impl<'a> spin_compose::ComponentSourceLoader for ComponentSourceLoader<'a> {
210211
.with_context(|| format!("componentizing {}", quoted_path(&path)))?;
211212
Ok(component.into())
212213
}
214+
215+
async fn load_source(&self, source: &Self::Source) -> anyhow::Result<Vec<u8>> {
216+
let path = self
217+
.wasm_loader
218+
.load_component_source("in-memory-component", source)
219+
.await?;
220+
let bytes = tokio::fs::read(&path)
221+
.await
222+
.with_context(|| format!("reading {}", quoted_path(&path)))?;
223+
let component = spin_componentize::componentize_if_necessary(&bytes)
224+
.with_context(|| format!("componentizing {}", quoted_path(&path)))?;
225+
Ok(component.into())
226+
}
213227
}
214228

215229
// This exists only to thwart the orphan rule

crates/factors-executor/src/lib.rs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ impl<T: RuntimeFactors, U: Send + 'static> FactorsExecutor<T, U> {
5555
runtime_config: T::RuntimeConfig,
5656
component_loader: &impl ComponentLoader<T, U>,
5757
trigger_type: Option<&str>,
58+
trigger_dependencies_composer: impl TriggerDependenciesComposer,
5859
) -> anyhow::Result<FactorsExecutorApp<T, U>> {
5960
let configured_app = self
6061
.factors
@@ -77,7 +78,11 @@ impl<T: RuntimeFactors, U: Send + 'static> FactorsExecutor<T, U> {
7778

7879
for component in components {
7980
let instance_pre = component_loader
80-
.load_instance_pre(&self.core_engine, &component)
81+
.load_instance_pre(
82+
&self.core_engine,
83+
&component,
84+
&trigger_dependencies_composer,
85+
)
8186
.await?;
8287
component_instance_pres.insert(component.id().to_string(), instance_pre);
8388
}
@@ -116,19 +121,57 @@ pub trait ComponentLoader<T: RuntimeFactors, U>: Sync {
116121
&self,
117122
engine: &spin_core::wasmtime::Engine,
118123
component: &AppComponent,
124+
trigger_dependencies_composer: &impl TriggerDependenciesComposer,
119125
) -> anyhow::Result<Component>;
120126

121127
/// Loads [`InstancePre`] for the given [`AppComponent`].
122128
async fn load_instance_pre(
123129
&self,
124130
engine: &spin_core::Engine<InstanceState<T::InstanceState, U>>,
125131
component: &AppComponent,
132+
trigger_dependencies_composer: &impl TriggerDependenciesComposer,
126133
) -> anyhow::Result<spin_core::InstancePre<InstanceState<T::InstanceState, U>>> {
127-
let component = self.load_component(engine.as_ref(), component).await?;
134+
let component = self
135+
.load_component(engine.as_ref(), component, trigger_dependencies_composer)
136+
.await?;
128137
engine.instantiate_pre(&component)
129138
}
130139
}
131140

141+
#[async_trait]
142+
pub trait TriggerDependenciesComposer: Send + Sync {
143+
async fn compose_trigger_dependencies(
144+
&self,
145+
trigger_dependencies: &HashMap<String, Vec<TriggerDependency>>,
146+
component: Vec<u8>,
147+
) -> anyhow::Result<Vec<u8>>;
148+
}
149+
150+
#[async_trait]
151+
impl TriggerDependenciesComposer for () {
152+
async fn compose_trigger_dependencies(
153+
&self,
154+
trigger_dependencies: &HashMap<String, Vec<TriggerDependency>>,
155+
component: Vec<u8>,
156+
) -> anyhow::Result<Vec<u8>> {
157+
if trigger_dependencies.is_empty() {
158+
Ok(component)
159+
} else {
160+
Err(anyhow::anyhow!("this trigger should not have dependencies"))
161+
}
162+
}
163+
}
164+
165+
pub struct TriggerDependency {
166+
pub data: TriggerDependencyData,
167+
pub dependency: spin_app::locked::LockedComponentDependency,
168+
}
169+
170+
pub enum TriggerDependencyData {
171+
InMemory(Vec<u8>),
172+
OnDisk(std::path::PathBuf),
173+
}
174+
132175
type InstancePre<T, U> =
133176
spin_core::InstancePre<InstanceState<<T as RuntimeFactors>::InstanceState, U>>;
134177

@@ -437,7 +480,7 @@ mod tests {
437480
let executor = Arc::new(FactorsExecutor::new(engine_builder, env.factors)?);
438481

439482
let factors_app = executor
440-
.load_app(app, Default::default(), &DummyComponentLoader, None)
483+
.load_app(app, Default::default(), &DummyComponentLoader, None, ())
441484
.await?;
442485

443486
let mut instance_builder = factors_app.prepare("empty")?;
@@ -463,6 +506,7 @@ mod tests {
463506
&self,
464507
engine: &spin_core::wasmtime::Engine,
465508
_component: &AppComponent,
509+
_trigger_dependencies_composer: &impl TriggerDependenciesComposer,
466510
) -> anyhow::Result<Component> {
467511
Ok(Component::new(engine, "(component)")?)
468512
}

0 commit comments

Comments
 (0)