@@ -13,7 +13,9 @@ use clap::{CommandFactory, Parser};
13
13
use reqwest:: Url ;
14
14
use spin_app:: locked:: LockedApp ;
15
15
use spin_common:: ui:: quoted_path;
16
- use spin_factor_outbound_networking:: validate_service_chaining_for_components;
16
+ use spin_factor_outbound_networking:: {
17
+ update_service_chaining_host_requirement, validate_service_chaining_for_components,
18
+ } ;
17
19
use spin_loader:: FilesMountStrategy ;
18
20
use spin_oci:: OciLoader ;
19
21
use spin_trigger:: cli:: { LaunchMetadata , SPIN_LOCAL_APP_DIR , SPIN_LOCKED_URL , SPIN_WORKING_DIR } ;
@@ -184,7 +186,7 @@ impl UpCommand {
184
186
return Ok ( ( ) ) ;
185
187
}
186
188
for cmd in trigger_cmds {
187
- let mut help_process = self . start_trigger ( cmd. clone ( ) , None , & [ ] ) . await ?;
189
+ let mut help_process = self . start_trigger ( cmd, None , & [ ] ) . await ?;
188
190
_ = help_process. wait ( ) . await ;
189
191
}
190
192
return Ok ( ( ) ) ;
@@ -213,6 +215,8 @@ impl UpCommand {
213
215
) ?;
214
216
}
215
217
218
+ self . update_locked_app ( & mut locked_app) ;
219
+
216
220
let trigger_types: HashSet < & str > = locked_app
217
221
. triggers
218
222
. iter ( )
@@ -225,13 +229,10 @@ impl UpCommand {
225
229
. with_context ( || format ! ( "Couldn't find trigger executor for {app_source}" ) ) ?;
226
230
let is_multi = trigger_cmds. len ( ) > 1 ;
227
231
228
- self . update_locked_app ( & mut locked_app) ;
229
- let locked_url = self . write_locked_app ( & locked_app, & working_dir) . await ?;
230
-
231
232
let local_app_dir = app_source. local_app_dir ( ) . map ( Into :: into) ;
232
233
233
234
let run_opts = RunTriggerOpts {
234
- locked_url ,
235
+ locked_app : locked_app . clone ( ) ,
235
236
working_dir,
236
237
local_app_dir,
237
238
} ;
@@ -284,26 +285,26 @@ impl UpCommand {
284
285
285
286
async fn get_trigger_launch_metas (
286
287
& self ,
287
- trigger_cmds : & [ Vec < String > ] ,
288
+ trigger_cmds : & [ TriggerCommand < ' _ > ] ,
288
289
) -> anyhow:: Result < HashMap < Vec < String > , LaunchMetadata > > {
289
290
let mut metas = HashMap :: new ( ) ;
290
291
291
- for trigger_cmd in trigger_cmds {
292
+ for t in trigger_cmds {
292
293
let mut meta_cmd = tokio:: process:: Command :: new ( std:: env:: current_exe ( ) . unwrap ( ) ) ;
293
- meta_cmd. args ( trigger_cmd ) ;
294
+ meta_cmd. args ( & t . cmd ) ;
294
295
meta_cmd. arg ( "--launch-metadata-only" ) ;
295
296
meta_cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ;
296
297
let meta_out = meta_cmd. spawn ( ) ?. wait_with_output ( ) . await ?;
297
298
let meta = serde_json:: from_slice :: < LaunchMetadata > ( & meta_out. stderr ) ?;
298
- metas. insert ( trigger_cmd . clone ( ) , meta) ;
299
+ metas. insert ( t . cmd . clone ( ) , meta) ;
299
300
}
300
301
301
302
Ok ( metas)
302
303
}
303
304
304
305
async fn start_trigger_processes (
305
306
self ,
306
- trigger_cmds : Vec < Vec < String > > ,
307
+ trigger_cmds : Vec < TriggerCommand < ' _ > > ,
307
308
run_opts : RunTriggerOpts ,
308
309
) -> anyhow:: Result < Vec < tokio:: process:: Child > > {
309
310
let is_multi = trigger_cmds. len ( ) > 1 ;
@@ -333,13 +334,13 @@ impl UpCommand {
333
334
let mut trigger_processes = Vec :: with_capacity ( trigger_cmds. len ( ) ) ;
334
335
335
336
for cmd in trigger_cmds {
336
- let meta = trigger_metas. as_ref ( ) . and_then ( |ms| ms. get ( & cmd) ) ;
337
+ let meta = trigger_metas. as_ref ( ) . and_then ( |ms| ms. get ( & cmd. cmd ) ) ;
337
338
let trigger_args = match meta {
338
339
Some ( m) => m. matches ( & trigger_args) ,
339
340
None => self . trigger_args . iter ( ) . collect ( ) ,
340
341
} ;
341
342
let child = self
342
- . start_trigger ( cmd. clone ( ) , Some ( run_opts. clone ( ) ) , & trigger_args)
343
+ . start_trigger ( cmd, Some ( run_opts. clone ( ) ) , & trigger_args)
343
344
. await
344
345
. context ( "Failed to start trigger process" ) ?;
345
346
trigger_processes. push ( child) ;
@@ -357,21 +358,25 @@ impl UpCommand {
357
358
358
359
async fn start_trigger (
359
360
& self ,
360
- trigger_cmd : Vec < String > ,
361
+ trigger : TriggerCommand < ' _ > ,
361
362
opts : Option < RunTriggerOpts > ,
362
363
trigger_args : & [ & OsString ] ,
363
364
) -> Result < tokio:: process:: Child , anyhow:: Error > {
364
365
// The docs for `current_exe` warn that this may be insecure because it could be executed
365
366
// via hard-link. I think it should be fine as long as we aren't `setuid`ing this binary.
366
367
let mut cmd = tokio:: process:: Command :: new ( std:: env:: current_exe ( ) . unwrap ( ) ) ;
367
- cmd. args ( & trigger_cmd ) ;
368
+ cmd. args ( & trigger . cmd ) ;
368
369
369
370
if let Some ( RunTriggerOpts {
370
- locked_url ,
371
+ locked_app ,
371
372
working_dir,
372
373
local_app_dir,
373
374
} ) = opts
374
375
{
376
+ let locked_url = self
377
+ . write_locked_app_for_trigger ( trigger. type_ , & locked_app, & working_dir)
378
+ . await ?;
379
+
375
380
cmd. env ( SPIN_LOCKED_URL , locked_url)
376
381
. env ( SPIN_WORKING_DIR , & working_dir)
377
382
. args ( trigger_args) ;
@@ -430,19 +435,33 @@ impl UpCommand {
430
435
!self . trigger_args . is_empty ( ) && !self . trigger_args [ 0 ] . to_string_lossy ( ) . starts_with ( '-' )
431
436
}
432
437
433
- async fn write_locked_app (
438
+ async fn write_locked_app_for_trigger (
434
439
& self ,
440
+ trigger_type : & str ,
435
441
locked_app : & LockedApp ,
436
442
working_dir : & Path ,
437
443
) -> Result < String , anyhow:: Error > {
438
- let locked_path = working_dir. join ( "spin.lock" ) ;
444
+ let locked_app_for_trigger = get_locked_app_for_trigger ( trigger_type, locked_app) ?;
445
+ let locked_path = working_dir. join ( format ! ( "spin-{trigger_type}.lock" ) ) ;
446
+ Self :: write_locked_app ( & locked_app_for_trigger, & locked_path) . await
447
+ }
448
+
449
+ async fn write_locked_app (
450
+ locked_app : & LockedApp ,
451
+ lock_file_path : & Path ,
452
+ ) -> Result < String , anyhow:: Error > {
439
453
let locked_app_contents =
440
454
serde_json:: to_vec_pretty ( & locked_app) . context ( "failed to serialize locked app" ) ?;
441
- tokio:: fs:: write ( & locked_path , locked_app_contents)
455
+ tokio:: fs:: write ( & lock_file_path , locked_app_contents)
442
456
. await
443
- . with_context ( || format ! ( "failed to write {}" , quoted_path( & locked_path) ) ) ?;
444
- let locked_url = Url :: from_file_path ( & locked_path)
445
- . map_err ( |_| anyhow ! ( "cannot convert to file URL: {}" , quoted_path( & locked_path) ) ) ?
457
+ . with_context ( || format ! ( "failed to write {}" , quoted_path( & lock_file_path) ) ) ?;
458
+ let locked_url = Url :: from_file_path ( lock_file_path)
459
+ . map_err ( |_| {
460
+ anyhow ! (
461
+ "cannot convert to file URL: {}" ,
462
+ quoted_path( & lock_file_path)
463
+ )
464
+ } ) ?
446
465
. to_string ( ) ;
447
466
448
467
Ok ( locked_url)
@@ -550,6 +569,23 @@ impl UpCommand {
550
569
}
551
570
}
552
571
572
+ fn get_locked_app_for_trigger (
573
+ trigger_type : & str ,
574
+ locked_app : & LockedApp ,
575
+ ) -> Result < LockedApp , anyhow:: Error > {
576
+ let components = locked_app
577
+ . triggers
578
+ . iter ( )
579
+ . filter ( |t| t. trigger_type == trigger_type)
580
+ . filter_map ( |t| t. trigger_config . get ( "component" ) )
581
+ . filter_map ( |j| j. as_str ( ) )
582
+ . collect :: < Vec < _ > > ( ) ;
583
+ let mut locked_app_for_trigger =
584
+ spin_app:: retain_components ( locked_app. clone ( ) , & components, & [ ] ) ?;
585
+ update_service_chaining_host_requirement ( & mut locked_app_for_trigger) ;
586
+ Ok ( locked_app_for_trigger)
587
+ }
588
+
553
589
fn is_flag_arg ( arg : & OsString ) -> bool {
554
590
if let Some ( s) = arg. to_str ( ) {
555
591
s. starts_with ( '-' )
@@ -602,7 +638,7 @@ fn kill_child_processes(pids: &[nix::unistd::Pid]) {
602
638
603
639
#[ derive( Clone ) ]
604
640
struct RunTriggerOpts {
605
- locked_url : String ,
641
+ locked_app : LockedApp ,
606
642
working_dir : PathBuf ,
607
643
local_app_dir : Option < PathBuf > ,
608
644
}
@@ -663,18 +699,29 @@ fn resolve_trigger_plugin(trigger_type: &str) -> Result<String> {
663
699
}
664
700
}
665
701
666
- fn trigger_command ( trigger_type : & str ) -> Vec < String > {
667
- vec ! [ "trigger" . to_owned( ) , trigger_type. to_owned( ) ]
702
+ struct TriggerCommand < ' a > {
703
+ type_ : & ' a str ,
704
+ cmd : Vec < String > ,
705
+ }
706
+
707
+ fn trigger_command ( trigger_type : & str ) -> TriggerCommand {
708
+ TriggerCommand {
709
+ type_ : trigger_type,
710
+ cmd : vec ! [ "trigger" . to_owned( ) , trigger_type. to_owned( ) ] ,
711
+ }
668
712
}
669
713
670
- fn trigger_commands_for_trigger_types ( trigger_types : Vec < & str > ) -> Result < Vec < Vec < String > > > {
714
+ fn trigger_commands_for_trigger_types ( trigger_types : Vec < & str > ) -> Result < Vec < TriggerCommand > > {
671
715
trigger_types
672
716
. iter ( )
673
717
. map ( |& t| match t {
674
718
"http" | "redis" => Ok ( trigger_command ( t) ) ,
675
719
_ => {
676
720
let cmd = resolve_trigger_plugin ( t) ?;
677
- Ok ( vec ! [ cmd] )
721
+ Ok ( TriggerCommand {
722
+ type_ : t,
723
+ cmd : vec ! [ cmd] ,
724
+ } )
678
725
}
679
726
} )
680
727
. collect ( )
0 commit comments