@@ -695,7 +695,6 @@ pub enum StackerCommand {
695695 ListContainers ( ListContainersCommand ) ,
696696 ConfigureFirewall ( ConfigureFirewallCommand ) ,
697697 ProbeEndpoints ( ProbeEndpointsCommand ) ,
698- CheckConnections ( CheckConnectionsCommand ) ,
699698 ActivatePipe ( ActivatePipeCommand ) ,
700699 DeactivatePipe ( DeactivatePipeCommand ) ,
701700 TriggerPipe ( TriggerPipeCommand ) ,
@@ -1705,23 +1704,6 @@ fn default_create_action() -> String {
17051704 "create" . to_string ( )
17061705}
17071706
1708- /// Default HTTP/HTTPS ports checked when none are specified in `check_connections`.
1709- #[ cfg( feature = "docker" ) ]
1710- fn default_check_ports ( ) -> Vec < u16 > {
1711- vec ! [ 80 , 443 , 8080 , 3000 , 8443 ]
1712- }
1713-
1714- /// Command to count active HTTP/HTTPS connections on the host.
1715- #[ cfg_attr( not( feature = "docker" ) , allow( dead_code) ) ]
1716- #[ derive( Debug , Clone , Deserialize ) ]
1717- pub struct CheckConnectionsCommand {
1718- #[ serde( default ) ]
1719- deployment_hash : String ,
1720- /// Specific TCP ports to inspect. Defaults to common HTTP/HTTPS ports when omitted.
1721- #[ serde( default ) ]
1722- ports : Option < Vec < u16 > > ,
1723- }
1724-
17251707pub fn parse_stacker_command ( cmd : & AgentCommand ) -> Result < Option < StackerCommand > > {
17261708 let normalized = cmd. name . trim ( ) . to_lowercase ( ) ;
17271709 match normalized. as_str ( ) {
@@ -1862,13 +1844,6 @@ pub fn parse_stacker_command(cmd: &AgentCommand) -> Result<Option<StackerCommand
18621844 payload. validate ( ) ?;
18631845 Ok ( Some ( StackerCommand :: ProbeEndpoints ( payload) ) )
18641846 }
1865- "check_connections" | "stacker.check_connections" => {
1866- let payload: CheckConnectionsCommand =
1867- serde_json:: from_value ( unwrap_params ( & cmd. params ) )
1868- . context ( "invalid check_connections payload" ) ?;
1869- let payload = payload. normalize ( ) . with_command_context ( cmd) ;
1870- payload. validate ( ) ?;
1871- Ok ( Some ( StackerCommand :: CheckConnections ( payload) ) )
18721847 "activate_pipe" | "stacker.activate_pipe" => {
18731848 let payload: ActivatePipeCommand = serde_json:: from_value ( unwrap_params ( & cmd. params ) )
18741849 . context ( "invalid activate_pipe payload" ) ?;
@@ -1905,12 +1880,6 @@ pub async fn execute_stacker_command(
19051880 return firewall:: handle_configure_firewall ( agent_cmd, data, firewall_policy) . await ;
19061881 }
19071882
1908- // Connection check doesn't require Docker either
1909- #[ cfg( feature = "docker" ) ]
1910- if let StackerCommand :: CheckConnections ( data) = command {
1911- return handle_check_connections ( agent_cmd, data) . await ;
1912- }
1913-
19141883 #[ cfg( feature = "docker" ) ]
19151884 {
19161885 execute_with_docker ( agent_cmd, command, firewall_policy, pipe_runtime) . await
@@ -2788,26 +2757,6 @@ impl ProbeEndpointsCommand {
27882757 }
27892758}
27902759
2791- impl CheckConnectionsCommand {
2792- fn normalize ( mut self ) -> Self {
2793- self . deployment_hash = trimmed ( & self . deployment_hash ) ;
2794- self
2795- }
2796-
2797- fn with_command_context ( mut self , agent_cmd : & AgentCommand ) -> Self {
2798- if self . deployment_hash . is_empty ( ) {
2799- if let Some ( hash) = & agent_cmd. deployment_hash {
2800- self . deployment_hash = hash. clone ( ) ;
2801- }
2802- }
2803- self
2804- }
2805-
2806- fn validate ( & self ) -> Result < ( ) > {
2807- Ok ( ( ) )
2808- }
2809- }
2810-
28112760fn trimmed ( value : & str ) -> String {
28122761 value. trim ( ) . to_string ( )
28132762}
@@ -3264,7 +3213,6 @@ async fn execute_with_docker(
32643213 StackerCommand :: ConfigureFirewall ( data) => {
32653214 firewall:: handle_configure_firewall ( agent_cmd, data, firewall_policy) . await
32663215 }
3267- StackerCommand :: CheckConnections ( data) => handle_check_connections ( agent_cmd, data) . await ,
32683216 }
32693217}
32703218
@@ -8285,88 +8233,6 @@ async fn handle_probe_endpoints(
82858233 Ok ( result)
82868234}
82878235
8288- #[ cfg( feature = "docker" ) ]
8289- async fn handle_check_connections (
8290- agent_cmd : & AgentCommand ,
8291- data : & CheckConnectionsCommand ,
8292- ) -> Result < CommandResult > {
8293- let mut result = base_result ( agent_cmd, & data. deployment_hash , "" , "check_connections" ) ;
8294-
8295- let ports = data. ports . clone ( ) . unwrap_or_else ( default_check_ports) ;
8296-
8297- // Run `ss -Htn state established` to list established TCP connections.
8298- // Output lines look like: "ESTAB 0 0 10.0.0.1:80 10.0.0.2:54321"
8299- let output = tokio:: process:: Command :: new ( "ss" )
8300- . args ( [ "-Htn" , "state" , "established" ] )
8301- . output ( )
8302- . await ;
8303-
8304- let raw_lines = match output {
8305- Ok ( o) if o. status . success ( ) => String :: from_utf8_lossy ( & o. stdout ) . to_string ( ) ,
8306- Ok ( o) => {
8307- let stderr = String :: from_utf8_lossy ( & o. stderr ) . to_string ( ) ;
8308- let error = make_error ( "ss_failed" , "Failed to run ss command" , Some ( stderr) ) ;
8309- result. status = "error" . to_string ( ) ;
8310- result. error = Some ( error. message . clone ( ) ) ;
8311- result. errors = Some ( vec ! [ error] ) ;
8312- return Ok ( result) ;
8313- }
8314- Err ( e) => {
8315- let error = make_error (
8316- "ss_unavailable" ,
8317- "ss command not available on this host" ,
8318- Some ( e. to_string ( ) ) ,
8319- ) ;
8320- result. status = "error" . to_string ( ) ;
8321- result. error = Some ( error. message . clone ( ) ) ;
8322- result. errors = Some ( vec ! [ error] ) ;
8323- return Ok ( result) ;
8324- }
8325- } ;
8326-
8327- // Count connections per port.
8328- let mut per_port: std:: collections:: HashMap < u16 , u32 > = ports. iter ( ) . map ( |& p| ( p, 0 ) ) . collect ( ) ;
8329- for line in raw_lines. lines ( ) {
8330- // Each token at position 3 (local addr:port) or 4 (peer addr:port) — we look at local.
8331- // Columns: State Recv-Q Send-Q Local-Address:Port Peer-Address:Port
8332- let parts: Vec < & str > = line. split_whitespace ( ) . collect ( ) ;
8333- if parts. len ( ) < 4 {
8334- continue ;
8335- }
8336- let local = parts[ 3 ] ;
8337- // Local may be "0.0.0.0:80" or "[::]:443" or "127.0.0.1:8080"
8338- if let Some ( port_str) = local. rsplit ( ':' ) . next ( ) {
8339- if let Ok ( port) = port_str. parse :: < u16 > ( ) {
8340- if let Some ( count) = per_port. get_mut ( & port) {
8341- * count += 1 ;
8342- }
8343- }
8344- }
8345- }
8346-
8347- let port_details: Vec < serde_json:: Value > = per_port
8348- . iter ( )
8349- . map ( |( & port, & connections) | json ! ( { "port" : port, "connections" : connections } ) )
8350- . collect ( ) ;
8351-
8352- let active_connections: u32 = per_port. values ( ) . sum ( ) ;
8353-
8354- result. result = Some ( json ! ( {
8355- "type" : "check_connections" ,
8356- "active_connections" : active_connections,
8357- "ports" : port_details,
8358- "ports_checked" : ports,
8359- "method" : "ss" ,
8360- "summary" : format!(
8361- "{} active HTTP connection(s) on {} port(s)" ,
8362- active_connections,
8363- ports. len( )
8364- ) ,
8365- } ) ) ;
8366-
8367- Ok ( result)
8368- }
8369-
83708236#[ cfg( test) ]
83718237mod tests {
83728238 use super :: * ;
@@ -9122,43 +8988,6 @@ mod tests {
91228988 assert ! ( parsed. is_none( ) ) ;
91238989 }
91248990
9125- // ── check_connections ────────────────────────────
9126- stacker_test ! (
9127- parses_check_connections_command_no_ports,
9128- "check_connections" ,
9129- json!( { } ) ,
9130- StackerCommand :: CheckConnections
9131- ) ;
9132- stacker_test ! (
9133- parses_check_connections_command_with_ports,
9134- "check_connections" ,
9135- json!( { "ports" : [ 80 , 443 , 8080 ] } ) ,
9136- StackerCommand :: CheckConnections
9137- ) ;
9138- stacker_test ! (
9139- parses_stacker_check_connections_command,
9140- "stacker.check_connections" ,
9141- json!( { } ) ,
9142- StackerCommand :: CheckConnections
9143- ) ;
9144-
9145- #[ test]
9146- fn check_connections_ports_are_deserialized ( ) {
9147- let cmd = AgentCommand {
9148- id : "cmd-cc" . into ( ) ,
9149- command_id : "cmd-cc" . into ( ) ,
9150- name : "check_connections" . into ( ) ,
9151- params : json ! ( { "ports" : [ 80 , 443 ] } ) ,
9152- deployment_hash : Some ( "hash-cc" . into ( ) ) ,
9153- app_code : None ,
9154- } ;
9155- let parsed = parse_stacker_command ( & cmd) . unwrap ( ) . unwrap ( ) ;
9156- match parsed {
9157- StackerCommand :: CheckConnections ( c) => {
9158- assert_eq ! ( c. ports, Some ( vec![ 80u16 , 443u16 ] ) ) ;
9159- assert_eq ! ( c. deployment_hash, "hash-cc" ) ;
9160- }
9161- _ => panic ! ( "expected CheckConnections variant" ) ,
91628991 #[ test]
91638992 fn parses_trigger_pipe_external_target_fields ( ) {
91648993 let cmd = AgentCommand {
@@ -9195,23 +9024,6 @@ mod tests {
91959024 }
91969025
91979026 #[ test]
9198- fn check_connections_defaults_to_no_ports ( ) {
9199- let cmd = AgentCommand {
9200- id : "cmd-cc2" . into ( ) ,
9201- command_id : "cmd-cc2" . into ( ) ,
9202- name : "check_connections" . into ( ) ,
9203- params : json ! ( { } ) ,
9204- deployment_hash : Some ( "hash-cc2" . into ( ) ) ,
9205- app_code : None ,
9206- } ;
9207- let parsed = parse_stacker_command ( & cmd) . unwrap ( ) . unwrap ( ) ;
9208- match parsed {
9209- StackerCommand :: CheckConnections ( c) => {
9210- assert ! ( c. ports. is_none( ) , "ports should be None when omitted" ) ;
9211- }
9212- _ => panic ! ( "expected CheckConnections variant" ) ,
9213- }
9214- }
92159027 fn parses_trigger_pipe_internal_target_fields ( ) {
92169028 let cmd = AgentCommand {
92179029 id : "cmd-trigger" . into ( ) ,
0 commit comments