Skip to content

Commit

Permalink
Consider dependencies during target validation
Browse files Browse the repository at this point in the history
Signed-off-by: itowlson <[email protected]>
  • Loading branch information
itowlson committed Sep 8, 2024
1 parent 1bb5ec0 commit d840496
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 105 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/compose/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ async-trait = "0.1"
indexmap = "2.2.6"
semver = "1"
spin-app = { path = "../app" }
spin-common = { path = "../common" }
spin-componentize = { workspace = true }
spin-serde = { path = "../serde" }
thiserror = "1"
Expand Down
89 changes: 68 additions & 21 deletions crates/compose/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anyhow::Context;
use indexmap::IndexMap;
use semver::Version;
use spin_app::locked::{self, InheritConfiguration, LockedComponent, LockedComponentDependency};
use spin_app::locked::InheritConfiguration as LockedInheritConfiguration;
use spin_serde::{DependencyName, KebabId};
use std::collections::BTreeMap;
use thiserror::Error;
Expand All @@ -28,18 +28,68 @@ use wac_graph::{CompositionGraph, NodeId};
/// composition graph into a byte array and return it.
pub async fn compose<'a, L: ComponentSourceLoader>(
loader: &'a L,
component: &LockedComponent,
component: &L::Component,
) -> Result<Vec<u8>, ComposeError> {
Composer::new(loader).compose(component).await
}

#[async_trait::async_trait]
pub trait DependencyLike {
fn inherit(&self) -> InheritConfiguration;
fn export(&self) -> &Option<String>;
}

pub enum InheritConfiguration {
All,
Some(Vec<String>),
}

#[async_trait::async_trait]
pub trait ComponentLike {
type Dependency: DependencyLike;

fn dependencies(
&self,
) -> impl std::iter::ExactSizeIterator<Item = (&DependencyName, &Self::Dependency)>;
fn id(&self) -> &str;
}

#[async_trait::async_trait]
impl ComponentLike for spin_app::locked::LockedComponent {
type Dependency = spin_app::locked::LockedComponentDependency;

fn dependencies(
&self,
) -> impl std::iter::ExactSizeIterator<Item = (&DependencyName, &Self::Dependency)> {
self.dependencies.iter()
}

fn id(&self) -> &str {
&self.id
}
}

#[async_trait::async_trait]
impl DependencyLike for spin_app::locked::LockedComponentDependency {
fn inherit(&self) -> InheritConfiguration {
match &self.inherit {
LockedInheritConfiguration::All => InheritConfiguration::All,
LockedInheritConfiguration::Some(cfgs) => InheritConfiguration::Some(cfgs.clone()),
}
}

fn export(&self) -> &Option<String> {
&self.export
}
}

/// This trait is used to load component source code from a locked component source across various embdeddings.
#[async_trait::async_trait]
pub trait ComponentSourceLoader {
async fn load_component_source(
&self,
source: &locked::LockedComponentSource,
) -> anyhow::Result<Vec<u8>>;
type Component: ComponentLike<Dependency = Self::Dependency>;
type Dependency: DependencyLike;
async fn load_component_source(&self, source: &Self::Component) -> anyhow::Result<Vec<u8>>;
async fn load_dependency_source(&self, source: &Self::Dependency) -> anyhow::Result<Vec<u8>>;
}

/// Represents an error that can occur when composing dependencies.
Expand Down Expand Up @@ -98,19 +148,19 @@ struct Composer<'a, L> {
}

impl<'a, L: ComponentSourceLoader> Composer<'a, L> {
async fn compose(mut self, component: &LockedComponent) -> Result<Vec<u8>, ComposeError> {
async fn compose(mut self, component: &L::Component) -> Result<Vec<u8>, ComposeError> {
let source = self
.loader
.load_component_source(&component.source)
.load_component_source(component)
.await
.map_err(ComposeError::PrepareError)?;

if component.dependencies.is_empty() {
if component.dependencies().len() == 0 {
return Ok(source);
}

let (world_id, instantiation_id) = self
.register_package(&component.id, None, source)
.register_package(component.id(), None, source)
.map_err(ComposeError::PrepareError)?;

let prepared = self.prepare_dependencies(world_id, component).await?;
Expand Down Expand Up @@ -150,15 +200,15 @@ impl<'a, L: ComponentSourceLoader> Composer<'a, L> {
async fn prepare_dependencies(
&mut self,
world_id: WorldId,
component: &LockedComponent,
component: &L::Component,
) -> Result<IndexMap<String, DependencyInfo>, ComposeError> {
let imports = self.graph.types()[world_id].imports.clone();

let import_keys = imports.keys().cloned().collect::<Vec<_>>();

let mut mappings: BTreeMap<String, Vec<DependencyInfo>> = BTreeMap::new();

for (dependency_name, dependency) in &component.dependencies {
for (dependency_name, dependency) in component.dependencies() {
let mut matched = Vec::new();

for import_name in &import_keys {
Expand All @@ -171,7 +221,7 @@ impl<'a, L: ComponentSourceLoader> Composer<'a, L> {

if matched.is_empty() {
return Err(ComposeError::UnmatchedDependencyName {
component_id: component.id.clone(),
component_id: component.id().to_owned(),
dependency_name: dependency_name.clone(),
});
}
Expand All @@ -195,7 +245,7 @@ impl<'a, L: ComponentSourceLoader> Composer<'a, L> {

if !conflicts.is_empty() {
return Err(ComposeError::DependencyConflicts {
component_id: component.id.clone(),
component_id: component.id().to_owned(),
conflicts: conflicts
.into_iter()
.map(|(import_name, infos)| {
Expand Down Expand Up @@ -300,19 +350,16 @@ impl<'a, L: ComponentSourceLoader> Composer<'a, L> {
async fn register_dependency(
&mut self,
dependency_name: DependencyName,
dependency: &LockedComponentDependency,
dependency: &L::Dependency,
) -> anyhow::Result<DependencyInfo> {
let mut dependency_source = self
.loader
.load_component_source(&dependency.source)
.await?;
let mut dependency_source = self.loader.load_dependency_source(dependency).await?;

let package_name = match &dependency_name {
DependencyName::Package(name) => name.package.to_string(),
DependencyName::Plain(name) => name.to_string(),
};

match &dependency.inherit {
match dependency.inherit() {
InheritConfiguration::Some(configurations) => {
if configurations.is_empty() {
// Configuration inheritance is disabled, apply deny_all adapter
Expand All @@ -333,7 +380,7 @@ impl<'a, L: ComponentSourceLoader> Composer<'a, L> {
manifest_name: dependency_name,
instantiation_id,
world_id,
export_name: dependency.export.clone(),
export_name: dependency.export().clone(),
})
}

Expand Down
4 changes: 4 additions & 0 deletions crates/environments/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = { workspace = true }

[dependencies]
anyhow = { workspace = true }
async-trait = "0.1"
futures = "0.3"
indexmap = "2.2.6"
miette = "7.2.0"
Expand All @@ -15,8 +16,11 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
spin-common = { path = "../common" }
spin-componentize = { path = "../componentize" }
spin-compose = { path = "../compose" }
spin-loader = { path = "../loader" }
spin-manifest = { path = "../manifest" }
spin-serde = { path = "../serde" }
tokio = { version = "1.23", features = ["fs"] }
tracing = { workspace = true }
wac-parser = "0.6.0"
wac-resolver = "0.6.0"
Expand Down
Loading

0 comments on commit d840496

Please sign in to comment.