55
66#![ allow( clippy:: result_large_err) ]
77
8+ use bollard:: Docker ;
89use bollard:: errors:: Error as BollardError ;
910use bollard:: models:: {
1011 ContainerCreateBody , ContainerState , ContainerStateStatusEnum , ContainerSummary ,
@@ -15,9 +16,7 @@ use bollard::models::{
1516use bollard:: query_parameters:: {
1617 CreateContainerOptionsBuilder , CreateImageOptions , DownloadFromContainerOptionsBuilder ,
1718 ListContainersOptionsBuilder , RemoveContainerOptionsBuilder , StopContainerOptionsBuilder ,
18- UploadToContainerOptionsBuilder ,
1919} ;
20- use bollard:: { Docker , body_full} ;
2120use bytes:: Bytes ;
2221use futures:: { Stream , StreamExt } ;
2322use openshell_core:: cdi:: { CdiContext , CdiSpecDirectory , cdi_spec_mount_path} ;
@@ -58,7 +57,6 @@ use openshell_core::proto_struct::{
5857} ;
5958use openshell_core:: { Config , Error , Result as CoreResult } ;
6059use std:: collections:: { HashMap , HashSet } ;
61- use std:: io:: Cursor ;
6260use std:: net:: { IpAddr , Ipv4Addr , SocketAddr } ;
6361use std:: path:: { Path , PathBuf } ;
6462use std:: pin:: Pin ;
@@ -861,11 +859,22 @@ impl DockerComputeDriver {
861859 . map_err ( |status| {
862860 DockerProvisioningFailure :: new ( "ContainerCreateFailed" , status. message ( ) )
863861 } ) ?;
864- write_sandbox_token_file ( sandbox, & self . config )
865- . await
866- . map_err ( |status| {
867- DockerProvisioningFailure :: new ( "SandboxTokenWriteFailed" , status. message ( ) )
868- } ) ?;
862+ if let Some ( cdi_context) = cdi_context. as_ref ( )
863+ && let Err ( status) = write_cdi_context_file ( sandbox, & self . config , cdi_context)
864+ {
865+ cleanup_cdi_context_file ( sandbox, & self . config ) ;
866+ return Err ( DockerProvisioningFailure :: new (
867+ "CdiContextWriteFailed" ,
868+ status. message ( ) ,
869+ ) ) ;
870+ }
871+ if let Err ( status) = write_sandbox_token_file ( sandbox, & self . config ) . await {
872+ cleanup_cdi_context_file ( sandbox, & self . config ) ;
873+ return Err ( DockerProvisioningFailure :: new (
874+ "SandboxTokenWriteFailed" ,
875+ status. message ( ) ,
876+ ) ) ;
877+ }
869878 if let Err ( err) = self
870879 . docker
871880 . create_container (
@@ -878,7 +887,7 @@ impl DockerComputeDriver {
878887 )
879888 . await
880889 {
881- cleanup_sandbox_token_file ( sandbox, & self . config ) ;
890+ cleanup_sandbox_state_files ( sandbox, & self . config ) ;
882891 return Err ( DockerProvisioningFailure :: from_status (
883892 "ContainerCreateFailed" ,
884893 create_status_from_docker_error ( "create docker sandbox container" , err) ,
@@ -891,30 +900,14 @@ impl DockerComputeDriver {
891900 HashMap :: from ( [ ( "container_name" . to_string ( ) , container_name. clone ( ) ) ] ) ,
892901 ) ;
893902
894- if let Some ( cdi_context) = cdi_context
895- && let Err ( err) = self . upload_cdi_context ( & container_name, & cdi_context) . await
896- {
897- self . cleanup_created_container_after_failure (
898- & sandbox. id ,
899- & container_name,
900- "CDI context upload failure" ,
901- )
902- . await ;
903- cleanup_sandbox_token_file ( sandbox, & self . config ) ;
904- return Err ( DockerProvisioningFailure :: from_status (
905- "CdiContextUploadFailed" ,
906- err,
907- ) ) ;
908- }
909-
910903 if let Err ( err) = self . docker . start_container ( & container_name, None ) . await {
911904 self . cleanup_created_container_after_failure (
912905 & sandbox. id ,
913906 & container_name,
914907 "container start failure" ,
915908 )
916909 . await ;
917- cleanup_sandbox_token_file ( sandbox, & self . config ) ;
910+ cleanup_sandbox_state_files ( sandbox, & self . config ) ;
918911 return Err ( DockerProvisioningFailure :: from_status (
919912 "ContainerStartFailed" ,
920913 create_status_from_docker_error ( "start docker sandbox container" , err) ,
@@ -964,27 +957,6 @@ impl DockerComputeDriver {
964957 }
965958 }
966959
967- async fn upload_cdi_context (
968- & self ,
969- container_name : & str ,
970- context : & CdiContext ,
971- ) -> Result < ( ) , Status > {
972- let archive = build_cdi_context_archive ( context) . map_err ( Status :: internal) ?;
973- self . docker
974- . upload_to_container (
975- container_name,
976- Some (
977- UploadToContainerOptionsBuilder :: default ( )
978- . path ( "/run" )
979- . no_overwrite_dir_non_dir ( "true" )
980- . build ( ) ,
981- ) ,
982- body_full ( Bytes :: from ( archive) ) ,
983- )
984- . await
985- . map_err ( |err| internal_status ( "upload CDI context to Docker container" , err) )
986- }
987-
988960 async fn delete_sandbox_inner (
989961 & self ,
990962 sandbox_id : & str ,
@@ -1012,11 +984,11 @@ impl DockerComputeDriver {
1012984 . await
1013985 {
1014986 Ok ( ( ) ) => {
1015- cleanup_sandbox_token_file ( & record. sandbox , & self . config ) ;
987+ cleanup_sandbox_state_files ( & record. sandbox , & self . config ) ;
1016988 return Ok ( true ) ;
1017989 }
1018990 Err ( err) if is_not_found_error ( & err) => {
1019- cleanup_sandbox_token_file ( & record. sandbox , & self . config ) ;
991+ cleanup_sandbox_state_files ( & record. sandbox , & self . config ) ;
1020992 return Ok ( true ) ;
1021993 }
1022994 Err ( err) => {
@@ -1039,11 +1011,11 @@ impl DockerComputeDriver {
10391011 . await
10401012 {
10411013 Ok ( ( ) ) => {
1042- cleanup_sandbox_token_file_for_delete ( sandbox_id, pending. as_ref ( ) , & self . config ) ;
1014+ cleanup_sandbox_state_files_for_delete ( sandbox_id, pending. as_ref ( ) , & self . config ) ;
10431015 Ok ( true )
10441016 }
10451017 Err ( err) if is_not_found_error ( & err) => {
1046- cleanup_sandbox_token_file_for_delete ( sandbox_id, pending. as_ref ( ) , & self . config ) ;
1018+ cleanup_sandbox_state_files_for_delete ( sandbox_id, pending. as_ref ( ) , & self . config ) ;
10471019 Ok ( pending. is_some ( ) )
10481020 }
10491021 Err ( err) => Err ( internal_status ( "delete docker sandbox container" , err) ) ,
@@ -1059,7 +1031,7 @@ impl DockerComputeDriver {
10591031 if let Some ( task) = record. task {
10601032 task. abort ( ) ;
10611033 }
1062- cleanup_sandbox_token_file ( & record. sandbox , & self . config ) ;
1034+ cleanup_sandbox_state_files ( & record. sandbox , & self . config ) ;
10631035 self . publish_deleted ( record. sandbox . id ) ;
10641036 return Ok ( ( ) ) ;
10651037 }
@@ -1287,7 +1259,7 @@ impl DockerComputeDriver {
12871259 sandbox : & DriverSandbox ,
12881260 failure : & DockerProvisioningFailure ,
12891261 ) {
1290- cleanup_sandbox_token_file ( sandbox, & self . config ) ;
1262+ cleanup_sandbox_state_files ( sandbox, & self . config ) ;
12911263 let snapshot = pending_sandbox_snapshot (
12921264 sandbox,
12931265 & self . config . sandbox_namespace ,
@@ -2375,6 +2347,7 @@ fn docker_volume_is_bind_backed(volume: &bollard::models::Volume) -> bool {
23752347fn build_binds (
23762348 sandbox : & DriverSandbox ,
23772349 config : & DockerDriverRuntimeConfig ,
2350+ gpu_device_ids : Option < & [ String ] > ,
23782351) -> Result < Vec < String > , Status > {
23792352 let mut binds = vec ! [ format!(
23802353 "{}:{}:ro,z" ,
@@ -2401,6 +2374,13 @@ fn build_binds(
24012374 SANDBOX_TOKEN_MOUNT_PATH
24022375 ) ) ;
24032376 }
2377+ if cdi_context_requested ( gpu_device_ids) {
2378+ binds. push ( format ! (
2379+ "{}:{}:ro,z" ,
2380+ cdi_context_host_path( sandbox, config) ?. display( ) ,
2381+ openshell_core:: cdi:: CDI_CONTEXT_PATH
2382+ ) ) ;
2383+ }
24042384 Ok ( binds)
24052385}
24062386
@@ -2427,6 +2407,57 @@ fn sandbox_token_host_path_by_id(
24272407 } )
24282408}
24292409
2410+ fn cdi_context_host_path (
2411+ sandbox : & DriverSandbox ,
2412+ config : & DockerDriverRuntimeConfig ,
2413+ ) -> Result < PathBuf , Status > {
2414+ cdi_context_host_path_by_id ( & sandbox. id , config)
2415+ }
2416+
2417+ fn cdi_context_host_path_by_id (
2418+ sandbox_id : & str ,
2419+ config : & DockerDriverRuntimeConfig ,
2420+ ) -> Result < PathBuf , Status > {
2421+ openshell_core:: driver_utils:: sandbox_token_path (
2422+ "docker-cdi-contexts" ,
2423+ Some ( & config. sandbox_namespace ) ,
2424+ sandbox_id,
2425+ )
2426+ . map ( |path| path. with_file_name ( openshell_core:: cdi:: CDI_CONTEXT_FILE_NAME ) )
2427+ . map_err ( |err| Status :: internal ( format ! ( "resolve CDI context state directory failed: {err}" ) ) )
2428+ }
2429+
2430+ fn write_cdi_context_file (
2431+ sandbox : & DriverSandbox ,
2432+ config : & DockerDriverRuntimeConfig ,
2433+ context : & CdiContext ,
2434+ ) -> Result < ( ) , Status > {
2435+ let path = cdi_context_host_path ( sandbox, config) ?;
2436+ if let Some ( parent) = path. parent ( ) {
2437+ openshell_core:: paths:: create_dir_restricted ( parent) . map_err ( |err| {
2438+ Status :: internal ( format ! (
2439+ "create CDI context directory {} failed: {err}" ,
2440+ parent. display( )
2441+ ) )
2442+ } ) ?;
2443+ }
2444+ let json = serde_json:: to_vec ( context)
2445+ . map_err ( |err| Status :: internal ( format ! ( "encode CDI context failed: {err}" ) ) ) ?;
2446+ std:: fs:: write ( & path, json) . map_err ( |err| {
2447+ Status :: internal ( format ! (
2448+ "write CDI context file {} failed: {err}" ,
2449+ path. display( )
2450+ ) )
2451+ } ) ?;
2452+ openshell_core:: paths:: set_file_owner_only ( & path) . map_err ( |err| {
2453+ Status :: internal ( format ! (
2454+ "restrict CDI context file {} failed: {err}" ,
2455+ path. display( )
2456+ ) )
2457+ } ) ?;
2458+ Ok ( ( ) )
2459+ }
2460+
24302461async fn write_sandbox_token_file (
24312462 sandbox : & DriverSandbox ,
24322463 config : & DockerDriverRuntimeConfig ,
@@ -2467,6 +2498,15 @@ fn cleanup_sandbox_token_file(sandbox: &DriverSandbox, config: &DockerDriverRunt
24672498 cleanup_sandbox_token_file_by_id ( & sandbox. id , config) ;
24682499}
24692500
2501+ fn cleanup_cdi_context_file ( sandbox : & DriverSandbox , config : & DockerDriverRuntimeConfig ) {
2502+ cleanup_cdi_context_file_by_id ( & sandbox. id , config) ;
2503+ }
2504+
2505+ fn cleanup_sandbox_state_files ( sandbox : & DriverSandbox , config : & DockerDriverRuntimeConfig ) {
2506+ cleanup_sandbox_token_file ( sandbox, config) ;
2507+ cleanup_cdi_context_file ( sandbox, config) ;
2508+ }
2509+
24702510fn cleanup_sandbox_token_file_for_delete (
24712511 sandbox_id : & str ,
24722512 pending : Option < & PendingSandboxRecord > ,
@@ -2479,6 +2519,27 @@ fn cleanup_sandbox_token_file_for_delete(
24792519 }
24802520}
24812521
2522+ fn cleanup_cdi_context_file_for_delete (
2523+ sandbox_id : & str ,
2524+ pending : Option < & PendingSandboxRecord > ,
2525+ config : & DockerDriverRuntimeConfig ,
2526+ ) {
2527+ if !sandbox_id. is_empty ( ) {
2528+ cleanup_cdi_context_file_by_id ( sandbox_id, config) ;
2529+ } else if let Some ( record) = pending {
2530+ cleanup_cdi_context_file ( & record. sandbox , config) ;
2531+ }
2532+ }
2533+
2534+ fn cleanup_sandbox_state_files_for_delete (
2535+ sandbox_id : & str ,
2536+ pending : Option < & PendingSandboxRecord > ,
2537+ config : & DockerDriverRuntimeConfig ,
2538+ ) {
2539+ cleanup_sandbox_token_file_for_delete ( sandbox_id, pending, config) ;
2540+ cleanup_cdi_context_file_for_delete ( sandbox_id, pending, config) ;
2541+ }
2542+
24822543fn cleanup_sandbox_token_file_by_id ( sandbox_id : & str , config : & DockerDriverRuntimeConfig ) {
24832544 let Ok ( path) = sandbox_token_host_path_by_id ( sandbox_id, config) else {
24842545 return ;
@@ -2498,6 +2559,25 @@ fn cleanup_sandbox_token_file_by_id(sandbox_id: &str, config: &DockerDriverRunti
24982559 }
24992560}
25002561
2562+ fn cleanup_cdi_context_file_by_id ( sandbox_id : & str , config : & DockerDriverRuntimeConfig ) {
2563+ let Ok ( path) = cdi_context_host_path_by_id ( sandbox_id, config) else {
2564+ return ;
2565+ } ;
2566+ if let Err ( err) = std:: fs:: remove_file ( & path)
2567+ && err. kind ( ) != std:: io:: ErrorKind :: NotFound
2568+ {
2569+ warn ! (
2570+ sandbox_id = %sandbox_id,
2571+ path = %path. display( ) ,
2572+ error = %err,
2573+ "Failed to remove Docker CDI context file"
2574+ ) ;
2575+ }
2576+ if let Some ( dir) = path. parent ( ) {
2577+ let _ = std:: fs:: remove_dir ( dir) ;
2578+ }
2579+ }
2580+
25012581#[ cfg( test) ]
25022582fn build_environment (
25032583 sandbox : & DriverSandbox ,
@@ -2663,64 +2743,6 @@ fn cdi_context_requested(gpu_device_ids: Option<&[String]>) -> bool {
26632743 gpu_device_ids. is_some_and ( |device_ids| !device_ids. is_empty ( ) )
26642744}
26652745
2666- fn build_cdi_context_archive ( context : & CdiContext ) -> Result < Vec < u8 > , String > {
2667- let json = serde_json:: to_vec_pretty ( context) . map_err ( |err| err. to_string ( ) ) ?;
2668- let mut archive = DockerTarArchiveBuilder :: new ( ) ;
2669- archive. append_dir ( "openshell" , 0o700 ) ?;
2670- archive. append_dir ( "openshell/supervisor" , 0o700 ) ?;
2671- archive. append_file (
2672- & format ! (
2673- "openshell/supervisor/{}" ,
2674- openshell_core:: cdi:: CDI_CONTEXT_FILE_NAME
2675- ) ,
2676- 0o600 ,
2677- json,
2678- ) ?;
2679- archive. into_inner ( )
2680- }
2681-
2682- struct DockerTarArchiveBuilder {
2683- inner : tar:: Builder < Vec < u8 > > ,
2684- }
2685-
2686- impl DockerTarArchiveBuilder {
2687- fn new ( ) -> Self {
2688- Self {
2689- inner : tar:: Builder :: new ( Vec :: new ( ) ) ,
2690- }
2691- }
2692-
2693- fn append_dir ( & mut self , path : & str , mode : u32 ) -> Result < ( ) , String > {
2694- let mut header = tar:: Header :: new_gnu ( ) ;
2695- header. set_entry_type ( tar:: EntryType :: Directory ) ;
2696- header. set_size ( 0 ) ;
2697- header. set_mode ( mode) ;
2698- header. set_uid ( 0 ) ;
2699- header. set_gid ( 0 ) ;
2700- header. set_cksum ( ) ;
2701- self . inner
2702- . append_data ( & mut header, path, std:: io:: empty ( ) )
2703- . map_err ( |err| err. to_string ( ) )
2704- }
2705-
2706- fn append_file ( & mut self , path : & str , mode : u32 , contents : Vec < u8 > ) -> Result < ( ) , String > {
2707- let mut header = tar:: Header :: new_gnu ( ) ;
2708- header. set_entry_type ( tar:: EntryType :: Regular ) ;
2709- header. set_size ( u64:: try_from ( contents. len ( ) ) . map_err ( |err| err. to_string ( ) ) ?) ;
2710- header. set_mode ( mode) ;
2711- header. set_uid ( 0 ) ;
2712- header. set_gid ( 0 ) ;
2713- header. set_cksum ( ) ;
2714- self . inner
2715- . append_data ( & mut header, path, Cursor :: new ( contents) )
2716- . map_err ( |err| err. to_string ( ) )
2717- }
2718-
2719- fn into_inner ( self ) -> Result < Vec < u8 > , String > {
2720- self . inner . into_inner ( ) . map_err ( |err| err. to_string ( ) )
2721- }
2722- }
2723-
27242746#[ cfg( test) ]
27252747fn build_container_create_body (
27262748 sandbox : & DriverSandbox ,
@@ -2874,7 +2896,7 @@ fn build_container_create_body_for_image(
28742896 pids_limit : docker_pids_limit ( config. sandbox_pids_limit ) ?,
28752897 device_requests,
28762898 binds : {
2877- let mut binds = build_binds ( sandbox, config) ?;
2899+ let mut binds = build_binds ( sandbox, config, gpu_device_ids ) ?;
28782900 binds. extend ( config. gpu . cdi_spec_bind_strings ( gpu_device_ids) ?) ;
28792901 binds. extend ( user_bind_strings) ;
28802902 Some ( binds)
0 commit comments