@@ -37,7 +37,7 @@ use crate::{
3737 config:: { Config , TlsConfig } ,
3838 kv:: {
3939 fetch_peers_from_bootnode, AppIdValidator , HttpsClientConfig , InstanceData , KvStore ,
40- NodeData , NodeStatus , WaveKvSyncService ,
40+ NodeData , NodeStatus , PortFlags , WaveKvSyncService ,
4141 } ,
4242 models:: { InstanceInfo , WgConf } ,
4343 proxy:: { create_acceptor_with_cert_resolver, AddressGroup , AddressInfo } ,
@@ -378,12 +378,16 @@ impl Proxy {
378378 } )
379379 }
380380
381- /// Register a CVM with the given app_id, instance_id and client_public_key
381+ /// Register a CVM with the given app_id, instance_id and client_public_key.
382+ ///
383+ /// `port_attrs = None` means the CVM didn't report port attributes (legacy
384+ /// CVM). The gateway will lazily fetch them via Info() on first connection.
382385 pub fn do_register_cvm (
383386 & self ,
384387 app_id : & str ,
385388 instance_id : & str ,
386389 client_public_key : & str ,
390+ port_attrs : Option < BTreeMap < u16 , PortFlags > > ,
387391 ) -> Result < RegisterCvmResponse > {
388392 let mut state = self . lock ( ) ;
389393
@@ -403,7 +407,7 @@ impl Proxy {
403407 bail ! ( "[{instance_id}] client public key is empty" ) ;
404408 }
405409 let client_info = state
406- . new_client_by_id ( instance_id, app_id, client_public_key)
410+ . new_client_by_id ( instance_id, app_id, client_public_key, port_attrs )
407411 . context ( "failed to allocate IP address for client" ) ?;
408412 if let Err ( err) = state. reconfigure ( ) {
409413 error ! ( "failed to reconfigure: {err:?}" ) ;
@@ -425,7 +429,7 @@ impl Proxy {
425429 } ) ,
426430 agent : Some ( GuestAgentConfig {
427431 external_port : port. into ( ) ,
428- internal_port : 8090 ,
432+ internal_port : state . config . proxy . agent_port . into ( ) ,
429433 domain : base_domain,
430434 app_address_ns_prefix : state. config . proxy . app_address_ns_prefix . clone ( ) ,
431435 } ) ,
@@ -449,6 +453,7 @@ fn build_state_from_kv_store(instances: BTreeMap<String, InstanceData>) -> Proxy
449453 reg_time : UNIX_EPOCH
450454 . checked_add ( Duration :: from_secs ( data. reg_time ) )
451455 . unwrap_or ( UNIX_EPOCH ) ,
456+ port_attrs : data. port_attrs ,
452457 connections : Default :: default ( ) ,
453458 } ;
454459 state. allocated_addresses . insert ( data. ip ) ;
@@ -742,6 +747,7 @@ fn reload_instances_from_kv_store(proxy: &Proxy, store: &KvStore) -> Result<()>
742747 reg_time : UNIX_EPOCH
743748 . checked_add ( Duration :: from_secs ( data. reg_time ) )
744749 . unwrap_or ( UNIX_EPOCH ) ,
750+ port_attrs : data. port_attrs . clone ( ) ,
745751 connections : Default :: default ( ) ,
746752 } ;
747753
@@ -823,6 +829,7 @@ impl ProxyState {
823829 id : & str ,
824830 app_id : & str ,
825831 public_key : & str ,
832+ port_attrs : Option < BTreeMap < u16 , PortFlags > > ,
826833 ) -> Result < InstanceInfo > {
827834 if id. is_empty ( ) {
828835 bail ! ( "instance_id is empty (no_instance_id is set?)" ) ;
@@ -841,6 +848,10 @@ impl ProxyState {
841848 // Update reg_time so other nodes will pick up the change
842849 existing. reg_time = SystemTime :: now ( ) ;
843850 }
851+ // Always update port_attrs from the latest registration so changes
852+ // take effect across re-registrations. A `None` here (legacy CVM)
853+ // wipes any previously-cached attrs so the lazy fetch path runs again.
854+ existing. port_attrs = port_attrs. clone ( ) ;
844855 let existing = existing. clone ( ) ;
845856 if self . valid_ip ( existing. ip ) {
846857 // Sync existing instance to KvStore (might be from legacy state)
@@ -849,6 +860,7 @@ impl ProxyState {
849860 ip : existing. ip ,
850861 public_key : existing. public_key . clone ( ) ,
851862 reg_time : encode_ts ( existing. reg_time ) ,
863+ port_attrs : existing. port_attrs . clone ( ) ,
852864 } ;
853865 if let Err ( err) = self . kv_store . sync_instance ( & existing. id , & data) {
854866 error ! ( "failed to sync existing instance to KvStore: {err:?}" ) ;
@@ -867,19 +879,58 @@ impl ProxyState {
867879 ip,
868880 public_key : public_key. to_string ( ) ,
869881 reg_time : SystemTime :: now ( ) ,
882+ port_attrs,
870883 connections : Default :: default ( ) ,
871884 } ;
872885 self . add_instance ( host_info. clone ( ) ) ;
873886 Ok ( host_info)
874887 }
875888
889+ /// Lookup an instance's IP. Returns `None` if the instance is unknown.
890+ pub ( crate ) fn instance_ip ( & self , instance_id : & str ) -> Option < Ipv4Addr > {
891+ self . state . instances . get ( instance_id) . map ( |i| i. ip )
892+ }
893+
894+ /// Lookup an instance's port_attrs. `None` means the CVM never reported
895+ /// them (legacy CVM), so the caller should fall back to fetching via Info().
896+ pub ( crate ) fn instance_port_attrs (
897+ & self ,
898+ instance_id : & str ,
899+ ) -> Option < BTreeMap < u16 , PortFlags > > {
900+ self . state . instances . get ( instance_id) ?. port_attrs . clone ( )
901+ }
902+
903+ /// Update an instance's port_attrs (used after a lazy fetch via Info()).
904+ /// Persists to the WaveKV store so other gateway nodes pick it up.
905+ pub ( crate ) fn update_instance_port_attrs (
906+ & mut self ,
907+ instance_id : & str ,
908+ attrs : BTreeMap < u16 , PortFlags > ,
909+ ) {
910+ let Some ( info) = self . state . instances . get_mut ( instance_id) else {
911+ return ;
912+ } ;
913+ info. port_attrs = Some ( attrs. clone ( ) ) ;
914+ let data = InstanceData {
915+ app_id : info. app_id . clone ( ) ,
916+ ip : info. ip ,
917+ public_key : info. public_key . clone ( ) ,
918+ reg_time : encode_ts ( info. reg_time ) ,
919+ port_attrs : Some ( attrs) ,
920+ } ;
921+ if let Err ( err) = self . kv_store . sync_instance ( instance_id, & data) {
922+ error ! ( "failed to sync updated port_attrs to KvStore: {err:?}" ) ;
923+ }
924+ }
925+
876926 fn add_instance ( & mut self , info : InstanceInfo ) {
877927 // Sync to KvStore
878928 let data = InstanceData {
879929 app_id : info. app_id . clone ( ) ,
880930 ip : info. ip ,
881931 public_key : info. public_key . clone ( ) ,
882932 reg_time : encode_ts ( info. reg_time ) ,
933+ port_attrs : info. port_attrs . clone ( ) ,
883934 } ;
884935 if let Err ( err) = self . kv_store . sync_instance ( & info. id , & data) {
885936 error ! ( "failed to sync instance to KvStore: {err:?}" ) ;
@@ -921,13 +972,15 @@ impl ProxyState {
921972 return Ok ( smallvec ! [ AddressInfo {
922973 ip: Ipv4Addr :: new( 127 , 0 , 0 , 1 ) ,
923974 counter: Default :: default ( ) ,
975+ instance_id: "localhost" . to_string( ) ,
924976 } ] ) ;
925977 }
926978 let n = self . config . proxy . connect_top_n ;
927979 if let Some ( instance) = self . state . instances . get ( id) {
928980 return Ok ( smallvec ! [ AddressInfo {
929981 ip: instance. ip,
930982 counter: instance. connections. clone( ) ,
983+ instance_id: instance. id. clone( ) ,
931984 } ] ) ;
932985 } ;
933986 let app_instances = self . state . apps . get ( id) . context ( "app not found" ) ?;
@@ -955,15 +1008,24 @@ impl ProxyState {
9551008 . filter_map ( |instance_id| {
9561009 let instance = self . state . instances . get ( instance_id) ?;
9571010 let ( _, elapsed) = handshakes. get ( & instance. public_key ) ?;
958- Some ( ( instance. ip , * elapsed, instance. connections . clone ( ) ) )
1011+ Some ( (
1012+ instance. ip ,
1013+ * elapsed,
1014+ instance. connections . clone ( ) ,
1015+ instance. id . clone ( ) ,
1016+ ) )
9591017 } )
9601018 . collect :: < SmallVec < [ _ ; 4 ] > > ( ) ,
9611019 } ;
9621020 instances. sort_by ( |a, b| a. 1 . cmp ( & b. 1 ) ) ;
9631021 instances. truncate ( n) ;
9641022 Ok ( instances
9651023 . into_iter ( )
966- . map ( |( ip, _, counter) | AddressInfo { ip, counter } )
1024+ . map ( |( ip, _, counter, instance_id) | AddressInfo {
1025+ ip,
1026+ counter,
1027+ instance_id,
1028+ } )
9671029 . collect ( ) )
9681030 }
9691031
@@ -973,6 +1035,7 @@ impl ProxyState {
9731035 return Some ( smallvec ! [ AddressInfo {
9741036 ip: info. ip,
9751037 counter: info. connections. clone( ) ,
1038+ instance_id: info. id. clone( ) ,
9761039 } ] ) ;
9771040 }
9781041
@@ -999,6 +1062,7 @@ impl ProxyState {
9991062 smallvec ! [ AddressInfo {
10001063 ip: info. ip,
10011064 counter: info. connections. clone( ) ,
1065+ instance_id: info. id. clone( ) ,
10021066 } ]
10031067 } )
10041068 }
@@ -1271,8 +1335,14 @@ impl GatewayRpc for RpcHandler {
12711335 . context ( "App authorization failed" ) ?;
12721336 let app_id = hex:: encode ( & app_info. app_id ) ;
12731337 let instance_id = hex:: encode ( & app_info. instance_id ) ;
1338+ let port_attrs = request. port_attrs . map ( |list| {
1339+ list. attrs
1340+ . into_iter ( )
1341+ . map ( |p| ( p. port as u16 , PortFlags { pp : p. pp } ) )
1342+ . collect :: < BTreeMap < u16 , PortFlags > > ( )
1343+ } ) ;
12741344 self . state
1275- . do_register_cvm ( & app_id, & instance_id, & request. client_public_key )
1345+ . do_register_cvm ( & app_id, & instance_id, & request. client_public_key , port_attrs )
12761346 }
12771347
12781348 async fn acme_info ( self ) -> Result < AcmeInfoResponse > {
0 commit comments