1
- use anyhow:: { anyhow, Context } ;
1
+ use anyhow:: anyhow;
2
2
3
3
mod environment_definition;
4
4
mod loader;
5
5
6
- use environment_definition:: { TargetEnvironment , TargetWorld , TriggerType } ;
6
+ use environment_definition:: { load_environment , TargetEnvironment , TriggerType } ;
7
7
pub use loader:: ResolutionContext ;
8
8
use loader:: { load_and_resolve_all, ComponentToValidate } ;
9
9
@@ -12,57 +12,22 @@ pub async fn validate_application_against_environment_ids(
12
12
app : & spin_manifest:: schema:: v2:: AppManifest ,
13
13
resolution_context : & ResolutionContext ,
14
14
) -> 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 ?;
16
16
validate_application_against_environments ( & envs, app, resolution_context) . await
17
17
}
18
18
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 (
52
20
envs : & [ TargetEnvironment ] ,
53
21
app : & spin_manifest:: schema:: v2:: AppManifest ,
54
22
resolution_context : & ResolutionContext ,
55
23
) -> anyhow:: Result < ( ) > {
56
24
use futures:: FutureExt ;
57
25
58
26
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) ) {
63
28
anyhow:: bail!(
64
29
"Environment {} does not support trigger type {trigger_type}" ,
65
- env. name
30
+ env. name( )
66
31
) ;
67
32
}
68
33
}
@@ -87,28 +52,9 @@ async fn validate_component_against_environments(
87
52
trigger_type : & TriggerType ,
88
53
component : & ComponentToValidate < ' _ > ,
89
54
) -> 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 ?;
112
58
}
113
59
114
60
tracing:: info!(
@@ -120,21 +66,21 @@ async fn validate_component_against_worlds(
120
66
}
121
67
122
68
async fn validate_wasm_against_any_world (
123
- env_name : & str ,
124
- target_world : & TargetWorld ,
69
+ env : & TargetEnvironment ,
70
+ world_names : & [ String ] ,
125
71
component : & ComponentToValidate < ' _ > ,
126
72
) -> anyhow:: Result < ( ) > {
127
73
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 }" ,
131
77
component. id( ) ,
132
78
component. source_description( ) ,
133
79
) ;
134
- match validate_wasm_against_world ( env_name , & target_str , component) . await {
80
+ match validate_wasm_against_world ( env , target_world , component) . await {
135
81
Ok ( ( ) ) => {
136
82
tracing:: info!(
137
- "Validated component {} {} against target world {target_str }" ,
83
+ "Validated component {} {} against target world {target_world }" ,
138
84
component. id( ) ,
139
85
component. source_description( ) ,
140
86
) ;
@@ -143,7 +89,7 @@ async fn validate_wasm_against_any_world(
143
89
Err ( e) => {
144
90
// Record the error, but continue in case a different world succeeds
145
91
tracing:: info!(
146
- "Rejecting component {} {} for target world {target_str } because {e:?}" ,
92
+ "Rejecting component {} {} for target world {target_world } because {e:?}" ,
147
93
component. id( ) ,
148
94
component. source_description( ) ,
149
95
) ;
@@ -155,34 +101,52 @@ async fn validate_wasm_against_any_world(
155
101
}
156
102
157
103
async fn validate_wasm_against_world (
158
- env_name : & str ,
159
- target_str : & str ,
104
+ env : & TargetEnvironment ,
105
+ target_world : & str ,
160
106
component : & ComponentToValidate < ' _ > ,
161
107
) -> 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 ( ) ;
163
120
164
121
let wac_text = format ! (
165
122
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 } {{ ... }};
168
125
export c...;
169
126
"#
170
127
) ;
171
128
172
129
let doc = wac_parser:: Document :: parse ( & wac_text) ?;
173
130
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);
175
137
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")?;
178
144
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 ( ) ;
184
147
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 ( ) ) ;
186
150
187
151
match doc. resolve ( packages) {
188
152
Ok ( _) => Ok ( ( ) ) ,
@@ -195,7 +159,7 @@ async fn validate_wasm_against_world(
195
159
}
196
160
Err ( wac_parser:: resolution:: Error :: PackageMissingExport { export, .. } ) => {
197
161
// 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( ) ) )
199
163
}
200
164
Err ( wac_parser:: resolution:: Error :: ImportNotInTarget { name, world, .. } ) => {
201
165
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