11use std:: {
2+ hash:: { DefaultHasher , Hash , Hasher } ,
23 path:: { Path , PathBuf } ,
34 process:: { Command , Stdio } ,
45} ;
@@ -12,69 +13,67 @@ use test_environment::{
1213} ;
1314
1415fn main ( ) {
15- let tests_dir = conformance_tests:: download_tests ( ) . unwrap ( ) ;
1616 let mut args = std:: env:: args ( ) . skip ( 1 ) ;
17- let spin_binary = & args
17+ let spin_binary: std :: path :: PathBuf = args
1818 . next ( )
19- . expect ( "expected first arg to be path to Spin binary" ) ;
20- let ctr_binary = & args
19+ . expect ( "expected first arg to be path to Spin binary" )
20+ . into ( ) ;
21+ let ctr_binary: std:: path:: PathBuf = args
2122 . next ( )
22- . expect ( "expected second arg to be path to ctr binary" ) ;
23+ . expect ( "expected second arg to be path to ctr binary" )
24+ . into ( ) ;
25+ conformance_tests:: run_tests ( move |test| run_test ( test, & spin_binary, & ctr_binary) ) . unwrap ( ) ;
26+ }
2327
24- ' test: for test in conformance_tests:: tests_iter ( & tests_dir) . unwrap ( ) {
25- if test. name . starts_with ( "tcp" ) {
26- // Skip TCP tests for now as shim cannot create sockets
27- continue ;
28- }
29- println ! ( "running test: {}" , test. name) ;
30- let mut services = vec ! [ "registry" . into( ) ] ;
31- for precondition in & test. config . preconditions {
32- match precondition {
33- conformance_tests:: config:: Precondition :: HttpEcho => {
34- services. push ( "http-echo" . into ( ) ) ;
35- }
36- conformance_tests:: config:: Precondition :: KeyValueStore ( k) => {
37- if k. label != "default" {
38- panic ! ( "unsupported label: {}" , k. label) ;
39- }
40- }
41- conformance_tests:: config:: Precondition :: TcpEcho => {
42- services. push ( "tcp-echo" . into ( ) ) ;
43- }
28+ fn run_test (
29+ test : conformance_tests:: Test ,
30+ spin_binary : & std:: path:: Path ,
31+ ctr_binary : & std:: path:: Path ,
32+ ) -> anyhow:: Result < ( ) > {
33+ println ! ( "running test: {}" , test. name) ;
34+ let mut services = vec ! [ "registry" . into( ) ] ;
35+ for precondition in & test. config . preconditions {
36+ match precondition {
37+ conformance_tests:: config:: Precondition :: HttpEcho => {
38+ services. push ( "http-echo" . into ( ) ) ;
4439 }
45- }
46- let env_config = SpinShim :: config (
47- ctr_binary. into ( ) ,
48- spin_binary. into ( ) ,
49- test. name . clone ( ) ,
50- test_environment:: services:: ServicesConfig :: new ( services) . unwrap ( ) ,
51- ) ;
52- let mut env = TestEnvironment :: up ( env_config, move |e| {
53- let mut manifest =
54- test_environment:: manifest_template:: EnvTemplate :: from_file ( & test. manifest )
55- . unwrap ( ) ;
56- manifest. substitute ( e, |_| None ) . unwrap ( ) ;
57- e. write_file ( "spin.toml" , manifest. contents ( ) ) ?;
58- e. copy_into ( & test. component , test. component . file_name ( ) . unwrap ( ) ) ?;
59- Ok ( ( ) )
60- } )
61- . unwrap ( ) ;
62- for invocation in test. config . invocations {
63- let conformance_tests:: config:: Invocation :: Http ( mut invocation) = invocation;
64- invocation. request . substitute_from_env ( & mut env) . unwrap ( ) ;
65- let shim = env. runtime_mut ( ) ;
66- if let Err ( e) = invocation. run ( |request| shim. make_http_request ( request) ) {
67- println ! ( "❌ test failed: {}" , test. name) ;
68- println ! ( "error: {}" , e) ;
69- for e in e. chain ( ) {
70- println ! ( "\t {}" , e) ;
40+ conformance_tests:: config:: Precondition :: KeyValueStore ( k) => {
41+ if k. label != "default" {
42+ panic ! ( "unsupported label: {}" , k. label) ;
7143 }
72-
73- continue ' test;
44+ }
45+ conformance_tests:: config:: Precondition :: TcpEcho => {
46+ services. push ( "tcp-echo" . into ( ) ) ;
47+ }
48+ conformance_tests:: config:: Precondition :: Sqlite => { }
49+ conformance_tests:: config:: Precondition :: Redis => {
50+ services. push ( "redis" . into ( ) ) ;
7451 }
7552 }
76- println ! ( "✅ test passed: {}" , test. name) ;
7753 }
54+ let env_config = SpinShim :: config (
55+ ctr_binary. into ( ) ,
56+ spin_binary. into ( ) ,
57+ test. name . clone ( ) ,
58+ test_environment:: services:: ServicesConfig :: new ( services) . unwrap ( ) ,
59+ & test. name ,
60+ ) ;
61+ let mut env = TestEnvironment :: up ( env_config, move |e| {
62+ let mut manifest =
63+ test_environment:: manifest_template:: EnvTemplate :: from_file ( & test. manifest ) . unwrap ( ) ;
64+ manifest. substitute ( e, |_| None ) . unwrap ( ) ;
65+ e. write_file ( "spin.toml" , manifest. contents ( ) ) ?;
66+ e. copy_into ( & test. component , test. component . file_name ( ) . unwrap ( ) ) ?;
67+ Ok ( ( ) )
68+ } )
69+ . unwrap ( ) ;
70+ for invocation in test. config . invocations {
71+ let conformance_tests:: config:: Invocation :: Http ( mut invocation) = invocation;
72+ invocation. request . substitute_from_env ( & mut env) . unwrap ( ) ;
73+ let shim = env. runtime_mut ( ) ;
74+ invocation. run ( |request| shim. make_http_request ( request) ) ?;
75+ }
76+ Ok ( ( ) )
7877}
7978
8079struct SpinShim {
@@ -85,17 +84,31 @@ struct SpinShim {
8584 io_mode : IoMode ,
8685}
8786
88- /// `ctr run` invocations require an ID that is unique to all currently running instances. Since
89- /// only one test runs at a time, we can reuse a constant ID.
90- const CTR_RUN_ID : & str = "run-id" ;
87+ fn hash < T > ( obj : T ) -> u64
88+ where
89+ T : Hash ,
90+ {
91+ let mut hasher = DefaultHasher :: new ( ) ;
92+ obj. hash ( & mut hasher) ;
93+ hasher. finish ( )
94+ }
95+
96+ /// Uses a track to get a random unused port
97+ fn get_available_port ( ) -> anyhow:: Result < u16 > {
98+ Ok ( std:: net:: TcpListener :: bind ( "localhost:0" ) ?
99+ . local_addr ( ) ?
100+ . port ( ) )
101+ }
91102
92103impl SpinShim {
93104 pub fn config (
94105 ctr_binary : PathBuf ,
95106 spin_binary : PathBuf ,
96107 oci_image : String ,
97108 services_config : ServicesConfig ,
109+ test_id : & str ,
98110 ) -> TestEnvironmentConfig < SpinShim > {
111+ let ctr_run_id = hash ( test_id) . to_string ( ) ;
99112 TestEnvironmentConfig {
100113 services_config,
101114 create_runtime : Box :: new ( move |env| {
@@ -107,7 +120,7 @@ impl SpinShim {
107120 ) ;
108121 SpinShim :: registry_push ( & spin_binary, & oci_image, env) ?;
109122 SpinShim :: image_pull ( & ctr_binary, & oci_image) ?;
110- SpinShim :: start ( & ctr_binary, env, & oci_image, CTR_RUN_ID )
123+ SpinShim :: start ( & ctr_binary, env, & oci_image, & ctr_run_id )
111124 } ) ,
112125 }
113126 }
@@ -125,12 +138,12 @@ impl SpinShim {
125138 }
126139
127140 pub fn image_pull ( ctr_binary_path : & Path , image : & str ) -> anyhow:: Result < ( ) > {
128- Command :: new ( ctr_binary_path)
141+ let output = Command :: new ( ctr_binary_path)
129142 . args ( [ "image" , "pull" ] )
130143 . arg ( image)
131144 . output ( )
132145 . context ( "failed to pull spin app with 'ctr'" ) ?;
133- // TODO: assess output
146+ anyhow :: ensure! ( output . status . success ( ) , "pulling image failed" ) ;
134147 Ok ( ( ) )
135148 }
136149
@@ -141,13 +154,21 @@ impl SpinShim {
141154 image : & str ,
142155 ctr_run_id : & str ,
143156 ) -> anyhow:: Result < Self > {
144- // TODO: consider enabling configuring a port
145- let port = 80 ;
157+ let port = get_available_port ( ) . context ( "no available port" ) ? ;
158+ let listen_adress_env = format ! ( "SPIN_HTTP_LISTEN_ADDR=0.0.0.0:{}" , port ) ;
146159 let mut ctr_cmd = std:: process:: Command :: new ( ctr_binary_path) ;
147160 let child = ctr_cmd
148161 . arg ( "run" )
149- . args ( [ "--rm" , "--net-host" , "--runtime" , "io.containerd.spin.v2" ] )
162+ . args ( [
163+ "--rm" ,
164+ "--net-host" ,
165+ "--runtime" ,
166+ "io.containerd.spin.v2" ,
167+ "--env" ,
168+ ] )
169+ . arg ( listen_adress_env)
150170 . arg ( image)
171+ // `ctr run` invocations require an ID that is unique to all currently running instances
151172 . arg ( ctr_run_id)
152173 // The container runtime expects at least one argument to the container
153174 . arg ( "bogus-arg" )
@@ -199,9 +220,9 @@ impl SpinShim {
199220 )
200221 }
201222
202- /// Make an HTTP request against Spin
223+ /// Make an HTTP request against the shim.
203224 ///
204- /// Will fail if Spin has already exited or if the io mode is not HTTP
225+ /// Will fail if the shim has already exited.
205226 pub fn make_http_request ( & mut self , request : Request < ' _ , String > ) -> anyhow:: Result < Response > {
206227 let IoMode :: Http ( port) = self . io_mode ;
207228 if let Some ( status) = self . try_wait ( ) ? {
0 commit comments