@@ -35,27 +35,27 @@ use openshell_core::net::set_tcp_nodelay_best_effort;
3535use openshell_core:: proto:: ProviderProfileCategory ;
3636use openshell_core:: proto:: {
3737 ApproveAllDraftChunksRequest , ApproveDraftChunkRequest , AttachSandboxProviderRequest ,
38- ClearDraftChunksRequest , ConfigureProviderRefreshRequest , CreateProviderRequest ,
39- CreateSandboxRequest , CreateSshSessionRequest , DeleteInferenceRouteRequest ,
40- DeleteProviderProfileRequest , DeleteProviderRefreshRequest , DeleteProviderRequest ,
41- DeleteSandboxRequest , DeleteServiceRequest , DetachSandboxProviderRequest , ExecSandboxRequest ,
42- ExposeServiceRequest , GetCurrentUserRequest , GetDraftHistoryRequest , GetDraftPolicyRequest ,
43- GetGatewayConfigRequest , GetInferenceRouteRequest , GetProviderProfileRequest ,
44- GetProviderRefreshStatusRequest , GetProviderRequest , GetSandboxConfigRequest ,
45- GetSandboxConfigResponse , GetSandboxLogsRequest , GetSandboxPolicyStatusRequest ,
46- GetSandboxRequest , GetServiceRequest , GpuResourceRequirements , ImportProviderProfilesRequest ,
47- LintProviderProfilesRequest , ListProviderProfilesRequest , ListProvidersRequest ,
48- ListSandboxPoliciesRequest , ListSandboxProvidersRequest , ListSandboxesRequest ,
49- ListServicesRequest , PolicySource , PolicyStatus , Provider ,
50- ProviderCredentialRefreshRecoveryAction , ProviderCredentialRefreshStatus ,
51- ProviderCredentialRefreshStrategy , ProviderCredentialTokenGrantType , ProviderProfile ,
52- ProviderProfileDiagnostic , ProviderProfileImportItem , RejectDraftChunkRequest ,
53- ResourceRequirements , RevokeSshSessionRequest , RotateProviderCredentialRequest , Sandbox ,
54- SandboxPhase , SandboxPolicy , SandboxSpec , SandboxTemplate , ServiceEndpointResponse ,
55- SetInferenceRouteRequest , SettingScope , StartSandboxRequest , StopSandboxRequest ,
56- TcpForwardFrame , TcpForwardInit , TcpRelayTarget , UpdateConfigRequest ,
57- UpdateProviderProfilesRequest , UpdateProviderRequest , WatchSandboxRequest , exec_sandbox_event ,
58- setting_value, tcp_forward_init,
38+ ClearDraftChunksRequest , ConfigureProviderRefreshRequest , CpuResourceRequirements ,
39+ CreateProviderRequest , CreateSandboxRequest , CreateSshSessionRequest ,
40+ DeleteInferenceRouteRequest , DeleteProviderProfileRequest , DeleteProviderRefreshRequest ,
41+ DeleteProviderRequest , DeleteSandboxRequest , DeleteServiceRequest ,
42+ DetachSandboxProviderRequest , ExecSandboxRequest , ExposeServiceRequest , GetCurrentUserRequest ,
43+ GetDraftHistoryRequest , GetDraftPolicyRequest , GetGatewayConfigRequest ,
44+ GetInferenceRouteRequest , GetProviderProfileRequest , GetProviderRefreshStatusRequest ,
45+ GetProviderRequest , GetSandboxConfigRequest , GetSandboxConfigResponse , GetSandboxLogsRequest ,
46+ GetSandboxPolicyStatusRequest , GetSandboxRequest , GetServiceRequest ,
47+ ImportProviderProfilesRequest , LintProviderProfilesRequest , ListProviderProfilesRequest ,
48+ ListProvidersRequest , ListSandboxPoliciesRequest , ListSandboxProvidersRequest ,
49+ ListSandboxesRequest , ListServicesRequest , MemoryResourceRequirements , PolicySource ,
50+ PolicyStatus , Provider , ProviderCredentialRefreshRecoveryAction ,
51+ ProviderCredentialRefreshStatus , ProviderCredentialRefreshStrategy ,
52+ ProviderCredentialTokenGrantType , ProviderProfile , ProviderProfileDiagnostic ,
53+ ProviderProfileImportItem , RejectDraftChunkRequest , ResourceRequirements ,
54+ RevokeSshSessionRequest , RotateProviderCredentialRequest , Sandbox , SandboxPhase , SandboxPolicy ,
55+ SandboxSpec , SandboxTemplate , ServiceEndpointResponse , SetInferenceRouteRequest , SettingScope ,
56+ StartSandboxRequest , StopSandboxRequest , TcpForwardFrame , TcpForwardInit , TcpRelayTarget ,
57+ UpdateConfigRequest , UpdateProviderProfilesRequest , UpdateProviderRequest , WatchSandboxRequest ,
58+ exec_sandbox_event , setting_value, tcp_forward_init,
5959} ;
6060use openshell_core:: settings;
6161use openshell_core:: { ObjectId , ObjectName , ObjectWorkspace } ;
@@ -224,41 +224,28 @@ fn sandbox_should_persist(keep: bool, forward: Option<&ForwardSpec>) -> bool {
224224 keep || forward. is_some ( )
225225}
226226
227- fn build_sandbox_resource_limits (
227+ pub fn build_cpu_resource_requirements (
228228 cpu : Option < & str > ,
229- memory : Option < & str > ,
230- ) -> Result < Option < prost_types:: Struct > > {
231- use prost_types:: { Struct , Value , value:: Kind } ;
232-
233- fn string_value ( value : String ) -> Value {
234- Value {
235- kind : Some ( Kind :: StringValue ( value) ) ,
236- }
237- }
229+ ) -> Result < Option < CpuResourceRequirements > > {
230+ let Some ( cpu) = cpu else {
231+ return Ok ( None ) ;
232+ } ;
238233
239- let mut limits = std:: collections:: BTreeMap :: new ( ) ;
240- if let Some ( cpu) = cpu {
241- limits. insert ( "cpu" . to_string ( ) , string_value ( validate_cpu_quantity ( cpu) ?) ) ;
242- }
243- if let Some ( memory) = memory {
244- limits. insert (
245- "memory" . to_string ( ) ,
246- string_value ( validate_memory_quantity ( memory) ?) ,
247- ) ;
248- }
234+ Ok ( Some ( CpuResourceRequirements {
235+ limit : validate_cpu_quantity ( cpu) ?,
236+ } ) )
237+ }
249238
250- if limits. is_empty ( ) {
239+ pub fn build_memory_resource_requirements (
240+ memory : Option < & str > ,
241+ ) -> Result < Option < MemoryResourceRequirements > > {
242+ let Some ( memory) = memory else {
251243 return Ok ( None ) ;
252- }
244+ } ;
253245
254- let mut fields = std:: collections:: BTreeMap :: new ( ) ;
255- fields. insert (
256- "limits" . to_string ( ) ,
257- Value {
258- kind : Some ( Kind :: StructValue ( Struct { fields : limits } ) ) ,
259- } ,
260- ) ;
261- Ok ( Some ( Struct { fields } ) )
246+ Ok ( Some ( MemoryResourceRequirements {
247+ limit : validate_memory_quantity ( memory) ?,
248+ } ) )
262249}
263250
264251fn parse_driver_config_json ( value : & str ) -> Result < prost_types:: Struct > {
@@ -370,9 +357,7 @@ pub struct SandboxCreateConfig<'a> {
370357 pub from : Option < & ' a str > ,
371358 pub uploads : & ' a [ ( String , Option < String > , bool ) ] ,
372359 pub keep : bool ,
373- pub gpu_requirements : Option < GpuResourceRequirements > ,
374- pub cpu : Option < & ' a str > ,
375- pub memory : Option < & ' a str > ,
360+ pub resource_requirements : Option < ResourceRequirements > ,
376361 pub driver_config_json : Option < & ' a str > ,
377362 pub editor : Option < Editor > ,
378363 pub providers : & ' a [ String ] ,
@@ -395,9 +380,7 @@ impl Default for SandboxCreateConfig<'_> {
395380 from : None ,
396381 uploads : & [ ] ,
397382 keep : false ,
398- gpu_requirements : None ,
399- cpu : None ,
400- memory : None ,
383+ resource_requirements : None ,
401384 driver_config_json : None ,
402385 editor : None ,
403386 providers : & [ ] ,
@@ -428,9 +411,7 @@ pub async fn sandbox_create(
428411 from,
429412 uploads,
430413 keep,
431- gpu_requirements,
432- cpu,
433- memory,
414+ resource_requirements,
434415 driver_config_json,
435416 editor,
436417 providers,
@@ -513,24 +494,20 @@ pub async fn sandbox_create(
513494 . await ?;
514495
515496 let policy = load_sandbox_policy ( policy) ?;
516- let resource_limits = build_sandbox_resource_limits ( cpu, memory) ?;
517497 let driver_config = driver_config_json
518498 . map ( parse_driver_config_json)
519499 . transpose ( ) ?;
520500
521- let template = if image. is_some ( ) || resource_limits . is_some ( ) || driver_config. is_some ( ) {
501+ let template = if image. is_some ( ) || driver_config. is_some ( ) {
522502 Some ( SandboxTemplate {
523503 image : image. unwrap_or_default ( ) ,
524- resources : resource_limits,
525504 driver_config,
526505 ..SandboxTemplate :: default ( )
527506 } )
528507 } else {
529508 None
530509 } ;
531510
532- let resource_requirements = gpu_requirements. map ( |gpu| ResourceRequirements { gpu : Some ( gpu) } ) ;
533-
534511 let main_terminal = tty_override
535512 . unwrap_or_else ( || std:: io:: stdin ( ) . is_terminal ( ) && std:: io:: stdout ( ) . is_terminal ( ) ) ;
536513 let main_command = if command. is_empty ( ) {
@@ -540,7 +517,7 @@ pub async fn sandbox_create(
540517 } ;
541518 let request = CreateSandboxRequest {
542519 spec : Some ( SandboxSpec {
543- resource_requirements,
520+ resource_requirements : resource_requirements . clone ( ) ,
544521 environment,
545522 policy,
546523 providers : configured_providers,
@@ -7404,9 +7381,9 @@ fn format_endpoint(endpoint: &openshell_core::proto::NetworkEndpoint) -> String
74047381#[ cfg( test) ]
74057382mod tests {
74067383 use super :: {
7407- PolicyGetView , ProvisioningStep , build_sandbox_resource_limits ,
7408- dockerfile_sources_supported_for_gateway , format_endpoint ,
7409- format_provider_attachment_table, git_sync_files, inferred_provider_type,
7384+ PolicyGetView , ProvisioningStep , build_cpu_resource_requirements ,
7385+ build_memory_resource_requirements , dockerfile_sources_supported_for_gateway ,
7386+ format_endpoint , format_provider_attachment_table, git_sync_files, inferred_provider_type,
74107387 parse_cli_setting_value, parse_credential_expiry_cli_value, parse_credential_expiry_pairs,
74117388 parse_credential_pairs, parse_driver_config_json, parse_secret_material_env_pairs,
74127389 policy_revision_to_json, provider_profile_allows_empty_credentials,
@@ -7812,52 +7789,29 @@ mod tests {
78127789 }
78137790
78147791 #[ test]
7815- fn build_sandbox_resource_limits_sets_limits_only ( ) {
7816- let resources = build_sandbox_resource_limits ( Some ( "500m" ) , Some ( "2Gi ") )
7817- . expect ( "resource limits should parse" )
7818- . expect ( "resource limits should be present" ) ;
7792+ fn build_cpu_resource_requirements_sets_typed_limit ( ) {
7793+ let cpu = build_cpu_resource_requirements ( Some ( "500m" ) )
7794+ . expect ( "CPU limit should parse" )
7795+ . expect ( "CPU requirements should be present" ) ;
78197796
7820- let limits = resources
7821- . fields
7822- . get ( "limits" )
7823- . and_then ( |value| value. kind . as_ref ( ) )
7824- . and_then ( |kind| match kind {
7825- prost_types:: value:: Kind :: StructValue ( inner) => Some ( inner) ,
7826- _ => None ,
7827- } )
7828- . expect ( "limits should be a struct" ) ;
7797+ assert_eq ! ( cpu. limit, "500m" ) ;
7798+ }
78297799
7830- assert_eq ! (
7831- limits
7832- . fields
7833- . get( "cpu" )
7834- . and_then( |value| value. kind. as_ref( ) )
7835- . and_then( |kind| match kind {
7836- prost_types:: value:: Kind :: StringValue ( value) => Some ( value. as_str( ) ) ,
7837- _ => None ,
7838- } ) ,
7839- Some ( "500m" )
7840- ) ;
7841- assert_eq ! (
7842- limits
7843- . fields
7844- . get( "memory" )
7845- . and_then( |value| value. kind. as_ref( ) )
7846- . and_then( |kind| match kind {
7847- prost_types:: value:: Kind :: StringValue ( value) => Some ( value. as_str( ) ) ,
7848- _ => None ,
7849- } ) ,
7850- Some ( "2Gi" )
7851- ) ;
7852- assert ! ( !resources. fields. contains_key( "requests" ) ) ;
7800+ #[ test]
7801+ fn build_memory_resource_requirements_sets_typed_limit ( ) {
7802+ let memory = build_memory_resource_requirements ( Some ( "2Gi" ) )
7803+ . expect ( "memory limit should parse" )
7804+ . expect ( "memory requirements should be present" ) ;
7805+
7806+ assert_eq ! ( memory. limit, "2Gi" ) ;
78537807 }
78547808
78557809 #[ test]
7856- fn build_sandbox_resource_limits_rejects_invalid_quantities ( ) {
7857- assert ! ( build_sandbox_resource_limits ( Some ( "0" ) , None ) . is_err( ) ) ;
7858- assert ! ( build_sandbox_resource_limits ( Some ( "half" ) , None ) . is_err( ) ) ;
7859- assert ! ( build_sandbox_resource_limits ( None , Some ( "0Gi" ) ) . is_err( ) ) ;
7860- assert ! ( build_sandbox_resource_limits ( None , Some ( "1.5Gi" ) ) . is_err( ) ) ;
7810+ fn build_cpu_and_memory_resource_requirements_reject_invalid_quantities ( ) {
7811+ assert ! ( build_cpu_resource_requirements ( Some ( "0" ) ) . is_err( ) ) ;
7812+ assert ! ( build_cpu_resource_requirements ( Some ( "half" ) ) . is_err( ) ) ;
7813+ assert ! ( build_memory_resource_requirements ( Some ( "0Gi" ) ) . is_err( ) ) ;
7814+ assert ! ( build_memory_resource_requirements ( Some ( "1.5Gi" ) ) . is_err( ) ) ;
78617815 }
78627816
78637817 #[ test]
@@ -8157,6 +8111,8 @@ mod tests {
81578111 fn provisioning_timeout_message_includes_condition_and_gpu_hint ( ) {
81588112 let resource_requirements = ResourceRequirements {
81598113 gpu : Some ( GpuResourceRequirements { count : None } ) ,
8114+ cpu : None ,
8115+ memory : None ,
81608116 } ;
81618117 let message = provisioning_timeout_message (
81628118 120 ,
@@ -8178,7 +8134,11 @@ mod tests {
81788134
81798135 #[ test]
81808136 fn provisioning_timeout_message_omits_gpu_hint_without_gpu_requirements ( ) {
8181- let resource_requirements = ResourceRequirements { gpu : None } ;
8137+ let resource_requirements = ResourceRequirements {
8138+ gpu : None ,
8139+ cpu : None ,
8140+ memory : None ,
8141+ } ;
81828142 let message = provisioning_timeout_message ( 120 , Some ( & resource_requirements) , None ) ;
81838143
81848144 assert_eq ! ( message, "sandbox provisioning timed out after 120s" ) ;
0 commit comments