diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index 9e07d79ff3..037a27bd9c 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -59,12 +59,14 @@ pub async fn build( }; if !skip_target_checks { - let resolution_context = - spin_environments::ResolutionContext::new(manifest_file.parent().unwrap()).await?; + let application = spin_environments::ApplicationToValidate::new( + manifest.clone(), + manifest_file.parent().unwrap(), + ) + .await?; let errors = spin_environments::validate_application_against_environment_ids( + &application, build_info.deployment_targets(), - manifest, - &resolution_context, ) .await?; diff --git a/crates/environments/src/lib.rs b/crates/environments/src/lib.rs index c5a232a01c..657bf94c0e 100644 --- a/crates/environments/src/lib.rs +++ b/crates/environments/src/lib.rs @@ -5,30 +5,26 @@ mod loader; use environment_definition::{load_environment, TargetEnvironment, TriggerType}; use futures::future::try_join_all; -pub use loader::ResolutionContext; -use loader::{load_and_resolve_all, ComponentToValidate}; +pub use loader::ApplicationToValidate; +use loader::ComponentToValidate; pub async fn validate_application_against_environment_ids( + application: &ApplicationToValidate, env_ids: &[impl AsRef], - app: &spin_manifest::schema::v2::AppManifest, - resolution_context: &ResolutionContext, ) -> anyhow::Result> { if env_ids.is_empty() { return Ok(Default::default()); } let envs = try_join_all(env_ids.iter().map(load_environment)).await?; - validate_application_against_environments(&envs, app, resolution_context).await + validate_application_against_environments(application, &envs).await } async fn validate_application_against_environments( + application: &ApplicationToValidate, envs: &[TargetEnvironment], - app: &spin_manifest::schema::v2::AppManifest, - resolution_context: &ResolutionContext, ) -> anyhow::Result> { - use futures::FutureExt; - - for trigger_type in app.triggers.keys() { + for trigger_type in application.trigger_types() { if let Some(env) = envs.iter().find(|e| !e.supports_trigger_type(trigger_type)) { anyhow::bail!( "Environment {} does not support trigger type {trigger_type}", @@ -37,13 +33,7 @@ async fn validate_application_against_environments( } } - let components_by_trigger_type_futs = app.triggers.iter().map(|(ty, ts)| { - load_and_resolve_all(app, ts, resolution_context) - .map(|css| css.map(|css| (ty.to_owned(), css))) - }); - let components_by_trigger_type = try_join_all(components_by_trigger_type_futs) - .await - .context("Failed to prepare components for target environment checking")?; + let components_by_trigger_type = application.components_by_trigger_type().await?; let mut errs = vec![]; diff --git a/crates/environments/src/loader.rs b/crates/environments/src/loader.rs index 458f8f4ca1..a039ba80b4 100644 --- a/crates/environments/src/loader.rs +++ b/crates/environments/src/loader.rs @@ -10,12 +10,6 @@ pub(crate) struct ComponentToValidate<'a> { wasm: Vec, } -struct ComponentSource<'a> { - id: &'a str, - source: &'a spin_manifest::schema::v2::ComponentSource, - dependencies: WrappedComponentDependencies, -} - impl<'a> ComponentToValidate<'a> { pub fn id(&self) -> &str { self.id @@ -30,75 +24,112 @@ impl<'a> ComponentToValidate<'a> { } } -pub async fn load_and_resolve_all<'a>( - app: &'a spin_manifest::schema::v2::AppManifest, - triggers: &'a [spin_manifest::schema::v2::Trigger], - resolution_context: &'a ResolutionContext, -) -> anyhow::Result>> { - let component_futures = triggers - .iter() - .map(|t| load_and_resolve_one(app, t, resolution_context)); - try_join_all(component_futures).await +pub struct ApplicationToValidate { + manifest: spin_manifest::schema::v2::AppManifest, + wasm_loader: spin_loader::WasmLoader, } -async fn load_and_resolve_one<'a>( - app: &'a spin_manifest::schema::v2::AppManifest, - trigger: &'a spin_manifest::schema::v2::Trigger, - resolution_context: &'a ResolutionContext, -) -> anyhow::Result> { - let component_spec = trigger - .component - .as_ref() - .ok_or_else(|| anyhow!("No component specified for trigger {}", trigger.id))?; - let (id, source, dependencies) = match component_spec { - spin_manifest::schema::v2::ComponentSpec::Inline(c) => { - (trigger.id.as_str(), &c.source, &c.dependencies) - } - spin_manifest::schema::v2::ComponentSpec::Reference(r) => { - let id = r.as_ref(); - let Some(component) = app.components.get(r) else { - anyhow::bail!( - "Component {id} specified for trigger {} does not exist", - trigger.id - ); - }; - (id, &component.source, &component.dependencies) - } - }; - - let component = ComponentSource { - id, - source, - dependencies: WrappedComponentDependencies::new(dependencies), - }; +impl ApplicationToValidate { + pub async fn new( + manifest: spin_manifest::schema::v2::AppManifest, + base_dir: impl AsRef, + ) -> anyhow::Result { + let wasm_loader = + spin_loader::WasmLoader::new(base_dir.as_ref().to_owned(), None, None).await?; + Ok(Self { + manifest, + wasm_loader, + }) + } - let loader = ComponentSourceLoader::new(resolution_context.wasm_loader()); + fn component_source<'a>( + &'a self, + trigger: &'a spin_manifest::schema::v2::Trigger, + ) -> anyhow::Result> { + let component_spec = trigger + .component + .as_ref() + .ok_or_else(|| anyhow!("No component specified for trigger {}", trigger.id))?; + let (id, source, dependencies) = match component_spec { + spin_manifest::schema::v2::ComponentSpec::Inline(c) => { + (trigger.id.as_str(), &c.source, &c.dependencies) + } + spin_manifest::schema::v2::ComponentSpec::Reference(r) => { + let id = r.as_ref(); + let Some(component) = self.manifest.components.get(r) else { + anyhow::bail!( + "Component {id} specified for trigger {} does not exist", + trigger.id + ); + }; + (id, &component.source, &component.dependencies) + } + }; + + Ok(ComponentSource { + id, + source, + dependencies: WrappedComponentDependencies::new(dependencies), + }) + } - let wasm = spin_compose::compose(&loader, &component).await.with_context(|| format!("Spin needed to compose dependencies for {id} as part of target checking, but composition failed"))?; + pub fn trigger_types(&self) -> impl Iterator { + self.manifest.triggers.keys() + } - Ok(ComponentToValidate { - id, - source_description: source_description(component.source), - wasm, - }) -} + pub fn triggers( + &self, + ) -> impl Iterator)> { + self.manifest.triggers.iter() + } -pub struct ResolutionContext { - wasm_loader: spin_loader::WasmLoader, -} + pub(crate) async fn components_by_trigger_type( + &self, + ) -> anyhow::Result>)>> { + use futures::FutureExt; + + let components_by_trigger_type_futs = self.triggers().map(|(ty, ts)| { + self.components_for_trigger(ts) + .map(|css| css.map(|css| (ty.to_owned(), css))) + }); + let components_by_trigger_type = try_join_all(components_by_trigger_type_futs) + .await + .context("Failed to prepare components for target environment checking")?; + Ok(components_by_trigger_type) + } -impl ResolutionContext { - pub async fn new(base_dir: impl AsRef) -> anyhow::Result { - let wasm_loader = - spin_loader::WasmLoader::new(base_dir.as_ref().to_owned(), None, None).await?; - Ok(Self { wasm_loader }) + async fn components_for_trigger<'a>( + &'a self, + triggers: &'a [spin_manifest::schema::v2::Trigger], + ) -> anyhow::Result>> { + let component_futures = triggers.iter().map(|t| self.load_and_resolve_trigger(t)); + try_join_all(component_futures).await } - fn wasm_loader(&self) -> &spin_loader::WasmLoader { - &self.wasm_loader + async fn load_and_resolve_trigger<'a>( + &'a self, + trigger: &'a spin_manifest::schema::v2::Trigger, + ) -> anyhow::Result> { + let component = self.component_source(trigger)?; + + let loader = ComponentSourceLoader::new(&self.wasm_loader); + + 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))?; + + Ok(ComponentToValidate { + id: component.id, + source_description: source_description(component.source), + wasm, + }) } } +struct ComponentSource<'a> { + id: &'a str, + source: &'a spin_manifest::schema::v2::ComponentSource, + dependencies: WrappedComponentDependencies, +} + struct ComponentSourceLoader<'a> { wasm_loader: &'a spin_loader::WasmLoader, } @@ -135,7 +166,7 @@ impl<'a> spin_compose::ComponentSourceLoader for ComponentSourceLoader<'a> { } // This exists only to thwart the orphan rule -pub(crate) struct WrappedComponentDependency { +struct WrappedComponentDependency { name: spin_serde::DependencyName, dependency: spin_manifest::schema::v2::ComponentDependency, }