diff --git a/.config/nextest.toml b/.config/nextest.toml
new file mode 100644
index 0000000..a747759
--- /dev/null
+++ b/.config/nextest.toml
@@ -0,0 +1,126 @@
+[profile.default]
+default-filter = "all()"
+
+retries = 2
+test-threads = "num-cpus"
+
+status-level = "retry"
+final-status-level = "pass"
+failure-output = "immediate"
+
+[test-groups]
+# Read-only tests that can run with high parallelism
+read_only = { max-threads = 16 }
+# Tests that create isolated virtual hosts
+isolated_vhosts = { max-threads = 8 }
+# Tests that modify users/permissions (global state)
+user_management = { max-threads = 1 }
+# Tests that modify virtual hosts (global state)
+vhost_management = { max-threads = 1 }
+# Tests that modify runtime parameters
+runtime_params = { max-threads = 1 }
+# Tests requiring complete isolation
+sequential = { max-threads = 1 }
+
+[[profile.default.overrides]]
+filter = 'test(list)'
+priority = 60
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_federation)'
+priority = 56
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_policies)'
+priority = 55
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_memory_breakdown)'
+priority = 50
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_export)'
+priority = 40
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_import)'
+priority = 30
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_shovel)'
+priority = 20
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(deprecated_features)'
+priority = 10
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'binary(federation_upstream_uri_modification_tests)'
+priority = 25
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'binary(shovel_source_uri_modification_tests)'
+priority = 24
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'binary(shovel_destination_uri_modification_tests)'
+priority = 23
+test-group = 'sequential'
+
+# Read-only tests that can run with high parallelism
+[[profile.default.overrides]]
+filter = 'binary(help_tests) or binary(health_check_tests) or binary(nodes_tests) or binary(feature_flag_tests)'
+priority = 80
+test-group = 'read_only'
+
+# Tests that create isolated virtual hosts
+[[profile.default.overrides]]
+filter = 'binary(queues_tests) or binary(exchanges_tests) or binary(bindings_tests) or binary(streams_tests) or binary(vhost_limits_tests) or binary(channels_tests) or binary(connections_tests)'
+priority = 70
+test-group = 'isolated_vhosts'
+
+# User management tests (global state)
+[[profile.default.overrides]]
+filter = 'binary(users_tests) or binary(permissions_tests) or binary(user_limits_tests)'
+priority = 65
+test-group = 'user_management'
+
+# Runtime parameter tests (excluding already sequential ones)
+[[profile.default.overrides]]
+filter = 'binary(runtime_parameters_tests)'
+priority = 26
+test-group = 'runtime_params'
+
+# Virtual host management (global vhost operations)
+[[profile.default.overrides]]
+filter = 'binary(vhosts_tests)'
+priority = 62
+test-group = 'vhost_management'
+
+# Virtual host delete_multiple tests (global vhost operations, can be flaky due to race conditions)
+[[profile.default.overrides]]
+filter = 'binary(vhosts_delete_multiple_tests)'
+priority = 61
+test-group = 'vhost_management'
+
+# Combined integration tests (creates global users)
+[[profile.default.overrides]]
+filter = 'binary(combined_integration_tests)'
+priority = 61
+test-group = 'user_management'
+
+# Tests that can run with moderate parallelism (no major conflicts)
+[[profile.default.overrides]]
+filter = 'binary(deprecated_feature_tests) or binary(test_commands_recommended_against_tests) or binary(feature_flag_management_tests)'
+priority = 75
+test-group = 'isolated_vhosts'
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..90a0d1c
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+version: 2
+updates:
+ - package-ecosystem: "cargo"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ open-pull-requests-limit: 10
+ reviewers:
+ - "michaelklishin"
+ assignees:
+ - "michaelklishin"
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 6bf76c8..dfa8f55 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -4,6 +4,7 @@ on:
push:
paths:
- ".github/workflows/ci.yaml"
+ - ".config/nextest.toml"
- "src/**"
- "tests/**"
- "Cargo.toml"
@@ -40,6 +41,9 @@ jobs:
build:
strategy:
matrix:
+ rabbitmq-series:
+ - "4.0"
+ - "4.1"
rust-version:
- stable
- beta
@@ -51,7 +55,7 @@ jobs:
services:
rabbitmq:
- image: rabbitmq:4-management
+ image: rabbitmq:${{ matrix.rabbitmq-series }}-management
ports:
- 15672:15672
- 5672:5672
@@ -72,4 +76,4 @@ jobs:
run: RUST_HTTP_API_CLIENT_RABBITMQCTL=DOCKER:${{job.services.rabbitmq.id}} bin/ci/before_build.sh
- name: Run tests
- run: RUST_BACKTRACE=1 NEXTEST_RETRIES=2 cargo nextest run -j 1 --workspace --no-fail-fast --all-features
\ No newline at end of file
+ run: RUST_BACKTRACE=1 NEXTEST_RETRIES=2 cargo nextest run --workspace --no-fail-fast --all-features
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index d26635b..556b5fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
.idea/*
.fleet/*
profile.json
+.tool-versions
+*.proptest-regressions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cac7ea0..45df4d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,664 @@
# rabbitmqadmin-ng Change Log
-## v0.28.0 (in development)
+## v2.17.0 (in development)
+
+No changes yet.
+
+
+## v2.16.0 (Oct 20, 2025)
+
+### Enhancements
+
+* `plugins` is a new command group for listing enabled plugins:
+
+ ```shell
+ # List plugins across all cluster nodes
+ rabbitmqadmin plugins list_all
+
+ # List plugins on a specific node
+ rabbitmqadmin plugins list_on_node --node rabbit@hostname
+ ```
+
+* Errors now include the `error` or `reason` field from the API response (if they were present there)
+
+* `--timeout` is a new global option limits HTTP API request execution timeout. The value is in seconds and defaults
+ to 60s:
+
+ ```shell
+ rabbitmqadmin --timeout 15 queues list
+ ```
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.66.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.66.0)
+
+
+## v2.15.0 (Sep 30, 2025)
+
+### Enhancements
+
+* `permissions` is a new command group for operations on user permissions:
+
+ ```shell
+ rabbitmqadmin permissions list
+
+ rabbitmqadmin permissions declare --user "user1" --configure ".*" --read ".*" --write ".*"
+
+ rabbitmqadmin permissions delete --user "user1"
+ ```
+
+* `user_limits` is a new command group for operations on [per-user limits](https://www.rabbitmq.com/docs/user-limits):
+
+ ```shell
+ rabbitmqadmin user_limits list
+
+ rabbitmqadmin user_limits declare --user "user1" --name "max-connections" --value "100"
+
+ rabbitmqadmin user_limits delete --user "user1" --name "max-connections"
+ ```
+
+* `vhost_limits` is a new command group for operations on [virtual host limits](https://www.rabbitmq.com/docs/vhosts#limits):
+
+ ```shell
+ rabbitmqadmin vhost_limits list
+
+ rabbitmqadmin vhost_limits declare --name "max-connections" --value "1000"
+
+ rabbitmqadmin vhost_limits delete --name "max-connections"
+ ```
+
+### Deprecations
+
+* "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]`).
+
+
+## v2.14.0 (Sep 30, 2025)
+
+### Enhancements
+
+* Several commands now have minimalistic progress indicators: `federation disable_tls_peer_verification_for_all_upstreams`, `federation enable_tls_peer_verification_for_all_upstreams`, `shovels disable_tls_peer_verification_for_all_source_uris`, `shovels disable_tls_peer_verification_for_all_destination_uris`, `shovels enable_tls_peer_verification_for_all_source_uris`, and `shovels enable_tls_peer_verification_for_all_destination_uris`
+
+* `vhosts delete_multiple` is a new command that deletes multiple virtual hosts matching a regular expression pattern:
+
+ ```shell
+ # Delete all virtual hosts matching a pattern (requires explicit approval)
+ rabbitmqadmin vhosts delete_multiple --name-pattern "test-.*" --approve
+
+ # Dry-run to see what would be deleted without actually deleting
+ rabbitmqadmin vhosts delete_multiple --name-pattern "staging-.*" --dry-run
+
+ # Non-interactive mode (no --approve flag needed)
+ rabbitmqadmin --non-interactive vhosts delete_multiple --name-pattern "temp-.*"
+ ```
+
+ One virtual host — named `/`, that is, the default one — is always skipped to preserve
+ at least one functional virtual host at all times.
+
+ **Important**: this command is **very destructive** and should be used with caution. Always test with `--dry-run` first.
+
+* `vhosts enable_deletion_protection` and `vhosts disable_deletion_protection` are two new commands
+ for managing [virtual host deletion protection](https://www.rabbitmq.com/docs/vhosts#deletion-protection):
+
+ ```shell
+ # Enable deletion protection for a virtual host
+ rabbitmqadmin vhosts enable_deletion_protection --name "production-vhost"
+
+ # Disable deletion protection for a virtual host
+ rabbitmqadmin vhosts disable_deletion_protection --name "production-vhost"
+ ```
+
+ Protected virtual hosts cannot be deleted, either individually using `vhosts delete` or
+ as part of bulk operations using `vhosts delete_multiple`. To delete a protected
+ virtual host, its protection must be lifted first.
+
+## v2.13.0 (Sep 26, 2025)
+
+### Enhancements
+
+* Memory breakdown commands (`show memory_breakdown_in_bytes` and `show memory_breakdown_in_percent`) now gracefully handle
+ cases where memory breakdown stats are not yet available on the target node
+
+* `shovel enable_tls_peer_verification_for_all_source_uris` is a new command that enables TLS peer verification
+ for all shovel source URIs:
+
+ ```shell
+ # The certificate and private key paths below refer
+ # to the files deployed to the target RabbitMQ node(s), not to the
+ # local files.
+ #
+ # As such, these arguments are command-specific and should not be confused
+ # with the global `--tls-ca-cert-file`, `--tls-cert-file`, and `--tls-key-file`
+ # arguments that are used by `rabbitmqadmin` itself to connect to the target node
+ # over the HTTP API.
+ rabbitmqadmin shovels enable_tls_peer_verification_for_all_source_uris \
+ --node-local-ca-certificate-bundle-path /path/to/node/local/ca_bundle.pem \
+ --node-local-client-certificate-file-path /path/to/node/local/client_certificate.pem \
+ --node-local-client-private-key-file-path /path/to/node/local/client_private_key.pem
+ ```
+
+ See [TLS guide](https://www.rabbitmq.com/docs/ssl#peer-verification) and [Shovel guide](https://www.rabbitmq.com/docs/shovel#tls) to learn more.
+
+* `shovel enable_tls_peer_verification_for_all_destination_uris` is a new command that enables TLS peer verification
+ for all shovel destination URIs:
+
+ ```shell
+ # Ditto, the certificate and private key paths below refer
+ # to the files deployed to the target RabbitMQ node(s), not to the
+ # local files.
+ rabbitmqadmin shovels enable_tls_peer_verification_for_all_destination_uris \
+ --node-local-ca-certificate-bundle-path /path/to/node/local/ca_bundle.pem \
+ --node-local-client-certificate-file-path /path/to/node/local/client_certificate.pem \
+ --node-local-client-private-key-file-path /path/to/node/local/client_private_key.pem
+ ```
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.59.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.59.0)
+
+
+## v2.12.0 (Sep 23, 2025)
+
+### Enhancements
+
+* `federation enable_tls_peer_verification_for_all_upstreams` is a new command that enables TLS peer verification
+ for all federation upstreams:
+
+ ```shell
+ # Note that the certificate and private key paths below refer
+ # to the files deployed to the target RabbitMQ node(s), not to the
+ # local files.
+ #
+ # As such, these arguments are command-specific and should not be confused
+ # with the global `--tls-ca-cert-file`, `--tls-cert-file`, and `--tls-key-file`
+ # arguments that are used by `rabbitmqadmin` itself to connect to the target node
+ # over the HTTP API.
+ rabbitmqadmin federation enable_tls_peer_verification_for_all_upstreams \
+ --node-local-ca-certificate-bundle-path /path/to/node/local/ca_bundle.pem \
+ --node-local-client-certificate-file-path /path/to/node/local/client_certificate.pem \
+ --node-local-client-private-key-file-path /path/to/node/local/client_private_key.pem
+ ```
+
+ See [TLS guide](https://www.rabbitmq.com/docs/ssl#peer-verification) and [Federation guide](https://www.rabbitmq.com/docs/federation#tls-connections) to learn more.
+
+ * `shovel disable_tls_peer_verification_for_all_source_uris` is a new command that disables TLS peer verification
+ for all shovel source URIs.
+
+ **Important**: this command should **only** be used to undo incorrect shovel source URIs, after a bad deployment, for example,
+ if [peer verification](https://www.rabbitmq.com/docs/ssl#peer-verification) was enabled before certificates and keys were
+ deployed.
+
+ * `shovel disable_tls_peer_verification_for_all_source_uris` is a new command that disables TLS peer verification
+ for all shovel source URIs.
+
+ **Important**: this command should **only** be used to undo incorrect shovel destination URIs (see above).
+
+* All `delete_*` and `clear_*` commands now support the `--idempotently` flag (previously it was just a few):
+ - `bindings delete`
+ - `close connection`
+ - `close user_connections`
+ - `connections close`
+ - `connections close_of_user`
+ - `exchanges delete`
+ - `exchanges unbind`
+ - `federation delete_upstream`
+ - `global_parameters clear`
+ - `operator_policies delete`
+ - `parameters clear`
+ - `policies delete`
+ - `queues delete`
+ - `shovels delete`
+ - `streams delete`
+ - `users delete`
+ - `vhosts delete`
+
+* Updated `delete_binding` to use the new `BindingDeletionParams` struct API
+
+## v2.11.0 (Sep 22, 2025)
+
+### Enhancements
+
+* `federation disable_tls_peer_verification_for_all_upstreams` is a new command that disables TLS peer verification
+ for all federation upstreams.
+
+ **Important**: this command should **only** be used to correct federation upstream URI after a bad deployment, for example,
+ if [peer verification](https://www.rabbitmq.com/docs/ssl#peer-verification) was enabled before certificates and keys were
+ deployed.
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.57.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.57.0)
+
+
+
+## v2.10.0 (Sep 18, 2025)
+
+### Enhancements
+
+* `definitions export_from_vhost` now supports `--transformations`:
+
+ ```shell
+ # previously only 'definitions export' supported --transformations
+ rabbitmqadmin --vhost "my-vhost" definitions export_from_vhost \
+ --transformations prepare_for_quorum_queue_migration,drop_empty_policies \
+ --file "my-vhost.definitions.json"
+ ```
+
+### Bug Fixes
+
+ * The `prepare_for_quorum_queue_migration` transformation did not remove CMQ-related keys
+ such as `x-ha-mode` from [optional queue arguments](https://www.rabbitmq.com/docs/queues#optional-arguments)
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.52.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.52.0)
+
+
+## v2.9.0 (Aug 25, 2025)
+
+### Enhancements
+
+ * RabbitMQ 4.2 forward compatibility: `shovels list_all` and `shovels list` now can render
+ [local shovel](https://github.com/rabbitmq/rabbitmq-server/pull/14256) rows
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.44.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.44.0)
+
+
+## v2.8.2 (Aug 19, 2025)
+
+### Enhancements
+
+ * `definitions export` is now compatible with RabbitMQ 3.10.0, a series that has
+ reached end of life (EOL) in late 2023
+
+### Upgrades
+
+ * RabbitMQ HTTP API client was upgraded to [`0.43.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.43.0)
+
+
+## v2.8.1 (Aug 14, 2025)
+
+### Bug Fixes
+
+ * `shovels list` and `shovels list_all` panicked when target cluster had at least one
+ static shovel
+
+### Upgrades
+
+ * RabbitMQ HTTP API client was upgraded to [`0.42.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.42.0)
+
+
+## v2.8.0 (Aug 11, 2025)
+
+### Bug Fixes
+
+ * `shovels list_all` panicked when one of the shovels was in the `terminated` state
+
+### Enhancements
+
+ * `shovels list` is a new command that lists shovels in a particular virtual host
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.41.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.41.0)
+
+
+## v2.7.2 (Aug 6, 2025)
+
+### Bug Fixes
+
+ * `shovels declare_amqp091` panicked when the `--source-exchange` argument was not provided,
+ even if `--source-queue` was
+
+
+## v2.7.1 (Jul 17, 2025)
+
+### Bug Fixes
+
+ * Improved handling of missing or impossible to load/parse `--tls-ca-cert-file` on the command line.
+
+ The tool now properly handles cases where a [CA certificate](https://www.rabbitmq.com/docs/ssl#peer-verification) file path is not provided, making
+ CA certificate loading optional rather than required, which prevents crashes when TLS is used
+ without a custom CA certificate bundle
+
+ * `show overview` could panic when run against a freshly booted RabbitMQ node that did not have certain
+ metrics/rates initialized and available. Now those metrics will use the default values for their types,
+ such as `0` and `0.0` for the counters, gauges, rates
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.40.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.40.0)
+
+
+## v2.7.0 (Jul 15, 2025)
### Enhancements
+ * `rabbitmqadmin.conf` now supports more TLS-related settings: `ca_certificate_bundle_path` (corresponds to `--tls-ca-cert-file` on the command line),
+ `client_certificate_file_path` (corresponds to `--tls-cert-file`), and `client_private_key_file_path` (corresponds to `--tls-key-file`).
+
+ As the names suggest, they are used to configure the CA certificate bundle file path, the client certificate file path,
+ and the client private key file path, respectively:
+
+ ```toml
+ [production]
+ hostname = "(redacted)"
+ port = 15671
+ username = "user-efe1f4d763f6"
+ password = "(redacted)"
+ tls = true
+ ca_certificate_bundle_path = "/path/to/ca_certificate.pem"
+ client_certificate_file_path = "/path/to/client_certificate.pem"
+ client_private_key_file_path = "/path/to/client_key.pem"
+ ```
+
+ To learn more, see [RabbitMQ's TLS guide](https://www.rabbitmq.com/docs/ssl).
+
+### Bug Fixes
+
+ * Tool version was unintentionally missing from `-h` output (but present in its long counterpart, `--help`)
+ * The `tls` setting in `rabbitmqadmin.conf`, a `--use-tls` equivalent, was not respected when connecting to a node
+ in certain cases
+
+## v2.6.0 (Jul 12, 2025)
+
+### Enhancements
+
+ * New command, `passwords salt_and_hash`, that implements the [password salting and hashing algorithm](https://www.rabbitmq.com/docs/passwords#computing-password-hash)
+ used by RabbitMQ's internal authentication backend:
+
+ ```shell
+ rabbitmqadmin passwords salt_and_hash "sEkr37^va1ue"
+ # => ┌───────────────┬──────────────────────────────────────────────────┐
+ # => │ Result │
+ # => ├───────────────┼──────────────────────────────────────────────────┤
+ # => │ key │ value │
+ # => ├───────────────┼──────────────────────────────────────────────────┤
+ # => │ password hash │ vRZC0bF0Ut4+6pmcQRSu87S/wRXdHRalgY5DV/5KDd5SzK69 │
+ # => └───────────────┴──────────────────────────────────────────────────┘
+ ```
+
+ This value can be passed as a `--password-hash` when creating a user with the `users declare`
+ command.
+
+ * `users declare` now supports a new argument, `--hashing-algorithm`, that accepts two
+ possible values: `sha256` (the default) and `sha512`:
+
+ ```shell
+ # RabbitMQ nodes must also be configured to use SHA-512 password hashing,
+ # or this user won't be able to authenticate against them
+ rabbitmqadmin users declare --username "username43742" --password "example_%^4@8s7" --hashing-algorithm "sha512"
+ ```
+
+ Target RabbitMQ nodes must be [configured](https://www.rabbitmq.com/docs/passwords#changing-algorithm) to use the same hashing algorithm (SHA-256 is
+ used by default).
+
+
+## v2.5.0 (Jul 11, 2025)
+
+### Enhancements
+
+ * `definitions export` now supports a new transformation: `prepare_for_quorum_queue_migration`.
+
+ ```shell
+ rabbitmqadmin definitions export --transformations prepare_for_quorum_queue_migration,drop_empty_policies --stdout
+ ```
+
+ This one not only strips off the CMQ-related keys
+ but also handles an incompatible `"overflow"`/`"x-overflow"` key value
+ and `"queue-mode"`/`"x-queue-mode"` keys, both not supported
+ by quorum queues.
+
+### Bug Fixes
+
+ * `export definitions` CLI interface was unintentionally different from that of `definitions export`.
+ Note that `export definitions` only exists for better backwards compatibility with `rabbitmqadmin` v1,
+ use `definitions export` when possible.
+
+
+## v2.4.0 (Jul 4, 2025)
+
+### Bug Fixes
+
+ * `connections list` failed to deserialize a list of connections that included direct connections
+ (as in the Erlang AMQP 0-9-1 client), namely local connections of shovels and federation links.
+
+ GitHub issue: [#68](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/68)
+
+### Upgrades
+
+ * RabbitMQ HTTP API client was upgraded to [`0.36.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.36.0)
+
+
+## v2.3.0 (Jun 30, 2025)
+
+ * RabbitMQ HTTP API client was upgraded to [`0.35.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.35.0) to fix a `connections list` command
+ panic.
+
+
+## v2.2.1 (Jun 20, 2025)
+
+### Bug Fixes
+
+ * Several `rabbitmqadmin.conf` settings were not merged correctly with
+ the command line arguments.
+
+ GitHub issue: [#58](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/58)
+
+
+## v2.2.0 (Jun 12, 2025)
+
+### Enhancements
+
+ * `connections` is a new command group for operations on connections
+ * `channels` is a new command group for operations on channels
+ * `operator_policies` is a new command group for working with operator policies.
+ It matches the `policies` group but acts on [operator policies](https://www.rabbitmq.com/docs/policies#operator-policies)
+ * `policies set` and `policies update` are two new aliases for `policies declare`. The former follows the naming
+ used by `rabbitmqctl` and the latter reflects the fact that the command can be used to update an existing policy,
+ in particular, to override its definition
+ * `policies patch` is a new command that updates a policy definition by merging the provided definition with the existing one
+ * `policies delete_definition_keys` is a new command that removes keys from a policy definition
+ * `policies delete_definition_keys_from_all_in` is a new command that removes definition keys from all policies in a virtual host
+ * `policies update_definition` is a new command that updates a policy definition key; for multi-key updates, see `policies patch
+ * `policies update_definitions_of_all_in` is a new command that updates a definition key for all policies in a virtual host
+ * `policies declare_override` is a new command that declares a policy that overrides another policy
+ * `policies declare_blanket` is a new command that declares a low priority policy that matches all objects not matched
+ by any other policies
+ * `parameters list_all` is a new command that lists all runtime parameters across all virtual hosts
+ * `parameters list_in` is a new command that lists runtime parameters of a given component (type)
+ in a specific virtual host
+
+
+## v2.1.0 (May 8, 2025)
+
+### Enhancements
+
+ * `bindings` is a new command group for operations on bindings
+ * `exchanges` is a new command group for operations on exchanges
+ * `global_parameters` is a new command group for operations on [global runtime parameters](https://www.rabbitmq.com/docs/parameters)
+ * `nodes` is a new command group for operations on nodes
+ * `parameters` is a new command group for operations on [runtime parameters](https://www.rabbitmq.com/docs/parameters)
+ * `queues` is a new command group for operations on queues
+ * `streams` is a new command group for operations on streams
+ * `users` is a new command group for operations on users
+ * `vhosts` is a new command group for operations on virtual hosts
+ * Command groups are now ordered alphabetically
+
+### Bug Fixes
+
+ * Both `-h` and `--help` now display relevant doc guide URLs.
+ Previously it was only the case for `--help`
+
+### Other Changes
+
+ * `vhosts declare` no longer has a default value for `--default-queue-type`.
+ Instead, the default will be controlled exclusively by RabbitMQ
+
+
+## v2.0.0 (Mar 31, 2025)
+
+### Enhancements
+
+#### Subcommand and Long Option Inference
+
+If the `RABBITMQADMIN_NON_INTERACTIVE_MODE` is not set to `true`, this tool
+now can infer subcommand and --long-option names.
+
+This means that a subcommand can be referenced with its unique prefix,
+that is,
+
+* 'del queue' will be inferred as 'delete queue'
+* 'del q --nam "a.queue"' will be inferred as 'delete queue --name "a.queue"'
+
+To enable each feature, set the following environment variables to
+'true':
+
+* `RABBITMQADMIN_INFER_SUBCOMMANDS`
+* `RABBITMQADMIN_INFER_LONG_OPTIONS`
+
+This feature is only meant to be used interactively. For non-interactive
+use, it can be potentially too dangerous to allow.
+
+#### Intentionally Restricted Environment Variable Support
+
+Environment variables have a number of serious downsides compared to a `rabbitmqadmin.conf`
+and the regular `--long-options` on the command line:
+
+1. Non-existent support for value types and validation ("everything is a string")
+2. Subprocess inheritance restrictions that can be very time-consuming to debug
+3. Different syntax for setting them between the classic POSIX-era shells (such as `bash`, `zsh`) and modern ones (such as [`nushell`](https://www.nushell.sh/))
+
+For these reasons and others, `rabbitmqadmin` v2 intentionally uses the configuration file and the
+CLI options over the environment variables.
+
+`rabbitmqadmin` v2 does, however, supports a number of environment variables for a few
+global settings that cannot be configured any other way (besides a CLI option),
+or truly represent an environment characteristic, e.g. either the non-interactive mode
+should be enabled.
+
+These environment variables are as follows:
+
+| Environment variable | Type | When used | Description |
+|--------------------------------------|---------------------------------------------------|---------------------------------------|--------------------------------------------------------------|
+| `RABBITMQADMIN_CONFIG_FILE_PATH` | Local filesystem path | Pre-flight (before command execution) | Same meaning as the global `--confg-file` argument |
+| `RABBITMQADMIN_NON_INTERACTIVE_MODE` | Boolean | Command execution | Enables the non-interactive mode.
Same meaning as the global `--non-interactive` argument |
+| `RABBITMQADMIN_QUIET_MODE` | Boolean | Command execution | Instructs the tool to produce less output.
Must be followed by `drop_empty_policies` to strip off the policies whose definition has become empty (and thus invalid at import time) after the removal of all classic queue mirroring-related keys |
+| `drop_empty_policies` | Should be used after `strip_cmq_keys_from_policies` to strip off the policies whose definition has become empty (and thus invalid at import time) after the removal of all classic queue mirroring-related keys |
+| `obfuscate_usernames` | Replaces usernames and passwords with dummy values.
For usernames the values used are: `obfuscated-username-1`, `obfuscated-username-2`, and so on.
For passwords the values generated are: `password-1`, `password-2`, and so forth.
This transformations updates both the users and the permissions sections, consistently |
+| `exclude_users` | Removes all users from the result. Commonly used together with `exclude_permissions` |
+| `exclude_permissions` | Removes all permissions from the result. Commonly used together with `exclude_users` |
+| `exclude_runtime_parameters` | Removes all runtime parameters (including federation upstreams, shovels, WSR and SDS settings in Tanzu RabbitMQ) from the result |
+| `exclude_policies` | Removes all policies from the result |
+| `no_op` | Does nothing. Can be used as the default in dynamically computed transformation lists, e.g. in scripts |
+
+#### Examples
+
+The following command applies two transformations named `strip_cmq_keys_from_policies` and `drop_empty_policies`
+that will strip all classic queue mirroring-related policy keys that RabbitMQ 3.13 nodes supported,
+then removes the policies that did not have any keys left (ended up having an empty definition):
+
+```shell
+# strips classic mirrored queue-related policy keys from the exported definitions, then prints them
+# to the standard output stream
+rabbitmqadmin definitions export --stdout --transformations strip_cmq_keys_from_policies,drop_empty_policies
+```
+
+The following example exports definitions without users and permissions:
+
+```shell
+# removes users and user permissions from the exported definitions, then prints them
+# to the standard output stream
+rabbitmqadmin definitions export --stdout --transformations exclude_users,exclude_permissions
+```
+
+To export definitions with usernames replaced by dummy values (usernames: `obfuscated-username-1`, `obfuscated-username-2`, and so on;
+passwords: `password-1`, `password-2`, and so forth), use the `obfuscate_usernames` transformation:
+
+```shell
+rabbitmqadmin definitions export --file /path/to/definitions.file.json --transformations obfuscate_usernames
+```
+
+### Declare a Policy
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies declare \
+ --name "policy-name-1" \
+ --pattern '^cq.1\..+' \
+ --apply-to "queues" \
+ --priority 10 \
+ --definition '{"max-length": 1000000}'
+```
+
+### Delete a Policy
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies delete --name "policy-name-1"
+```
+
+### List All Policies
+
+```shell
+rabbitmqadmin policies list
+```
+
+### List Policies in A Virtual Host
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies list_in
+```
+
+### List Policies Matching an Object
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies list_matching_object --name "cq.1" --type "classic_queue"
+
+rabbitmqadmin --vhost "vh-1" policies list_matching_object --name "qq.1" --type "quorum_queue"
+
+rabbitmqadmin --vhost "vh-1" policies list_matching_object --name "topics.events" --type "exchange"
+```
+
+### Patch (Perform a Partial Update on) a Policy
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies patch \
+ --name "policy-name-1" \
+ --definition '{"max-length": 7777777, "max-length-bytes": 3333333333}'
+```
+
+### Remove One Or More Policy Definition Keys
+
+```shell
+rabbitmqadmin policies delete_definition_keys \
+ --name "policy-name-2" \
+ --definition-keys max-length-bytes,max-length
+```
+
+### Declare an [Override Policy](https://www.rabbitmq.com/docs/policies#override)
+
+[Override policies](https://www.rabbitmq.com/docs/policies#override) are temporarily declared
+policies that match the same objects as an existing policy but have a higher priority
+and a slightly different definition.
+
+This is a potentially safer alternative to patching policies, say, during [Blue-Green deployment migrations](https://www.rabbitmq.com/docs/blue-green-upgrade).
+
+Override policies are meant to be relatively short lived.
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies declare_override \
+ --name "policy-name-1" \
+ --override-name "tmp.overrides.policy-name-1" \
+ --apply-to "queues" \
+ --definition '{"federation-upstream-set": "all"}'
+```
+
+### Declare a [Blanket Policy](https://www.rabbitmq.com/docs/policies#blanket)
+
+A [blanket policy](https://www.rabbitmq.com/docs/policies#blanket) is a policy with a negative priority that
+matches all names. That is, it is a policy that matches everything not matched by other policies (that usually
+will have positive priorities).
+
+Blanket policies are most useful in combination with override policies
+covered above during [Blue-Green deployment migrations](https://www.rabbitmq.com/docs/blue-green-upgrade).
+
+Blanket policies are meant to be relatively short lived.
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies declare_blanket \
+ --name "blanket-queuues" \
+ --apply-to "queues" \
+ --definition '{"federation-upstream-set": "all"}'
+```
+
+
+### Import Definition
+
+To import definitions from the standard input, use `definitions import --stdin`:
+
+```shell
+cat /path/to/definitions.file.json | rabbitmqadmin definitions import --stdin
+```
+
+To import definitions from a file, use `definitions import --file /path/to/definitions.file.json`:
+
+```shell
+rabbitmqadmin definitions import --file /path/to/definitions.file.json
+```
+
+### Declare an AMQP 0-9-1 Shovel
+
+To declare a [dynamic shovel](https://www.rabbitmq.com/docs/shovel-dynamic) that uses AMQP 0-9-1 for both source and desitnation, use
+`shovel declare_amqp091`:
+
+```shell
+rabbitmqadmin shovel declare_amqp091 --name my-amqp091-shovel \
+ --source-uri amqp://username:s3KrE7@source.hostname:5672 \
+ --destination-uri amqp://username:s3KrE7@source.hostname:5672 \
+ --ack-mode "on-confirm" \
+ --source-queue "src.queue" \
+ --destination-queue "dest.queue" \
+ --predeclared-source false \
+ --predeclared-destination false
+```
+
+### Declare an AMQP 1.0 Shovel
+
+To declare a [dynamic shovel](https://www.rabbitmq.com/docs/shovel-dynamic) that uses AMQP 1.0 for both source and desitnation, use
+`shovel declare_amqp10`.
+
+Note that
+
+1. With AMQP 1.0 shovels, credentials in the URI are mandatory (there are no defaults)
+2. With AMQP 1.0 shovels, the topology must be pre-declared (an equivalent of `--predeclared-source true` and `--predeclared-destination true` for AMQP 0-9-1 shovels)
+2. AMQP 1.0 shovels should use [AMQP 1.0 addresses v2](https://www.rabbitmq.com/docs/amqp#addresses)
+
+```shell
+rabbitmqadmin shovel declare_amqp10 --name my-amqp1.0-shovel \
+ --source-uri "amqp://username:s3KrE7@source.hostname:5672?hostname=vhost:src-vhost" \
+ --destination-uri "amqp://username:s3KrE7@source.hostname:5672?hostname=vhost:dest-vhost" \
+ --ack-mode "on-confirm" \
+ --source-address "/queues/src.queue" \
+ --destination-address "/queues/dest.queue"
+```
+
+### List Shovels
+
+To list shovels across all virtual hosts, use `shovel list_all`:
+
+```shell
+rabbitmqadmin shovel list_all
+```
+
+### Delete a Shovel
+
+To delete a shovel, use `shovel delete --name`:
+
+```shell
+rabbitmqadmin shovel delete --name my-amqp091-shovel
+```
+
+### List Federation Upstreams
+
+To list [federation upstreams](https://www.rabbitmq.com/docs/federation) across all virtual hosts, use `federation list_all_upstreams`:
+
+```shell
+rabbitmqadmin federation list_all_upstreams
+```
+
+### Create a Federation Upstream for Exchange Federation
+
+To create a [federation upstream](https://www.rabbitmq.com/docs/federated-exchanges), use `federation declare_upstream_for_exchanges`.
+This command provides a reduced set of options, only those that are relevant
+specifically to exchange federation.
+
+```shell
+rabbitmqadmin --vhost "local-vhost" federation declare_upstream_for_exchanges --name "pollux" \
+ --uri "amqp://pollux.eng.megacorp.local:5672/remote-vhost" \
+ --ack-mode 'on-publish' \
+ --prefetch-count 2000 \
+ --exchange-name "overridden.name" \
+ --queue-type quorum \
+ --bind-using-nowait true
+```
+
+### Create a Federation Upstream for Queue Federation
+
+To create a [federation upstream](https://www.rabbitmq.com/docs/federated-queues), use `declare_upstream_for_queues`.
+This command provides a reduced set of options, only those that are relevant
+specifically to queue federation.
+
+```shell
+rabbitmqadmin --vhost "local-vhost" federation declare_upstream_for_queues --name "clusters.sirius" \
+ --uri "amqp://sirius.eng.megacorp.local:5672/remote-vhost" \
+ --ack-mode 'on-publish' \
+ --prefetch-count 2000 \
+ --queue-name "overridden.name" \
+ --consumer-tag "overriden.ctag"
+```
+
+### Create a Universal Federation Upstream
+
+To create a [federation upstream](https://www.rabbitmq.com/docs/federation) that will be (or can be)
+used for federating both queues and exchanges, use `declare_upstream`. It combines
+[all the federation options](https://www.rabbitmq.com/docs/federation-reference), that is,
+the options of both `declare_upstream_for_queues` and `declare_upstream_for_exchanges`.
+
+```shell
+rabbitmqadmin --vhost "local-vhost" federation declare_upstream --name "pollux" \
+ --uri "amqp://pollux.eng.megacorp.local:5672/remove-vhost" \
+ --ack-mode 'on-publish' \
+ --prefetch-count 2000 \
+ --queue-name "overridden.name" \
+ --consumer-tag "overriden.ctag" \
+ --exchange-name "overridden.name" \
+ --queue-type quorum \
+ --bind-using-nowait true
+```
+
+### Delete a Federation Upstream
+
+To delete a [federation upstream](https://www.rabbitmq.com/docs/federation), use 'federation delete_upstream',
+which takes a virtual host and an upstream name:
+
+```shell
+rabbitmqadmin --vhost "local-vhost" federation delete_upstream --name "upstream.to.delete"
+```
+
+### List Federation Links
+
+To list all [federation links](https://www.rabbitmq.com/docs/federation) across all virtual hosts, use `federation list_all_links`:
+
+```shell
+rabbitmqadmin federation list_all_links
+```
+
+### Create a User
+
+```shell
+# Salt and hash a cleartext password value, and output the resultign hash.
+# See https://www.rabbitmq.com/docs/passwords to learn more.
+rabbitmqadmin passwords salt_and_hash "cleartext value"
+```
+
+```shell
+rabbitmqadmin users declare --name "new-user" --password "secure-password" --tags "monitoring,management"
+```
+
+```shell
+# Create user with administrator tag using pre-hashed password
+# (use 'rabbitmqadmin passwords salt_and_hash' to generate the hash)
+rabbitmqadmin users declare --name "admin-user" --password-hash "{value produced by 'rabbitmqadmin passwords salt_and_hash'}" --tags "administrator"
+```
+
+```shell
+# If RabbitMQ nodes are configured to use SHA512 for passwords, add `--hashing-algorithm`.
+# See https://www.rabbitmq.com/docs/passwords to learn more.
+rabbitmqadmin users declare --name "secure-user" --password-hash "{SHA512-hashed-password}" --hashing-algorithm "SHA512" --tags "monitoring"
+```
+
+### Delete a User
+
+```shell
+rabbitmqadmin users delete --name "user-to-delete"
+```
+
+```shell
+# Idempotent deletion (won't fail if user doesn't exist)
+rabbitmqadmin users delete --name "user-to-delete" --idempotently
+```
+
+### Grant Permissions to a User
+
+```shell
+rabbitmqadmin users permissions --name "app-user" --configure ".*" --write ".*" --read ".*"
+```
+
+```shell
+rabbitmqadmin --vhost "production" users permissions --name "app-user" --configure "^amq\.gen.*|^aliveness-test$" --write ".*" --read ".*"
+```
+
+### Create a Binding
+
+```shell
+rabbitmqadmin --vhost "events" bindings declare --source "events.topic" --destination-type "queue" --destination "events.processing" --routing-key "user.created"
+```
+
+```shell
+rabbitmqadmin --vhost "events" bindings declare --source "events.fanout" --destination-type "exchange" --destination "events.archived" --routing-key "" --arguments '{"x-match": "all"}'
+```
+
+### Delete a Binding
+
+```shell
+rabbitmqadmin --vhost "events" bindings delete --source "events.topic" --destination-type "queue" --destination "events.processing" --routing-key "user.created"
+```
+
+### List Connections
+
+```shell
+rabbitmqadmin connections list
+```
+
+```shell
+# List connections for a specific user
+rabbitmqadmin connections list --user "app-user"
+```
+
+### Close Connections
+
+```shell
+# Close a specific connection by name
+rabbitmqadmin connections close --name "connection-name"
+```
+
+```shell
+# Close all connections from a specific user
+rabbitmqadmin connections close --user "problem-user" --reason "Maintenance window"
+```
+
+### List Channels
+
+```shell
+rabbitmqadmin channels list
+```
+
+```shell
+# List channels in a specific virtual host
+rabbitmqadmin --vhost "production" channels list
+```
+
+### Run Health Checks
+
+```shell
+# Check for local alarms
+rabbitmqadmin health_check local_alarms
+```
+
+```shell
+# Check for cluster-wide alarms
+rabbitmqadmin health_check cluster_wide_alarms
+```
+
+```shell
+# Check if node is quorum critical
+rabbitmqadmin health_check node_is_quorum_critical
+```
+
+```shell
+# Check for deprecated features in use
+rabbitmqadmin health_check deprecated_features_in_use
+```
+
+```shell
+# Check if a port listener is running
+rabbitmqadmin health_check port_listener --port 5672
+```
+
+```shell
+# Check if a protocol listener is running
+rabbitmqadmin health_check protocol_listener --protocol "amqp"
+```
+
+### Set Runtime Parameters
+
+```shell
+rabbitmqadmin --vhost "events" parameters declare --component "federation-upstream" --name "upstream-1" --value '{"uri": "amqp://remote-server", "ack-mode": "on-publish"}'
+```
+
+```shell
+rabbitmqadmin parameters delete --component "federation-upstream" --name "upstream-1"
+```
+
+### Set Global Parameters
+
+```shell
+rabbitmqadmin global_parameters declare --name "cluster_name" --value '"production-cluster"'
+```
+
+```shell
+rabbitmqadmin global_parameters delete --name "cluster_name"
+```
+
+### Declare Operator Policies
+
+```shell
+rabbitmqadmin --vhost "production" operator_policies declare --name "ha-policy" --pattern "^ha\." --definition '{"ha-mode": "exactly", "ha-params": 3}' --priority 1 --apply-to "queues"
+```
+
+### List Operator Policies
+
+```shell
+rabbitmqadmin operator_policies list
+```
+
+### Delete Operator Policies
+
+```shell
+rabbitmqadmin --vhost "production" operator_policies delete --name "ha-policy"
+```
+
+### Manage Passwords
+
+```shell
+# Change user password
+rabbitmqadmin passwords change --name "app-user" --new-password "new-secure-password"
+```
+
+### Rebalance Quorum Queue Leaders
+
+```shell
+# Rebalances leader members (replicas) for all quorum queue
+rabbitmqadmin rebalance all
+```
+
+### Stream Operations
+
+```shell
+# List streams
+rabbitmqadmin streams list
+```
+
+```shell
+# Declare a stream
+rabbitmqadmin --vhost "logs" streams declare --name "application.logs" --max-age "7d" --max-length-bytes "10GB"
+```
+
+```shell
+# Delete a stream
+rabbitmqadmin --vhost "logs" streams delete --name "old.stream"
+```
+
+### Node Operations
+
+```shell
+# List cluster nodes
+rabbitmqadmin nodes list
+```
+
+```shell
+# Show node information
+rabbitmqadmin nodes show --name "rabbit@server1"
+```
+
+
+## Subcommand and Long Option Inference
+
+This feature is available only in the `main` branch
+at the moment.
+
+If the `RABBITMQADMIN_NON_INTERACTIVE_MODE` is not set to `true`, this tool
+now can infer subcommand and --long-option names.
+
+This means that a subcommand can be referenced with its unique prefix,
+that is,
+
+* 'del queue' will be inferred as 'delete queue'
+* 'del q --nam "a.queue"' will be inferred as 'delete queue --name "a.queue"'
+
+To enable each feature, set the following environment variables to
+'true':
+
+* `RABBITMQADMIN_INFER_SUBCOMMANDS`
+* `RABBITMQADMIN_INFER_LONG_OPTIONS`
+
+This feature is only meant to be used interactively. For non-interactive
+use, it can be potentially too dangerous to allow.
+
## Configuration Files
`rabbitmqadmin` v2 supports [TOML](https://toml.io/en/)-based configuration files
-stores groups of HTTP API connection settings under aliases ("node names" in original `rabbitmqadmin` speak).
+stores groups of HTTP API connection settings under aliases ("node names" in original `rabbitmqadmin` speak).
Here is an example `rabbitmqadmin` v2 configuration file:
@@ -510,12 +1083,48 @@ the original version of `rabbitmqadmin`. It can be overridden on the command lin
rabbitmqadmin --config $HOME/.configuration/rabbitmqadmin.conf --node staging show churn
```
+## Intentionally Restricted Environment Variable Support
+
+Environment variables have a number of serious downsides compared to a `rabbitmqadmin.conf`
+and the regular `--long-options` on the command line:
+
+1. Non-existent support for value types and validation ("everything is a string")
+2. Subprocess inheritance restrictions that can be very time-consuming to debug
+3. Different syntax for setting them between the classic POSIX-era shells (such as `bash`, `zsh`) and modern ones (such as [`nushell`](https://www.nushell.sh/))
+
+For these reasons and others, `rabbitmqadmin` v2 intentionally uses the configuration file and the
+CLI options over the environment variables.
+
+`rabbitmqadmin` v2 does, however, supports a number of environment variables for a few
+global settings that cannot be configured any other way (besides a CLI option),
+or truly represent an environment characteristic, e.g. either the non-interactive mode
+should be enabled.
+
+These environment variables are as follows:
+
+| Environment variable | Type | When used | Description |
+|--------------------------------------|---------------------------------------------------|---------------------------------------|--------------------------------------------------------------|
+| `RABBITMQADMIN_CONFIG_FILE_PATH` | Local filesystem path | Pre-flight (before command execution) | Same meaning as the global `--confg-file` argument |
+| `RABBITMQADMIN_NON_INTERACTIVE_MODE` | Boolean | Command execution | Enables the non-interactive mode.
Same meaning as the global `--non-interactive` argument |
+| `RABBITMQADMIN_QUIET_MODE` | Boolean | Command execution | Instructs the tool to produce less output.
Same meaning as the global `--quiet` argument |
+| `RABBITMQADMIN_NODE_ALIAS` | String | Command execution | Same meaning as the global `--node` argument |
+| `RABBITMQADMIN_TARGET_HOST` | String | Command execution | Same meaning as the global `--host` argument |
+| `RABBITMQADMIN_TARGET_PORT` | Positive integer | Command execution | Same meaning as the global `--port` argument |
+| `RABBITMQADMIN_API_PATH_PREFIX` | String | Command execution | Same meaning as the global `--path-prefix` argument |
+| `RABBITMQADMIN_TARGET_VHOST` | String | Command execution | Same meaning as the global `--vhost` argument |
+| `RABBITMQADMIN_BASE_URI` | String | Command execution | Same meaning as the global `--base-uri` argument |
+| `RABBITMQADMIN_USE_TLS` | Boolean | Command execution | Same meaning as the global `--tls` argument |
+| `RABBITMQADMIN_USERNAME` | String | Command execution | Same meaning as the global `--username` argument |
+| `RABBITMQADMIN_PASSWORD` | String | Command execution | Same meaning as the global `--password` argument |
+| `RABBITMQADMIN_TABLE_STYLE` | Enum, see `--table-style` in `rabbitmqadmin help` | Command execution | Same meaning as the global `--table-style` argument |
+
+
## Project Goals Compared to `rabbitmqadmin` v1
This version of `rabbitmqadmin` has a few ideas in mind:
-* This is a major version bump. Therefore, reasonable breaking changes are OK. `rabbitmqadmin` hasn't seen a revision in fourteen years
+* This is a major version bump. Therefore, reasonable breaking changes are OK. `rabbitmqadmin` hasn't seen a revision in fifteen years
* Some features in `rabbitmqadmin` v1 arguably should never have been built-ins,
external tools for data processing and [modern shells](https://www.nushell.sh/) can manipulate tabular data
better than `rabbitmqadmin` ever would
@@ -556,7 +1165,7 @@ rabbitmqadmin-v1 --vhost "vh-2" declare queue name="qq.1" type="quorum" durable=
```shell
# Note: --auto-delete
-rabbitmqadmin --vhost "vh-2" declare queue --name "qq.1" --type "quorum" --durable true --auto-delete false
+rabbitmqadmin --vhost "vh-2" declare queue --name "qq.1" --type "quorum" --durable true --auto-delete false
```
### Global Arguments Come First
@@ -605,8 +1214,14 @@ password = "staging-1d20cfbd9d"
[production]
hostname = "(redacted)"
port = 15671
+
username = "user-efe1f4d763f6"
password = "(redacted)"
+
+tls = true
+ca_certificate_bundle_path = "/path/to/ca_certificate.pem"
+client_certificate_file_path = "/path/to/client_certificate.pem"
+client_private_key_file_path = "/path/to/client_key.pem"
```
diff --git a/bin/ci/before_build.sh b/bin/ci/before_build.sh
index 9ceabcd..3ab4af5 100755
--- a/bin/ci/before_build.sh
+++ b/bin/ci/before_build.sh
@@ -22,6 +22,9 @@ $CTL add_vhost /
$CTL add_user guest guest
$CTL set_permissions -p / guest ".*" ".*" ".*"
+cargo -q run '--' vhosts delete_multiple --name-pattern "^rabbitmqadmin" --dry-run --table-style modern
+cargo -q run '--' --non-interactive vhosts delete_multiple --name-pattern "^rabbitmqadmin"
+
$CTL add_vhost "rust/rabbitmqadmin"
$CTL set_permissions -p "rust/rabbitmqadmin" guest ".*" ".*" ".*"
diff --git a/src/cli.rs b/src/cli.rs
index f66e87e..a2e90ae 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -16,19 +16,22 @@ 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, value_parser};
+use clap::{Arg, ArgAction, ArgGroup, Command, crate_name, crate_version, value_parser};
use rabbitmq_http_client::commons::{
- BindingDestinationType, ExchangeType, MessageTransferAcknowledgementMode, PolicyTarget,
- QueueType, SupportedProtocol,
+ BindingDestinationType, ChannelUseMode, ExchangeType, MessageTransferAcknowledgementMode,
+ PolicyTarget, QueueType, SupportedProtocol,
};
+use rabbitmq_http_client::password_hashing::HashingAlgorithm;
use rabbitmq_http_client::requests::FederationResourceCleanupMode;
-pub fn parser() -> Command {
+pub fn parser(pre_flight_settings: PreFlightSettings) -> Command {
let after_help = color_print::cformat!(
r#"
Documentation and Community Resources
+ rabbitmqadmin docs: {}
RabbitMQ docs: {}
GitHub Discussions: {}
Discord server: {}
@@ -36,20 +39,402 @@ pub fn parser() -> Command {
Contribute
On GitHub: {}"#,
+ RABBITMQADMIN_DOC_GUIDE_URL,
RABBITMQ_DOC_GUIDES_URL,
GITHUB_DISCUSSIONS_URL,
DISCORD_SERVER_INVITATION_URL,
GITHUB_REPOSITORY_URL
);
- Command::new("rabbitmqadmin")
- .version(clap::crate_version!())
- .author("RabbitMQ Core Team")
- .about("rabbitmqadmin gen 2")
+ 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 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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#"Doc guides:
+
+ * {}
+ * {}
+ * {}
+ * {}"#,
+ 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 polling. Only suitable for development and test environments."))
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!("Doc guide: {}", 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!(
+ "Doc guide: {}",
+ 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#"Doc guides:
+
+ * {}
+ * {}"#,
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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 (inefficiently) message(s) to a queue or a stream. Only suitable for development and test environments."))
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!("Doc guide: {}", 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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 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!(
+ "Doc guide: {}",
+ 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 = [
+ bindings_group,
+ channels_group,
+ close_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,
+ 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: {}",
- clap::crate_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)
// --config-file
@@ -57,6 +442,7 @@ pub fn parser() -> Command {
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),
)
@@ -67,6 +453,7 @@ pub fn parser() -> Command {
Arg::new("node_alias")
.short('N')
.long("node")
+ .env("RABBITMQADMIN_NODE_ALIAS")
.required(false)
.default_value(DEFAULT_NODE_ALIAS),
)
@@ -75,6 +462,8 @@ pub fn parser() -> Command {
Arg::new("host")
.short('H')
.long("host")
+ .alias("hostname")
+ .env("RABBITMQADMIN_TARGET_HOST")
.help("HTTP API hostname to use when connecting"),
)
.visible_alias("hostname")
@@ -83,16 +472,17 @@ pub fn parser() -> Command {
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))
- .default_value(DEFAULT_PORT_STR),
+ .value_parser(value_parser!(u16)),
)
// --base-uri
.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"]),
@@ -101,6 +491,7 @@ pub fn parser() -> Command {
.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'"),
)
// --vhost
@@ -108,6 +499,10 @@ pub fn parser() -> Command {
Arg::new("vhost")
.short('V')
.long("vhost")
+ // IMPORTANT: this means that subcommands won't be able to override --vhost or -V,
+ // otherwise the parser will panic. MK.
+ .global(true)
+ .env("RABBITMQADMIN_TARGET_VHOST")
.help("target virtual host. Defaults to '/'"),
)
// --username
@@ -115,6 +510,7 @@ pub fn parser() -> Command {
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
@@ -125,6 +521,7 @@ pub fn parser() -> Command {
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"),
)
@@ -143,42 +540,52 @@ pub fn parser() -> Command {
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)
- .requires("tls-ca-cert-file"),
+ .action(ArgAction::SetTrue),
)
// --tls-ca-cert-file
.arg(
- Arg::new("tls-ca-cert-file")
+ Arg::new("ca_certificate_bundle_path")
.long("tls-ca-cert-file")
.required(false)
- .requires("tls")
.help("Local path to a CA certificate file in the PEM format")
.value_parser(value_parser!(PathBuf)),
)
// --tls-cert-file
.arg(
- Arg::new("tls-cert-file")
+ Arg::new("client_certificate_file_path")
.long("tls-cert-file")
.required(false)
- .requires("tls-key-file")
+ .requires("tls")
.help("Local path to a client certificate file in the PEM format")
.value_parser(value_parser!(PathBuf)),
)
// --tls-key-file
.arg(
- Arg::new("tls-key-file")
+ Arg::new("client_private_key_file_path")
.long("tls-key-file")
.required(false)
- .requires("tls-cert-file")
+ .requires("tls")
.help("Local path to a client private key file in the PEM format")
.value_parser(value_parser!(PathBuf)),
)
+ // --timeout
+ .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..)),
+ )
// --quiet
.arg(
Arg::new("quiet")
.short('q')
.long("quiet")
+ .env("RABBITMQADMIN_QUIET_MODE")
.help("produce less output")
.required(false)
.value_parser(value_parser!(bool))
@@ -188,6 +595,8 @@ pub fn parser() -> Command {
.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)
@@ -198,914 +607,1580 @@ pub fn parser() -> Command {
.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)
- .subcommand_value_name("command")
- .subcommands([
- Command::new("show")
- .about("overview")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- MONITORING_GUIDE_URL
- ))
- .subcommand_value_name("summary")
- .subcommands(show_subcommands()),
- Command::new("list")
- .about("lists objects by type")
- .subcommand_value_name("objects")
- .subcommands(list_subcommands()),
- Command::new("declare")
- .about("creates or declares things")
- .subcommand_value_name("object")
- .subcommands(declare_subcommands()),
- Command::new("delete")
- .about("deletes objects")
- .subcommand_value_name("object")
- .subcommands(delete_subcommands()),
- Command::new("purge")
- .about("purges queues")
- .subcommand_value_name("queue")
- .subcommands(purge_subcommands()),
- Command::new("policies")
- .about("operations on policies")
- .subcommand_value_name("policy")
- .subcommands(policies_subcommands()),
- Command::new("health_check")
- .about("runs health checks")
- .subcommand_value_name("check")
- .subcommands(health_check_subcommands())
- .after_long_help(color_print::cformat!(
- r#"Doc guides:
-
- * {}
- * {}"#,
- HEALTH_CHECK_GUIDE_URL,
- DEPRECATED_FEATURE_GUIDE_URL
- )),
- Command::new("close")
- .about("closes connections")
- .subcommand_value_name("connection")
- .subcommands(close_subcommands()),
- Command::new("rebalance")
- .about("rebalances queue leaders")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- QUORUM_QUEUE_GUIDE_URL
- ))
- .subcommand_value_name("queues")
- .subcommands(rebalance_subcommands()),
- Command::new("definitions")
- .about("operations on definitions (everything except for messages: virtual hosts, queues, streams, exchanges, bindings, users, etc)")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- DEFINITION_GUIDE_URL
- ))
- .subcommand_value_name("export")
- .subcommands(definitions_subcommands()),
- Command::new("export")
- .about("see 'definitions export'")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- DEFINITION_GUIDE_URL
- ))
- .subcommand_value_name("definitions")
- .subcommands(export_subcommands()),
- Command::new("import")
- .about("see 'definitions import'")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- DEFINITION_GUIDE_URL
- ))
- .subcommand_value_name("definitions")
- .subcommands(import_subcommands()),
- Command::new("feature_flags")
- .about("operations on feature flags")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- FEATURE_FLAG_GUIDE_URL
- ))
- .subcommand_value_name("feature flag")
- .subcommands(feature_flags_subcommands()),
- Command::new("deprecated_features")
- .about("operations on deprecated features")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- DEPRECATED_FEATURE_GUIDE_URL
- ))
- .subcommand_value_name("deprecated feature")
- .subcommands(deprecated_features_subcommands()),
- Command::new("publish")
- .about(color_print::cstr!("publishes (inefficiently) message(s) to a queue or a stream. Only suitable for development and test environments."))
- .after_long_help(color_print::cformat!("Doc guide: {}", PUBLISHER_GUIDE_URL))
- .subcommand_value_name("message")
- .subcommands(publish_subcommands()),
- Command::new("get")
- .about(color_print::cstr!("fetches message(s) from a queue or stream via polling. Only suitable for development and test environments."))
- .after_long_help(color_print::cformat!("Doc guide: {}", POLLING_CONSUMER_GUIDE_URL))
- .subcommand_value_name("message")
- .subcommands(get_subcommands()),
- Command::new("shovels")
- .about("Operations on shovels")
- .after_long_help(color_print::cformat!("Doc guide: {}", SHOVEL_GUIDE_URL))
- .subcommand_value_name("shovels")
- .subcommands(shovel_subcommands()),
- Command::new("federation")
- .about("Operations on federation upstreams and links")
- .after_long_help(color_print::cformat!(
- r#"Doc guides:
-
- * {}
- * {}
- * {}
- * {}"#,
- FEDERATION_GUIDE_URL,
- FEDERATED_EXCHANGES_GUIDE_URL,
- FEDERATED_QUEUES_GUIDE_URL,
- FEDERATION_REFERENCE_URL
- ))
- .subcommands(federation_subcommands()),
- Command::new("tanzu")
- .about("Tanzu RabbitMQ-specific commands")
- // TODO: documentation link
- .subcommand_value_name("subcommand")
- .subcommands(tanzu_subcommands()),
- ])
-}
-
-fn list_subcommands() -> [Command; 19] {
- [
- Command::new("nodes").long_about("Lists cluster members"),
- Command::new("users").long_about("Lists users in the internal database"),
- Command::new("vhosts")
- .long_about("Lists virtual hosts")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- VIRTUAL_HOST_GUIDE_URL
- )),
- Command::new("permissions")
- .long_about("Lists user permissions")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- ACCESS_CONTROL_GUIDE_URL
- )),
- Command::new("connections")
- .long_about("Lists client connections")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- CONNECTION_GUIDE_URL
- )),
- Command::new("user_connections")
- .arg(
- Arg::new("username")
- .short('u')
- .long("username")
- .required(true)
- .help("Name of the user whose connections to list"),
- )
- .long_about("Lists client connections that authenticated with a specific username")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- CONNECTION_GUIDE_URL
- )),
- Command::new("channels")
- .long_about("Lists AMQP 0-9-1 channels")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- CHANNEL_GUIDE_URL
- )),
- Command::new("queues")
- .long_about("Lists queues and streams")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- QUEUE_GUIDE_URL
- )),
- Command::new("exchanges").long_about("Lists exchanges"),
- Command::new("bindings").long_about("Lists bindings"),
- Command::new("consumers")
- .long_about("Lists consumers")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- CONSUMER_GUIDE_URL
- )),
- 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_long_help(color_print::cformat!(
- "Doc guide: {}",
- RUNTIME_PARAMETER_GUIDE_URL
- )),
- Command::new("policies")
- .long_about("Lists policies")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- POLICY_GUIDE_URL
- )),
- Command::new("operator_policies")
- .long_about("Lists operator policies")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- OPERATOR_POLICY_GUIDE_URL
- )),
- Command::new("vhost_limits")
- .long_about("Lists virtual host (resource) limits")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- VIRTUAL_HOST_GUIDE_URL
- )),
- Command::new("user_limits")
- .arg(
- Arg::new("user")
- .long("user")
- .help("username")
- .required(false),
- )
- .long_about("Lists per-user (resource) limits")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- USER_LIMIT_GUIDE_URL
- )),
- Command::new("feature_flags")
- .long_about("Lists feature flags and their cluster state")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- FEATURE_FLAG_GUIDE_URL
- )),
- Command::new("deprecated_features")
- .long_about("Lists all deprecated features")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- DEPRECATED_FEATURE_GUIDE_URL
- )),
- Command::new("deprecated_features_in_use")
- .long_about("Lists the deprecated features that are in used in the cluster")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- DEPRECATED_FEATURE_GUIDE_URL
- )),
- ]
+ .subcommands(command_groups)
}
-fn declare_subcommands() -> [Command; 12] {
- [
- 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("tags")
- .long("tags")
- .help("a list of comma-separated tags")
- .default_value(""),
- ),
- Command::new("vhost")
- .about("creates a virtual host")
- .after_long_help(color_print::cformat!("Doc guide:: {}", 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)
- .default_value(DEFAULT_QUEUE_TYPE)
- .help(color_print::cformat!("default queue type, one of: classic, quorum, stream"))
- )
- .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?"),
- ),
- Command::new("permissions")
- .about("grants permissions to a user")
- .after_long_help(color_print::cformat!("Doc guide:: {}", ACCESS_CONTROL_GUIDE_URL))
- .arg(
- Arg::new("user")
- .long("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),
- ),
- Command::new("queue")
- .about("declares a queue or a stream")
- .after_long_help(color_print::cformat!("Doc guide:: {}", 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 exchange arguments")
- .required(false)
- .default_value("{}")
- .value_parser(value_parser!(String)),
- ),
- Command::new("stream")
- .about("declares a stream")
- .after_long_help(color_print::cformat!("Doc guide:: {}", 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 exchange arguments")
- .required(false)
- .default_value("{}")
- .value_parser(value_parser!(String)),
- ),
- 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)),
- ),
- 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)),
- ),
- Command::new("parameter").
- about("sets a runtime parameter")
- .after_long_help(color_print::cformat!("Doc guide:: {}", RUNTIME_PARAMETER_GUIDE_URL))
- .arg(
- Arg::new("name")
- .long("name")
- .help("parameter's name")
- .required(true)
- ).arg(
+fn list_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ 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!(
+ "Doc guide: {}",
+ VIRTUAL_HOST_GUIDE_URL
+ ));
+ let vhost_limits_cmd = Command::new("vhost_limits")
+ .long_about("Lists virtual host (resource) limits")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ VIRTUAL_HOST_GUIDE_URL
+ ));
+ let connections_cmd = Command::new("connections")
+ .long_about("Lists client connections")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ CONNECTION_GUIDE_URL
+ ));
+ let channels_cmd = Command::new("channels")
+ .long_about("Lists AMQP 0-9-1 channels")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ CHANNEL_GUIDE_URL
+ ));
+ let queues_cmd = Command::new("queues")
+ .long_about("Lists queues and streams")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ QUEUE_GUIDE_URL
+ ));
+ let exchanges_cmd = Command::new("exchanges").long_about("Lists exchanges");
+ 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!(
+ "Doc guide: {}",
+ CONSUMER_GUIDE_URL
+ ));
+ let parameters_cmd = Command::new("parameters")
+ .arg(
Arg::new("component")
.long("component")
- .help("component (eg. federation)")
- .required(true))
- .arg(
- Arg::new("value")
- .long("value")
- .help("parameter's value")
- .required(true)),
- Command::new("policy")
- .about("creates or updates a policy")
- .after_long_help(color_print::cformat!("Doc guide:: {}", 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),
- ),
- Command::new("operator_policy")
- .about("creates or updates an operator policy")
- .after_long_help(color_print::cformat!("Doc guide:: {}", 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),
- ),
- Command::new("vhost_limit")
- .about("set a vhost limit")
- .after_long_help(color_print::cformat!("Doc guide:: {}", 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),
- ),
- Command::new("user_limit")
- .about("set a user limit")
- .after_long_help(color_print::cformat!("Doc guide:: {}", USER_LIMIT_GUIDE_URL))
- .arg(
- Arg::new("user")
- .long("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),
- )
- ]
-}
-
-fn show_subcommands() -> [Command; 5] {
- let overview_cmd = Command::new("overview")
- .about("displays a 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("for troubleshooting: displays the computed HTTP API endpoint URI");
- let memory_breakdown_in_bytes_cmd = Command::new("memory_breakdown_in_bytes")
- .about("provides a memory footprint breakdown (in bytes) for the target node")
+ .help("component (for example: federation-upstream, vhost-limits)")
+ .required(false),
+ )
+ .long_about("Lists runtime parameters")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ));
+ let policies_cmd = Command::new("policies")
+ .long_about("Lists policies")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ POLICY_GUIDE_URL
+ ));
+ let operator_policies_cmd = Command::new("operator_policies")
+ .long_about("Lists operator policies")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ OPERATOR_POLICY_GUIDE_URL
+ ));
+ let users_cmd = Command::new("users").long_about("Lists users in the internal database");
+ let permissions_cmd = Command::new("permissions")
+ .long_about("Lists user permissions")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ ACCESS_CONTROL_GUIDE_URL
+ ));
+ let user_connections_cmd = Command::new("user_connections")
.arg(
- Arg::new("node")
- .long("node")
- .help("target node, must be a cluster member")
- .required(true),
+ Arg::new("username")
+ .short('u')
+ .long("username")
+ .required(true)
+ .help("Name of the user whose connections should be listed"),
)
- .after_long_help(color_print::cformat!(
- "Doc guide:: {}",
- MEMORY_FOOTPRINT_GUIDE_URL
+ .long_about("Lists client connections that authenticated with a specific username")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ CONNECTION_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")
+ let user_limits_cmd = Command::new("user_limits")
.arg(
- Arg::new("node")
- .long("node")
- .help("target node, must be a cluster member")
- .required(true),
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(false),
)
- .after_long_help(color_print::cformat!(
- "Doc guide:: {}",
- MEMORY_FOOTPRINT_GUIDE_URL
+ .long_about("Lists per-user (resource) limits")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ FEATURE_FLAG_GUIDE_URL
+ ));
+ let deprecated_features_cmd = Command::new("deprecated_features")
+ .long_about("Lists all deprecated features")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ DEPRECATED_FEATURE_GUIDE_URL
));
-
- [
- overview_cmd,
- churn_cmd,
- endpoint_cmd,
- memory_breakdown_in_bytes_cmd,
- memory_breakdown_in_percent_cmd,
- ]
-}
-
-fn delete_subcommands() -> [Command; 13] {
- 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);
-
[
- Command::new("user")
- .about("deletes a user")
- .arg(
- Arg::new("name")
- .long("name")
- .help("username")
- .required(true),
- )
- .arg(idempotently_arg.clone()),
- Command::new("vhost")
- .about("deletes a virtual host")
- .arg(
- Arg::new("name")
- .long("name")
- .help("virtual host")
- .required(true),
- )
- .arg(idempotently_arg.clone()),
- Command::new("permissions")
- .about("revokes user permissions to a given vhost")
- .arg(
- Arg::new("user")
- .long("user")
- .help("username")
- .required(true),
- )
- .arg(idempotently_arg.clone()),
- Command::new("queue")
- .about("deletes a queue")
- .arg(
- Arg::new("name")
- .long("name")
- .help("queue name")
- .required(true),
- )
- .arg(idempotently_arg.clone()),
- Command::new("stream")
- .about("deletes a stream")
- .arg(
- Arg::new("name")
- .long("name")
- .help("stream name")
- .required(true),
- )
- .arg(idempotently_arg.clone()),
- Command::new("exchange")
- .about("deletes an exchange")
- .arg(
- Arg::new("name")
- .long("name")
- .help("exchange name")
- .required(true),
- )
- .arg(idempotently_arg.clone()),
- 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)),
- ),
- 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),
- ),
- Command::new("policy").about("deletes a policy").arg(
- Arg::new("name")
- .long("name")
- .help("policy name")
- .required(true),
- ),
- Command::new("operator_policy")
- .about("deletes an operator policy")
- .arg(
- Arg::new("name")
- .long("name")
- .help("operator policy name")
- .required(true),
- ),
- 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),
- ),
- Command::new("user_limit")
- .about("clears a user limit")
- .arg(
- Arg::new("user")
- .long("user")
- .help("username")
- .required(true),
- )
- .arg(
- Arg::new("name")
- .long("name")
- .help("limit name (eg. max-connections, max-queues)")
- .required(true),
- ),
- Command::new("shovel")
- .about("delete a shovel")
- .arg(idempotently_arg.clone())
- .arg(
- Arg::new("name")
- .long("name")
- .help("shovel name")
- .required(true),
- ),
+ 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 purge_subcommands() -> [Command; 1] {
- [Command::new("queue")
- .long_about("purges (permanently removes unacknowledged messages from) a queue")
+fn declare_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let user_cmd = Command::new("user")
+ .about("Creates a user")
.arg(
Arg::new("name")
.long("name")
- .help("name of the queue to purge")
- .required(true),
- )]
-}
-
-fn policies_subcommands() -> [Command; 5] {
- let declare_cmd = Command::new("declare")
- .about("creates or updates a policy")
- .after_long_help(color_print::cformat!("Doc guide:: {}", POLICY_GUIDE_URL))
- .arg(
- Arg::new("name")
- .long("name")
- .help("policy name")
+ .help("username")
.required(true),
)
.arg(
- Arg::new("pattern")
- .long("pattern")
- .help("the pattern that is used to match entity (queue, stream, exchange) names")
- .required(true),
+ 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("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::new("password")
+ .long("password")
+ .help(color_print::cformat!(
+ "prefer providing a hash, see {}",
+ PASSWORD_GUIDE_URL
+ ))
+ .required(false)
+ .default_value(""),
)
.arg(
- Arg::new("priority")
- .long("priority")
- .help("policy priority (only the policy with the highest priority is effective)")
+ Arg::new("hashing_algorithm")
+ .long("hashing-algorithm")
.required(false)
- .default_value("0"),
+ .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("definition")
- .long("definition")
- .help("policy definition")
- .required(true),
- );
-
- let list_cmd = Command::new("list")
- .long_about("lists policies")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- POLICY_GUIDE_URL
- ));
-
- let delete_cmd = Command::new("delete").about("deletes a policy").arg(
- Arg::new("name")
- .long("name")
- .help("policy name")
- .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)),
+ Arg::new("tags")
+ .long("tags")
+ .help("a list of comma-separated tags")
+ .default_value(""),
);
-
- let list_matching_cmd = Command::new("list_matching_object")
- .about("lists policies that match an object (queue, stream, exchange) name")
+ let vhost_cmd = Command::new("vhost")
+ .about("Creates a virtual host")
+ .after_help(color_print::cformat!("Doc guide:: {}", VIRTUAL_HOST_GUIDE_URL))
.arg(
Arg::new("name")
.long("name")
- .help("name to verify")
+ .help("virtual host name")
.required(true),
)
.arg(
- Arg::new("type")
- .long("type")
- .value_parser(value_parser!(PolicyTarget))
- .required(true)
- .help("target type, one of 'queues', 'streams', 'exchanges'"),
- );
-
- [
- declare_cmd,
+ Arg::new("default_queue_type")
+ .long("default-queue-type")
+ .required(false)
+ .help(color_print::cformat!("default queue type, one of: classic, quorum, stream"))
+ )
+ .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!(
+ "Doc guide:: {}",
+ ACCESS_CONTROL_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("user")
+ .long("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!(
+ "Doc guide:: {}",
+ 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 exchange 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!(
+ "Doc guide:: {}",
+ 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 exchange 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!(
+ "Doc guide:: {}",
+ 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!("Doc guide:: {}", 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!("Doc guide:: {}", 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!(
+ "Doc guide:: {}",
+ 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!(
+ "Doc guide:: {}",
+ USER_LIMIT_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("user")
+ .long("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 {
+ 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!(
+ "Doc guide:: {}",
+ 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!(
+ "Doc guide:: {}",
+ 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 {
+ 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("user")
+ .long("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("user")
+ .long("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 {
+ 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 binding_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ 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 {
+ let declare_cmd = Command::new("declare")
+ .about("Declares a queue or a stream")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ 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 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 a queue")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("queue name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let list_cmd = Command::new("list")
+ .long_about("Lists queues and streams")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ QUEUE_GUIDE_URL
+ ));
+ 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, list_cmd, purge_cmd, rebalance_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn streams_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let declare_cmd = Command::new("declare")
+ .about("Declares a stream")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ 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 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 a queue")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("queue name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let list_cmd = Command::new("list")
+ .long_about("Lists streams and queues and")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ STREAM_GUIDE_URL
+ ));
+ [declare_cmd, delete_cmd, list_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn parameters_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ));
+
+ let set_cmd = Command::new("set")
+ .alias("declare")
+ .about("Sets a runtime parameter")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ 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 {
+ 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!(
+ "Doc guide: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ));
+
+ let set_cmd = Command::new("set")
+ .alias("declare")
+ .about("Sets a global runtime parameter")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ 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 {
+ 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!("Doc guide:: {}", 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!(
+ "Doc guide: {}",
+ 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 {
+ 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!("Doc guide:: {}", 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!("Doc guide:: {}", 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!(
+ "Doc guide: {}",
+ 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_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),
+ );
+
+ [
+ declare_cmd,
+ declare_override_cmd,
+ declare_blanket_cmd,
delete_cmd,
+ delete_definition_keys_cmd,
+ delete_definition_keys_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 health_check_subcommands() -> [Command; 6] {
+fn health_check_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
let node_is_quorum_critical_after_help = color_print::cformat!(
r#"
Doc guides:
@@ -1117,36 +2192,36 @@ fn health_check_subcommands() -> [Command; 6] {
);
let local_alarms = Command::new("local_alarms")
- .about("checks if there are any resource alarms in effect on the target node");
+ .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");
+ .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_long_help(node_is_quorum_critical_after_help);
+ .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_long_help(color_print::cformat!(
+ .about("Fails if there are any deprecated features in use in the cluster")
+ .after_help(color_print::cformat!(
"Doc guide: {}",
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",
+ "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_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
"Doc guide: {}",
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",
+ "Verifies that there's a reachable TCP listener on the given protocol alias on the target node",
)
.arg(
Arg::new("protocol")
@@ -1154,7 +2229,7 @@ fn health_check_subcommands() -> [Command; 6] {
.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_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
"Doc guide: {}",
HEALTH_CHECK_GUIDE_URL
));
@@ -1167,46 +2242,458 @@ fn health_check_subcommands() -> [Command; 6] {
port_listener,
protocol_listener,
]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
}
-fn rebalance_subcommands() -> [Command; 1] {
- [Command::new("queues").about("rebalances queue leaders")]
+fn rebalance_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ 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() -> [Command; 2] {
+fn close_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ 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")
+ .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 {
+ let list_cmd = Command::new("list")
+ .long_about("Lists all channels across all virtual hosts")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ "https://www.rabbitmq.com/docs/channels"
+ ));
+
+ [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 {
+ 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!(
+ "Doc guide: {}",
+ CONNECTION_GUIDE_URL
+ ));
+ 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!(
+ "Doc guide: {}",
+ 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 definitions_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let export_cmd = Command::new("export")
+ .about("Export cluster-wide definitions")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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 {
+ 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("connection name (identifying string)")
+ .help("exchange name")
.required(true),
- );
- let close_user_connections = Command::new("user_connections")
- .about("closes all connections that authenticated with a specific username")
+ )
+ .arg(idempotently_arg.clone());
+ let list_cmd = Command::new("list").long_about("Lists exchanges");
+ let unbind_cmd = Command::new("unbind")
+ .about("Deletes a binding")
.arg(
- Arg::new("username")
- .short('u')
- .long("username")
- .help("Name of the user whose connections to close")
+ Arg::new("source")
+ .long("source")
+ .help("source exchange")
.required(true),
- );
- [close_connection, close_user_connections]
+ )
+ .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 definitions_subcommands() -> [Command; 4] {
- let export_cmd = Command::new("export")
- .about("export cluster-wide definitions")
- .after_long_help(color_print::cformat!(
+fn export_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let definitions = Command::new("definitions")
+ .about("Export cluster-wide definitions")
+ .after_help(color_print::cformat!(
"Doc guide: {}",
DEFINITION_GUIDE_URL
))
.arg(
Arg::new("file")
+ .group("output")
.long("file")
- .help("output file path or '-' for standard output")
+ .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)
- .default_value("-"),
+ .num_args(0)
+ .action(ArgAction::SetTrue)
+ .conflicts_with("file"),
)
.arg(
Arg::new("transformations")
@@ -1219,6 +2706,7 @@ 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
@@ -1229,6 +2717,7 @@ Supported transformations:
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
@@ -1241,162 +2730,566 @@ Examples:
.action(ArgAction::Append)
.required(false),
);
+ [definitions]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
- let export_from_vhost_cmd = Command::new("export_from_vhost")
- .about("export definitions of a specific virtual host")
- .after_long_help(color_print::cformat!(
+fn import_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ [Command::new("definitions")
+ .about("Prefer 'definitions import'")
+ .after_help(color_print::cformat!(
"Doc guide: {}",
DEFINITION_GUIDE_URL
))
.arg(
- Arg::new("file")
- .long("file")
- .help("output file path or '-' for standard output")
+ 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 {
+ let list_cmd = Command::new("list")
+ .long_about("Lists feature flags and their cluster state")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ FEATURE_FLAG_GUIDE_URL
+ ));
+
+ let enable_cmd = Command::new("enable")
+ .long_about("Enables a feature flag")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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 {
+ let list_cmd = Command::new("list")
+ .long_about("Lists deprecated features")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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 {
+ let list_all_cmd = Command::new("list_all")
+ .about("Lists plugins across all cluster nodes")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ 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 {
+ let list_cmd = Command::new("list")
+ .long_about("Lists cluster nodes")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide:: {}",
+ 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!(
+ "Doc guide:: {}",
+ 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 {
+ let list_cmd = Command::new("list")
+ .long_about("Lists virtual hosts")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ VIRTUAL_HOST_GUIDE_URL
+ ));
+
+ let declare_cmd = Command::new("declare")
+ .about("Creates a virtual host")
+ .after_help(color_print::cformat!("Doc guide:: {}", 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: classic, quorum, stream"))
+ )
+ .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!("DANGER ZONE. Deletes multiple virtual hosts at once using a name matching pattern"))
+ .after_help(color_print::cformat!("Doc guide:: {}", 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!(
+ "Doc guide:: {}",
+ 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!(
+ "Doc guide:: {}",
+ 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 {
+ 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");
+ let permissions_cmd = Command::new("permissions")
+ .long_about("Lists user permissions")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ 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!(
+ "Doc guide: {}",
+ CONNECTION_GUIDE_URL
+ ));
+ let limits_cmd = Command::new("limits")
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(false),
+ )
+ .long_about("Lists per-user (resource) limits")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ 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 {
+ 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)
- .default_value("-"),
+ .value_parser(value_parser!(HashingAlgorithm))
+ .default_value("SHA256")
+ .help("The hashing algorithm to use: SHA256 or SHA512"),
);
- let import_cmd = Command::new("import")
- .about("import cluster-wide definitions (of multiple virtual hosts)")
- .after_long_help(color_print::cformat!(
+ [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 {
+ 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!(
"Doc guide: {}",
- DEFINITION_GUIDE_URL
+ ACCESS_CONTROL_GUIDE_URL
+ ));
+
+ let declare_cmd = Command::new("declare")
+ .about("grants permissions to a user")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ ACCESS_CONTROL_GUIDE_URL
))
.arg(
- Arg::new("file")
- .long("file")
- .help("JSON file with cluster-wide definitions")
+ Arg::new("user")
+ .long("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 import_into_vhost_cmd = Command::new("import_into_vhost")
- .about("import a virtual host-specific definitions file into a virtual host")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- DEFINITION_GUIDE_URL
- ))
+ let delete_cmd = Command::new("delete")
+ .about("Revokes user permissions to a given vhost")
.arg(
- Arg::new("file")
- .long("file")
- .help("JSON file with virtual host-specific definitions")
+ Arg::new("user")
+ .long("user")
+ .help("username")
.required(true),
- );
+ )
+ .arg(idempotently_arg.clone());
- [
- export_cmd,
- export_from_vhost_cmd,
- import_cmd,
- import_into_vhost_cmd,
- ]
+ [list_cmd, declare_cmd, delete_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
}
-fn export_subcommands() -> [Command; 1] {
- let definitions = Command::new("definitions")
- .about("prefer 'definitions export'")
- .after_long_help(color_print::cformat!(
+pub fn user_limits_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let list_cmd = Command::new("list")
+ .long_about("Lists per-user (resource) limits")
+ .after_help(color_print::cformat!(
"Doc guide: {}",
- DEFINITION_GUIDE_URL
+ USER_LIMIT_GUIDE_URL
))
.arg(
- Arg::new("file")
- .long("file")
- .help("output path")
- .required(false)
- .default_value("-"),
- )
- .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:
-
- * strip_cmq_keys_from_policies
- * drop_empty_policies
-
-Example use: --transformations strip_cmq_keys_from_policies,drop_empty_policies
- "#,
- )
- .num_args(1..)
- .value_delimiter(',')
- .action(ArgAction::Append)
+ Arg::new("user")
+ .long("user")
+ .help("username")
.required(false),
);
- [definitions]
-}
-fn import_subcommands() -> [Command; 1] {
- [Command::new("definitions")
- .about("prefer 'definitions import'")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- DEFINITION_GUIDE_URL
+ let declare_cmd = Command::new("declare")
+ .about("Set a user limit")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ USER_LIMIT_GUIDE_URL
))
.arg(
- Arg::new("file")
- .long("file")
- .help("JSON file with definitions")
+ Arg::new("user")
+ .long("user")
+ .help("username")
.required(true),
- )]
-}
-
-pub fn feature_flags_subcommands() -> [Command; 3] {
- let list_cmd = Command::new("list")
- .long_about("lists feature flags and their cluster state")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- FEATURE_FLAG_GUIDE_URL
- ));
-
- let enable_cmd = Command::new("enable")
- .long_about("enables a feature flag")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- FEATURE_FLAG_GUIDE_URL
- ))
+ )
.arg(
Arg::new("name")
.long("name")
- .help("feature flag name (identifier)")
+ .help("limit name (eg. max-connections, max-queues)")
+ .required(true),
+ )
+ .arg(
+ Arg::new("value")
+ .long("value")
+ .help("limit value")
.required(true),
);
- let enable_all_cmd = Command::new("enable_all")
- .long_about("enables all stable feature flags")
- .after_long_help(color_print::cformat!(
- "Doc guide: {}",
- FEATURE_FLAG_GUIDE_URL
- ));
+ let delete_cmd = Command::new("delete")
+ .about("Clears a user limit")
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(true),
+ )
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("limit name (eg. max-connections, max-queues)")
+ .required(true),
+ );
- [list_cmd, enable_cmd, enable_all_cmd]
+ [list_cmd, declare_cmd, delete_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
}
-pub fn deprecated_features_subcommands() -> [Command; 2] {
+pub fn vhost_limits_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
let list_cmd = Command::new("list")
- .long_about("lists deprecated features")
- .after_long_help(color_print::cformat!(
+ .long_about("Lists virtual host (resource) limits")
+ .after_help(color_print::cformat!(
"Doc guide: {}",
- DEPRECATED_FEATURE_GUIDE_URL
+ VIRTUAL_HOST_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_long_help(color_print::cformat!(
- "Doc guide: {}",
- DEPRECATED_FEATURE_GUIDE_URL
- ));
+ let declare_cmd = Command::new("declare")
+ .about("Set a vhost limit")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ 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),
+ );
- [list_cmd, list_in_use_cmd]
+ 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() -> [Command; 1] {
+pub fn publish_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
[Command::new("message")
- .about("publishes a message to an exchange")
- .about(color_print::cstr!("publishes (inefficiently) message(s) to a queue or a stream. Only suitable for development and test environments. Prefer messaging or streaming protocol clients!"))
- .after_long_help(color_print::cformat!("Doc guide: {}", PUBLISHER_GUIDE_URL))
+ .about(color_print::cstr!("Publishes (inefficiently) message(s) to a queue or a stream. Only suitable for development and test environments. Prefer messaging or streaming protocol clients!"))
+ .after_help(color_print::cformat!("Doc guide: {}", PUBLISHER_GUIDE_URL))
.arg(
Arg::new("routing_key")
.short('k')
@@ -1429,12 +3322,15 @@ pub fn publish_subcommands() -> [Command; 1] {
.default_value("{}")
.help("Message properties"),
)]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
}
-pub fn get_subcommands() -> [Command; 1] {
+pub fn get_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
[Command::new("messages")
.about(color_print::cstr!("Fetches (via polling, very inefficiently) message(s) from a queue. Only suitable for development and test environments"))
- .after_long_help(color_print::cformat!("Doc guide: {}", POLLING_CONSUMER_GUIDE_URL))
+ .after_help(color_print::cformat!("Doc guide: {}", POLLING_CONSUMER_GUIDE_URL))
.arg(
Arg::new("queue")
.short('q')
@@ -1458,12 +3354,29 @@ pub fn get_subcommands() -> [Command; 1] {
.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() -> [Command; 4] {
+pub fn shovel_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ 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_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ SHOVEL_GUIDE_URL
+ ));
+
+ let list_cmd = Command::new("list")
+ .long_about("Lists shovels in a specific virtual host")
+ .after_help(color_print::cformat!(
"Doc guide: {}",
SHOVEL_GUIDE_URL
));
@@ -1472,7 +3385,7 @@ pub fn shovel_subcommands() -> [Command; 4] {
.long_about(
"Declares a dynamic shovel that uses AMQP 0-9-1 for both source and destination",
)
- .after_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
"Doc guide: {}",
SHOVEL_GUIDE_URL
))
@@ -1544,7 +3457,7 @@ pub fn shovel_subcommands() -> [Command; 4] {
Arg::new("reconnect_delay")
.long("reconnect-delay")
.default_value("5")
- .value_parser(value_parser!(u16)),
+ .value_parser(value_parser!(u32)),
)
.group(
ArgGroup::new("destination")
@@ -1562,7 +3475,7 @@ pub fn shovel_subcommands() -> [Command; 4] {
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_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
"Doc guide: {}",
SHOVEL_GUIDE_URL
))
@@ -1586,12 +3499,12 @@ pub fn shovel_subcommands() -> [Command; 4] {
Arg::new("reconnect_delay")
.long("reconnect-delay")
.default_value("5")
- .value_parser(value_parser!(u16)),
+ .value_parser(value_parser!(u32)),
);
let delete_cmd = Command::new("delete")
.long_about("Deletes a dynamic shovel")
- .after_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
"Doc guide: {}",
SHOVEL_GUIDE_URL
))
@@ -1600,15 +3513,136 @@ pub fn shovel_subcommands() -> [Command; 4] {
.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")
+ // shorter, displayed in the shovels group's help
+ .about(color_print::cstr!("Use only in case of emergency. Disables TLS peer verification for all shovels."))
+ // longer, displayed in the command's help
+ .long_about(color_print::cstr!("Use only in case of emergency. Disables TLS peer verification for all shovels by updating their source and destination URIs' 'verify' parameter."))
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ 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!("Use only in case of emergency. Disables TLS peer verification for all shovel destination URIs."))
+ .long_about(color_print::cstr!("Use only in case of emergency. Disables TLS peer verification for all shovel destination URIs by updating their 'verify' parameter."))
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ 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#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ 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#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ SHOVEL_GUIDE_URL,
+ TLS_GUIDE_URL,
+ "https://www.rabbitmq.com/docs/shovel#tls-connections"
+ ));
- [list_all_cmd, declare_091_cmd, declare_10_cmd, delete_cmd]
+ [
+ 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() -> [Command; 6] {
+fn federation_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ 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_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
r#"Doc guides:
* {}
@@ -1621,7 +3655,7 @@ fn federation_subcommands() -> [Command; 6] {
let declare_upstream = Command::new("declare_upstream")
.long_about("Declares a federation upstream to be used with both exchange and queue federation")
- .after_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
r#"Doc guides:
* {}
@@ -1649,7 +3683,7 @@ fn federation_subcommands() -> [Command; 6] {
Arg::new("reconnect_delay")
.long("reconnect-delay")
.default_value("5")
- .value_parser(value_parser!(u16))
+ .value_parser(value_parser!(u32))
.help("Reconnection delay in seconds")
)
.arg(
@@ -1663,9 +3697,9 @@ fn federation_subcommands() -> [Command; 6] {
Arg::new("prefetch_count")
.long("prefetch-count")
.default_value("1000")
- .value_parser(value_parser!(u16))
+ .value_parser(value_parser!(u32))
.help("The prefetch value to use with internal consumers")
- .value_parser(value_parser!(u16))
+ .value_parser(value_parser!(u32))
)
.arg(
Arg::new("ack_mode")
@@ -1715,6 +3749,12 @@ fn federation_subcommands() -> [Command; 6] {
.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")
@@ -1730,7 +3770,7 @@ fn federation_subcommands() -> [Command; 6] {
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_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
r#"Doc guides:
* {}
@@ -1756,7 +3796,7 @@ fn federation_subcommands() -> [Command; 6] {
Arg::new("reconnect_delay")
.long("reconnect-delay")
.default_value("5")
- .value_parser(value_parser!(u16))
+ .value_parser(value_parser!(u32))
.help("Reconnection delay in seconds")
)
.arg(
@@ -1770,9 +3810,8 @@ fn federation_subcommands() -> [Command; 6] {
Arg::new("prefetch_count")
.long("prefetch-count")
.default_value("1000")
- .value_parser(value_parser!(u16))
+ .value_parser(value_parser!(u32))
.help("The prefetch value to use with internal consumers")
- .value_parser(value_parser!(u16))
)
.arg(
Arg::new("ack_mode")
@@ -1781,6 +3820,18 @@ fn federation_subcommands() -> [Command; 6] {
.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")
@@ -1795,7 +3846,7 @@ fn federation_subcommands() -> [Command; 6] {
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_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
r#"Doc guides:
* {}
@@ -1821,7 +3872,7 @@ fn federation_subcommands() -> [Command; 6] {
Arg::new("reconnect_delay")
.long("reconnect-delay")
.default_value("5")
- .value_parser(value_parser!(u16))
+ .value_parser(value_parser!(u32))
.help("Reconnection delay in seconds")
)
.arg(
@@ -1835,9 +3886,8 @@ fn federation_subcommands() -> [Command; 6] {
Arg::new("prefetch_count")
.long("prefetch-count")
.default_value("1000")
- .value_parser(value_parser!(u16))
+ .value_parser(value_parser!(u32))
.help("The prefetch value to use with internal consumers")
- .value_parser(value_parser!(u16))
)
.arg(
Arg::new("ack_mode")
@@ -1870,6 +3920,12 @@ fn federation_subcommands() -> [Command; 6] {
.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")
@@ -1891,7 +3947,7 @@ fn federation_subcommands() -> [Command; 6] {
let delete_upstream = Command::new("delete_upstream")
.long_about("Declares a federation upstream")
- .after_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
"Doc guide: {}",
FEDERATION_GUIDE_URL
))
@@ -1900,11 +3956,12 @@ fn federation_subcommands() -> [Command; 6] {
.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_long_help(color_print::cformat!(
+ .after_help(color_print::cformat!(
r#"Doc guides:
* {}
@@ -1917,6 +3974,58 @@ fn federation_subcommands() -> [Command; 6] {
FEDERATION_REFERENCE_URL
));
+ let disable_tls_peer_verification_cmd = Command::new("disable_tls_peer_verification_for_all_upstreams")
+ // shorter, displayed in the federation group's help
+ .about(color_print::cstr!("Use only in case of emergency. Disables TLS peer verification for all federation upstreams."))
+ // longer, displayed in the command's help
+ .long_about(color_print::cstr!("Use only in case of emergency. Disables TLS peer verification for all federation upstreams by updating their 'verify' parameter."))
+
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ 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#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ FEDERATION_GUIDE_URL,
+ TLS_GUIDE_URL,
+ "https://www.rabbitmq.com/docs/federation#tls-connections"
+ ));
+
[
list_all_upstreams,
declare_upstream,
@@ -1924,5 +4033,10 @@ fn federation_subcommands() -> [Command; 6] {
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()
}
diff --git a/src/commands.rs b/src/commands.rs
index a8db825..877d5f5 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -13,29 +13,42 @@
// limitations under the License.
#![allow(clippy::result_large_err)]
+use crate::constants::{DEFAULT_BLANKET_POLICY_PRIORITY, DEFAULT_VHOST};
+use crate::errors::CommandRunError;
+use crate::output::ProgressReporter;
+use crate::pre_flight;
use clap::ArgMatches;
use rabbitmq_http_client::blocking_api::Client;
use rabbitmq_http_client::blocking_api::Result as ClientResult;
use rabbitmq_http_client::commons;
+use rabbitmq_http_client::commons::QueueType;
+use rabbitmq_http_client::commons::{
+ BindingDestinationType, ChannelUseMode, TlsPeerVerificationMode,
+};
use rabbitmq_http_client::commons::{ExchangeType, SupportedProtocol};
use rabbitmq_http_client::commons::{MessageTransferAcknowledgementMode, UserLimitTarget};
use rabbitmq_http_client::commons::{PolicyTarget, VirtualHostLimitTarget};
+use rabbitmq_http_client::password_hashing::{HashingAlgorithm, HashingError};
+use rabbitmq_http_client::requests::shovels::OwnedShovelParams;
use rabbitmq_http_client::requests::{
Amqp10ShovelDestinationParams, Amqp10ShovelParams, Amqp10ShovelSourceParams,
Amqp091ShovelDestinationParams, Amqp091ShovelParams, Amqp091ShovelSourceParams,
- EnforcedLimitParams, ExchangeFederationParams, FEDERATION_UPSTREAM_COMPONENT,
- FederationResourceCleanupMode, FederationUpstreamParams, QueueFederationParams,
- RuntimeParameterDefinition,
+ BindingDeletionParams, DEFAULT_FEDERATION_PREFETCH, EnforcedLimitParams,
+ ExchangeFederationParams, FEDERATION_UPSTREAM_COMPONENT, FederationResourceCleanupMode,
+ FederationUpstreamParams, PolicyParams, QueueFederationParams, RuntimeParameterDefinition,
};
-use std::fs;
-use std::process;
-use rabbitmq_http_client::commons::BindingDestinationType;
-use rabbitmq_http_client::commons::QueueType;
-use rabbitmq_http_client::transformers::TransformationChain;
+use rabbitmq_http_client::transformers::{TransformationChain, VirtualHostTransformationChain};
use rabbitmq_http_client::{password_hashing, requests, responses};
+use regex::Regex;
+use serde::de::DeserializeOwned;
+use serde_json::Value;
+use std::fs;
+use std::io;
+use std::process;
+use tabled::Tabled;
-type APIClient<'a> = Client<&'a str, &'a str, &'a str>;
+type APIClient = Client;
pub fn show_overview(client: APIClient) -> ClientResult {
client.overview()
@@ -44,7 +57,7 @@ pub fn show_overview(client: APIClient) -> ClientResult {
pub fn show_memory_breakdown(
client: APIClient,
command_args: &ArgMatches,
-) -> ClientResult {
+) -> ClientResult