Skip to content

Commit 25863b6

Browse files
Bridge the gap between the "verb" command groups and "noun" ones
1 parent 7d114e2 commit 25863b6

File tree

8 files changed

+282
-18
lines changed

8 files changed

+282
-18
lines changed

CHANGELOG.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,41 @@
22

33
## v2.15.0 (in development)
44

5-
No changes yet.
5+
### Enhancements
6+
7+
* `permissions` is a new command group for operations on user permissions:
8+
9+
```shell
10+
rabbitmqadmin permissions list
11+
12+
rabbitmqadmin permissions declare --user "user1" --configure ".*" --read ".*" --write ".*"
13+
14+
rabbitmqadmin permissions delete --user "user1"
15+
```
16+
17+
* `user_limits` is a new command group for operations on per-user limits:
18+
19+
```shell
20+
rabbitmqadmin user_limits list
21+
22+
rabbitmqadmin user_limits declare --user "user1" --name "max-connections" --value "100"
23+
24+
rabbitmqadmin user_limits delete --user "user1" --name "max-connections"
25+
```
26+
27+
* `vhost_limits` is a new command group for operations on virtual host limits:
28+
29+
```shell
30+
rabbitmqadmin vhost_limits list
31+
32+
rabbitmqadmin vhost_limits declare --name "max-connections" --value "1000"
33+
34+
rabbitmqadmin vhost_limits delete --name "max-connections"
35+
```
36+
37+
### Deprecations
38+
39+
* "Verb" command groups (`list [object]`, `declare [object]`, `delete [object]`) are now deprecated in favor of the "noun" group commands (such as `users [operation]` or `permissions [operation]`).
640

741

842
## v2.14.0 (Sep 30, 2025)

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rabbitmqadmin"
3-
version = "2.14.0"
3+
version = "2.15.0"
44
edition = "2024"
55

66
description = "rabbitmqadmin v2 is a modern CLI tool for the RabbitMQ HTTP API"

src/cli.rs

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,16 @@ pub fn parser(pre_flight_settings: PreFlightSettings) -> Command {
220220
PASSWORD_GUIDE_URL
221221
))
222222
.subcommands(passwords_subcommands(pre_flight_settings.clone()));
223+
let permissions_group = Command::new("permissions")
224+
.about("Operations on user permissions")
225+
.infer_subcommands(pre_flight_settings.infer_subcommands)
226+
.infer_long_args(pre_flight_settings.infer_long_options)
227+
.after_help(color_print::cformat!(
228+
"<bold>Doc guide</bold>: {}",
229+
ACCESS_CONTROL_GUIDE_URL
230+
))
231+
.subcommand_value_name("permission")
232+
.subcommands(permissions_subcommands(pre_flight_settings.clone()));
223233
let policies_group = Command::new("policies")
224234
.about("Operations on policies")
225235
.infer_subcommands(pre_flight_settings.infer_subcommands)
@@ -304,11 +314,31 @@ pub fn parser(pre_flight_settings: PreFlightSettings) -> Command {
304314
))
305315
.subcommand_value_name("subcommand")
306316
.subcommands(users_subcommands(pre_flight_settings.clone()));
317+
let user_limits_group = Command::new("user_limits")
318+
.about("Operations on per-user (resource) limits")
319+
.infer_subcommands(pre_flight_settings.infer_subcommands)
320+
.infer_long_args(pre_flight_settings.infer_long_options)
321+
.after_help(color_print::cformat!(
322+
"<bold>Doc guide</bold>: {}",
323+
USER_LIMIT_GUIDE_URL
324+
))
325+
.subcommand_value_name("user_limit")
326+
.subcommands(user_limits_subcommands(pre_flight_settings.clone()));
307327
let vhosts_group = Command::new("vhosts")
308328
.about("Virtual host operations")
309329
.infer_subcommands(pre_flight_settings.infer_subcommands)
310330
.infer_long_args(pre_flight_settings.infer_long_options)
311331
.subcommands(vhosts_subcommands(pre_flight_settings.clone()));
332+
let vhost_limits_group = Command::new("vhost_limits")
333+
.about("Operations on virtual host (resource) limits")
334+
.infer_subcommands(pre_flight_settings.infer_subcommands)
335+
.infer_long_args(pre_flight_settings.infer_long_options)
336+
.after_help(color_print::cformat!(
337+
"<bold>Doc guide</bold>: {}",
338+
VIRTUAL_HOST_LIMIT_GUIDE_URL
339+
))
340+
.subcommand_value_name("vhost_limit")
341+
.subcommands(vhost_limits_subcommands(pre_flight_settings.clone()));
312342

