1- use anyhow:: { anyhow, Context } ;
1+ use anyhow:: anyhow;
22
33mod environment_definition;
44mod loader;
55
6- use environment_definition:: { TargetEnvironment , TargetWorld , TriggerType } ;
6+ use environment_definition:: { load_environment , TargetEnvironment , TriggerType } ;
77pub use loader:: ResolutionContext ;
88use loader:: { load_and_resolve_all, ComponentToValidate } ;
99
@@ -12,57 +12,22 @@ pub async fn validate_application_against_environment_ids(
1212 app : & spin_manifest:: schema:: v2:: AppManifest ,
1313 resolution_context : & ResolutionContext ,
1414) -> anyhow:: Result < ( ) > {
15- let envs = join_all_result ( env_ids. map ( resolve_environment_id ) ) . await ?;
15+ let envs = join_all_result ( env_ids. map ( load_environment ) ) . await ?;
1616 validate_application_against_environments ( & envs, app, resolution_context) . await
1717}
1818
19- async fn resolve_environment_id ( id : & str ) -> anyhow:: Result < TargetEnvironment > {
20- let ( name, ver) = id. split_once ( '@' ) . ok_or ( anyhow ! (
21- "Target environment '{id}' does not specify a version"
22- ) ) ?;
23- let client = oci_distribution:: Client :: default ( ) ;
24- let auth = oci_distribution:: secrets:: RegistryAuth :: Anonymous ;
25- let env_def_ref =
26- oci_distribution:: Reference :: try_from ( format ! ( "ghcr.io/itowlson/spinenvs/{name}:{ver}" ) ) ?;
27- let ( man, _digest) = client
28- . pull_manifest ( & env_def_ref, & auth)
29- . await
30- . with_context ( || format ! ( "Failed to find environment '{id}' in registry" ) ) ?;
31- let im = match man {
32- oci_distribution:: manifest:: OciManifest :: Image ( im) => im,
33- oci_distribution:: manifest:: OciManifest :: ImageIndex ( _ind) => {
34- anyhow:: bail!( "Environment '{id}' definition is unusable - stored in registry in incorrect format" )
35- }
36- } ;
37- let the_layer = & im. layers [ 0 ] ;
38- let mut out = Vec :: with_capacity ( the_layer. size . try_into ( ) . unwrap_or_default ( ) ) ;
39- client
40- . pull_blob ( & env_def_ref, the_layer, & mut out)
41- . await
42- . with_context ( || {
43- format ! ( "Failed to download environment '{id}' definition from registry" )
44- } ) ?;
45- let te = serde_json:: from_slice ( & out) . with_context ( || {
46- format ! ( "Failed to load environment '{id}' definition - invalid JSON schema" )
47- } ) ?;
48- Ok ( te)
49- }
50-
51- pub async fn validate_application_against_environments (
19+ async fn validate_application_against_environments (
5220 envs : & [ TargetEnvironment ] ,
5321 app : & spin_manifest:: schema:: v2:: AppManifest ,
5422 resolution_context : & ResolutionContext ,
5523) -> anyhow:: Result < ( ) > {
5624 use futures:: FutureExt ;
5725
5826 for trigger_type in app. triggers . keys ( ) {
59- if let Some ( env) = envs
60- . iter ( )
61- . find ( |e| !e. environments . contains_key ( trigger_type) )
62- {
27+ if let Some ( env) = envs. iter ( ) . find ( |e| !e. supports_trigger_type ( trigger_type) ) {
6328 anyhow:: bail!(
6429 "Environment {} does not support trigger type {trigger_type}" ,
65- env. name
30+ env. name( )
6631 ) ;
6732 }
6833 }
@@ -87,28 +52,9 @@ async fn validate_component_against_environments(
8752 trigger_type : & TriggerType ,
8853 component : & ComponentToValidate < ' _ > ,
8954) -> anyhow:: Result < ( ) > {
90- let worlds = envs
91- . iter ( )
92- . map ( |e| {
93- e. environments
94- . get ( trigger_type)
95- . ok_or ( anyhow ! (
96- "Environment '{}' doesn't support trigger type {trigger_type}" ,
97- e. name
98- ) )
99- . map ( |w| ( e. name . as_str ( ) , w) )
100- } )
101- . collect :: < Result < std:: collections:: HashSet < _ > , _ > > ( ) ?;
102- validate_component_against_worlds ( worlds. into_iter ( ) , component) . await ?;
103- Ok ( ( ) )
104- }
105-
106- async fn validate_component_against_worlds (
107- target_worlds : impl Iterator < Item = ( & str , & TargetWorld ) > ,
108- component : & ComponentToValidate < ' _ > ,
109- ) -> anyhow:: Result < ( ) > {
110- for ( env_name, target_world) in target_worlds {
111- validate_wasm_against_any_world ( env_name, target_world, component) . await ?;
55+ for env in envs {
56+ let worlds = env. worlds ( trigger_type) ;
57+ validate_wasm_against_any_world ( env, & worlds, component) . await ?;
11258 }
11359
11460 tracing:: info!(
@@ -120,21 +66,21 @@ async fn validate_component_against_worlds(
12066}
12167
12268async fn validate_wasm_against_any_world (
123- env_name : & str ,
124- target_world : & TargetWorld ,
69+ env : & TargetEnvironment ,
70+ world_names : & [ String ] ,
12571 component : & ComponentToValidate < ' _ > ,
12672) -> anyhow:: Result < ( ) > {
12773 let mut result = Ok ( ( ) ) ;
128- for target_str in target_world . versioned_names ( ) {
129- tracing:: info !(
130- "Trying component {} {} against target world {target_str }" ,
74+ for target_world in world_names {
75+ tracing:: debug !(
76+ "Trying component {} {} against target world {target_world }" ,
13177 component. id( ) ,
13278 component. source_description( ) ,
13379 ) ;
134- match validate_wasm_against_world ( env_name , & target_str , component) . await {
80+ match validate_wasm_against_world ( env , target_world , component) . await {
13581 Ok ( ( ) ) => {
13682 tracing:: info!(
137- "Validated component {} {} against target world {target_str }" ,
83+ "Validated component {} {} against target world {target_world }" ,
13884 component. id( ) ,
13985 component. source_description( ) ,
14086 ) ;
@@ -143,7 +89,7 @@ async fn validate_wasm_against_any_world(
14389 Err ( e) => {
14490 // Record the error, but continue in case a different world succeeds
14591 tracing:: info!(
146- "Rejecting component {} {} for target world {target_str } because {e:?}" ,
92+ "Rejecting component {} {} for target world {target_world } because {e:?}" ,
14793 component. id( ) ,
14894 component. source_description( ) ,
14995 ) ;
@@ -155,34 +101,52 @@ async fn validate_wasm_against_any_world(
155101}
156102
157103async fn validate_wasm_against_world (
158- env_name : & str ,
159- target_str : & str ,
104+ env : & TargetEnvironment ,
105+ target_world : & str ,
160106 component : & ComponentToValidate < ' _ > ,
161107) -> anyhow:: Result < ( ) > {
162- let comp_name = "root:component" ;
108+ // Because we are abusing a composition tool to do validation, we have to
109+ // provide a name by which to refer to the component in the dummy composition.
110+ let component_name = "root:component" ;
111+ let component_key = wac_types:: BorrowedPackageKey :: from_name_and_version ( component_name, None ) ;
112+
113+ // wac is going to get the world from the environment package bytes.
114+ // This constructs a key for that mapping.
115+ let env_pkg_name = env. package_namespaced_name ( ) ;
116+ let env_pkg_key =
117+ wac_types:: BorrowedPackageKey :: from_name_and_version ( & env_pkg_name, env. package_version ( ) ) ;
118+
119+ let env_name = env. name ( ) ;
163120
164121 let wac_text = format ! (
165122 r#"
166- package validate:[email protected] targets {target_str }; 167- let c = new {comp_name } {{ ... }};
123+ package validate:[email protected] targets {target_world }; 124+ let c = new {component_name } {{ ... }};
168125 export c...;
169126 "#
170127 ) ;
171128
172129 let doc = wac_parser:: Document :: parse ( & wac_text) ?;
173130
174- let compkey = wac_types:: BorrowedPackageKey :: from_name_and_version ( comp_name, None ) ;
131+ // TODO: if we end up needing the registry, we need to do this dance
132+ // for things we are providing separately, or the registry will try to
133+ // hoover them up and will fail.
134+ // let mut refpkgs = wac_resolver::packages(&doc)?;
135+ // refpkgs.shift_remove(&env_pkg_key);
136+ // refpkgs.shift_remove(&component_key);
175137
176- let mut refpkgs = wac_resolver:: packages ( & doc) ?;
177- refpkgs. retain ( |k, _| k != & compkey) ;
138+ // TODO: determine if this is needed in circumstances other than the simple test
139+ // let reg_resolver = wac_resolver::RegistryPackageResolver::new(Some("wa.dev"), None).await?;
140+ // let mut packages = reg_resolver
141+ // .resolve(&refpkgs)
142+ // .await
143+ // .context("reg_resolver.resolve failed")?;
178144
179- let reg_resolver = wac_resolver:: RegistryPackageResolver :: new ( Some ( "wa.dev" ) , None ) . await ?;
180- let mut packages = reg_resolver
181- . resolve ( & refpkgs)
182- . await
183- . context ( "reg_resolver.resolve failed" ) ?;
145+ let mut packages: indexmap:: IndexMap < wac_types:: BorrowedPackageKey , Vec < u8 > > =
146+ Default :: default ( ) ;
184147
185- packages. insert ( compkey, component. wasm_bytes ( ) . to_vec ( ) ) ;
148+ packages. insert ( env_pkg_key, env. package_bytes ( ) . to_vec ( ) ) ;
149+ packages. insert ( component_key, component. wasm_bytes ( ) . to_vec ( ) ) ;
186150
187151 match doc. resolve ( packages) {
188152 Ok ( _) => Ok ( ( ) ) ,
@@ -195,7 +159,7 @@ async fn validate_wasm_against_world(
195159 }
196160 Err ( wac_parser:: resolution:: Error :: PackageMissingExport { export, .. } ) => {
197161 // TODO: The export here seems wrong - it seems to contain the world name rather than the interface name
198- Err ( anyhow ! ( "Component {} ({}) can't run in environment {env_name} because world {target_str } requires an export named {export}, which the component does not provide" , component. id( ) , component. source_description( ) ) )
162+ Err ( anyhow ! ( "Component {} ({}) can't run in environment {env_name} because world {target_world } requires an export named {export}, which the component does not provide" , component. id( ) , component. source_description( ) ) )
199163 }
200164 Err ( wac_parser:: resolution:: Error :: ImportNotInTarget { name, world, .. } ) => {
201165 Err ( anyhow ! ( "Component {} ({}) can't run in environment {env_name} because world {world} does not provide an import named {name}, which the component requires" , component. id( ) , component. source_description( ) ) )
0 commit comments