Skip to content

Commit bd3d0a7

Browse files
New health checks: 'protocol_listener', 'port_listener'
Closes #35. Closes #36.
1 parent f2f08ad commit bd3d0a7

File tree

8 files changed

+144
-27
lines changed

8 files changed

+144
-27
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ reqwest = { version = "0.12", features = [
1515
"multipart",
1616
"default-tls"
1717
] }
18-
rabbitmq_http_client = { version = "0.13.0", features = ["core", "blocking", "tabled"] }
18+
rabbitmq_http_client = { version = "0.14.0", features = ["core", "blocking", "tabled"] }
1919
serde = { version = "1.0", features = ["derive", "std"] }
2020
serde_json = "1"
2121
tabled = "0.17"

src/cli.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use std::path::PathBuf;
1616
use super::constants::*;
1717
use super::static_urls::*;
1818
use clap::{Arg, ArgAction, Command};
19-
use rabbitmq_http_client::commons::{BindingDestinationType, ExchangeType, QueueType};
19+
use rabbitmq_http_client::commons::{
20+
BindingDestinationType, ExchangeType, QueueType, SupportedProtocol,
21+
};
2022

2123
pub fn parser() -> Command {
2224
let after_help = color_print::cformat!(
@@ -911,7 +913,7 @@ fn purge_subcommands() -> [Command; 1] {
911913
)]
912914
}
913915