313343
let command_groups = [
314344
bindings_group,
@@ -332,6 +362,7 @@ pub fn parser(pre_flight_settings: PreFlightSettings) -> Command {
332362
operator_policies_group,
333363
parameters_group,
334364
passwords_group,
365+
permissions_group,
335366
policies_group,
336367
publish_group,
337368
purge_group,
@@ -342,7 +373,9 @@ pub fn parser(pre_flight_settings: PreFlightSettings) -> Command {
342373
streams_group,
343374
tanzu_group,
344375
users_group,
376+
user_limits_group,
345377
vhosts_group,
378+
vhost_limits_group,
346379
];
347380

348381
Command::new("rabbitmqadmin")
@@ -2948,6 +2981,162 @@ pub fn passwords_subcommands(pre_flight_settings: PreFlightSettings) -> [Command
29482981
[hash_password].map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
29492982
}
29502983

2984+
pub fn permissions_subcommands(pre_flight_settings: PreFlightSettings) -> [Command; 3] {
2985+
let idempotently_arg = Arg::new("idempotently")
2986+
.long("idempotently")
2987+
.value_parser(value_parser!(bool))
2988+
.action(ArgAction::SetTrue)
2989+
.help("do not consider 404 Not Found API responses to be errors")
2990+
.required(false);
2991+
2992+
let list_cmd = Command::new("list")
2993+
.long_about("Lists user permissions")
2994+
.after_help(color_print::cformat!(
2995+
"<bold>Doc guide</bold>: {}",
2996+
ACCESS_CONTROL_GUIDE_URL
2997+
));
2998+
2999+
let declare_cmd = Command::new("declare")
3000+
.about("grants permissions to a user")
3001+
.after_help(color_print::cformat!(
3002+
"<bold>Doc guide:</bold>: {}",
3003+
ACCESS_CONTROL_GUIDE_URL
3004+
))
3005+
.arg(
3006+
Arg::new("user")
3007+
.long("user")
3008+
.help("username")
3009+
.required(true),
3010+
)
3011+
.arg(
3012+
Arg::new("configure")
3013+
.long("configure")
3014+
.help("name pattern for configuration access")
3015+
.required(true),
3016+
)
3017+
.arg(
3018+
Arg::new("read")
3019+
.long("read")
3020+
.help("name pattern for read access")
3021+
.required(true),
3022+
)
3023+
.arg(
3024+
Arg::new("write")
3025+
.long("write")
3026+
.help("name pattern for write access")
3027+
.required(true),
3028+
);
3029+
3030+
let delete_cmd = Command::new("delete")
3031+
.about("Revokes user permissions to a given vhost")
3032+
.arg(
3033+
Arg::new("user")
3034+
.long("user")
3035+
.help("username")
3036+
.required(true),
3037+
)
3038+
.arg(idempotently_arg.clone());
3039+
3040+
[list_cmd, declare_cmd, delete_cmd]
3041+
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
3042+
}
3043+
3044+
pub fn user_limits_subcommands(pre_flight_settings: PreFlightSettings) -> [Command; 3] {
3045+
let list_cmd = Command::new("list")
3046+
.long_about("Lists per-user (resource) limits")
3047+
.after_help(color_print::cformat!(
3048+
"<bold>Doc guide</bold>: {}",
3049+
USER_LIMIT_GUIDE_URL
3050+
))
3051+
.arg(
3052+
Arg::new("user")
3053+
.long("user")
3054+
.help("username")
3055+
.required(false),
3056+
);
3057+
3058+
let declare_cmd = Command::new("declare")
3059+
.about("Set a user limit")
3060+
.after_help(color_print::cformat!(
3061+
"<bold>Doc guide:</bold>: {}",
3062+
USER_LIMIT_GUIDE_URL
3063+
))
3064+
.arg(
3065+
Arg::new("user")
3066+
.long("user")
3067+
.help("username")
3068+
.required(true),
3069+
)
3070+
.arg(
3071+
Arg::new("name")
3072+
.long("name")
3073+
.help("limit name (eg. max-connections, max-queues)")
3074+
.required(true),
3075+
)
3076+
.arg(
3077+
Arg::new("value")
3078+
.long("value")
3079+
.help("limit value")
3080+
.required(true),
3081+
);
3082+
3083+
let delete_cmd = Command::new("delete")
3084+
.about("Clears a user limit")
3085+
.arg(
3086+
Arg::new("user")
3087+
.long("user")
3088+
.help("username")
3089+
.required(true),
3090+
)
3091+
.arg(
3092+
Arg::new("name")
3093+
.long("name")
3094+
.help("limit name (eg. max-connections, max-queues)")
3095+
.required(true),
3096+
);
3097+
3098+
[list_cmd, declare_cmd, delete_cmd]
3099+
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
3100+
}
3101+
3102+
pub fn vhost_limits_subcommands(pre_flight_settings: PreFlightSettings) -> [Command; 3] {
3103+
let list_cmd = Command::new("list")
3104+
.long_about("Lists virtual host (resource) limits")
3105+
.after_help(color_print::cformat!(
3106+
"<bold>Doc guide</bold>: {}",
3107+
VIRTUAL_HOST_GUIDE_URL
3108+
));
3109+
3110+
let declare_cmd = Command::new("declare")
3111+
.about("Set a vhost limit")
3112+
.after_help(color_print::cformat!(
3113+
"<bold>Doc guide:</bold>: {}",
3114+
VIRTUAL_HOST_LIMIT_GUIDE_URL
3115+
))
3116+
.arg(
3117+
Arg::new("name")
3118+
.long("name")
3119+
.help("limit name (eg. max-connections, max-queues)")
3120+
.required(true),
3121+
)
3122+
.arg(
3123+
Arg::new("value")
3124+
.long("value")
3125+
.help("limit value")
3126+
.required(true),
3127+
);
3128+
3129+
let delete_cmd = Command::new("delete").about("delete a vhost limit").arg(
3130+
Arg::new("name")
3131+
.long("name")
3132+
.help("limit name (eg. max-connections, max-queues)")
3133+
.required(true),
3134+
);
3135+
3136+
[list_cmd, declare_cmd, delete_cmd]
3137+
.map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
3138+
}
3139+
29513140
pub fn publish_subcommands(pre_flight_settings: PreFlightSettings) -> [Command; 1] {
29523141
[Command::new("message")
29533142
.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!"))

src/main.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,20 @@ fn dispatch_common_subcommand(
910910
let result = commands::salt_and_hash_password(second_level_args);
911911
res_handler.show_salted_and_hashed_value(result)
912912
}
913+
("permissions", "list") => {
914+
let result = commands::list_permissions(client);
915+
res_handler.tabular_result(result)
916+
}
917+
("permissions", "declare") => {
918+
let result = commands::declare_permissions(client, &vhost, second_level_args)
919+
.map_err(Into::into);
920+
res_handler.no_output_on_success(result);
921+
}
922+
("permissions", "delete") => {
923+
let result =
924+
commands::delete_permissions(client, &vhost, second_level_args).map_err(Into::into);
925+
res_handler.no_output_on_success(result);
926+
}
913927
("policies", "declare") => {
914928
let result = commands::declare_policy(client, &vhost, second_level_args);
915929
res_handler.no_output_on_success(result);
@@ -1153,6 +1167,19 @@ fn dispatch_common_subcommand(
11531167
let result = commands::list_permissions(client);
11541168
res_handler.tabular_result(result)
11551169
}
1170+
("user_limits", "list") => {
1171+
let result = commands::list_user_limits(client, second_level_args);
1172+
res_handler.tabular_result(result)
1173+
}
1174+
("user_limits", "declare") => {
1175+
let result =
1176+
commands::declare_user_limit(client, second_level_args).map_err(Into::into);
1177+
res_handler.no_output_on_success(result);
1178+
}
1179+
("user_limits", "delete") => {
1180+
let result = commands::delete_user_limit(client, second_level_args).map_err(Into::into);
1181+
res_handler.no_output_on_success(result);
1182+
}
11561183
("vhosts", "declare") => {
11571184
let result = commands::declare_vhost(client, second_level_args).map_err(Into::into);
11581185
res_handler.no_output_on_success(result);
@@ -1191,6 +1218,20 @@ fn dispatch_common_subcommand(
11911218
.map_err(Into::into);
11921219
res_handler.no_output_on_success(result);
11931220
}
1221+
("vhost_limits", "list") => {
1222+
let result = commands::list_vhost_limits(client, &vhost);
1223+
res_handler.tabular_result(result)
1224+
}
1225+
("vhost_limits", "declare") => {
1226+
let result = commands::declare_vhost_limit(client, &vhost, second_level_args)
1227+
.map_err(Into::into);
1228+
res_handler.no_output_on_success(result);
1229+
}
1230+
("vhost_limits", "delete") => {
1231+
let result =
1232+
commands::delete_vhost_limit(client, &vhost, second_level_args).map_err(Into::into);
1233+
res_handler.no_output_on_success(result);
1234+
}
11941235
_ => {
11951236
let error = CommandRunError::UnknownCommandTarget {
11961237
command: pair.0.into(),

tests/permissions_tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ fn test_list_permissions() -> Result<(), Box<dyn Error>> {
3131
]);
3232

3333
run_succeeds([
34-
"declare",
3534
"permissions",
35+
"declare",
3636
"--user",
3737
username,
3838
"--configure",
@@ -43,14 +43,14 @@ fn test_list_permissions() -> Result<(), Box<dyn Error>> {
4343
"baz",
4444
]);
4545

46-
run_succeeds(["list", "permissions"]).stdout(
46+
run_succeeds(["permissions", "list"]).stdout(
4747
output_includes("foo")
4848
.and(output_includes("bar"))
4949
.and(output_includes("baz")),
5050
);
5151

52-
run_succeeds(["delete", "permissions", "--user", username]);
53-
run_succeeds(["list", "permissions"]).stdout(output_includes(username).not());
52+
run_succeeds(["permissions", "delete", "--user", username]);
53+
run_succeeds(["permissions", "list"]).stdout(output_includes(username).not());
5454
run_succeeds(["delete", "user", "--name", username]);
5555

5656
Ok(())

0 commit comments

Comments
 (0)