@@ -20,7 +20,7 @@ use openshell_bootstrap::{
2020 save_active_gateway, store_gateway_metadata,
2121} ;
2222use openshell_bootstrap:: { GatewayMetadataSource , ListedGateway } ;
23- use openshell_core:: proto:: { GetGatewayInfoRequest , HealthRequest , ServiceStatus } ;
23+ use openshell_core:: proto:: { ExtensionKind , GetGatewayInfoRequest , HealthRequest , ServiceStatus } ;
2424use std:: io:: IsTerminal ;
2525use std:: path:: PathBuf ;
2626use tonic:: { Code , Status } ;
@@ -33,6 +33,7 @@ struct GatewayInfoView {
3333 status : String ,
3434 version : String ,
3535 compute_drivers : Vec < ComputeDriverInfoView > ,
36+ extensions : Vec < ExtensionInfoView > ,
3637}
3738
3839#[ derive( Debug , Clone ) ]
@@ -47,6 +48,17 @@ struct ComputeDriverCapabilitiesView {
4748 driver_version : String ,
4849}
4950
51+ #[ derive( Debug , Clone ) ]
52+ struct ExtensionInfoView {
53+ kind : String ,
54+ configured_name : String ,
55+ implementation_name : String ,
56+ implementation_version : String ,
57+ protocol_version : String ,
58+ supported_capabilities : Vec < String > ,
59+ required_capabilities : Vec < String > ,
60+ }
61+
5062/// Show gateway status.
5163#[ allow( clippy:: branches_sharing_code) ]
5264pub async fn gateway_status (
@@ -376,6 +388,19 @@ pub async fn gateway_info(
376388 } ) ?
377389 . into_inner ( ) ;
378390
391+ let extensions = info
392+ . extensions
393+ . into_iter ( )
394+ . map ( |extension| ExtensionInfoView {
395+ kind : extension_kind_name ( extension. kind ) . to_string ( ) ,
396+ configured_name : extension. configured_name ,
397+ implementation_name : extension. implementation_name ,
398+ implementation_version : extension. implementation_version ,
399+ protocol_version : format ! ( "{}.{}" , extension. protocol_major, extension. protocol_minor) ,
400+ supported_capabilities : extension. supported_capabilities ,
401+ required_capabilities : extension. required_capabilities ,
402+ } )
403+ . collect ( ) ;
379404 let view = GatewayInfoView {
380405 gateway : gateway_name. to_string ( ) ,
381406 server : server. to_string ( ) ,
@@ -396,6 +421,7 @@ pub async fn gateway_info(
396421 }
397422 } )
398423 . collect ( ) ,
424+ extensions,
399425 } ;
400426
401427 print_gateway_info ( & view, output)
@@ -422,10 +448,52 @@ fn print_gateway_info(view: &GatewayInfoView, output: &str) -> Result<()> {
422448 println ! ( " {} {}" , "Status:" . dimmed( ) , view. status) ;
423449 println ! ( " {} {}" , "Version:" . dimmed( ) , view. version) ;
424450 print_compute_driver_info ( & view. compute_drivers ) ;
451+ print_extension_info ( & view. extensions ) ;
425452
426453 Ok ( ( ) )
427454}
428455
456+ fn extension_kind_name ( kind : i32 ) -> & ' static str {
457+ match ExtensionKind :: try_from ( kind) . unwrap_or ( ExtensionKind :: Unspecified ) {
458+ ExtensionKind :: ComputeDriver => "compute-driver" ,
459+ ExtensionKind :: CredentialDriver => "credential-driver" ,
460+ ExtensionKind :: GatewayInterceptor => "gateway-interceptor" ,
461+ ExtensionKind :: SupervisorMiddleware => "supervisor-middleware" ,
462+ ExtensionKind :: Unspecified => "unspecified" ,
463+ }
464+ }
465+
466+ fn print_extension_info ( extensions : & [ ExtensionInfoView ] ) {
467+ if extensions. is_empty ( ) {
468+ return ;
469+ }
470+ println ! ( " {}" , "Extensions:" . dimmed( ) ) ;
471+ for extension in extensions {
472+ println ! ( " {} ({})" , extension. configured_name, extension. kind) ;
473+ println ! (
474+ " {} {} {} (protocol {})" ,
475+ "Implementation:" . dimmed( ) ,
476+ extension. implementation_name,
477+ extension. implementation_version,
478+ extension. protocol_version
479+ ) ;
480+ if !extension. supported_capabilities . is_empty ( ) {
481+ println ! (
482+ " {} {}" ,
483+ "Capabilities:" . dimmed( ) ,
484+ extension. supported_capabilities. join( ", " )
485+ ) ;
486+ }
487+ if !extension. required_capabilities . is_empty ( ) {
488+ println ! (
489+ " {} {}" ,
490+ "Requires gateway:" . dimmed( ) ,
491+ extension. required_capabilities. join( ", " )
492+ ) ;
493+ }
494+ }
495+ }
496+
429497fn print_compute_driver_info ( drivers : & [ ComputeDriverInfoView ] ) {
430498 if drivers. is_empty ( ) {
431499 return ;
@@ -467,6 +535,15 @@ fn gateway_info_to_json(view: &GatewayInfoView) -> serde_json::Value {
467535 } ,
468536 } ) )
469537 . collect:: <Vec <_>>( ) ,
538+ "extensions" : view. extensions. iter( ) . map( |extension| serde_json:: json!( {
539+ "kind" : & extension. kind,
540+ "configured_name" : & extension. configured_name,
541+ "implementation_name" : & extension. implementation_name,
542+ "implementation_version" : & extension. implementation_version,
543+ "protocol_version" : & extension. protocol_version,
544+ "supported_capabilities" : & extension. supported_capabilities,
545+ "required_capabilities" : & extension. required_capabilities,
546+ } ) ) . collect:: <Vec <_>>( ) ,
470547 } )
471548}
472549
@@ -1507,9 +1584,9 @@ pub fn gateway_remove(name: &str) -> Result<()> {
15071584#[ cfg( test) ]
15081585mod tests {
15091586 use super :: {
1510- ComputeDriverCapabilitiesView , ComputeDriverInfoView , GatewayAuthenticationState ,
1511- GatewayInfoView , TlsOptions , format_gateway_select_header, format_gateway_select_items ,
1512- gateway_add, gateway_auth_label, gateway_authentication_state,
1587+ ComputeDriverCapabilitiesView , ComputeDriverInfoView , ExtensionInfoView ,
1588+ GatewayAuthenticationState , GatewayInfoView , TlsOptions , format_gateway_select_header,
1589+ format_gateway_select_items , gateway_add, gateway_auth_label, gateway_authentication_state,
15131590 gateway_env_override_warning, gateway_info_to_json, gateway_remote_label,
15141591 gateway_select_with, gateway_to_json, gateway_type_label, http_health_check,
15151592 import_local_package_mtls_bundle, mtls_certs_exist_for_gateway, package_managed_tls_dirs,
@@ -1824,6 +1901,15 @@ mod tests {
18241901 driver_version: "0.0.75" . to_string( ) ,
18251902 } ,
18261903 } ] ,
1904+ extensions : vec ! [ ExtensionInfoView {
1905+ kind: "compute-driver" . to_string( ) ,
1906+ configured_name: "podman" . to_string( ) ,
1907+ implementation_name: "openshell/podman" . to_string( ) ,
1908+ implementation_version: "0.0.75" . to_string( ) ,
1909+ protocol_version: "1.0" . to_string( ) ,
1910+ supported_capabilities: vec![ "openshell.compute.contract" . to_string( ) ] ,
1911+ required_capabilities: vec![ "openshell.compute.contract" . to_string( ) ] ,
1912+ } ] ,
18271913 } ;
18281914
18291915 let json = gateway_info_to_json ( & view) ;
@@ -1836,6 +1922,7 @@ mod tests {
18361922 json[ "compute_drivers" ] [ 0 ] [ "capabilities" ] [ "driver_name" ] ,
18371923 "podman"
18381924 ) ;
1925+ assert_eq ! ( json[ "extensions" ] [ 0 ] [ "protocol_version" ] , "1.0" ) ;
18391926 assert_eq ! (
18401927 json[ "compute_drivers" ] [ 0 ] [ "capabilities" ] [ "driver_version" ] ,
18411928 "0.0.75"
@@ -1851,6 +1938,7 @@ mod tests {
18511938 status : "healthy" . to_string ( ) ,
18521939 version : "0.0.74" . to_string ( ) ,
18531940 compute_drivers : Vec :: new ( ) ,
1941+ extensions : Vec :: new ( ) ,
18541942 } ;
18551943
18561944 let json = gateway_info_to_json ( & view) ;
0 commit comments