914-
fn health_check_subcommands() -> [Command; 4] {
916+
fn health_check_subcommands() -> [Command; 6] {
915917
let node_is_quorum_critical_after_help = color_print::cformat!(
916918
r#"
917919
<bold>Doc guides</bold>:
@@ -936,11 +938,42 @@ fn health_check_subcommands() -> [Command; 4] {
936938
DEPRECATED_FEATURE_GUIDE_URL
937939
));
938940

941+
let port_listener = Command::new("port_listener")
942+
.about(
943+
"verifies that there's a reachable TCP listener on the given port on the target node",
944+
)
945+
.arg(
946+
Arg::new("port")
947+
.long("port")
948+
.value_parser(clap::value_parser!(u16)),
949+
)
950+
.after_long_help(color_print::cformat!(
951+
"<bold>Doc guide</bold>: {}",
952+
HEALTH_CHECK_GUIDE_URL
953+
));
954+
955+
let protocol_listener = Command::new("protocol_listener")
956+
.about(
957+
"verifies that there's a reachable TCP listener on the given protocol alias on the target node",
958+
)
959+
.arg(
960+
Arg::new("protocol")
961+
.long("protocol")
962+
.value_parser(clap::value_parser!(SupportedProtocol))
963+
.long_help("An alias for one of the protocols that RabbitMQ supports, with or without TLS: 'amqp', 'amqp/ssl', 'stream', 'stream/ssl', 'mqtt', 'mqtt/ssl', 'stomp', 'stomp/ssl', 'http/web-mqtt', 'https/web-mqtt', 'http/web-stomp', 'https/web-stomp', 'http/prometheus', 'https/prometheus', 'http', 'https'"),
964+
)
965+
.after_long_help(color_print::cformat!(
966+
"<bold>Doc guide</bold>: {}",
967+
HEALTH_CHECK_GUIDE_URL
968+
));
969+
939970
[
940971
local_alarms,
941972
cluster_wide_alarms,
942973
node_is_quorum_critical,
943974
deprecated_features_in_use,
975+
port_listener,
976+
protocol_listener,
944977
]
945978
}
946979

src/commands.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515

1616
use clap::ArgMatches;
1717
use rabbitmq_http_client::commons;
18-
use rabbitmq_http_client::commons::ExchangeType;
1918
use rabbitmq_http_client::commons::UserLimitTarget;
2019
use rabbitmq_http_client::commons::VirtualHostLimitTarget;
20+
use rabbitmq_http_client::commons::{ExchangeType, SupportedProtocol};
2121
use std::fs;
2222
use std::process;
2323

@@ -591,6 +591,27 @@ pub fn health_check_node_is_quorum_critical(client: APIClient) -> ClientResult<(
591591
client.health_check_if_node_is_quorum_critical()
592592
}
593593

594+
pub fn health_check_port_listener(
595+
client: APIClient,
596+
command_args: &ArgMatches,
597+
) -> ClientResult<()> {
598+
// the flag is required
599+
let port = command_args.get_one::<u16>("port").cloned().unwrap();
600+
client.health_check_port_listener(port)
601+
}
602+
603+
pub fn health_check_protocol_listener(
604+
client: APIClient,
605+
command_args: &ArgMatches,
606+
) -> ClientResult<()> {
607+
// the flag is required
608+
let proto = command_args
609+
.get_one::<SupportedProtocol>("protocol")
610+
.cloned()
611+
.unwrap();
612+
client.health_check_protocol_listener(proto)
613+
}
614+
594615
pub fn close_connection(client: APIClient, command_args: &ArgMatches) -> ClientResult<()> {
595616
// the flag is required
596617
let name = command_args.get_one::<String>("name").unwrap();

src/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,14 @@ fn dispatch_subcommand(
348348
let result = commands::health_check_node_is_quorum_critical(client);
349349
res_handler.health_check_result(result);
350350
}
351+
("health_check", "port_listener") => {
352+
let result = commands::health_check_port_listener(client, command_args);
353+
res_handler.health_check_result(result);
354+
}
355+
("health_check", "protocol_listener") => {
356+
let result = commands::health_check_protocol_listener(client, command_args);
357+
res_handler.health_check_result(result);
358+
}
351359
("rebalance", "queues") => {
352360
let result = commands::rebalance_queues(client);
353361
res_handler.no_output_on_success(result);

src/output.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ impl ResultHandler {
187187

188188
println!("{}", table);
189189
}
190-
_ => {
190+
Err(e) => {
191+
println!("Error: {:?}", e);
191192
self.exit_code = Some(ExitCode::Unavailable);
192193
}
193194
}

src/tables.rs

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414
use rabbitmq_http_client::blocking_api::HttpClientError;
15-
use rabbitmq_http_client::responses::{HealthCheckFailureDetails, Overview};
15+
use rabbitmq_http_client::responses::{
16+
ClusterAlarmCheckDetails, HealthCheckFailureDetails, Overview, QuorumCriticalityCheckDetails,
17+
};
1618
use reqwest::StatusCode;
1719
use tabled::settings::Panel;
1820
use tabled::{Table, Tabled};
@@ -135,20 +137,18 @@ pub fn failure_details(error: &HttpClientError) -> Table {
135137
},
136138
];
137139

138-
match details {
139-
HealthCheckFailureDetails::AlarmCheck(details) => {
140-
data.push(RowOfTwoStrings {
141-
key: "reason",
142-
value: details.reason.as_str(),
143-
});
144-
}
145-
HealthCheckFailureDetails::NodeIsQuorumCritical(details) => {
146-
data.push(RowOfTwoStrings {
147-
key: "reason",
148-
value: details.reason.as_str(),
149-
});
140+
let reason = match details {
141+
HealthCheckFailureDetails::AlarmCheck(details) => details.reason.clone(),
142+
HealthCheckFailureDetails::NodeIsQuorumCritical(details) => details.reason.clone(),
143+
HealthCheckFailureDetails::NoActivePortListener(details) => details.reason.clone(),
144+
HealthCheckFailureDetails::NoActiveProtocolListener(details) => {
145+
details.reason.clone()
150146
}
151147
};
148+
data.push(RowOfTwoStrings {
149+
key: "reason",
150+
value: reason.as_str(),
151+
});
152152

153153
let tb = Table::builder(data);
154154
tb.build()
@@ -281,6 +281,8 @@ pub fn health_check_failure(
281281
let reason = match details {
282282
HealthCheckFailureDetails::AlarmCheck(ref details) => details.reason.clone(),
283283
HealthCheckFailureDetails::NodeIsQuorumCritical(ref details) => details.reason.clone(),
284+
HealthCheckFailureDetails::NoActivePortListener(ref details) => details.reason.clone(),
285+
HealthCheckFailureDetails::NoActiveProtocolListener(ref details) => details.reason.clone(),
284286
};
285287
let code_str = format!("{}", status_code);
286288

@@ -304,24 +306,32 @@ pub fn health_check_failure(
304306
];
305307
let mut tb = Table::builder(vec);
306308
match details {
307-
HealthCheckFailureDetails::AlarmCheck(
308-
rabbitmq_http_client::responses::ClusterAlarmCheckDetails { reason: _, alarms },
309-
) => {
309+
HealthCheckFailureDetails::AlarmCheck(ClusterAlarmCheckDetails { reason: _, alarms }) => {
310310
for alarm in alarms {
311311
let key = format!("alarm in effect on node {}", alarm.node);
312312
let value = alarm.resource;
313313
tb.push_record([key.as_str(), value.as_str()]);
314314
}
315315
}
316-
HealthCheckFailureDetails::NodeIsQuorumCritical(
317-
rabbitmq_http_client::responses::QuorumCriticalityCheckDetails { reason: _, queues },
318-
) => {
316+
HealthCheckFailureDetails::NodeIsQuorumCritical(QuorumCriticalityCheckDetails {
317+
reason: _,
318+
queues,
319+
}) => {
319320
for q in queues {
320321
let key = "affected queue, stream or internal component";
321322
let value = q.readable_name;
322323
tb.push_record([key, value.as_str()]);
323324
}
324325
}
326+
HealthCheckFailureDetails::NoActivePortListener(details) => {
327+
tb.push_record(["inactive port", details.inactive_port.to_string().as_str()]);
328+
}
329+
HealthCheckFailureDetails::NoActiveProtocolListener(details) => {
330+
tb.push_record([
331+
"inactive protocol",
332+
details.inactive_protocol.to_string().as_str(),
333+
]);
334+
}
325335
};
326336

327337
tb.build()

tests/health_check_tests.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use predicates::prelude::*;
1515

1616
mod test_helpers;
17-
use test_helpers::run_succeeds;
17+
use test_helpers::{run_fails, run_succeeds};
1818

1919
#[test]
2020
fn test_health_check_local_alarms() -> Result<(), Box<dyn std::error::Error>> {
@@ -30,3 +30,47 @@ fn test_health_check_cluster_wide_alarms() -> Result<(), Box<dyn std::error::Err
3030

3131
Ok(())
3232
}
33+
34+
#[test]
35+
fn test_health_check_port_listener_succeeds() -> Result<(), Box<dyn std::error::Error>> {
36+
run_succeeds(["health_check", "port_listener", "--port", "15672"])
37+
.stdout(predicate::str::contains("passed"));
38+
39+
Ok(())
40+
}
41+
42+
#[test]
43+
fn test_health_check_port_listener_fails() -> Result<(), Box<dyn std::error::Error>> {
44+
run_fails(["health_check", "port_listener", "--port", "15679"])
45+
.stdout(predicate::str::contains("failed"));
46+
47+
Ok(())
48+
}
49+
50+
#[test]
51+
fn test_health_check_protocol_listener_succeeds() -> Result<(), Box<dyn std::error::Error>> {
52+
run_succeeds(["health_check", "protocol_listener", "--protocol", "amqp"])
53+
.stdout(predicate::str::contains("passed"));
54+
55+
Ok(())
56+
}
57+
58+
#[test]
59+
fn test_health_check_protocol_listener_fails() -> Result<(), Box<dyn std::error::Error>> {
60+
run_fails([
61+
"health_check",
62+
"protocol_listener",
63+
"--protocol",
64+
"https/prometheus",
65+
])
66+
.stdout(predicate::str::contains("failed"));
67+
run_fails([
68+
"health_check",
69+
"protocol_listener",
70+
"--protocol",
71+
"unknown/proto",
72+
])
73+
.stdout(predicate::str::contains("failed"));
74+
75+
Ok(())
76+
}

0 commit comments

Comments
 (0)