Skip to content

Commit c960d87

Browse files
committed
Support non-local Wasm. Support alternate worlds.
Signed-off-by: itowlson <[email protected]>
1 parent fe24cda commit c960d87

File tree

8 files changed

+538
-261
lines changed

8 files changed

+538
-261
lines changed

Cargo.lock

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

crates/build/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ anyhow = "1.0.57"
99
futures = "0.3.21"
1010
serde = { version = "1.0", features = ["derive"] }
1111
spin-common = { path = "../common" }
12-
spin-environments = { path = "../environments" } # TODO: it would be good to break this dependency because not everything should have to drag this dep around
12+
spin-environments = { path = "../environments" }
1313
spin-manifest = { path = "../manifest" }
1414
subprocess = "0.2.8"
1515
terminal = { path = "../terminal" }

crates/compose/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ spin-componentize = { workspace = true }
1818
spin-serde = { path = "../serde" }
1919
thiserror = "1"
2020
tokio = { version = "1.23", features = ["fs"] }
21-
wac-graph = "0.5.0"
21+
wac-graph = "0.6.0"
2222

2323
[lints]
2424
workspace = true

crates/environments/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ serde = { version = "1.0", features = ["derive"] }
1515
serde_json = "1.0"
1616
spin-common = { path = "../common" }
1717
spin-componentize = { path = "../componentize" }
18+
spin-loader = { path = "../loader" }
1819
spin-manifest = { path = "../manifest" }
1920
tracing = { workspace = true }
20-
wac-parser = "0.5.0"
21-
wac-resolver = "0.5.0"
22-
wac-types = "0.5.0"
21+
wac-parser = "0.6.0"
22+
wac-resolver = "0.6.0"
23+
wac-types = "0.6.0"
2324
wasm-pkg-loader = "0.4.1"
2425

2526
[lints]

crates/environments/src/lib.rs

Lines changed: 80 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,28 @@ use wasm_pkg_loader::PackageRef;
88
struct TargetWorld {
99
wit_package: PackageRef,
1010
package_ver: String, // TODO: tidy to semver::Version
11-
world_name: String,
11+
world_name: WorldNames,
12+
}
13+
14+
#[derive(Debug, Eq, Hash, PartialEq, serde::Deserialize)]
15+
#[serde(untagged)]
16+
enum WorldNames {
17+
Exactly(String),
18+
AnyOf(Vec<String>),
1219
}
1320

1421
impl TargetWorld {
15-
fn versioned_name(&self) -> String {
16-
format!(
17-
"{}/{}@{}",
18-
self.wit_package, self.world_name, self.package_ver
19-
)
22+
fn versioned_name(&self, world_name: &str) -> String {
23+
format!("{}/{}@{}", self.wit_package, world_name, self.package_ver)
24+
}
25+
26+
fn versioned_names(&self) -> Vec<String> {
27+
match &self.world_name {
28+
WorldNames::Exactly(name) => vec![self.versioned_name(name)],
29+
WorldNames::AnyOf(names) => {
30+
names.iter().map(|name| self.versioned_name(name)).collect()
31+
}
32+
}
2033
}
2134
}
2235

@@ -59,22 +72,20 @@ fn component_source<'a>(
5972
.component
6073
.as_ref()
6174
.ok_or_else(|| anyhow!("No component specified for trigger {}", trigger.id))?;
62-
let (id, csrc) = match component_spec {
75+
let (id, source) = match component_spec {
6376
spin_manifest::schema::v2::ComponentSpec::Inline(c) => (trigger.id.as_str(), &c.source),
64-
spin_manifest::schema::v2::ComponentSpec::Reference(r) => (
65-
r.as_ref(),
66-
&app.components
67-
.get(r)
68-
.ok_or_else(|| {
69-
anyhow!(
70-
"Component {r} specified for trigger {} does not exist",
71-
trigger.id
72-
)
73-
})?
74-
.source,
75-
),
77+
spin_manifest::schema::v2::ComponentSpec::Reference(r) => {
78+
let id = r.as_ref();
79+
let Some(component) = app.components.get(r) else {
80+
anyhow::bail!(
81+
"Component {id} specified for trigger {} does not exist",
82+
trigger.id
83+
);
84+
};
85+
(id, &component.source)
86+
}
7687
};
77-
Ok(ComponentToValidate { id, source: csrc })
88+
Ok(ComponentToValidate { id, source })
7889
}
7990

8091
pub async fn validate_application_against_environment_ids(
@@ -180,32 +191,28 @@ async fn validate_component_against_environments(
180191
.map(|w| (e.name.as_str(), w))
181192
})
182193
.collect::<Result<std::collections::HashSet<_>, _>>()?;
183-
validate_file_against_worlds(worlds.into_iter(), component, resolution_context).await?;
194+
validate_component_against_worlds(worlds.into_iter(), component, resolution_context).await?;
184195
Ok(())
185196
}
186197

