pub mod dispatch;
use std::path::PathBuf;
use super::constants::*;
use super::static_urls::*;
use super::tanzu_cli::tanzu_subcommands;
use crate::config::PreFlightSettings;
use crate::output::TableStyle;
use clap::{Arg, ArgAction, ArgGroup, Command, crate_name, crate_version, value_parser};
use rabbitmq_http_client::commons::{
BindingDestinationType, ChannelUseMode, ExchangeType, MessageTransferAcknowledgementMode,
PolicyTarget, QueueType, SupportedProtocol,
};
use rabbitmq_http_client::password_hashing::HashingAlgorithm;
use rabbitmq_http_client::requests::FederationResourceCleanupMode;
pub use bel7_cli::CompletionShell;
pub fn parser(pre_flight_settings: PreFlightSettings) -> Command {
let after_help = color_print::cformat!(
r#"
<bold><blue>Documentation and Community Resources</blue></bold>
rabbitmqadmin docs: {}
RabbitMQ docs: {}
GitHub Discussions: {}
Discord server: {}
<bold><blue>Contribute</blue></bold>
On GitHub: {}"#,
RABBITMQADMIN_DOC_GUIDE_URL,
RABBITMQ_DOC_GUIDES_URL,
GITHUB_DISCUSSIONS_URL,
DISCORD_SERVER_INVITATION_URL,
GITHUB_REPOSITORY_URL
);
let auth_attempts_group = Command::new("auth_attempts")
.about("Authentication attempt statistics")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
ACCESS_CONTROL_GUIDE_URL
))
.arg_required_else_help(true)
.subcommands(auth_attempts_subcommands(pre_flight_settings.clone()));
let bindings_group = Command::new("bindings")
.about("Operations on bindings")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.subcommand_value_name("binding")
.arg_required_else_help(true)
.subcommands(binding_subcommands(pre_flight_settings.clone()));
let channels_group = Command::new("channels")
.about("Operations on channels")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.arg_required_else_help(true)
.subcommands(channels_subcommands(pre_flight_settings.clone()));
let close_group = Command::new("close")
.about("Closes connections")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.arg_required_else_help(true)
.subcommands(close_subcommands(pre_flight_settings.clone()));
let connections_group = Command::new("connections")
.about("Operations on connections")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.arg_required_else_help(true)
.subcommands(connections_subcommands(pre_flight_settings.clone()));
let config_file_group = Command::new("config_file")
.about("Operations on the local configuration file")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.arg_required_else_help(true)
.subcommands(config_file_subcommands(pre_flight_settings.clone()));
let declare_group = Command::new("declare")
.about("Creates or declares objects")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.arg_required_else_help(true)
.subcommands(declare_subcommands(pre_flight_settings.clone()));
let definitions_group = Command::new("definitions")
.about("Operations on definitions (everything except for messages: virtual hosts, queues, streams, exchanges, bindings, users, etc)")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEFINITION_GUIDE_URL
))
.subcommand_value_name("export")
.arg_required_else_help(true)
.subcommands(definitions_subcommands(pre_flight_settings.clone()));
let delete_group = Command::new("delete")
.about("Deletes objects")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.arg_required_else_help(true)
.subcommands(delete_subcommands(pre_flight_settings.clone()));
let deprecated_features_group = Command::new("deprecated_features")
.about("Operations on deprecated features")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEPRECATED_FEATURE_GUIDE_URL
))
.subcommand_value_name("deprecated feature")
.arg_required_else_help(true)
.subcommands(deprecated_features_subcommands(pre_flight_settings.clone()));
let exchanges_group = Command::new("exchanges")
.about("Operations on exchanges")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.subcommand_value_name("exchange")
.arg_required_else_help(true)
.subcommands(exchanges_subcommands(pre_flight_settings.clone()));
let export_group = Command::new("export")
.about("See 'definitions export'")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEFINITION_GUIDE_URL
))
.subcommand_value_name("definitions")
.arg_required_else_help(true)
.subcommands(export_subcommands(pre_flight_settings.clone()));
let feature_flags_group = Command::new("feature_flags")
.about("Operations on feature flags")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
FEATURE_FLAG_GUIDE_URL
))
.subcommand_value_name("feature flag")
.arg_required_else_help(true)
.subcommands(feature_flags_subcommands(pre_flight_settings.clone()));
let federation_group = Command::new("federation")
.about("Operations on federation upstreams and links")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}
* {}"#,
FEDERATION_GUIDE_URL,
FEDERATED_EXCHANGES_GUIDE_URL,
FEDERATED_QUEUES_GUIDE_URL,
FEDERATION_REFERENCE_URL
))
.arg_required_else_help(true)
.subcommands(federation_subcommands(pre_flight_settings.clone()));
let get_group = Command::new("get")
.about(color_print::cstr!("Fetches message(s) from a queue or stream via <bold><red>polling</red></bold>. <bold><red>Only suitable for development and test environments</red></bold>."))
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", POLLING_CONSUMER_GUIDE_URL))
.subcommand_value_name("message")
.arg_required_else_help(true)
.subcommands(get_subcommands(pre_flight_settings.clone()));
let global_parameters_group = Command::new("global_parameters")
.about("Operations on global runtime parameters")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
RUNTIME_PARAMETER_GUIDE_URL
))
.subcommand_value_name("runtime_parameter")
.arg_required_else_help(true)
.subcommands(global_parameters_subcommands(pre_flight_settings.clone()));
let health_check_group = Command::new("health_check")
.about("Runs health checks")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.subcommand_value_name("check")
.arg_required_else_help(true)
.subcommands(health_check_subcommands(pre_flight_settings.clone()))
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}"#,
HEALTH_CHECK_GUIDE_URL,
DEPRECATED_FEATURE_GUIDE_URL
));
let import_group = Command::new("import")
.about("See 'definitions import'")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEFINITION_GUIDE_URL
))
.subcommand_value_name("definitions")
.arg_required_else_help(true)
.subcommands(import_subcommands(pre_flight_settings.clone()));
let list_group = Command::new("list")
.about("Lists objects")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.arg_required_else_help(true)
.subcommands(list_subcommands(pre_flight_settings.clone()));
let nodes_group = Command::new("nodes")
.about("Node operations")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.arg_required_else_help(true)
.subcommands(nodes_subcommands(pre_flight_settings.clone()));
let operator_policies_group = Command::new("operator_policies")
.about("Operations on operator policies")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
POLICY_GUIDE_URL
))
.subcommand_value_name("operator policy")
.arg_required_else_help(true)
.subcommands(operator_policies_subcommands(pre_flight_settings.clone()));
let parameters_group = Command::new("parameters")
.about("Operations on runtime parameters")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
RUNTIME_PARAMETER_GUIDE_URL
))
.subcommand_value_name("runtime_parameter")
.arg_required_else_help(true)
.subcommands(parameters_subcommands(pre_flight_settings.clone()));
let passwords_group = Command::new("passwords")
.about("Operations on passwords")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
PASSWORD_GUIDE_URL
))
.arg_required_else_help(true)
.subcommands(passwords_subcommands(pre_flight_settings.clone()));
let permissions_group = Command::new("permissions")
.about("Operations on user permissions")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
ACCESS_CONTROL_GUIDE_URL
))
.subcommand_value_name("permission")
.arg_required_else_help(true)
.subcommands(permissions_subcommands(pre_flight_settings.clone()));
let plugins_group = Command::new("plugins")
.about("List enabled plugins")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
PLUGIN_GUIDE_URL
))
.subcommand_value_name("plugin")
.arg_required_else_help(true)
.subcommands(plugins_subcommands(pre_flight_settings.clone()));
let policies_group = Command::new("policies")
.about("Operations on policies")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
POLICY_GUIDE_URL
))
.subcommand_value_name("policy")
.arg_required_else_help(true)
.subcommands(policies_subcommands(pre_flight_settings.clone()));
let publish_group = Command::new("publish")
.about(color_print::cstr!("Publishes (<red>inefficiently</red>) message(s) to a queue or a stream. <bold><red>Only suitable for development and test environments</red></bold>."))
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", PUBLISHER_GUIDE_URL))
.subcommand_value_name("message")
.arg_required_else_help(true)
.subcommands(publish_subcommands(pre_flight_settings.clone()));
let purge_group = Command::new("purge")
.about("Purges queues")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.subcommand_value_name("queue")
.arg_required_else_help(true)
.subcommands(purge_subcommands(pre_flight_settings.clone()));
let queues_group = Command::new("queues")
.about("Operations on queues")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.subcommand_value_name("queue")
.arg_required_else_help(true)
.subcommands(queues_subcommands(pre_flight_settings.clone()));
let rebalance_group = Command::new("rebalance")
.about("Rebalancing of leader replicas")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
QUORUM_QUEUE_GUIDE_URL
))
.subcommand_value_name("queues")
.arg_required_else_help(true)
.subcommands(rebalance_subcommands(pre_flight_settings.clone()));
let show_group = Command::new("show")
.about("Overview, memory footprint breakdown, and more")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
MONITORING_GUIDE_URL
))
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.arg_required_else_help(true)
.subcommands(show_subcommands(pre_flight_settings.clone()));
let shovels_group = Command::new("shovels")
.about("Operations on shovels")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
SHOVEL_GUIDE_URL
))
.subcommand_value_name("shovels")
.arg_required_else_help(true)
.subcommands(shovel_subcommands(pre_flight_settings.clone()));
let streams_group = Command::new("streams")
.about("Operations on streams")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.subcommand_value_name("stream")
.arg_required_else_help(true)
.subcommands(streams_subcommands(pre_flight_settings.clone()));
let tanzu_group = Command::new("tanzu")
.about("Tanzu RabbitMQ-specific commands")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
COMMERCIAL_OFFERINGS_GUIDE_URL
))
.subcommand_value_name("subcommand")
.arg_required_else_help(true)
.subcommands(tanzu_subcommands());
let users_group = Command::new("users")
.about("Operations on users")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
ACCESS_CONTROL_GUIDE_URL
))
.subcommand_value_name("subcommand")
.arg_required_else_help(true)
.subcommands(users_subcommands(pre_flight_settings.clone()));
let user_limits_group = Command::new("user_limits")
.about("Operations on per-user (resource) limits")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
USER_LIMIT_GUIDE_URL
))
.subcommand_value_name("user_limit")
.arg_required_else_help(true)
.subcommands(user_limits_subcommands(pre_flight_settings.clone()));
let vhosts_group = Command::new("vhosts")
.about("Virtual host operations")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.arg_required_else_help(true)
.subcommands(vhosts_subcommands(pre_flight_settings.clone()));
let shell_group = Command::new("shell")
.about("Shell-related operations")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.arg_required_else_help(true)
.subcommands(shell_subcommands());
let vhost_limits_group = Command::new("vhost_limits")
.about("Operations on virtual host (resource) limits")
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
VIRTUAL_HOST_LIMIT_GUIDE_URL
))
.subcommand_value_name("vhost_limit")
.arg_required_else_help(true)
.subcommands(vhost_limits_subcommands(pre_flight_settings.clone()));
let command_groups = [
auth_attempts_group,
bindings_group,
channels_group,
close_group,
config_file_group,
connections_group,
declare_group,
definitions_group,
delete_group,
deprecated_features_group,
exchanges_group,
export_group,
feature_flags_group,
federation_group,
get_group,
global_parameters_group,
health_check_group,
import_group,
list_group,
nodes_group,
operator_policies_group,
parameters_group,
passwords_group,
permissions_group,
plugins_group,
policies_group,
publish_group,
purge_group,
queues_group,
rebalance_group,
shell_group,
show_group,
shovels_group,
streams_group,
tanzu_group,
users_group,
user_limits_group,
vhosts_group,
vhost_limits_group,
];
Command::new(crate_name!())
.version(crate_version!())
.author("The RabbitMQ Core Team")
.about(format!("{} gen 2, version: {}", crate_name!(), crate_version!()))
.long_about(format!(
"RabbitMQ CLI that uses the HTTP API. Version: {}",
crate_version!()
))
.infer_subcommands(pre_flight_settings.infer_subcommands)
.infer_long_args(pre_flight_settings.infer_long_options)
.after_help(after_help)
.disable_version_flag(true)
.arg(
Arg::new("version")
.long("version")
.action(ArgAction::Version)
.help("Print version"),
)
.arg(
Arg::new("config_file_path")
.short('c')
.long("config")
.env("RABBITMQADMIN_CONFIG_FILE_PATH")
.value_parser(value_parser!(PathBuf))
.default_value(DEFAULT_CONFIG_FILE_PATH),
)
.arg(
Arg::new("node_alias")
.short('N')
.long("node")
.env("RABBITMQADMIN_NODE_ALIAS")
.required(false)
.default_value(DEFAULT_NODE_ALIAS),
)
.arg(
Arg::new("host")
.short('H')
.long("host")
.alias("hostname")
.env("RABBITMQADMIN_TARGET_HOST")
.help("HTTP API hostname to use when connecting"),
)
.visible_alias("hostname")
.arg(
Arg::new("port")
.short('P')
.long("port")
.env("RABBITMQADMIN_TARGET_PORT")
.help("HTTP API port to use when connecting")
.required(false)
.value_parser(value_parser!(u16)),
)
.arg(
Arg::new("base_uri")
.short('U')
.long("base-uri")
.env("RABBITMQADMIN_BASE_URI")
.help("base HTTP API endpoint URI")
.required(false)
.conflicts_with_all(["host", "port"]),
)
.arg(
Arg::new("path_prefix")
.long("path-prefix")
.env("RABBITMQADMIN_API_PATH_PREFIX")
.help("use if target node uses a path prefix. Defaults to '/api'"),
)
.arg(
Arg::new("vhost")
.short('V')
.long("vhost")
.global(true)
.env("RABBITMQADMIN_TARGET_VHOST")
.help("target virtual host. Defaults to '/'"),
)
.arg(
Arg::new("username")
.short('u')
.long("username")
.env("RABBITMQADMIN_USERNAME")
.help(format!(
"this user must have the permissions for HTTP API access, see {}",
HTTP_API_ACCESS_PERMISSIONS_GUIDE_URL
)),
)
.arg(
Arg::new("password")
.short('p')
.long("password")
.env("RABBITMQADMIN_PASSWORD")
.requires("username")
.help("requires username to be specified via --username or in the config file"),
)
.arg(
Arg::new("insecure")
.short('k')
.long("insecure")
.required(false)
.help("disables TLS peer (certificate chain) verification")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("tls")
.long("use-tls")
.help("use TLS (HTTPS) for HTTP API requests ")
.env("RABBITMQADMIN_USE_TLS")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("ca_certificate_bundle_path")
.long("tls-ca-cert-file")
.required(false)
.help("Local path to a CA certificate file in the PEM format")
.value_parser(value_parser!(PathBuf)),
)
.arg(
Arg::new("client_certificate_file_path")
.long("tls-cert-file")
.required(false)
.requires("tls")
.help("Local path to a client certificate file in the PEM format")
.value_parser(value_parser!(PathBuf)),
)
.arg(
Arg::new("client_private_key_file_path")
.long("tls-key-file")
.required(false)
.requires("tls")
.help("Local path to a client private key file in the PEM format")
.value_parser(value_parser!(PathBuf)),
)
.arg(
Arg::new("timeout")
.long("timeout")
.env("RABBITMQADMIN_TIMEOUT")
.help("HTTP API request timeout in seconds. Must be greater than 0")
.required(false)
.default_value("60")
.value_parser(value_parser!(u64).range(1..)),
)
.arg(
Arg::new("quiet")
.short('q')
.long("quiet")
.env("RABBITMQADMIN_QUIET_MODE")
.help("produce less output")
.required(false)
.conflicts_with("verbose")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.env("RABBITMQADMIN_VERBOSE")
.help("enable verbose logging of HTTP requests and responses")
.required(false)
.conflicts_with("quiet")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("non_interactive")
.long("non-interactive")
.global(true)
.env("RABBITMQADMIN_NON_INTERACTIVE_MODE")
.help("pass when invoking from scripts")
.conflicts_with("table_style")
.required(false)
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("table_style")
.long("table-style")
.global(true)
.env("RABBITMQADMIN_TABLE_STYLE")
.help("style preset to apply to output tables: modern, borderless, ascii, dots, psql, markdown, sharp")
.conflicts_with("non_interactive")
.required(false)
.value_parser(value_parser!(TableStyle))
)
.subcommand_required(true)
.subcommands(command_groups)
}
fn pagination_args() -> [Arg; 2] {
[
Arg::new("page")
.long("page")
.help("page number (1-indexed)")
.required(false)
.value_parser(value_parser!(u64).range(1..)),
Arg::new("page_size")
.long("page-size")
.help("number of results per page (default: 100, max: 500)")
.required(false)
.value_parser(value_parser!(u64).range(1..=500)),
]
}
fn columns_arg() -> Arg {
Arg::new("columns")
.long("columns")
.help("comma-separated list of columns to display")
.required(false)
.value_parser(value_parser!(String))
}
fn list_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let nodes_cmd = Command::new("nodes").long_about("Lists cluster members");
let vhosts_cmd = Command::new("vhosts")
.long_about("Lists virtual hosts")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
VIRTUAL_HOST_GUIDE_URL
));
let vhost_limits_cmd = Command::new("vhost_limits")
.long_about("Lists virtual host (resource) limits")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
VIRTUAL_HOST_GUIDE_URL
));
let connections_cmd = Command::new("connections")
.long_about("Lists client connections")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
CONNECTION_GUIDE_URL
))
.args(pagination_args());
let channels_cmd = Command::new("channels")
.long_about("Lists AMQP 0-9-1 channels")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
CHANNEL_GUIDE_URL
))
.args(pagination_args());
let queues_cmd = Command::new("queues")
.long_about("Lists queues and streams")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
QUEUE_GUIDE_URL
))
.args(pagination_args());
let exchanges_cmd = Command::new("exchanges")
.long_about("Lists exchanges")
.args(pagination_args());
let bindings_cmd = Command::new("bindings").long_about("Lists bindings");
let consumers_cmd = Command::new("consumers")
.long_about("Lists consumers")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
CONSUMER_GUIDE_URL
));
let parameters_cmd = Command::new("parameters")
.arg(
Arg::new("component")
.long("component")
.help("component (for example: federation-upstream, vhost-limits)")
.required(false),
)
.long_about("Lists runtime parameters")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
RUNTIME_PARAMETER_GUIDE_URL
));
let policies_cmd = Command::new("policies")
.long_about("Lists policies")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
POLICY_GUIDE_URL
));
let operator_policies_cmd = Command::new("operator_policies")
.long_about("Lists operator policies")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
OPERATOR_POLICY_GUIDE_URL
));
let users_cmd = Command::new("users")
.long_about("Lists users in the internal database")
.args(pagination_args());
let permissions_cmd = Command::new("permissions")
.long_about("Lists user permissions")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
ACCESS_CONTROL_GUIDE_URL
));
let user_connections_cmd = Command::new("user_connections")
.arg(
Arg::new("username")
.short('u')
.long("username")
.required(true)
.help("Name of the user whose connections should be listed"),
)
.long_about("Lists client connections that authenticated with a specific username")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
CONNECTION_GUIDE_URL
));
let user_limits_cmd = Command::new("user_limits")
.arg(
Arg::new("username")
.long("username")
.alias("user")
.help("username")
.required(false),
)
.long_about("Lists per-user (resource) limits")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
USER_LIMIT_GUIDE_URL
));
let feature_flags_cmd = Command::new("feature_flags")
.long_about("Lists feature flags and their cluster state")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
FEATURE_FLAG_GUIDE_URL
));
let deprecated_features_cmd = Command::new("deprecated_features")
.long_about("Lists all deprecated features")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEPRECATED_FEATURE_GUIDE_URL
));
let deprecated_features_in_use_cmd = Command::new("deprecated_features_in_use")
.long_about("Lists the deprecated features that are in used in the cluster")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEPRECATED_FEATURE_GUIDE_URL
));
[
nodes_cmd,
users_cmd,
vhosts_cmd,
permissions_cmd,
connections_cmd,
user_connections_cmd,
channels_cmd,
queues_cmd,
exchanges_cmd,
bindings_cmd,
consumers_cmd,
parameters_cmd,
policies_cmd,
operator_policies_cmd,
vhost_limits_cmd,
user_limits_cmd,
feature_flags_cmd,
deprecated_features_cmd,
deprecated_features_in_use_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn declare_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let user_cmd = Command::new("user")
.about("Creates a user")
.arg(
Arg::new("name")
.long("name")
.help("username")
.required(true),
)
.arg(
Arg::new("password_hash")
.help(color_print::cformat!(
"salted password hash, see {}",
PASSWORD_GUIDE_URL
))
.long("password-hash")
.required(false)
.default_value(""),
)
.arg(
Arg::new("password")
.long("password")
.help(color_print::cformat!(
"prefer providing a hash, see {}",
PASSWORD_GUIDE_URL
))
.required(false)
.default_value(""),
)
.arg(
Arg::new("hashing_algorithm")
.long("hashing-algorithm")
.required(false)
.conflicts_with("password_hash")
.requires("password")
.value_parser(value_parser!(HashingAlgorithm))
.default_value("SHA256")
.help("The hashing algorithm to use: SHA256 or SHA512"),
)
.arg(
Arg::new("tags")
.long("tags")
.help("a list of comma-separated tags")
.default_value(""),
);
let vhost_cmd = Command::new("vhost")
.about("Creates a virtual host")
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", VIRTUAL_HOST_GUIDE_URL))
.arg(
Arg::new("name")
.long("name")
.help("virtual host name")
.required(true),
)
.arg(
Arg::new("default_queue_type")
.long("default-queue-type")
.required(false)
.help(color_print::cformat!("default queue type, one of: <bold>classic</bold>, <bright-blue>quorum</bright-blue>, <bright-magenta>stream</bright-magenta>"))
)
.arg(
Arg::new("description")
.long("description")
.required(false)
.help("what's the purpose of this virtual host?"),
)
.arg(
Arg::new("tracing")
.long("tracing")
.required(false)
.action(ArgAction::SetTrue)
.help("should tracing be enabled for this virtual host?"),
);
let permissions_cmd = Command::new("permissions")
.about("grants permissions to a user")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
ACCESS_CONTROL_GUIDE_URL
))
.arg(
Arg::new("username")
.long("username")
.alias("user")
.help("username")
.required(true),
)
.arg(
Arg::new("configure")
.long("configure")
.help("name pattern for configuration access")
.required(true),
)
.arg(
Arg::new("read")
.long("read")
.help("name pattern for read access")
.required(true),
)
.arg(
Arg::new("write")
.long("write")
.help("name pattern for write access")
.required(true),
);
let queue_cmd = Command::new("queue")
.about("Declares a queue or a stream")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
QUEUE_GUIDE_URL
))
.arg(Arg::new("name").long("name").required(true).help("name"))
.arg(
Arg::new("type")
.long("type")
.help("queue type")
.value_parser(value_parser!(QueueType))
.required(false)
.default_value("classic"),
)
.arg(
Arg::new("durable")
.long("durable")
.help("should it persist after a restart")
.required(false)
.value_parser(value_parser!(bool)),
)
.arg(
Arg::new("auto_delete")
.long("auto-delete")
.help("should it be deleted when the last consumer disconnects")
.required(false)
.value_parser(value_parser!(bool)),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional queue arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
);
let stream_cmd = Command::new("stream")
.about("Declares a stream")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
STREAM_GUIDE_URL
))
.arg(Arg::new("name").long("name").required(true).help("name"))
.arg(
Arg::new("expiration")
.long("expiration")
.help("stream expiration, e.g. 12h for 12 hours, 7D for 7 days, or 1M for 1 month")
.required(true)
.value_parser(value_parser!(String)),
)
.arg(
Arg::new("max_length_bytes")
.long("max-length-bytes")
.help("maximum stream length in bytes")
.required(false)
.value_parser(value_parser!(u64)),
)
.arg(
Arg::new("max_segment_length_bytes")
.long("stream-max-segment-size-bytes")
.help("maximum stream segment file length in bytes")
.required(false)
.value_parser(value_parser!(u64)),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional stream arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
);
let exchange_cmd = Command::new("exchange")
.about("Declares an exchange")
.arg(
Arg::new("name")
.long("name")
.help("exchange name")
.required(true),
)
.arg(
Arg::new("type")
.long("type")
.help("exchange type")
.value_parser(value_parser!(ExchangeType))
.required(false),
)
.arg(
Arg::new("durable")
.long("durable")
.help("should it persist after a restart")
.required(false)
.value_parser(value_parser!(bool)),
)
.arg(
Arg::new("auto_delete")
.long("auto-delete")
.help("should it be deleted when the last queue is unbound")
.required(false)
.value_parser(value_parser!(bool)),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional exchange arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
);
let binding_cmd = Command::new("binding")
.about("Creates a binding between a source exchange and a destination (a queue or an exchange)")
.arg(
Arg::new("source")
.long("source")
.help("source exchange")
.required(true),
)
.arg(
Arg::new("destination_type")
.long("destination-type")
.help("destination type: exchange or queue")
.required(true)
.value_parser(value_parser!(BindingDestinationType)),
)
.arg(
Arg::new("destination")
.long("destination")
.help("destination exchange/queue name")
.required(true),
)
.arg(
Arg::new("routing_key")
.long("routing-key")
.help("routing key")
.required(true),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
);
let parameter_cmd = Command::new("parameter")
.about("Sets a runtime parameter")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
RUNTIME_PARAMETER_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("parameter's name")
.required(true),
)
.arg(
Arg::new("component")
.long("component")
.help("component (eg. federation)")
.required(true),
)
.arg(
Arg::new("value")
.long("value")
.help("parameter's value")
.required(true),
);
let policy_cmd = Command::new("policy")
.about("Creates or updates a policy")
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", POLICY_GUIDE_URL))
.arg(
Arg::new("name")
.long("name")
.help("policy name")
.required(true),
)
.arg(
Arg::new("pattern")
.long("pattern")
.help("the pattern that is used to match entity (queue, stream, exchange) names")
.required(true),
)
.arg(
Arg::new("apply_to")
.long("apply-to")
.alias("applies-to")
.help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
.value_parser(value_parser!(PolicyTarget))
.required(true),
)
.arg(
Arg::new("priority")
.long("priority")
.help("policy priority (only the policy with the highest priority is effective)")
.required(false)
.default_value("0"),
)
.arg(
Arg::new("definition")
.long("definition")
.help("policy definition")
.required(true),
);
let operator_policy_cmd = Command::new("operator_policy")
.about("Creates or updates an operator policy")
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", OPERATOR_POLICY_GUIDE_URL))
.arg(
Arg::new("name")
.long("name")
.help("operator policy name")
.required(true),
)
.arg(
Arg::new("pattern")
.long("pattern")
.help("queue/exchange name pattern")
.required(true),
)
.arg(
Arg::new("apply_to")
.long("apply-to")
.alias("applies-to")
.help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
.value_parser(value_parser!(PolicyTarget))
.required(true),
)
.arg(
Arg::new("priority")
.long("priority")
.help("policy priority (only the policy with the highest priority is effective)")
.required(false)
.default_value("0"),
)
.arg(
Arg::new("definition")
.long("definition")
.help("policy definition")
.required(true),
);
let vhost_limit_cmd = Command::new("vhost_limit")
.about("Set a vhost limit")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
VIRTUAL_HOST_LIMIT_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("limit name (eg. max-connections, max-queues)")
.required(true),
)
.arg(
Arg::new("value")
.long("value")
.help("limit value")
.required(true),
);
let user_limit_cmd = Command::new("user_limit")
.about("Set a user limit")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
USER_LIMIT_GUIDE_URL
))
.arg(
Arg::new("username")
.long("username")
.alias("user")
.help("username")
.required(true),
)
.arg(
Arg::new("name")
.long("name")
.help("limit name (eg. max-connections, max-queues)")
.required(true),
)
.arg(
Arg::new("value")
.long("value")
.help("limit value")
.required(true),
);
[
user_cmd,
vhost_cmd,
permissions_cmd,
queue_cmd,
stream_cmd,
exchange_cmd,
binding_cmd,
parameter_cmd,
policy_cmd,
operator_policy_cmd,
vhost_limit_cmd,
user_limit_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn show_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let overview_cmd = Command::new("overview")
.about("Displays essential information about target node and its cluster");
let churn_cmd = Command::new("churn").about("Displays object churn metrics");
let endpoint_cmd = Command::new("endpoint")
.about("Displays the computed HTTP API endpoint URI. Use for troubleshooting only.");
let memory_breakdown_in_bytes_cmd = Command::new("memory_breakdown_in_bytes")
.about("Provides a memory footprint breakdown (in bytes) for the target node")
.arg(
Arg::new("node")
.long("node")
.help("target node, must be a cluster member")
.required(true),
)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
MEMORY_FOOTPRINT_GUIDE_URL
));
let memory_breakdown_in_percent_cmd = Command::new("memory_breakdown_in_percent")
.about("Provides a memory footprint breakdown (in percent) for the target node")
.arg(
Arg::new("node")
.long("node")
.help("target node, must be a cluster member")
.required(true),
)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
MEMORY_FOOTPRINT_GUIDE_URL
));
[
overview_cmd,
churn_cmd,
endpoint_cmd,
memory_breakdown_in_bytes_cmd,
memory_breakdown_in_percent_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn delete_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let user_cmd = Command::new("user")
.about("Deletes a user")
.arg(
Arg::new("name")
.long("name")
.help("username")
.required(true),
)
.arg(idempotently_arg.clone());
let vhost_cmd = Command::new("vhost")
.about("Deletes a virtual host")
.arg(
Arg::new("name")
.long("name")
.help("virtual host")
.required(true),
)
.arg(idempotently_arg.clone());
let permissions_cmd = Command::new("permissions")
.about("Revokes user permissions to a given vhost")
.arg(
Arg::new("username")
.long("username")
.alias("user")
.help("username")
.required(true),
)
.arg(idempotently_arg.clone());
let queue_cmd = Command::new("queue")
.about("Deletes a queue")
.arg(
Arg::new("name")
.long("name")
.help("queue name")
.required(true),
)
.arg(idempotently_arg.clone());
let stream_cmd = Command::new("stream")
.about("Deletes a stream")
.arg(
Arg::new("name")
.long("name")
.help("stream name")
.required(true),
)
.arg(idempotently_arg.clone());
let exchange_cmd = Command::new("exchange")
.about("Deletes an exchange")
.arg(
Arg::new("name")
.long("name")
.help("exchange name")
.required(true),
)
.arg(idempotently_arg.clone());
let binding_cmd = Command::new("binding")
.about("Deletes a binding")
.arg(
Arg::new("source")
.long("source")
.help("source exchange")
.required(true),
)
.arg(
Arg::new("destination_type")
.long("destination-type")
.help("destination type: exchange or queue")
.required(true),
)
.arg(
Arg::new("destination")
.long("destination")
.help("destination exchange/queue name")
.required(true),
)
.arg(
Arg::new("routing_key")
.long("routing-key")
.help("routing key")
.required(true),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
)
.arg(idempotently_arg.clone());
let parameter_cmd = Command::new("parameter")
.about("Clears a runtime parameter")
.arg(
Arg::new("name")
.long("name")
.help("parameter's name")
.required(true),
)
.arg(
Arg::new("component")
.long("component")
.help("component (eg. federation-upstream)")
.required(true),
)
.arg(idempotently_arg.clone());
let policy_cmd = Command::new("policy")
.about("Deletes a policy")
.arg(
Arg::new("name")
.long("name")
.help("policy name")
.required(true),
)
.arg(idempotently_arg.clone());
let operator_policy_cmd = Command::new("operator_policy")
.about("Deletes an operator policy")
.arg(
Arg::new("name")
.long("name")
.help("operator policy name")
.required(true),
)
.arg(idempotently_arg.clone());
let vhost_limit_cmd = Command::new("vhost_limit")
.about("delete a vhost limit")
.arg(
Arg::new("name")
.long("name")
.help("limit name (eg. max-connections, max-queues)")
.required(true),
);
let user_limit_cmd = Command::new("user_limit")
.about("Clears a user limit")
.arg(
Arg::new("username")
.long("username")
.alias("user")
.help("username")
.required(true),
)
.arg(
Arg::new("name")
.long("name")
.help("limit name (eg. max-connections, max-queues)")
.required(true),
);
let shovel_cmd = Command::new("shovel")
.about("Delete a shovel")
.arg(idempotently_arg.clone())
.arg(
Arg::new("name")
.long("name")
.help("shovel name")
.required(true),
);
[
user_cmd,
vhost_cmd,
permissions_cmd,
queue_cmd,
stream_cmd,
exchange_cmd,
binding_cmd,
parameter_cmd,
policy_cmd,
operator_policy_cmd,
vhost_limit_cmd,
user_limit_cmd,
shovel_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn purge_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let queue_cmd = Command::new("queue")
.long_about("Purges (permanently removes unacknowledged messages from) a queue")
.arg(
Arg::new("name")
.long("name")
.help("name of the queue to purge")
.required(true),
);
[queue_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn auth_attempts_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let stats_cmd = Command::new("stats")
.about("Displays authentication attempt statistics for a cluster node")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
ACCESS_CONTROL_GUIDE_URL
))
.arg(
Arg::new("node")
.long("node")
.help("target node, must be a cluster member")
.required(true),
);
[stats_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn binding_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let declare_cmd = Command::new("declare")
.about("Creates a binding between a source exchange and a destination (a queue or an exchange)")
.arg(
Arg::new("source")
.long("source")
.help("source exchange")
.required(true),
)
.arg(
Arg::new("destination_type")
.long("destination-type")
.help("destination type: exchange or queue")
.required(true)
.value_parser(value_parser!(BindingDestinationType)),
)
.arg(
Arg::new("destination")
.long("destination")
.help("destination exchange/queue name")
.required(true),
)
.arg(
Arg::new("routing_key")
.long("routing-key")
.help("routing key")
.required(true),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
);
let delete_cmd = Command::new("delete")
.about("Deletes a binding")
.arg(
Arg::new("source")
.long("source")
.help("source exchange")
.required(true),
)
.arg(
Arg::new("destination_type")
.long("destination-type")
.help("destination type: exchange or queue")
.required(true),
)
.arg(
Arg::new("destination")
.long("destination")
.help("destination exchange/queue name")
.required(true),
)
.arg(
Arg::new("routing_key")
.long("routing-key")
.help("routing key")
.required(true),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
)
.arg(idempotently_arg.clone());
let list_cmd = Command::new("list").long_about("Lists bindings");
[declare_cmd, delete_cmd, list_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn queues_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let declare_cmd = Command::new("declare")
.about("Declares a queue or a stream")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
QUEUE_GUIDE_URL
))
.arg(Arg::new("name").long("name").required(true).help("name"))
.arg(
Arg::new("type")
.long("type")
.help("queue type")
.value_parser(value_parser!(QueueType))
.required(false)
.default_value("classic"),
)
.arg(
Arg::new("durable")
.long("durable")
.help("should it persist after a restart")
.required(false)
.value_parser(value_parser!(bool)),
)
.arg(
Arg::new("auto_delete")
.long("auto-delete")
.help("should it be deleted when the last consumer disconnects")
.required(false)
.value_parser(value_parser!(bool)),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional queue arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
);
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let delete_cmd = Command::new("delete")
.about("Deletes a queue")
.arg(
Arg::new("name")
.long("name")
.help("queue name")
.required(true),
)
.arg(idempotently_arg.clone());
let bulk_delete_cmd = Command::new("delete_multiple")
.about(color_print::cstr!("<bold><red>DANGER ZONE.</red></bold> Deletes multiple queues at once using a name matching pattern"))
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
QUEUE_GUIDE_URL
))
.arg(
Arg::new("name_pattern")
.long("name-pattern")
.help("a regular expression that will be used to match queue names")
.required(true),
)
.arg(
Arg::new("approve")
.long("approve")
.action(ArgAction::SetTrue)
.help("this operation is very destructive and requires an explicit approval")
.required(false),
)
.arg(
Arg::new("dry_run")
.long("dry-run")
.action(ArgAction::SetTrue)
.help("show what would be deleted without performing the actual deletion")
.required(false),
)
.arg(idempotently_arg.clone());
let list_cmd = Command::new("list")
.long_about("Lists queues and streams")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
QUEUE_GUIDE_URL
))
.args(pagination_args())
.arg(columns_arg());
let show_cmd = Command::new("show")
.about("Shows details for a single queue")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
QUEUE_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("queue name")
.required(true),
)
.arg(columns_arg());
let purge_cmd = Command::new("purge")
.long_about("Purges (permanently removes unacknowledged messages from) a queue")
.arg(
Arg::new("name")
.long("name")
.help("name of the queue to purge")
.required(true),
);
let rebalance_cmd = Command::new("rebalance").about("Rebalances queue leaders");
[
declare_cmd,
delete_cmd,
bulk_delete_cmd,
list_cmd,
purge_cmd,
rebalance_cmd,
show_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn streams_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let declare_cmd = Command::new("declare")
.about("Declares a stream")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
STREAM_GUIDE_URL
))
.arg(Arg::new("name").long("name").required(true).help("name"))
.arg(
Arg::new("expiration")
.long("expiration")
.help("stream expiration, e.g. 12h for 12 hours, 7D for 7 days, or 1M for 1 month")
.required(true)
.value_parser(value_parser!(String)),
)
.arg(
Arg::new("max_length_bytes")
.long("max-length-bytes")
.help("maximum stream length in bytes")
.required(false)
.value_parser(value_parser!(u64)),
)
.arg(
Arg::new("max_segment_length_bytes")
.long("stream-max-segment-size-bytes")
.help("maximum stream segment file length in bytes")
.required(false)
.value_parser(value_parser!(u64)),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional stream arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
);
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let delete_cmd = Command::new("delete")
.about("Deletes a stream")
.arg(
Arg::new("name")
.long("name")
.help("stream name")
.required(true),
)
.arg(idempotently_arg.clone());
let list_cmd = Command::new("list")
.long_about("Lists streams")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
STREAM_GUIDE_URL
))
.args(pagination_args())
.arg(columns_arg());
let show_cmd = Command::new("show")
.about("Shows details for a single stream")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
STREAM_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("stream name")
.required(true),
)
.arg(columns_arg());
[declare_cmd, delete_cmd, list_cmd, show_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn parameters_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let list_all_cmd = Command::new("list_all")
.long_about("Lists all runtime parameters across all virtual hosts")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
RUNTIME_PARAMETER_GUIDE_URL
));
let list_cmd = Command::new("list")
.arg(
Arg::new("component")
.long("component")
.help("component (for example: federation-upstream, vhost-limits)")
.required(false),
)
.long_about("Lists runtime parameters")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
RUNTIME_PARAMETER_GUIDE_URL
));
let list_in_cmd = Command::new("list_in")
.arg(
Arg::new("component")
.long("component")
.help("component (for example: federation-upstream, vhost-limits)")
.required(true),
)
.long_about("Lists runtime parameters")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
RUNTIME_PARAMETER_GUIDE_URL
));
let set_cmd = Command::new("set")
.alias("declare")
.about("Sets a runtime parameter")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
RUNTIME_PARAMETER_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("parameter's name")
.required(true),
)
.arg(
Arg::new("component")
.long("component")
.help("component (eg. federation)")
.required(true),
)
.arg(
Arg::new("value")
.long("value")
.help("parameter's value")
.required(true),
);
let clear_cmd = Command::new("clear")
.alias("delete")
.about("Clears (deletes) a runtime parameter")
.arg(
Arg::new("name")
.long("name")
.help("parameter's name")
.required(true),
)
.arg(
Arg::new("component")
.long("component")
.help("component (eg. federation-upstream)")
.required(true),
)
.arg(idempotently_arg.clone());
[clear_cmd, list_all_cmd, list_cmd, list_in_cmd, set_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn global_parameters_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let list_cmd = Command::new("list")
.long_about("Lists global runtime parameters")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
RUNTIME_PARAMETER_GUIDE_URL
));
let set_cmd = Command::new("set")
.alias("declare")
.about("Sets a global runtime parameter")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
RUNTIME_PARAMETER_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("parameter's name")
.required(true),
)
.arg(
Arg::new("value")
.long("value")
.help("parameter's value")
.required(true),
);
let clear_cmd = Command::new("clear")
.alias("delete")
.about("Clears (deletes) a global runtime parameter")
.arg(
Arg::new("name")
.long("name")
.help("parameter's name")
.required(true),
)
.arg(idempotently_arg.clone());
[clear_cmd, list_cmd, set_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn operator_policies_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let declare_cmd = Command::new("declare")
.visible_aliases(vec!["update", "set"])
.about("Creates or updates an operator policy")
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", POLICY_GUIDE_URL))
.arg(
Arg::new("name")
.long("name")
.help("operator policy name")
.required(true),
)
.arg(
Arg::new("pattern")
.long("pattern")
.help("the pattern that is used to match entity (queue, stream, exchange) names")
.required(true),
)
.arg(
Arg::new("apply_to")
.long("apply-to")
.alias("applies-to")
.help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
.value_parser(value_parser!(PolicyTarget))
.required(true),
)
.arg(
Arg::new("priority")
.long("priority")
.help("operator policy priority (only the policy with the highest priority is effective)")
.required(false)
.default_value("0"),
)
.arg(
Arg::new("definition")
.long("definition")
.help("operator policy definition")
.required(true),
);
let list_cmd = Command::new("list")
.long_about("Lists operator policies")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
POLICY_GUIDE_URL
));
let delete_cmd = Command::new("delete")
.about("Deletes an operator policy")
.arg(
Arg::new("name")
.long("name")
.help("policy name")
.required(true),
)
.arg(idempotently_arg.clone());
let delete_definition_key_cmd = Command::new("delete_definition_keys")
.about("Deletes definition keys from an operator policy, unless it is the only key")
.arg(
Arg::new("name")
.long("name")
.help("operator policy name")
.required(true),
)
.arg(
Arg::new("definition_keys")
.long("definition-keys")
.num_args(1..)
.value_delimiter(',')
.action(ArgAction::Append)
.help("comma-separated definition keys"),
);
let delete_definition_key_from_all_in_cmd = Command::new("delete_definition_keys_from_all_in")
.about("Deletes a definition key from all operator policies in a virtual host, unless it is the only key")
.arg(
Arg::new("definition_keys")
.long("definition-keys")
.num_args(1..)
.value_delimiter(',')
.action(ArgAction::Append)
.help("comma-separated definition keys")
);
let list_in_cmd = Command::new("list_in")
.about("Lists operator policies in a specific virtual host")
.arg(
Arg::new("apply_to")
.long("apply-to")
.alias("applies-to")
.value_parser(value_parser!(PolicyTarget)),
);
let list_matching_cmd = Command::new("list_matching_object")
.about("Lists operator policies that match an object (queue, stream, exchange) name")
.arg(
Arg::new("name")
.long("name")
.help("name to verify")
.required(true),
)
.arg(
Arg::new("type")
.long("type")
.value_parser(value_parser!(PolicyTarget))
.required(true)
.help("target type, one of 'queues', 'streams', 'exchanges'"),
);
let patch_cmd = Command::new("patch")
.about("Merges a set of keys into existing operator policy definitions")
.arg(
Arg::new("name")
.long("name")
.help("operator policy name")
.required(true),
)
.arg(
Arg::new("definition")
.long("definition")
.help("operator policy definition changes to merge into the existing ones"),
);
let update_cmd = Command::new("update_definition")
.about("Updates an operator policy definition key")
.arg(
Arg::new("name")
.long("name")
.help("operator policy name")
.required(true),
)
.arg(
Arg::new("definition_key")
.long("definition-key")
.help("operator policy definition key to update")
.required(true),
)
.arg(
Arg::new("definition_value")
.long("new-value")
.help("new definition value to set")
.required(true),
);
let update_all_in_cmd = Command::new("update_definitions_of_all_in")
.about("Updates a definition key in all operator policies in a virtual host")
.arg(
Arg::new("definition_key")
.long("definition-key")
.help("operator policy definition key to update")
.required(true),
)
.arg(
Arg::new("definition_value")
.long("new-value")
.help("new operator definition value to set")
.required(true),
);
[
declare_cmd,
delete_cmd,
delete_definition_key_cmd,
delete_definition_key_from_all_in_cmd,
list_cmd,
list_in_cmd,
list_matching_cmd,
patch_cmd,
update_cmd,
update_all_in_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn policies_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let declare_cmd = Command::new("declare")
.visible_aliases(vec!["update", "set"])
.about("Creates or updates a policy")
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", POLICY_GUIDE_URL))
.arg(
Arg::new("name")
.long("name")
.help("policy name")
.required(true),
)
.arg(
Arg::new("pattern")
.long("pattern")
.help("the pattern that is used to match entity (queue, stream, exchange) names")
.required(true),
)
.arg(
Arg::new("apply_to")
.long("apply-to")
.alias("applies-to")
.help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
.value_parser(value_parser!(PolicyTarget))
.required(true),
)
.arg(
Arg::new("priority")
.long("priority")
.help("policy priority (only the policy with the highest priority is effective)")
.required(false)
.default_value("0"),
)
.arg(
Arg::new("definition")
.long("definition")
.help("policy definition")
.required(true),
);
let declare_override_cmd = Command::new("declare_override")
.about("Declares a new policy from an existing one, with a higher priority, and merges a set of keys into the new overriding policy definition")
.arg(
Arg::new("name")
.long("name")
.help("the name of the policy to create an override for")
.required(true),
)
.arg(
Arg::new("override_name")
.long("override-name")
.help("the name of the new overriding policy. If omitted, an 'override' suffix will be added to the original name.")
.required(false),
)
.arg(
Arg::new("definition")
.long("definition")
.help("additional definitions to merge into the new overriding policy"),
);
let declare_blanket_cmd = Command::new("declare_blanket")
.about("Creates a low priority blanket policy, a policy that matches all objects not matched by any other policy")
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", POLICY_GUIDE_URL))
.arg(
Arg::new("name")
.long("name")
.help("blanket policy name")
.required(true),
)
.arg(
Arg::new("apply_to")
.long("apply-to")
.alias("applies-to")
.help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
.value_parser(value_parser!(PolicyTarget))
.required(true),
)
.arg(
Arg::new("definition")
.long("definition")
.help("policy definition")
.required(true),
);
let list_cmd = Command::new("list")
.long_about("Lists policies")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
POLICY_GUIDE_URL
));
let delete_cmd = Command::new("delete")
.about("Deletes a policy")
.arg(
Arg::new("name")
.long("name")
.help("policy name")
.required(true),
)
.arg(idempotently_arg.clone());
let delete_definition_keys_cmd = Command::new("delete_definition_keys")
.about("Deletes a definition key from a policy, unless it is the only key")
.arg(
Arg::new("name")
.long("name")
.help("policy name")
.required(true),
)
.arg(
Arg::new("definition_keys")
.long("definition-keys")
.num_args(1..)
.value_delimiter(',')
.action(ArgAction::Append)
.help("comma-separated definition keys"),
);
let delete_definition_keys_from_all_in_cmd = Command::new("delete_definition_keys_from_all_in")
.about("Deletes definition keys from all policies in a virtual host, unless it is the only policy key")
.arg(
Arg::new("definition_keys")
.long("definition-keys")
.help("comma-separated definition keys")
.num_args(1..)
.value_delimiter(',')
.action(ArgAction::Append)
.required(true)
);
let list_in_cmd = Command::new("list_in")
.about("Lists policies in a specific virtual host")
.arg(
Arg::new("apply_to")
.long("apply-to")
.alias("applies-to")
.value_parser(value_parser!(PolicyTarget)),
);
let list_conflicting_cmd = Command::new("list_conflicting")
.about("Lists policies that have conflicting priorities across all virtual hosts")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
POLICY_GUIDE_URL
));
let list_conflicting_in_cmd = Command::new("list_conflicting_in")
.about("Lists policies that have conflicting priorities in a specific virtual host")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
POLICY_GUIDE_URL
));
let list_matching_cmd = Command::new("list_matching_object")
.about("Lists policies that match an object (queue, stream, exchange) name")
.arg(
Arg::new("name")
.long("name")
.help("name to verify")
.required(true),
)
.arg(
Arg::new("type")
.long("type")
.value_parser(value_parser!(PolicyTarget))
.required(true)
.help("target type, one of 'queues', 'streams', 'exchanges'"),
);
let patch_cmd = Command::new("patch")
.about("Merges a set of keys into existing policy definitions")
.arg(
Arg::new("name")
.long("name")
.help("policy name")
.required(true),
)
.arg(
Arg::new("definition")
.long("definition")
.help("policy definition changes to merge into the existing ones"),
);
let update_cmd = Command::new("update_definition")
.about("Updates a policy definition key")
.arg(
Arg::new("name")
.long("name")
.help("policy name")
.required(true),
)
.arg(
Arg::new("definition_key")
.long("definition-key")
.help("policy definition key to update")
.required(true),
)
.arg(
Arg::new("definition_value")
.long("new-value")
.help("new definition value to set")
.required(true),
);
let update_all_in_cmd = Command::new("update_definitions_of_all_in")
.about("Updates a definition key in all policies in a virtual host")
.arg(
Arg::new("definition_key")
.long("definition-key")
.help("policy definition key to update")
.required(true),
)
.arg(
Arg::new("definition_value")
.long("new-value")
.help("new definition value to set")
.required(true),
);
let delete_definition_keys_from_all_cmd = Command::new("delete_definition_keys_from_all")
.about("Deletes definition keys from all policies in the cluster, unless it is the only policy key")
.arg(
Arg::new("definition_keys")
.long("definition-keys")
.help("comma-separated definition keys")
.num_args(1..)
.value_delimiter(',')
.action(ArgAction::Append)
.required(true),
);
let update_all_cmd = Command::new("update_definitions_of_all")
.about("Updates a definition key in all policies in the cluster")
.arg(
Arg::new("definition_key")
.long("definition-key")
.help("policy definition key to update")
.required(true),
)
.arg(
Arg::new("definition_value")
.long("new-value")
.help("new definition value to set")
.required(true),
);
[
declare_cmd,
declare_override_cmd,
declare_blanket_cmd,
delete_cmd,
delete_definition_keys_cmd,
delete_definition_keys_from_all_cmd,
delete_definition_keys_from_all_in_cmd,
list_cmd,
list_conflicting_cmd,
list_conflicting_in_cmd,
list_in_cmd,
list_matching_cmd,
patch_cmd,
update_cmd,
update_all_cmd,
update_all_in_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn health_check_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let node_is_quorum_critical_after_help = color_print::cformat!(
r#"
<bold>Doc guides</bold>:
* {}
* {}"#,
QUORUM_QUEUE_FAILURE_HANDLING_GUIDE_URL,
UPGRADE_GUIDE_URL
);
let local_alarms = Command::new("local_alarms")
.about("Checks if there are any resource alarms in effect on the target node");
let cluster_wide_alarms = Command::new("cluster_wide_alarms")
.about("Checks if there are any resource alarms in effect across the entire cluster");
let node_is_quorum_critical = Command::new("node_is_quorum_critical")
.about("Fails if there are queues/streams with minimum online quorum (queues/streams that will lose their quorum if the target node shuts down)")
.after_help(node_is_quorum_critical_after_help);
let deprecated_features_in_use = Command::new("deprecated_features_in_use")
.about("Fails if there are any deprecated features in use in the cluster")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEPRECATED_FEATURE_GUIDE_URL
));
let port_listener = Command::new("port_listener")
.about(
"Verifies that there's a reachable TCP listener on the given port on the target node",
)
.arg(
Arg::new("port")
.long("port")
.value_parser(value_parser!(u16)),
)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
HEALTH_CHECK_GUIDE_URL
));
let protocol_listener = Command::new("protocol_listener")
.about(
"Verifies that there's a reachable TCP listener on the given protocol alias on the target node",
)
.arg(
Arg::new("protocol")
.long("protocol")
.value_parser(value_parser!(SupportedProtocol))
.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'"),
)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
HEALTH_CHECK_GUIDE_URL
));
[
local_alarms,
cluster_wide_alarms,
node_is_quorum_critical,
deprecated_features_in_use,
port_listener,
protocol_listener,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn rebalance_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let queues_cmd = Command::new("queues").about("Rebalances queue leaders");
[queues_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn close_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let close_connection = Command::new("connection")
.about("Closes a client connection")
.arg(
Arg::new("name")
.long("name")
.help("connection name (identifying string)")
.required(true),
)
.arg(idempotently_arg.clone());
let close_user_connections = Command::new("user_connections")
.about("Closes all connections that authenticated with a specific username")
.arg(
Arg::new("username")
.short('u')
.long("username")
.help("Name of the user whose connections to close")
.required(true),
)
.arg(idempotently_arg.clone());
[close_connection, close_user_connections]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn channels_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let list_cmd = Command::new("list")
.long_about("Lists all channels across all virtual hosts")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
"https://www.rabbitmq.com/docs/channels"
))
.args(pagination_args());
[list_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn connections_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let close_connection = Command::new("close")
.about("Closes a client connection")
.arg(
Arg::new("name")
.long("name")
.help("connection name (identifying string)")
.required(true),
)
.arg(idempotently_arg.clone());
let close_user_connections = Command::new("close_of_user")
.about("Closes all connections that are authenticated with a specific username")
.arg(
Arg::new("username")
.short('u')
.long("username")
.help("Name of the user whose connections should be closed")
.required(true),
)
.arg(idempotently_arg.clone());
let list_cmd = Command::new("list")
.long_about("Lists client connections")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
CONNECTION_GUIDE_URL
))
.args(pagination_args());
let list_user_connections_cmd = Command::new("list_of_user")
.arg(
Arg::new("username")
.short('u')
.long("username")
.required(true)
.help("Name of the user whose connections should be listed"),
)
.long_about("Lists client connections that are authenticated with a specific username")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
CONNECTION_GUIDE_URL
));
[
close_connection,
close_user_connections,
list_cmd,
list_user_connections_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn node_entry_args() -> Vec<Arg> {
vec![
Arg::new("node")
.short('N')
.long("node")
.help("node entry name (defaults to the hostname)")
.required(false),
Arg::new("host")
.short('H')
.long("host")
.help("target node hostname")
.required(false),
Arg::new("port")
.short('P')
.long("port")
.help("target node HTTP API port")
.required(false)
.value_parser(value_parser!(u16)),
Arg::new("base_uri")
.short('U')
.long("base-uri")
.help("base HTTP API endpoint URI")
.required(false)
.conflicts_with_all(["host", "port"]),
Arg::new("path_prefix")
.long("path-prefix")
.help("HTTP API path prefix (defaults to '/api')")
.required(false),
Arg::new("username")
.short('u')
.long("username")
.help("username for authentication")
.required(false),
Arg::new("password")
.short('p')
.long("password")
.help("password for authentication")
.required(false),
Arg::new("scheme")
.short('s')
.long("scheme")
.help("connection scheme (http or https)")
.required(false)
.value_parser(["http", "https"]),
Arg::new("vhost")
.short('V')
.long("vhost")
.help("default virtual host")
.required(false),
Arg::new("tls")
.long("use-tls")
.help("use TLS (HTTPS) for connections to this node")
.action(ArgAction::SetTrue),
Arg::new("tls_ca_cert_file")
.long("tls-ca-cert-file")
.help("path to a CA certificate file in PEM format")
.required(false)
.value_parser(value_parser!(PathBuf)),
Arg::new("tls_cert_file")
.long("tls-cert-file")
.help("path to a client certificate file in PEM format")
.required(false)
.value_parser(value_parser!(PathBuf)),
Arg::new("tls_key_file")
.long("tls-key-file")
.help("path to a client private key file in PEM format")
.required(false)
.value_parser(value_parser!(PathBuf)),
Arg::new("create_file_if_missing")
.long("create-file-if-missing")
.help("create the configuration file if it does not exist")
.action(ArgAction::SetTrue),
]
}
fn config_file_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let hidden_vhost_arg = Arg::new("vhost").short('V').long("vhost").hide(true);
let hidden_table_style_arg = Arg::new("table_style").long("table-style").hide(true);
let show_path_cmd = Command::new("show_path")
.about("Shows the path to the local rabbitmqadmin configuration file")
.arg(hidden_vhost_arg.clone());
let show_cmd = Command::new("show")
.about("Shows the contents of the local rabbitmqadmin configuration file")
.arg(hidden_vhost_arg.clone())
.arg(
Arg::new("reveal_passwords")
.long("reveal-passwords")
.help("reveal passwords in the output table instead of masked values (********)")
.required(false)
.num_args(0..=1)
.default_missing_value("true")
.value_parser(value_parser!(bool))
.action(ArgAction::Set)
.value_name("boolean"),
);
let add_node_cmd = Command::new("add_node")
.about("Adds a new node entry to the local rabbitmqadmin configuration file")
.args(node_entry_args())
.arg(hidden_table_style_arg.clone());
let update_node_cmd = Command::new("update_node")
.about("Updates an existing node entry in the local rabbitmqadmin configuration file (or creates one if it does not exist)")
.args(node_entry_args())
.arg(hidden_table_style_arg.clone());
let delete_node_cmd = Command::new("delete_node")
.about("Deletes a node entry from the local rabbitmqadmin configuration file")
.arg(hidden_vhost_arg)
.arg(hidden_table_style_arg)
.arg(
Arg::new("node")
.short('N')
.long("node")
.help("node entry name to delete")
.required(true),
)
.arg(
Arg::new("create_file_if_missing")
.long("create-file-if-missing")
.help("create the configuration file if it does not exist")
.action(ArgAction::SetTrue),
);
[
show_path_cmd,
show_cmd,
add_node_cmd,
update_node_cmd,
delete_node_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn definitions_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let export_cmd = Command::new("export")
.about("Export cluster-wide definitions")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEFINITION_GUIDE_URL
))
.arg(
Arg::new("file")
.group("output")
.long("file")
.help("output file path")
.required(false)
.default_value("-")
.conflicts_with("stdout"),
)
.arg(
Arg::new("stdout")
.group("output")
.long("stdout")
.help("print result to the standard output stream")
.required(false)
.num_args(0)
.action(ArgAction::SetTrue)
.conflicts_with("file"),
)
.arg(
Arg::new("transformations")
.long("transformations")
.short('t')
.long_help(
r#"
A comma-separated list of names of the definition transformations to apply.
Supported transformations:
* prepare_for_quorum_queue_migration
* strip_cmq_keys_from_policies
* drop_empty_policies
* obfuscate_usernames
* exclude_users
* exclude_permissions
* exclude_runtime_parameters
* exclude_policies
* no_op
All unknown transformations will be ignored (will be replaced with a `no_op`).
Examples:
* --transformations prepare_for_quorum_queue_migration,drop_empty_policies
* --transformations strip_cmq_keys_from_policies,drop_empty_policies
* --transformations exclude_users,exclude_permissions
* --transformations obfuscate_usernames
* --transformations exclude_runtime_parameters,exclude_policies
* --transformations no_op
"#,
)
.num_args(1..)
.value_delimiter(',')
.action(ArgAction::Append)
.required(false),
);
let export_from_vhost_cmd = Command::new("export_from_vhost")
.about("Export definitions of a specific virtual host")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEFINITION_GUIDE_URL
))
.arg(
Arg::new("file")
.group("output")
.long("file")
.help("output file path")
.required(false)
.default_value("-")
.conflicts_with("stdout"),
)
.arg(
Arg::new("stdout")
.group("output")
.long("stdout")
.help("print result to the standard output stream")
.required(false)
.num_args(0)
.action(ArgAction::SetTrue)
.conflicts_with("file"),
)
.arg(
Arg::new("transformations")
.long("transformations")
.short('t')
.long_help(
r#"
A comma-separated list of names of the definition transformations to apply.
Supported transformations:
* prepare_for_quorum_queue_migration
* strip_cmq_keys_from_policies
* drop_empty_policies
* no_op
All unknown transformations will be ignored (will be replaced with a `no_op`).
Examples:
* --transformations prepare_for_quorum_queue_migration,drop_empty_policies
* --transformations strip_cmq_keys_from_policies,drop_empty_policies
* --transformations no_op
"#,
)
.num_args(1..)
.value_delimiter(',')
.action(ArgAction::Append)
.required(false),
);
let import_cmd = Command::new("import")
.about("Import cluster-wide definitions (of multiple virtual hosts)")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEFINITION_GUIDE_URL
))
.arg(
Arg::new("file")
.group("input")
.long("file")
.help("cluster-wide definitions JSON file path; mutually exclusive with --stdin")
.required(true)
.conflicts_with("stdin"),
)
.arg(
Arg::new("stdin")
.group("input")
.long("stdin")
.help("read input JSON from the standard input stream, mutually exclusive with --file")
.required(false)
.num_args(0)
.action(ArgAction::SetTrue)
.conflicts_with("file"),
);
let import_into_vhost_cmd = Command::new("import_into_vhost")
.about("Import a virtual host-specific definitions file into a virtual host")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEFINITION_GUIDE_URL
))
.arg(
Arg::new("file")
.group("input")
.long("file")
.help("cluster-wide definitions JSON file path; mutually exclusive with --stdin")
.required(true)
.conflicts_with("stdin"),
)
.arg(
Arg::new("stdin")
.group("input")
.long("stdin")
.help("read input JSON from the standard input stream, mutually exclusive with --file")
.required(false)
.num_args(0)
.action(ArgAction::SetTrue)
.conflicts_with("file"),
);
[
export_cmd,
export_from_vhost_cmd,
import_cmd,
import_into_vhost_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn exchanges_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let bind_cmd = Command::new("bind")
.about("Creates a binding between a source exchange and a destination (a queue or an exchange)")
.arg(
Arg::new("source")
.long("source")
.help("source exchange")
.required(true),
)
.arg(
Arg::new("destination_type")
.long("destination-type")
.help("destination type: exchange or queue")
.required(true)
.value_parser(value_parser!(BindingDestinationType)),
)
.arg(
Arg::new("destination")
.long("destination")
.help("destination exchange/queue name")
.required(true),
)
.arg(
Arg::new("routing_key")
.long("routing-key")
.help("routing key")
.required(true),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
);
let declare_cmd = Command::new("declare")
.about("Declares an exchange")
.arg(
Arg::new("name")
.long("name")
.help("exchange name")
.required(true),
)
.arg(
Arg::new("type")
.long("type")
.help("exchange type")
.value_parser(value_parser!(ExchangeType))
.required(false),
)
.arg(
Arg::new("durable")
.long("durable")
.help("should it persist after a restart")
.required(false)
.value_parser(value_parser!(bool)),
)
.arg(
Arg::new("auto_delete")
.long("auto-delete")
.help("should it be deleted when the last queue is unbound")
.required(false)
.value_parser(value_parser!(bool)),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional exchange arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
);
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let delete_cmd = Command::new("delete")
.about("Deletes an exchange")
.arg(
Arg::new("name")
.long("name")
.help("exchange name")
.required(true),
)
.arg(idempotently_arg.clone());
let list_cmd = Command::new("list")
.long_about("Lists exchanges")
.args(pagination_args());
let unbind_cmd = Command::new("unbind")
.about("Deletes a binding")
.arg(
Arg::new("source")
.long("source")
.help("source exchange")
.required(true),
)
.arg(
Arg::new("destination_type")
.long("destination-type")
.help("destination type: exchange or queue")
.required(true),
)
.arg(
Arg::new("destination")
.long("destination")
.help("destination exchange/queue name")
.required(true),
)
.arg(
Arg::new("routing_key")
.long("routing-key")
.help("routing key")
.required(true),
)
.arg(
Arg::new("arguments")
.long("arguments")
.help("additional arguments")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
)
.arg(idempotently_arg.clone());
[bind_cmd, declare_cmd, delete_cmd, list_cmd, unbind_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn export_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let definitions = Command::new("definitions")
.about("Export cluster-wide definitions")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEFINITION_GUIDE_URL
))
.arg(
Arg::new("file")
.group("output")
.long("file")
.help("output file path")
.required(false)
.default_value("-")
.conflicts_with("stdout"),
)
.arg(
Arg::new("stdout")
.group("output")
.long("stdout")
.help("print result to the standard output stream")
.required(false)
.num_args(0)
.action(ArgAction::SetTrue)
.conflicts_with("file"),
)
.arg(
Arg::new("transformations")
.long("transformations")
.short('t')
.long_help(
r#"
A comma-separated list of names of the definition transformations to apply.
Supported transformations:
* no_op
* prepare_for_quorum_queue_migration
* strip_cmq_keys_from_policies
* drop_empty_policies
* obfuscate_usernames
* exclude_users
* exclude_permissions
* exclude_runtime_parameters
* exclude_policies
Examples:
* --transformations prepare_for_quorum_queue_migration,drop_empty_policies
* --transformations strip_cmq_keys_from_policies,drop_empty_policies
* --transformations exclude_users,exclude_permissions
* --transformations obfuscate_usernames
* --transformations exclude_runtime_parameters,exclude_policies
* --transformations no_op
"#,
)
.num_args(1..)
.value_delimiter(',')
.action(ArgAction::Append)
.required(false),
);
[definitions]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn import_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
[Command::new("definitions")
.about("Prefer 'definitions import'")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEFINITION_GUIDE_URL
))
.arg(
Arg::new("file")
.long("file")
.help("JSON file with definitions")
.required(true),
)]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn feature_flags_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let list_cmd = Command::new("list")
.long_about("Lists feature flags and their cluster state")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
FEATURE_FLAG_GUIDE_URL
));
let enable_cmd = Command::new("enable")
.long_about("Enables a feature flag")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
FEATURE_FLAG_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("feature flag name (identifier)")
.required(true),
);
let enable_all_cmd = Command::new("enable_all")
.long_about("Enables all stable feature flags")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
FEATURE_FLAG_GUIDE_URL
));
[list_cmd, enable_cmd, enable_all_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn deprecated_features_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let list_cmd = Command::new("list")
.long_about("Lists deprecated features")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEPRECATED_FEATURE_GUIDE_URL
));
let list_in_use_cmd = Command::new("list_used")
.long_about("Lists the deprecated features that are found to be in use in the cluster")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
DEPRECATED_FEATURE_GUIDE_URL
));
[list_cmd, list_in_use_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn plugins_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let list_all_cmd = Command::new("list_all")
.about("Lists plugins across all cluster nodes")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
PLUGIN_GUIDE_URL
));
let list_on_node_cmd = Command::new("list_on_node")
.about("Lists plugins enabled on a specific node")
.arg(
Arg::new("node")
.long("node")
.help("target node, must be a cluster member")
.required(true),
)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
PLUGIN_GUIDE_URL
));
[list_all_cmd, list_on_node_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn nodes_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let list_cmd = Command::new("list")
.long_about("Lists cluster nodes")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
CLUSTERING_GUIDE_URL
));
let memory_breakdown_in_bytes_cmd = Command::new("memory_breakdown_in_bytes")
.about("Provides a memory footprint breakdown (in bytes) for the target node")
.arg(
Arg::new("node")
.long("node")
.help("target node, must be a cluster member")
.required(true),
)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
MEMORY_FOOTPRINT_GUIDE_URL
));
let memory_breakdown_in_percent_cmd = Command::new("memory_breakdown_in_percent")
.about("Provides a memory footprint breakdown (in percent) for the target node")
.arg(
Arg::new("node")
.long("node")
.help("target node, must be a cluster member")
.required(true),
)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
MEMORY_FOOTPRINT_GUIDE_URL
));
[
list_cmd,
memory_breakdown_in_percent_cmd,
memory_breakdown_in_bytes_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn vhosts_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let list_cmd = Command::new("list")
.long_about("Lists virtual hosts")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
VIRTUAL_HOST_GUIDE_URL
));
let declare_cmd = Command::new("declare")
.about("Creates a virtual host")
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", VIRTUAL_HOST_GUIDE_URL))
.arg(
Arg::new("name")
.long("name")
.help("virtual host name")
.required(true),
)
.arg(
Arg::new("default_queue_type")
.long("default-queue-type")
.required(false)
.help(color_print::cformat!("default queue type, one of: <bold>classic</bold>, <bright-blue>quorum</bright-blue>, <bright-magenta>stream</bright-magenta>"))
)
.arg(
Arg::new("description")
.long("description")
.required(false)
.help("what's the purpose of this virtual host?"),
)
.arg(
Arg::new("tracing")
.long("tracing")
.required(false)
.action(ArgAction::SetTrue)
.help("should tracing be enabled for this virtual host?"),
);
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let delete_cmd = Command::new("delete")
.about("Deletes a virtual host")
.arg(
Arg::new("name")
.long("name")
.help("virtual host")
.required(true),
)
.arg(idempotently_arg.clone());
let bulk_delete_cmd = Command::new("delete_multiple")
.about(color_print::cstr!("<bold><red>DANGER ZONE.</red></bold> Deletes multiple virtual hosts at once using a name matching pattern"))
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", VIRTUAL_HOST_GUIDE_URL))
.arg(
Arg::new("name_pattern")
.long("name-pattern")
.help("a regular expression that will be used to match virtual host names")
.required(true),
)
.arg(
Arg::new("approve")
.long("approve")
.action(ArgAction::SetTrue)
.help("this operation is very destructive and requires an explicit approval")
.required(false),
)
.arg(
Arg::new("dry_run")
.long("dry-run")
.action(ArgAction::SetTrue)
.help("show what would be deleted without performing the actual deletion")
.required(false),
)
.arg(idempotently_arg.clone());
let enable_deletion_protection_cmd = Command::new("enable_deletion_protection")
.about("Enables deletion protection for a virtual host")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
VHOST_DELETION_PROTECTION_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("virtual host name")
.required(true),
);
let disable_deletion_protection_cmd = Command::new("disable_deletion_protection")
.about("Disables deletion protection for a virtual host")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
VHOST_DELETION_PROTECTION_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("virtual host name")
.required(true),
);
[
list_cmd,
declare_cmd,
delete_cmd,
bulk_delete_cmd,
enable_deletion_protection_cmd,
disable_deletion_protection_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn users_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let declare_cmd = Command::new("declare")
.about("Creates a user")
.arg(
Arg::new("name")
.long("name")
.help("username")
.required(true),
)
.arg(
Arg::new("password_hash")
.help(color_print::cformat!(
"salted password hash, see {}",
PASSWORD_GUIDE_URL
))
.long("password-hash")
.required(false)
.default_value(""),
)
.arg(
Arg::new("password")
.long("password")
.help(color_print::cformat!(
"prefer providing a hash, see {}",
PASSWORD_GUIDE_URL
))
.required(false)
.default_value(""),
)
.arg(
Arg::new("hashing_algorithm")
.long("hashing-algorithm")
.required(false)
.conflicts_with("password_hash")
.requires("password")
.value_parser(value_parser!(HashingAlgorithm))
.default_value("SHA256")
.help("The hashing algorithm to use: SHA256 or SHA512"),
)
.arg(
Arg::new("tags")
.long("tags")
.help("a list of comma-separated tags")
.default_value(""),
);
let list_cmd = Command::new("list")
.long_about("Lists users in the internal database")
.args(pagination_args());
let permissions_cmd = Command::new("permissions")
.long_about("Lists user permissions")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
ACCESS_CONTROL_GUIDE_URL
));
let connections_cmd = Command::new("connections")
.arg(
Arg::new("username")
.short('u')
.long("username")
.required(true)
.help("Name of the user whose connections should be listed"),
)
.long_about("Lists client connections that authenticated with a specific username")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
CONNECTION_GUIDE_URL
));
let limits_cmd = Command::new("limits")
.arg(
Arg::new("username")
.long("username")
.alias("user")
.help("username")
.required(false),
)
.long_about("Lists per-user (resource) limits")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
USER_LIMIT_GUIDE_URL
));
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let delete_cmd = Command::new("delete")
.about("Deletes a user")
.arg(
Arg::new("name")
.long("name")
.help("username")
.required(true),
)
.arg(idempotently_arg.clone());
[
connections_cmd,
declare_cmd,
delete_cmd,
limits_cmd,
list_cmd,
permissions_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn passwords_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let hash_password = Command::new("salt_and_hash")
.arg(
Arg::new("password")
.required(true)
.help("A cleartext password value to hash"),
)
.arg(
Arg::new("hashing_algorithm")
.long("hashing-algorithm")
.required(false)
.value_parser(value_parser!(HashingAlgorithm))
.default_value("SHA256")
.help("The hashing algorithm to use: SHA256 or SHA512"),
);
[hash_password]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn permissions_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let list_cmd = Command::new("list")
.long_about("Lists user permissions")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
ACCESS_CONTROL_GUIDE_URL
));
let declare_cmd = Command::new("declare")
.about("grants permissions to a user")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
ACCESS_CONTROL_GUIDE_URL
))
.arg(
Arg::new("username")
.long("username")
.alias("user")
.help("username")
.required(true),
)
.arg(
Arg::new("configure")
.long("configure")
.help("name pattern for configuration access")
.required(true),
)
.arg(
Arg::new("read")
.long("read")
.help("name pattern for read access")
.required(true),
)
.arg(
Arg::new("write")
.long("write")
.help("name pattern for write access")
.required(true),
);
let delete_cmd = Command::new("delete")
.about("Revokes user permissions to a given vhost")
.arg(
Arg::new("username")
.long("username")
.alias("user")
.help("username")
.required(true),
)
.arg(idempotently_arg.clone());
[list_cmd, declare_cmd, delete_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn user_limits_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let list_cmd = Command::new("list")
.long_about("Lists per-user (resource) limits")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
USER_LIMIT_GUIDE_URL
))
.arg(
Arg::new("username")
.long("username")
.alias("user")
.help("username")
.required(false),
);
let declare_cmd = Command::new("declare")
.about("Set a user limit")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
USER_LIMIT_GUIDE_URL
))
.arg(
Arg::new("username")
.long("username")
.alias("user")
.help("username")
.required(true),
)
.arg(
Arg::new("name")
.long("name")
.help("limit name (eg. max-connections, max-queues)")
.required(true),
)
.arg(
Arg::new("value")
.long("value")
.help("limit value")
.required(true),
);
let delete_cmd = Command::new("delete")
.about("Clears a user limit")
.arg(
Arg::new("username")
.long("username")
.alias("user")
.help("username")
.required(true),
)
.arg(
Arg::new("name")
.long("name")
.help("limit name (eg. max-connections, max-queues)")
.required(true),
);
[list_cmd, declare_cmd, delete_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn vhost_limits_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let list_cmd = Command::new("list")
.long_about("Lists virtual host (resource) limits")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
VIRTUAL_HOST_GUIDE_URL
));
let declare_cmd = Command::new("declare")
.about("Set a vhost limit")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
VIRTUAL_HOST_LIMIT_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("limit name (eg. max-connections, max-queues)")
.required(true),
)
.arg(
Arg::new("value")
.long("value")
.help("limit value")
.required(true),
);
let delete_cmd = Command::new("delete").about("delete a vhost limit").arg(
Arg::new("name")
.long("name")
.help("limit name (eg. max-connections, max-queues)")
.required(true),
);
[list_cmd, declare_cmd, delete_cmd]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn publish_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
[Command::new("message")
.about(color_print::cstr!("Publishes (<red>inefficiently</red>) message(s) to a queue or a stream. <bold><red>Only suitable for development and test environments</red></bold>. Prefer messaging or streaming protocol clients!"))
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", PUBLISHER_GUIDE_URL))
.arg(
Arg::new("routing_key")
.short('k')
.long("routing-key")
.required(false)
.default_value("")
.help("Name of virtual host"),
)
.arg(
Arg::new("exchange")
.short('e')
.long("exchange")
.required(false)
.default_value("")
.help("Exchange name (defaults to empty)"),
)
.arg(
Arg::new("payload")
.short('m')
.long("payload")
.required(false)
.default_value("test")
.help("Message payload/body"),
)
.arg(
Arg::new("properties")
.short('p')
.long("properties")
.required(false)
.default_value("{}")
.help("Message properties"),
)]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn get_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
[Command::new("messages")
.about(color_print::cstr!("Fetches (via <red>polling, very inefficiently</red>) message(s) from a queue. <bold><red>Only suitable for development and test environments</red></bold>"))
.after_help(color_print::cformat!("<bold>Doc guide</bold>: {}", POLLING_CONSUMER_GUIDE_URL))
.arg(
Arg::new("queue")
.short('q')
.long("queue")
.required(true)
.help("Target queue or stream name"),
)
.arg(
Arg::new("count")
.short('c')
.long("count")
.required(false)
.default_value("1")
.help("Maximum number of messages to consume"),
)
.arg(
Arg::new("ack_mode")
.short('a')
.long("ack-mode")
.required(false)
.default_value("ack_requeue_false")
.help("Accepted values are: ack_requeue_false, reject_requeue_false, ack_requeue_true, reject_requeue_true"),
)]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
pub fn shovel_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let list_all_cmd = Command::new("list_all")
.long_about("Lists shovels in all virtual hosts")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
SHOVEL_GUIDE_URL
));
let list_cmd = Command::new("list")
.long_about("Lists shovels in a specific virtual host")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
SHOVEL_GUIDE_URL
));
let declare_091_cmd = Command::new("declare_amqp091")
.long_about(
"Declares a dynamic shovel that uses AMQP 0-9-1 for both source and destination",
)
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
SHOVEL_GUIDE_URL
))
.arg(Arg::new("name").short('n').long("name").required(true))
.arg(Arg::new("source_uri").long("source-uri").required(true))
.arg(
Arg::new("destination_uri")
.long("destination-uri")
.required(true),
)
.arg(
Arg::new("ack_mode")
.long("ack-mode")
.help("One of: on-confirm, on-publish, no-ack")
.default_value("on-confirm")
.value_parser(value_parser!(MessageTransferAcknowledgementMode)),
)
.arg(
Arg::new("source_queue")
.long("source-queue")
.conflicts_with("source_exchange"),
)
.arg(
Arg::new("source_exchange")
.long("source-exchange")
.conflicts_with("source_queue"),
)
.arg(
Arg::new("source_exchange_key")
.long("source-exchange-routing-key")
.conflicts_with("source_queue")
.requires("source_exchange"),
)
.group(
ArgGroup::new("source")
.args(["source_queue", "source_exchange"])
.required(true),
)
.arg(
Arg::new("destination_queue")
.long("destination-queue")
.conflicts_with("destination_exchange"),
)
.arg(
Arg::new("destination_exchange")
.long("destination-exchange")
.conflicts_with("destination_queue"),
)
.arg(
Arg::new("destination_exchange_key")
.long("destination-exchange-routing-key")
.conflicts_with("destination_queue"),
)
.arg(
Arg::new("predeclared_source")
.long("predeclared-source")
.help("The source topology will be pre-declared (should not be declared by the shovel)")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
)
.arg(
Arg::new("predeclared_destination")
.long("predeclared-destination")
.help("The destination topology will be pre-declared (should not be declared by the shovel)")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
)
.arg(
Arg::new("reconnect_delay")
.long("reconnect-delay")
.default_value("5")
.value_parser(value_parser!(u32)),
)
.group(
ArgGroup::new("destination")
.args(["destination_queue", "destination_exchange"])
.required(true),
)
.arg(
Arg::new("publish_properties")
.long("publish-properties")
.help("A JSON object with message properties for the Shovel to set")
.required(false)
.default_value("{}")
.value_parser(value_parser!(String)),
);
let declare_10_cmd = Command::new("declare_amqp10")
.long_about("Declares a dynamic shovel that uses AMQP 1.0 for both source and destination")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
SHOVEL_GUIDE_URL
))
.arg(Arg::new("name").short('n').long("name").required(true))
.arg(Arg::new("source_uri").long("source-uri").required(true))
.arg(
Arg::new("destination_uri")
.long("destination-uri")
.required(true),
)
.arg(
Arg::new("ack_mode")
.long("ack-mode")
.help("One of: on-confirm, on-publish, no-ack")
.default_value("on-confirm")
.value_parser(value_parser!(MessageTransferAcknowledgementMode)),
)
.arg(Arg::new("source_address").long("source-address"))
.arg(Arg::new("destination_address").long("destination-address"))
.arg(
Arg::new("reconnect_delay")
.long("reconnect-delay")
.default_value("5")
.value_parser(value_parser!(u32)),
);
let delete_cmd = Command::new("delete")
.long_about("Deletes a dynamic shovel")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
SHOVEL_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("shovel name (identifier)")
.required(true),
)
.arg(idempotently_arg.clone());
let disable_tls_peer_verification_cmd = Command::new("disable_tls_peer_verification_for_all_source_uris")
.about(color_print::cstr!("<bold><red>Use only in case of emergency</red></bold>. Disables TLS peer verification for all shovels."))
.long_about(color_print::cstr!("<bold><red>Use only in case of emergency</red></bold>. Disables TLS peer verification for all shovels by updating their source and destination URIs' 'verify' parameter."))
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}"#,
SHOVEL_GUIDE_URL,
TLS_GUIDE_URL,
"https://www.rabbitmq.com/docs/shovel#tls-connections"
));
let disable_tls_peer_verification_dest_cmd = Command::new("disable_tls_peer_verification_for_all_destination_uris")
.about(color_print::cstr!("<bold><red>Use only in case of emergency</red></bold>. Disables TLS peer verification for all shovel destination URIs."))
.long_about(color_print::cstr!("<bold><red>Use only in case of emergency</red></bold>. Disables TLS peer verification for all shovel destination URIs by updating their 'verify' parameter."))
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}"#,
SHOVEL_GUIDE_URL,
TLS_GUIDE_URL,
"https://www.rabbitmq.com/docs/shovel#tls-connections"
));
let enable_tls_peer_verification_source_cmd = Command::new("enable_tls_peer_verification_for_all_source_uris")
.about("Enables TLS peer verification for all shovel source URIs with provided [RabbitMQ node-local] certificate paths.")
.long_about("Enables TLS peer verification for all shovel source URIs by updating their 'verify' parameter and adding [RabbitMQ node-local] certificate and private key file paths.")
.arg(
Arg::new("node_local_ca_certificate_bundle_path")
.long("node-local-ca-certificate-bundle-path")
.help("Path to the CA certificate bundle file on the target RabbitMQ node(s)")
.required(true)
.value_name("path")
)
.arg(
Arg::new("node_local_client_certificate_file_path")
.long("node-local-client-certificate-file-path")
.help("Path to the client certificate file on the target RabbitMQ node(s)")
.required(true)
.value_name("path")
)
.arg(
Arg::new("node_local_client_private_key_file_path")
.long("node-local-client-private-key-file-path")
.help("Path to the client private key file on the target RabbitMQ node(s)")
.required(true)
.value_name("path")
)
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}"#,
SHOVEL_GUIDE_URL,
TLS_GUIDE_URL,
"https://www.rabbitmq.com/docs/shovel#tls-connections"
));
let enable_tls_peer_verification_dest_cmd = Command::new("enable_tls_peer_verification_for_all_destination_uris")
.about("Enables TLS peer verification for all shovel destination URIs with provided [RabbitMQ node-local] certificate paths.")
.long_about("Enables TLS peer verification for all shovel destination URIs by updating their 'verify' parameter and adding [RabbitMQ node-local] certificate and private key file paths.")
.arg(
Arg::new("node_local_ca_certificate_bundle_path")
.long("node-local-ca-certificate-bundle-path")
.help("Path to the CA certificate bundle file on the target RabbitMQ node(s)")
.required(true)
.value_name("path")
)
.arg(
Arg::new("node_local_client_certificate_file_path")
.long("node-local-client-certificate-file-path")
.help("Path to the client certificate file on the target RabbitMQ node(s)")
.required(true)
.value_name("path")
)
.arg(
Arg::new("node_local_client_private_key_file_path")
.long("node-local-client-private-key-file-path")
.help("Path to the client private key file on the target RabbitMQ node(s)")
.required(true)
.value_name("path")
)
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}"#,
SHOVEL_GUIDE_URL,
TLS_GUIDE_URL,
"https://www.rabbitmq.com/docs/shovel#tls-connections"
));
[
list_all_cmd,
list_cmd,
declare_091_cmd,
declare_10_cmd,
delete_cmd,
disable_tls_peer_verification_cmd,
disable_tls_peer_verification_dest_cmd,
enable_tls_peer_verification_source_cmd,
enable_tls_peer_verification_dest_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn federation_subcommands(pre_flight_settings: PreFlightSettings) -> Vec<Command> {
let idempotently_arg = Arg::new("idempotently")
.long("idempotently")
.value_parser(value_parser!(bool))
.action(ArgAction::SetTrue)
.help("do not consider 404 Not Found API responses to be errors")
.required(false);
let list_all_upstreams = Command::new("list_all_upstreams")
.long_about("Lists federation upstreams in all virtual hosts")
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}"#,
FEDERATION_GUIDE_URL,
FEDERATED_EXCHANGES_GUIDE_URL,
FEDERATED_QUEUES_GUIDE_URL
));
let declare_upstream = Command::new("declare_upstream")
.long_about("Declares a federation upstream to be used with both exchange and queue federation")
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}
* {}"#,
FEDERATION_GUIDE_URL,
FEDERATED_EXCHANGES_GUIDE_URL,
FEDERATED_QUEUES_GUIDE_URL,
FEDERATION_REFERENCE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("upstream name (identifier)")
.required(true),
)
.arg(
Arg::new("uri")
.long("uri")
.help("the URI to use to connect to this upstream")
.required(true),
)
.arg(
Arg::new("reconnect_delay")
.long("reconnect-delay")
.default_value("5")
.value_parser(value_parser!(u32))
.help("Reconnection delay in seconds")
)
.arg(
Arg::new("trust_user_id")
.long("trust-user-id")
.default_value("true")
.value_parser(value_parser!(bool))
.help("If set to true, federation will pass through any validated user-id from the upstream, even though it cannot validate it")
)
.arg(
Arg::new("prefetch_count")
.long("prefetch-count")
.default_value("1000")
.value_parser(value_parser!(u32))
.help("The prefetch value to use with internal consumers")
.value_parser(value_parser!(u32))
)
.arg(
Arg::new("ack_mode")
.long("ack-mode")
.value_parser(value_parser!(MessageTransferAcknowledgementMode))
.help("Accepted values are: on-confirm, on-publish, no-ack")
.default_value("on-confirm"),
)
.arg(
Arg::new("queue_name")
.long("queue-name")
.help("queue federation: the queue name to use on the upstream. Defaults to the federated queue name")
)
.arg(
Arg::new("consumer_tag")
.long("consumer-tag")
.help("Custom consumer tag to use for the internal federation consumer")
.requires("queue_name")
)
.arg(
Arg::new("exchange_name")
.long("exchange-name")
.help("exchange federation: the exchange name to use on the upstream. Defaults to the federated exchange name")
)
.arg(
Arg::new("queue_type")
.long("queue-type")
.help(color_print::cformat!("exchange federation: the type of the internal queue to use, one of: <bold>classic</bold>, <bright-blue>quorum</bright-blue>, <bright-magenta>stream</bright-magenta>"))
)
.arg(
Arg::new("max_hops")
.long("max-hops")
.default_value("1")
.value_parser(value_parser!(u8))
)
.arg(
Arg::new("bind_nowait")
.long("bind-using-nowait")
.default_value("false")
.value_parser(value_parser!(bool))
)
.arg(
Arg::new("resource_cleanup_mode")
.long("resource-cleanup-mode")
.default_value("default")
.value_parser(value_parser!(FederationResourceCleanupMode))
)
.arg(
Arg::new("channel_use_mode")
.long("channel-use-mode")
.default_value("multiple")
.value_parser(value_parser!(ChannelUseMode))
)
.arg(
Arg::new("ttl")
.long("ttl")
.long_help("exchange federation: the TTL to apply to the internal queue")
.value_parser(value_parser!(u32))
)
.arg(
Arg::new("message_ttl")
.long("message-ttl")
.long_help("exchange federation: the message TTL to use with the internal queue")
.value_parser(value_parser!(u32))
);
let declare_upstream_for_queue_federation = Command::new("declare_upstream_for_queues")
.long_about("Declares an upstream that will be used only for queue federation")
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}"#,
FEDERATION_GUIDE_URL,
FEDERATED_QUEUES_GUIDE_URL,
FEDERATION_REFERENCE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("upstream name (identifier)")
.required(true),
)
.arg(
Arg::new("uri")
.long("uri")
.help("the URI to use to connect to this upstream")
.required(true),
)
.arg(
Arg::new("reconnect_delay")
.long("reconnect-delay")
.default_value("5")
.value_parser(value_parser!(u32))
.help("Reconnection delay in seconds")
)
.arg(
Arg::new("trust_user_id")
.long("trust-user-id")
.default_value("true")
.value_parser(value_parser!(bool))
.help("If set to true, federation will pass through any validated user-id from the upstream, even though it cannot validate it")
)
.arg(
Arg::new("prefetch_count")
.long("prefetch-count")
.default_value("1000")
.value_parser(value_parser!(u32))
.help("The prefetch value to use with internal consumers")
)
.arg(
Arg::new("ack_mode")
.long("ack-mode")
.value_parser(value_parser!(MessageTransferAcknowledgementMode))
.help("Accepted values are: on-confirm, on-publish, no-ack")
.default_value("on-confirm"),
)
.arg(
Arg::new("bind_nowait")
.long("bind-using-nowait")
.default_value("false")
.value_parser(value_parser!(bool))
)
.arg(
Arg::new("channel_use_mode")
.long("channel-use-mode")
.default_value("multiple")
.value_parser(value_parser!(ChannelUseMode))
)
.arg(
Arg::new("queue_name")
.long("queue-name")
.help("queue federation: the queue name to use on the upstream. Defaults to the federated queue name")
)
.arg(
Arg::new("consumer_tag")
.long("consumer-tag")
.help("Custom consumer tag to use for the internal federation consumer")
.requires("queue_name")
);
let declare_upstream_for_exchange_federation = Command::new("declare_upstream_for_exchanges")
.long_about("Declares an upstream that will be used only for exchange federation")
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}"#,
FEDERATION_GUIDE_URL,
FEDERATED_EXCHANGES_GUIDE_URL,
FEDERATION_REFERENCE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("upstream name (identifier)")
.required(true),
)
.arg(
Arg::new("uri")
.long("uri")
.help("the URI to use to connect to this upstream")
.required(true),
)
.arg(
Arg::new("reconnect_delay")
.long("reconnect-delay")
.default_value("5")
.value_parser(value_parser!(u32))
.help("Reconnection delay in seconds")
)
.arg(
Arg::new("trust_user_id")
.long("trust-user-id")
.default_value("true")
.value_parser(value_parser!(bool))
.help("If set to true, federation will pass through any validated user-id from the upstream, even though it cannot validate it")
)
.arg(
Arg::new("prefetch_count")
.long("prefetch-count")
.default_value("1000")
.value_parser(value_parser!(u32))
.help("The prefetch value to use with internal consumers")
)
.arg(
Arg::new("ack_mode")
.long("ack-mode")
.value_parser(value_parser!(MessageTransferAcknowledgementMode))
.help("Accepted values are: on-confirm, on-publish, no-ack")
.default_value("on-confirm"),
)
.arg(
Arg::new("exchange_name")
.long("exchange-name")
.help("exchange federation: the exchange name to use on the upstream. Defaults to the federated exchange name")
)
.arg(
Arg::new("queue_type")
.long("queue-type")
.help(color_print::cformat!("exchange federation: the type of the internal queue to use, one of: <bold>classic</bold>, <bright-blue>quorum</bright-blue>, <bright-magenta>stream</bright-magenta>"))
)
.arg(
Arg::new("max_hops")
.long("max-hops")
.default_value("1")
.value_parser(value_parser!(u8))
)
.arg(
Arg::new("resource_cleanup_mode")
.long("resource-cleanup-mode")
.default_value("default")
.value_parser(value_parser!(FederationResourceCleanupMode))
)
.arg(
Arg::new("channel_use_mode")
.long("channel-use-mode")
.default_value("multiple")
.value_parser(value_parser!(ChannelUseMode))
)
.arg(
Arg::new("bind_nowait")
.long("bind-using-nowait")
.default_value("false")
.value_parser(value_parser!(bool))
)
.arg(
Arg::new("ttl")
.long("ttl")
.long_help("exchange federation: the TTL to apply to the internal queue")
.value_parser(value_parser!(u32))
)
.arg(
Arg::new("message_ttl")
.long("message-ttl")
.long_help("exchange federation: the message TTL to use with the internal queue")
.value_parser(value_parser!(u32))
);
let delete_upstream = Command::new("delete_upstream")
.long_about("Declares a federation upstream")
.after_help(color_print::cformat!(
"<bold>Doc guide</bold>: {}",
FEDERATION_GUIDE_URL
))
.arg(
Arg::new("name")
.long("name")
.help("upstream name (identifier)")
.required(true),
)
.arg(idempotently_arg.clone());
let list_all_links = Command::new("list_all_links")
.long_about("List federation links in all virtual hosts")
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}
* {}"#,
FEDERATION_GUIDE_URL,
FEDERATED_EXCHANGES_GUIDE_URL,
FEDERATED_QUEUES_GUIDE_URL,
FEDERATION_REFERENCE_URL
));
let disable_tls_peer_verification_cmd = Command::new("disable_tls_peer_verification_for_all_upstreams")
.about(color_print::cstr!("<bold><red>Use only in case of emergency</red></bold>. Disables TLS peer verification for all federation upstreams."))
.long_about(color_print::cstr!("<bold><red>Use only in case of emergency</red></bold>. Disables TLS peer verification for all federation upstreams by updating their 'verify' parameter."))
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}"#,
FEDERATION_GUIDE_URL,
TLS_GUIDE_URL,
"https://www.rabbitmq.com/docs/federation#tls-connections"
));
let enable_tls_peer_verification_cmd = Command::new("enable_tls_peer_verification_for_all_upstreams")
.about("Enables TLS peer verification for all federation upstreams with provided [RabbitMQ node-local] certificate paths.")
.long_about("Enables TLS peer verification for all federation upstreams by updating their 'verify' parameter and adding [RabbitMQ node-local] certificate and private key file paths.")
.arg(
Arg::new("node_local_ca_certificate_bundle_path")
.long("node-local-ca-certificate-bundle-path")
.help("Path to the CA certificate bundle file on the target RabbitMQ node(s)")
.required(true)
.value_name("path")
)
.arg(
Arg::new("node_local_client_certificate_file_path")
.long("node-local-client-certificate-file-path")
.help("Path to the client certificate file on the target RabbitMQ node(s)")
.required(true)
.value_name("path")
)
.arg(
Arg::new("node_local_client_private_key_file_path")
.long("node-local-client-private-key-file-path")
.help("Path to the client private key file on the target RabbitMQ node(s)")
.required(true)
.value_name("path")
)
.after_help(color_print::cformat!(
r#"<bold>Doc guides</bold>:
* {}
* {}
* {}"#,
FEDERATION_GUIDE_URL,
TLS_GUIDE_URL,
"https://www.rabbitmq.com/docs/federation#tls-connections"
));
[
list_all_upstreams,
declare_upstream,
declare_upstream_for_exchange_federation,
declare_upstream_for_queue_federation,
delete_upstream,
list_all_links,
disable_tls_peer_verification_cmd,
enable_tls_peer_verification_cmd,
]
.into_iter()
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
.collect()
}
fn shell_subcommands() -> Vec<Command> {
let completions_cmd = Command::new("completions")
.about("Generate shell completion scripts for the CLI")
.long_about(
"Generates shell completion scripts for bash, zsh, fish, elvish, or nushell.\n\n\
If --shell is not specified, the shell is detected from the SHELL environment variable.\n\
If detection fails, bash is used as the default.",
)
.arg(
Arg::new("shell")
.long("shell")
.help("Target shell (bash, zsh, fish, elvish, nushell)")
.required(false)
.value_parser(value_parser!(CompletionShell)),
);
vec![completions_cmd]
}