@@ -1030,6 +1030,7 @@ enum DoctorCommands {
10301030}
10311031
10321032#[ derive( Subcommand , Debug ) ]
1033+ #[ allow( clippy:: large_enum_variant) ]
10331034enum SandboxCommands {
10341035 /// Create a sandbox.
10351036 #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
@@ -1088,21 +1089,33 @@ enum SandboxCommands {
10881089 ///
10891090 /// This implies --gpu. Kubernetes-backed gateways schedule pods with
10901091 /// the corresponding nvidia.com/gpu resource limit.
1091- #[ arg( long, value_name = "COUNT" , value_parser = clap:: value_parser!( u32 ) . range( 1 ..) ) ]
1092+ #[ arg( long, value_name = "COUNT" , value_parser = clap:: value_parser!( u32 ) . range( 1 ..) , conflicts_with = "gpu_device" ) ]
10921093 gpu_count : Option < u32 > ,
10931094
1094- /// Set compute resource requests and limits as JSON.
1095- ///
1096- /// The JSON must be an object with the same shape as
1097- /// SandboxTemplate.resources, for example:
1098- /// {"requests":{"cpu":"2"},"limits":{"cpu":"16"}}
1099- #[ arg( long, value_name = "JSON" ) ]
1100- resources_json : Option < String > ,
1095+ /// Minimum CPU cores requested, e.g. "500m" or "2".
1096+ #[ arg( long, value_name = "QUANTITY" ) ]
1097+ cpu_request : Option < String > ,
1098+
1099+ /// Maximum CPU cores allowed, e.g. "2" or "4".
1100+ #[ arg( long, value_name = "QUANTITY" ) ]
1101+ cpu_limit : Option < String > ,
1102+
1103+ /// Minimum memory requested, e.g. "512Mi" or "4Gi".
1104+ #[ arg( long, value_name = "QUANTITY" ) ]
1105+ memory_request : Option < String > ,
1106+
1107+ /// Maximum memory allowed, e.g. "1Gi" or "8Gi".
1108+ #[ arg( long, value_name = "QUANTITY" ) ]
1109+ memory_limit : Option < String > ,
1110+
1111+ /// Driver-specific resource configuration as KEY=VALUE.
1112+ #[ arg( long = "resource-config" , value_name = "KEY=VALUE" ) ]
1113+ resource_config : Vec < String > ,
11011114
11021115 /// Target a driver-specific GPU device. Docker and Podman use CDI device IDs
11031116 /// (for example "nvidia.com/gpu=0"); VM uses a PCI BDF or index.
11041117 /// Only valid with --gpu. When omitted with --gpu, the driver uses its default GPU selection.
1105- #[ arg( long, requires = "gpu" ) ]
1118+ #[ arg( long, requires = "gpu" , conflicts_with = "gpu_count" ) ]
11061119 gpu_device : Option < String > ,
11071120
11081121 /// Provider names to attach to this sandbox.
@@ -2380,7 +2393,11 @@ async fn main() -> Result<()> {
23802393 editor,
23812394 gpu,
23822395 gpu_count,
2383- resources_json,
2396+ cpu_request,
2397+ cpu_limit,
2398+ memory_request,
2399+ memory_limit,
2400+ resource_config,
23842401 gpu_device,
23852402 providers,
23862403 policy,
@@ -2447,8 +2464,14 @@ async fn main() -> Result<()> {
24472464 upload_spec. as_ref ( ) ,
24482465 keep,
24492466 gpu,
2450- gpu_count,
2451- resources_json. as_deref ( ) ,
2467+ run:: SandboxResourceArgs {
2468+ cpu_request : cpu_request. as_deref ( ) ,
2469+ cpu_limit : cpu_limit. as_deref ( ) ,
2470+ memory_request : memory_request. as_deref ( ) ,
2471+ memory_limit : memory_limit. as_deref ( ) ,
2472+ gpu_count,
2473+ driver_config : & resource_config,
2474+ } ,
24522475 gpu_device. as_deref ( ) ,
24532476 editor,
24542477 & providers,
@@ -3853,27 +3876,66 @@ mod tests {
38533876 }
38543877
38553878 #[ test]
3856- fn sandbox_create_resources_json_parses ( ) {
3879+ fn sandbox_create_resource_spec_flags_parse ( ) {
38573880 let cli = Cli :: try_parse_from ( [
38583881 "openshell" ,
38593882 "sandbox" ,
38603883 "create" ,
3861- "--resources-json" ,
3862- r#"{"requests":{"cpu":"2"},"limits":{"cpu":"16"}}"# ,
3884+ "--cpu-request" ,
3885+ "2" ,
3886+ "--cpu-limit" ,
3887+ "4" ,
3888+ "--memory-request" ,
3889+ "8Gi" ,
3890+ "--memory-limit" ,
3891+ "16Gi" ,
3892+ "--resource-config" ,
3893+ "kubernetes.resource-name=nvidia.com/gpu" ,
38633894 ] )
3864- . expect ( "sandbox create --resources-json should parse" ) ;
3895+ . expect ( "sandbox create resource flags should parse" ) ;
38653896
38663897 match cli. command {
38673898 Some ( Commands :: Sandbox {
3868- command : Some ( SandboxCommands :: Create { resources_json, .. } ) ,
3899+ command :
3900+ Some ( SandboxCommands :: Create {
3901+ cpu_request,
3902+ cpu_limit,
3903+ memory_request,
3904+ memory_limit,
3905+ resource_config,
3906+ ..
3907+ } ) ,
38693908 ..
38703909 } ) => {
3910+ assert_eq ! ( cpu_request. as_deref( ) , Some ( "2" ) ) ;
3911+ assert_eq ! ( cpu_limit. as_deref( ) , Some ( "4" ) ) ;
3912+ assert_eq ! ( memory_request. as_deref( ) , Some ( "8Gi" ) ) ;
3913+ assert_eq ! ( memory_limit. as_deref( ) , Some ( "16Gi" ) ) ;
38713914 assert_eq ! (
3872- resources_json . as_deref ( ) ,
3873- Some ( r#"{"requests":{"cpu":"2"},"limits":{"cpu":"16"}}"# )
3915+ resource_config ,
3916+ vec! [ "kubernetes.resource-name=nvidia.com/gpu" . to_string ( ) ]
38743917 ) ;
38753918 }
38763919 other => panic ! ( "expected sandbox create command, got: {other:?}" ) ,
38773920 }
38783921 }
3922+
3923+ #[ test]
3924+ fn sandbox_create_gpu_count_conflicts_with_gpu_device ( ) {
3925+ let result = Cli :: try_parse_from ( [
3926+ "openshell" ,
3927+ "sandbox" ,
3928+ "create" ,
3929+ "--gpu" ,
3930+ "--gpu-count" ,
3931+ "2" ,
3932+ "--gpu-device" ,
3933+ "nvidia.com/gpu=0" ,
3934+ ] ) ;
3935+
3936+ assert ! (
3937+ result. is_err( ) ,
3938+ "sandbox create should reject combining --gpu-count and --gpu-device"
3939+ ) ;
3940+ }
38793941}
0 commit comments