187198
impl ResolutionContext {
188-
async fn load_wasm(
189-
&self,
190-
source: &spin_manifest::schema::v2::ComponentSource,
191-
) -> anyhow::Result<Vec<u8>> {
192-
if let spin_manifest::schema::v2::ComponentSource::Local(path) = source {
193-
let wasm_file = self.base_dir.join(path);
194-
Ok(std::fs::read(&wasm_file)
195-
.with_context(|| format!("Can't read Wasm file {}", quoted_path(wasm_file)))?)
196-
} else {
197-
anyhow::bail!("can't do non-local component sources yet");
198-
}
199+
async fn load_wasm(&self, component: &ComponentToValidate<'_>) -> anyhow::Result<Vec<u8>> {
200+
let loader = spin_loader::WasmLoader::new(self.base_dir.clone(), None, None).await?;
201+
let wasm_file = loader
202+
.load_component_source(component.id, component.source)
203+
.await?;
204+
std::fs::read(&wasm_file)
205+
.with_context(|| format!("Can't read Wasm file {}", quoted_path(wasm_file)))
199206
}
200207
}
201208

202-
async fn validate_file_against_worlds(
209+
async fn validate_component_against_worlds(
203210
target_worlds: impl Iterator<Item = (&str, &TargetWorld)>,
204211
component: &ComponentToValidate<'_>,
205212
resolution_context: &ResolutionContext,
206213
) -> anyhow::Result<()> {
207214
let raw_wasm = resolution_context
208-
.load_wasm(component.source)
215+
.load_wasm(component)
209216
.await
210217
.with_context(|| format!("Couldn't read Wasm {}", component.source_description()))?;
211218
// FUTURE: take in manifest composition as well
@@ -218,14 +225,8 @@ async fn validate_file_against_worlds(
218225
})?;
219226

220227
for (env_name, target_world) in target_worlds {
221-
validate_wasm_against_world(env_name, target_world, component, cooked_wasm.as_ref())
228+
validate_wasm_against_any_world(env_name, target_world, component, cooked_wasm.as_ref())
222229
.await?;
223-
tracing::info!(
224-
"Validated component {} {} against target world {}",
225-
component.id,
226-
component.source_description(),
227-
target_world.versioned_name()
228-
);
229230
}
230231

231232
tracing::info!(
@@ -236,13 +237,48 @@ async fn validate_file_against_worlds(
236237
Ok(())
237238
}
238239

239-
async fn validate_wasm_against_world(
240+
async fn validate_wasm_against_any_world(
240241
env_name: &str,
241242
target_world: &TargetWorld,
242243
component: &ComponentToValidate<'_>,
243244
wasm: &[u8],
244245
) -> anyhow::Result<()> {
245-
let target_str = target_world.versioned_name();
246+
let mut result = Ok(());
247+
for target_str in target_world.versioned_names() {
248+
tracing::info!(
249+
"Trying component {} {} against target world {target_str}",
250+
component.id,
251+
component.source_description(),
252+
);
253+
match validate_wasm_against_world(env_name, &target_str, component, wasm).await {
254+
Ok(()) => {
255+
tracing::info!(
256+
"Validated component {} {} against target world {target_str}",
257+
component.id,
258+
component.source_description(),
259+
);
260+
return Ok(());
261+
}
262+
Err(e) => {
263+
// Record the error, but continue in case a different world succeeds
264+
tracing::info!(
265+
"Rejecting component {} {} for target world {target_str} because {e:?}",
266+
component.id,
267+
component.source_description(),
268+
);
269+
result = Err(e);
270+
}
271+
}
272+
}
273+
result
274+
}
275+
276+
async fn validate_wasm_against_world(
277+
env_name: &str,
278+
target_str: &str,
279+
component: &ComponentToValidate<'_>,
280+
wasm: &[u8],
281+
) -> anyhow::Result<()> {
246282
let comp_name = "root:component";
247283

248284
let wac_text = format!(
@@ -266,7 +302,7 @@ async fn validate_wasm_against_world(
266302
.await
267303
.context("reg_resolver.resolve failed")?;
268304

269-
packages.insert(compkey, wasm.to_owned().to_vec());
305+
packages.insert(compkey, wasm.to_vec());
270306

271307
match doc.resolve(packages) {
272308
Ok(_) => Ok(()),

crates/loader/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ mod fs;
2323
mod http;
2424
mod local;
2525

26+
pub use local::WasmLoader;
27+
2628
/// Maximum number of files to copy (or download) concurrently
2729
pub(crate) const MAX_FILE_LOADING_CONCURRENCY: usize = 16;
2830

0 commit comments

Comments
 (0)