55
66use azure_core:: { error:: ErrorKind , Result , Url } ;
77use std:: {
8- env, io,
8+ env, fmt , io,
99 path:: Path ,
1010 process:: { ExitStatus , Stdio } ,
11+ str:: FromStr ,
1112 sync:: Arc ,
1213 time:: Duration ,
1314} ;
@@ -23,6 +24,11 @@ const KESTREL_CERT_PASSWORD_ENV: &str = "ASPNETCORE_Kestrel__Certificates__Defau
2324const KESTREL_CERT_PASSWORD : & str = "password" ;
2425const PROXY_MANUAL_START : & str = "PROXY_MANUAL_START" ;
2526const SYSTEM_TEAMPROJECTID : & str = "SYSTEM_TEAMPROJECTID" ;
27+ const MIN_VERSION : Version = Version {
28+ major : 20241213 ,
29+ minor : 1 ,
30+ metadata : None ,
31+ } ;
2632
2733pub async fn start (
2834 test_data_dir : impl AsRef < Path > ,
@@ -91,10 +97,27 @@ async fn wait_till_listening(stdout: &mut Option<ChildStdout>) -> Result<Url> {
9197 // Wait for the process to respond to requests and check output until pattern: "Now listening on: http://0.0.0.0:60583" (random port).
9298 let mut reader = BufReader :: new ( stdout) . lines ( ) ;
9399 while let Some ( line) = reader. next_line ( ) . await ? {
94- const PATTERN : & str = "Now listening on: " ;
100+ const RUNNING_PATTERN : & str = "Running proxy version is Azure.Sdk.Tools.TestProxy " ;
101+ const LISTENING_PATTERN : & str = "Now listening on: " ;
102+
103+ if let Some ( idx) = line. find ( RUNNING_PATTERN ) {
104+ let idx = idx + RUNNING_PATTERN . len ( ) ;
105+ let version: Version = line[ idx..] . parse ( ) ?;
106+ tracing:: event!( target: crate :: SPAN_TARGET , Level :: INFO , "starting test-proxy version {version}" ) ;
107+
108+ // Need to check version since `test-proxy start` does not fail with unknown parameters.
109+ if version < MIN_VERSION {
110+ return Err ( azure_core:: Error :: message (
111+ ErrorKind :: Io ,
112+ format ! ( "test-proxy older than required version {MIN_VERSION}" ) ,
113+ ) ) ;
114+ }
115+
116+ continue ;
117+ }
95118
96- if let Some ( idx) = line. find ( PATTERN ) {
97- let idx = idx + PATTERN . len ( ) ;
119+ if let Some ( idx) = line. find ( LISTENING_PATTERN ) {
120+ let idx = idx + LISTENING_PATTERN . len ( ) ;
98121 let url = line[ idx..] . parse ( ) ?;
99122 tracing:: event!( target: crate :: SPAN_TARGET , Level :: INFO , "listening on {url}" ) ;
100123
@@ -166,13 +189,21 @@ impl Drop for Proxy {
166189pub struct ProxyOptions {
167190 /// Allow insecure upstream SSL certs.
168191 pub insecure : bool ,
192+
193+ /// Number of seconds to automatically shut down when no activity.
194+ pub auto_shutdown_in_seconds : u32 ,
169195}
170196
171197impl ProxyOptions {
172198 fn copy_to ( & self , args : & mut Vec < String > ) {
173199 if self . insecure {
174200 args. push ( "--insecure" . into ( ) ) ;
175201 }
202+
203+ args. extend_from_slice ( & [
204+ "--auto-shutdown-in-seconds" . into ( ) ,
205+ self . auto_shutdown_in_seconds . to_string ( ) ,
206+ ] ) ;
176207 }
177208}
178209
@@ -184,3 +215,136 @@ pub struct Session {
184215 #[ allow( dead_code) ]
185216 pub ( crate ) span : Span ,
186217}
218+
219+ #[ derive( Debug , Default , Eq , PartialEq , Ord , PartialOrd ) ]
220+ struct Version {
221+ major : i32 ,
222+ minor : i32 ,
223+ metadata : Option < String > ,
224+ }
225+
226+ impl fmt:: Display for Version {
227+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
228+ if let Some ( metadata) = & self . metadata {
229+ return write ! ( f, "{}.{}-{metadata}" , self . major, self . minor) ;
230+ }
231+ write ! ( f, "{}.{}" , self . major, self . minor)
232+ }
233+ }
234+
235+ impl FromStr for Version {
236+ type Err = azure_core:: Error ;
237+
238+ fn from_str ( s : & str ) -> std:: result:: Result < Self , Self :: Err > {
239+ let mut v = Version :: default ( ) ;
240+
241+ // cspell:ignore splitn
242+ let mut cur = s. splitn ( 2 , '.' ) ;
243+ if let Some ( major) = cur. next ( ) {
244+ v. major = major. parse ( ) ?;
245+ } else {
246+ return Err ( azure_core:: Error :: message (
247+ ErrorKind :: DataConversion ,
248+ "major version required" ,
249+ ) ) ;
250+ }
251+ if let Some ( minor) = cur. next ( ) {
252+ let mut cur = minor. splitn ( 2 , '-' ) ;
253+ if let Some ( minor) = cur. next ( ) {
254+ v. minor = minor. parse ( ) ?;
255+ }
256+ v. metadata = cur. next ( ) . map ( String :: from) ;
257+ }
258+
259+ Ok ( v)
260+ }
261+ }
262+
263+ #[ cfg( test) ]
264+ mod tests {
265+ use super :: * ;
266+
267+ #[ test]
268+ fn version_eq ( ) {
269+ let a = Version {
270+ major : 1 ,
271+ ..Default :: default ( )
272+ } ;
273+ let b = Version {
274+ major : 1 ,
275+ ..Default :: default ( )
276+ } ;
277+ assert_eq ! ( a, b) ;
278+
279+ let a = Version {
280+ major : 1 ,
281+ minor : 2 ,
282+ metadata : Some ( "preview" . into ( ) ) ,
283+ } ;
284+ let b = Version {
285+ major : 1 ,
286+ minor : 2 ,
287+ metadata : Some ( "preview" . into ( ) ) ,
288+ } ;
289+ assert_eq ! ( a, b) ;
290+ }
291+
292+ #[ test]
293+ fn version_cmp ( ) {
294+ let a = Version {
295+ major : 20240107 ,
296+ minor : 1 ,
297+ ..Default :: default ( )
298+ } ;
299+ let b = Version {
300+ major : 20240107 ,
301+ minor : 2 ,
302+ ..Default :: default ( )
303+ } ;
304+ let c = Version {
305+ major : 20240109 ,
306+ minor : 1 ,
307+ metadata : Some ( "1" . into ( ) ) ,
308+ } ;
309+ let d = Version {
310+ major : 20240109 ,
311+ minor : 1 ,
312+ metadata : Some ( "2" . into ( ) ) ,
313+ } ;
314+ assert ! ( a == a) ;
315+ assert ! ( a < b) ;
316+ assert ! ( b > a) ;
317+ assert ! ( b < c) ;
318+ assert ! ( c != d) ;
319+ assert ! ( c < d) ;
320+ }
321+
322+ #[ test]
323+ fn version_fmt ( ) {
324+ let mut v = Version {
325+ major : 1 ,
326+ ..Default :: default ( )
327+ } ;
328+ assert_eq ! ( v. to_string( ) , "1.0" ) ;
329+
330+ v. minor = 2 ;
331+ v. metadata = Some ( "preview" . into ( ) ) ;
332+ assert_eq ! ( v. to_string( ) , "1.2-preview" ) ;
333+ }
334+
335+ #[ test]
336+ fn version_parse ( ) {
337+ let mut v = Version {
338+ major : 1 ,
339+ ..Default :: default ( )
340+ } ;
341+ assert ! ( matches!( "1" . parse:: <Version >( ) , Ok ( ver) if ver == v) ) ;
342+ assert ! ( matches!( "1.0" . parse:: <Version >( ) , Ok ( ver) if ver == v) ) ;
343+
344+ v. minor = 2 ;
345+ assert ! ( matches!( "1.2" . parse:: <Version >( ) , Ok ( ver) if ver == v) ) ;
346+
347+ v. metadata = Some ( "preview" . into ( ) ) ;
348+ assert ! ( matches!( "1.2-preview" . parse:: <Version >( ) , Ok ( ver) if ver == v) ) ;
349+ }
350+ }
0 commit comments