@@ -8,15 +8,28 @@ use wasm_pkg_loader::PackageRef;
8
8
struct TargetWorld {
9
9
wit_package : PackageRef ,
10
10
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 > ) ,
12
19
}
13
20
14
21
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
+ }
20
33
}
21
34
}
22
35
@@ -59,22 +72,20 @@ fn component_source<'a>(
59
72
. component
60
73
. as_ref ( )
61
74
. 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 {
63
76
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
+ }
76
87
} ;
77
- Ok ( ComponentToValidate { id, source : csrc } )
88
+ Ok ( ComponentToValidate { id, source } )
78
89
}
79
90
80
91
pub async fn validate_application_against_environment_ids (
@@ -180,32 +191,28 @@ async fn validate_component_against_environments(
180
191
. map ( |w| ( e. name . as_str ( ) , w) )
181
192
} )
182
193
. 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 ?;
184
195
Ok ( ( ) )
185
196
}
186
197
187
198
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) ) )
199
206
}
200
207
}
201
208
202
- async fn validate_file_against_worlds (
209
+ async fn validate_component_against_worlds (
203
210
target_worlds : impl Iterator < Item = ( & str , & TargetWorld ) > ,
204
211
component : & ComponentToValidate < ' _ > ,
205
212
resolution_context : & ResolutionContext ,
206
213
) -> anyhow:: Result < ( ) > {
207
214
let raw_wasm = resolution_context
208
- . load_wasm ( component. source )
215
+ . load_wasm ( component)
209
216
. await
210
217
. with_context ( || format ! ( "Couldn't read Wasm {}" , component. source_description( ) ) ) ?;
211
218
// FUTURE: take in manifest composition as well
@@ -218,14 +225,8 @@ async fn validate_file_against_worlds(
218
225
} ) ?;
219
226
220
227
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 ( ) )
222
229
. await ?;
223
- tracing:: info!(
224
- "Validated component {} {} against target world {}" ,
225
- component. id,
226
- component. source_description( ) ,
227
- target_world. versioned_name( )
228
- ) ;
229
230
}
230
231
231
232
tracing:: info!(
@@ -236,13 +237,48 @@ async fn validate_file_against_worlds(
236
237
Ok ( ( ) )
237
238
}
238
239
239
- async fn validate_wasm_against_world (
240
+ async fn validate_wasm_against_any_world (
240
241
env_name : & str ,
241
242
target_world : & TargetWorld ,
242
243
component : & ComponentToValidate < ' _ > ,
243
244
wasm : & [ u8 ] ,
244
245
) -> 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 < ( ) > {
246
282
let comp_name = "root:component" ;
247
283
248
284
let wac_text = format ! (
@@ -266,7 +302,7 @@ async fn validate_wasm_against_world(
266
302
. await
267
303
. context ( "reg_resolver.resolve failed" ) ?;
268
304
269
- packages. insert ( compkey, wasm. to_owned ( ) . to_vec ( ) ) ;
305
+ packages. insert ( compkey, wasm. to_vec ( ) ) ;
270
306
271
307
match doc. resolve ( packages) {
272
308
Ok ( _) => Ok ( ( ) ) ,
0 commit comments