@@ -781,11 +781,24 @@ pub(super) async fn handle_list_sandbox_templates(
781781 let limit = clamp_limit ( request. limit , 100 , MAX_PAGE_SIZE ) ;
782782 let templates = if request. all_workspaces {
783783 require_platform_admin ( & state. admin_role , & principal) ?;
784- state
785- . store
786- . list_all_messages :: < SandboxWorkloadTemplate > ( limit, request. offset )
787- . await
788- . map_err ( |e| Status :: internal ( format ! ( "list sandbox templates failed: {e}" ) ) ) ?
784+ if request. label_selector . is_empty ( ) {
785+ state
786+ . store
787+ . list_all_messages :: < SandboxWorkloadTemplate > ( limit, request. offset )
788+ . await
789+ . map_err ( |e| Status :: internal ( format ! ( "list sandbox templates failed: {e}" ) ) ) ?
790+ } else {
791+ crate :: grpc:: validation:: validate_label_selector ( & request. label_selector ) ?;
792+ state
793+ . store
794+ . list_all_messages_with_selector :: < SandboxWorkloadTemplate > (
795+ & request. label_selector ,
796+ limit,
797+ request. offset ,
798+ )
799+ . await
800+ . map_err ( |e| Status :: internal ( format ! ( "list sandbox templates failed: {e}" ) ) ) ?
801+ }
789802 } else {
790803 let authz = authorize_workspace (
791804 & state. store ,
@@ -798,11 +811,27 @@ pub(super) async fn handle_list_sandbox_templates(
798811 let workspace = super :: workspace:: resolve_workspace ( state. store . as_ref ( ) , & authz. workspace )
799812 . await ?
800813 . name ;
801- state
802- . store
803- . list_messages :: < SandboxWorkloadTemplate > ( & workspace, limit, request. offset )
804- . await
805- . map_err ( |e| Status :: internal ( format ! ( "list sandbox templates failed: {e}" ) ) ) ?
814+ if request. label_selector . is_empty ( ) {
815+ state
816+ . store
817+ . list_messages :: < SandboxWorkloadTemplate > ( & workspace, limit, request. offset )
818+ . await
819+ . map_err ( |e| Status :: internal ( format ! ( "list sandbox templates failed: {e}" ) ) ) ?
820+ } else {
821+ crate :: grpc:: validation:: validate_label_selector ( & request. label_selector ) ?;
822+ state
823+ . store
824+ . list_messages_with_selector :: < SandboxWorkloadTemplate > (
825+ & workspace,
826+ & request. label_selector ,
827+ limit,
828+ request. offset ,
829+ )
830+ . await
831+ . map_err ( |e| {
832+ Status :: internal ( format ! ( "list sandbox templates with selector failed: {e}" ) )
833+ } ) ?
834+ }
806835 } ;
807836 Ok ( Response :: new ( ListSandboxTemplatesResponse { templates } ) )
808837}
@@ -4222,6 +4251,7 @@ mod tests {
42224251 offset : 0 ,
42234252 workspace : "default" . to_string ( ) ,
42244253 all_workspaces : false ,
4254+ label_selector : String :: new ( ) ,
42254255 } ) ,
42264256 )
42274257 . await
@@ -4255,6 +4285,61 @@ mod tests {
42554285 assert_eq ! ( missing. code( ) , tonic:: Code :: NotFound ) ;
42564286 }
42574287
4288+ #[ tokio:: test]
4289+ async fn sandbox_template_list_filters_by_label_selector ( ) {
4290+ let state = test_server_state ( ) . await ;
4291+
4292+ let mut gpu = test_workload_template ( "gpu-kata" ) ;
4293+ gpu. metadata
4294+ . as_mut ( )
4295+ . expect ( "metadata" )
4296+ . labels
4297+ . insert ( "team" . to_string ( ) , "runtime" . to_string ( ) ) ;
4298+ handle_create_sandbox_template (
4299+ & state,
4300+ authed_request ( CreateSandboxTemplateRequest {
4301+ template : Some ( gpu) ,
4302+ workspace : "default" . to_string ( ) ,
4303+ } ) ,
4304+ )
4305+ . await
4306+ . expect ( "gpu template create should succeed" ) ;
4307+
4308+ let mut cpu = test_workload_template ( "cpu-base" ) ;
4309+ cpu. metadata
4310+ . as_mut ( )
4311+ . expect ( "metadata" )
4312+ . labels
4313+ . insert ( "team" . to_string ( ) , "batch" . to_string ( ) ) ;
4314+ handle_create_sandbox_template (
4315+ & state,
4316+ authed_request ( CreateSandboxTemplateRequest {
4317+ template : Some ( cpu) ,
4318+ workspace : "default" . to_string ( ) ,
4319+ } ) ,
4320+ )
4321+ . await
4322+ . expect ( "cpu template create should succeed" ) ;
4323+
4324+ let listed = handle_list_sandbox_templates (
4325+ & state,
4326+ authed_request ( ListSandboxTemplatesRequest {
4327+ limit : 100 ,
4328+ offset : 0 ,
4329+ workspace : "default" . to_string ( ) ,
4330+ all_workspaces : false ,
4331+ label_selector : "team=runtime" . to_string ( ) ,
4332+ } ) ,
4333+ )
4334+ . await
4335+ . expect ( "template list with label selector should succeed" )
4336+ . into_inner ( )
4337+ . templates ;
4338+
4339+ assert_eq ! ( listed. len( ) , 1 ) ;
4340+ assert_eq ! ( listed[ 0 ] . object_name( ) , "gpu-kata" ) ;
4341+ }
4342+
42584343 #[ tokio:: test]
42594344 async fn sandbox_template_create_rejects_whitespace_name ( ) {
42604345 let state = test_server_state ( ) . await ;
@@ -4279,6 +4364,7 @@ mod tests {
42794364 offset : 0 ,
42804365 workspace : "default" . to_string ( ) ,
42814366 all_workspaces : false ,
4367+ label_selector : String :: new ( ) ,
42824368 } ) ,
42834369 )
42844370 . await
@@ -4326,6 +4412,7 @@ mod tests {
43264412 offset : 0 ,
43274413 workspace : "beta" . to_string ( ) ,
43284414 all_workspaces : false ,
4415+ label_selector : String :: new ( ) ,
43294416 } ) ,
43304417 )
43314418 . await
0 commit comments