diff --git a/.github/workflows/check-build-system-equivalence-release-branches.yaml b/.github/workflows/check-build-system-equivalence-release-branches.yaml index 5b3d5f15a197..4b69e03bb3b6 100644 --- a/.github/workflows/check-build-system-equivalence-release-branches.yaml +++ b/.github/workflows/check-build-system-equivalence-release-branches.yaml @@ -9,7 +9,7 @@ jobs: with: ref: refs/heads/main erlang_version: 26.2 - elixir_version: 1.15 + elixir_version: 1.17 project_version: 4.0.0 check-v4_0_x: @@ -17,7 +17,7 @@ jobs: with: ref: refs/heads/main erlang_version: 26.2 - elixir_version: 1.15 + elixir_version: 1.17 project_version: 4.0.0 check-v3_13_x: @@ -25,7 +25,7 @@ jobs: with: ref: refs/heads/v3.13.x erlang_version: 26.2 - elixir_version: 1.15 + elixir_version: 1.17 project_version: 3.13.0 check-v3_12_x: @@ -33,5 +33,5 @@ jobs: with: ref: refs/heads/v3.12.x erlang_version: 26.1 - elixir_version: 1.15 + elixir_version: 1.17 project_version: 3.12.0 diff --git a/.github/workflows/check-build-system-equivalence.yaml b/.github/workflows/check-build-system-equivalence.yaml index bcc4c16ac800..d79d8297340f 100644 --- a/.github/workflows/check-build-system-equivalence.yaml +++ b/.github/workflows/check-build-system-equivalence.yaml @@ -23,7 +23,7 @@ on: elixir_version: description: 'Elixir version to build with' required: true - default: "1.15" + default: "1.17" project_version: description: 'PROJECT_VERSION used for make' required: true diff --git a/.github/workflows/test-management-ui-for-pr.yaml b/.github/workflows/test-management-ui-for-pr.yaml index 5a80d78f13c8..76c0181a14f2 100644 --- a/.github/workflows/test-management-ui-for-pr.yaml +++ b/.github/workflows/test-management-ui-for-pr.yaml @@ -20,7 +20,7 @@ jobs: - chrome include: - erlang_version: "26.2" - elixir_version: 1.15.7 + elixir_version: 1.17 env: SELENIUM_DIR: selenium DOCKER_NETWORK: rabbitmq_net diff --git a/.github/workflows/test-plugin-mixed.yaml b/.github/workflows/test-plugin-mixed.yaml index ab42665841b1..416db6f0745f 100644 --- a/.github/workflows/test-plugin-mixed.yaml +++ b/.github/workflows/test-plugin-mixed.yaml @@ -35,7 +35,7 @@ jobs: # - khepri include: - erlang_version: 26 - elixir_version: 1.15 + elixir_version: 1.17 timeout-minutes: 120 steps: - name: LOAD REPO CACHE diff --git a/.github/workflows/test-plugin.yaml b/.github/workflows/test-plugin.yaml index 9726df808c23..3c163ba059a3 100644 --- a/.github/workflows/test-plugin.yaml +++ b/.github/workflows/test-plugin.yaml @@ -32,7 +32,7 @@ jobs: - khepri include: - erlang_version: 26 - elixir_version: 1.15 + elixir_version: 1.17 timeout-minutes: 120 steps: - name: LOAD REPO CACHE diff --git a/deps/rabbit/src/rabbit_definitions.erl b/deps/rabbit/src/rabbit_definitions.erl index 9ebc5e074f63..c698c2ed64fe 100644 --- a/deps/rabbit/src/rabbit_definitions.erl +++ b/deps/rabbit/src/rabbit_definitions.erl @@ -1071,10 +1071,13 @@ list_vhosts() -> [vhost_definition(V) || V <- rabbit_vhost:all()]. vhost_definition(VHost) -> + Name = vhost:get_name(VHost), + DQT = rabbit_queue_type:short_alias_of(rabbit_vhost:default_queue_type(Name)), #{ - <<"name">> => vhost:get_name(VHost), + <<"name">> => Name, <<"limits">> => vhost:get_limits(VHost), - <<"metadata">> => vhost:get_metadata(VHost) + <<"metadata">> => vhost:get_metadata(VHost), + <<"default_queue_type">> => DQT }. list_users() -> diff --git a/deps/rabbit/src/rabbit_queue_type.erl b/deps/rabbit/src/rabbit_queue_type.erl index 938588da6662..207b1c6a5634 100644 --- a/deps/rabbit/src/rabbit_queue_type.erl +++ b/deps/rabbit/src/rabbit_queue_type.erl @@ -11,6 +11,7 @@ -behaviour(rabbit_registry_class). -include("amqqueue.hrl"). +-include("vhost.hrl"). -include_lib("rabbit_common/include/rabbit.hrl"). -include_lib("amqp10_common/include/amqp10_types.hrl"). @@ -22,7 +23,10 @@ feature_flag_name/1, to_binary/1, default/0, + default_alias/0, fallback/0, + inject_dqt/1, + vhosts_with_dqt/1, is_enabled/1, is_compatible/4, declare/2, @@ -317,6 +321,15 @@ short_alias_of(rabbit_stream_queue) -> %% AMQP 1.0 management client short_alias_of({utf8, <<"stream">>}) -> <<"stream">>; +%% for cases where this function is used for +%% formatting of values that already might use these +%% short aliases +short_alias_of(<<"quorum">>) -> + <<"quorum">>; +short_alias_of(<<"classic">>) -> + <<"classic">>; +short_alias_of(<<"stream">>) -> + <<"stream">>; short_alias_of(_Other) -> undefined. @@ -343,6 +356,10 @@ default() -> fallback()), rabbit_data_coercion:to_atom(V). +-spec default_alias() -> binary(). +default_alias() -> + short_alias_of(default()). + -spec to_binary(module()) -> binary(). to_binary(rabbit_classic_queue) -> <<"classic">>; @@ -833,6 +850,29 @@ known_queue_type_names() -> QTypeBins = lists:map(fun(X) -> atom_to_binary(X) end, QueueTypes), ?KNOWN_QUEUE_TYPES ++ QTypeBins. +inject_dqt(VHost) when ?is_vhost(VHost) -> + inject_dqt(vhost:to_map(VHost)); +inject_dqt(VHost) when is_list(VHost) -> + inject_dqt(rabbit_data_coercion:to_map(VHost)); +inject_dqt(M = #{default_queue_type := undefined}) -> + NQT = short_alias_of(default()), + Meta0 = maps:get(metadata, M, #{}), + Meta = Meta0#{default_queue_type => NQT}, + + M#{default_queue_type => NQT, metadata => Meta}; +inject_dqt(M = #{default_queue_type := DQT}) -> + NQT = short_alias_of(DQT), + Meta0 = maps:get(metadata, M, #{}), + Meta = Meta0#{default_queue_type => NQT}, + + M#{default_queue_type => NQT, metadata => Meta}. + +-spec vhosts_with_dqt([any()]) -> [map()]. +vhosts_with_dqt(List) when is_list(List) -> + %% inject DQT (default queue type) at the top level and + %% its metadata + lists:map(fun inject_dqt/1, List). + -spec check_queue_limits(amqqueue:amqqueue()) -> ok | {error, queue_limit_exceeded, Reason :: string(), Args :: term()}. diff --git a/deps/rabbit/src/rabbit_vhost.erl b/deps/rabbit/src/rabbit_vhost.erl index 00c148e275ea..29edac91bdd0 100644 --- a/deps/rabbit/src/rabbit_vhost.erl +++ b/deps/rabbit/src/rabbit_vhost.erl @@ -511,13 +511,14 @@ default_queue_type(VirtualHost) -> default_queue_type(VirtualHost, rabbit_queue_type:fallback()). -spec default_queue_type(VirtualHost :: vhost:name(), Fallback :: rabbit_queue_type:queue_type()) -> rabbit_queue_type:queue_type(). default_queue_type(VirtualHost, FallbackQueueType) -> + NodeDefault = application:get_env(rabbit, default_queue_type, FallbackQueueType), case exists(VirtualHost) of - false -> FallbackQueueType; + false -> NodeDefault; true -> Record = lookup(VirtualHost), case vhost:get_default_queue_type(Record) of - undefined -> FallbackQueueType; - <<"undefined">> -> FallbackQueueType; + undefined -> NodeDefault; + <<"undefined">> -> NodeDefault; Type -> Type end end. @@ -622,8 +623,19 @@ i(tracing, VHost) -> rabbit_trace:enabled(vhost:get_name(VHost)); i(cluster_state, VHost) -> vhost_cluster_state(vhost:get_name(VHost)); i(description, VHost) -> vhost:get_description(VHost); i(tags, VHost) -> vhost:get_tags(VHost); -i(default_queue_type, VHost) -> vhost:get_default_queue_type(VHost); -i(metadata, VHost) -> vhost:get_metadata(VHost); +i(default_queue_type, VHost) -> rabbit_queue_type:short_alias_of(default_queue_type(vhost:get_name(VHost))); +i(metadata, VHost) -> + DQT = rabbit_queue_type:short_alias_of(default_queue_type(vhost:get_name(VHost))), + case vhost:get_metadata(VHost) of + undefined -> + #{default_queue_type => DQT}; + M = #{default_queue_type := undefined} -> + M#{default_queue_type => DQT}; + M = #{default_queue_type := QT} -> + M#{default_queue_type => rabbit_queue_type:short_alias_of(QT)}; + M when is_map(M) -> + M#{default_queue_type => DQT} + end; i(Item, VHost) -> rabbit_log:error("Don't know how to compute a virtual host info item '~ts' for virtual host '~tp'", [Item, VHost]), throw({bad_argument, Item}). diff --git a/deps/rabbit/src/vhost.erl b/deps/rabbit/src/vhost.erl index bece0b49c957..34eb726a3a56 100644 --- a/deps/rabbit/src/vhost.erl +++ b/deps/rabbit/src/vhost.erl @@ -26,11 +26,14 @@ get_description/1, get_tags/1, get_default_queue_type/1, + set_limits/2, set_metadata/2, merge_metadata/2, new_metadata/3, - is_tagged_with/2 + is_tagged_with/2, + + to_map/1 ]). -define(record_version, vhost_v2). @@ -196,3 +199,13 @@ new_metadata(Description, Tags, DefaultQueueType) -> -spec is_tagged_with(vhost(), tag()) -> boolean(). is_tagged_with(VHost, Tag) -> lists:member(Tag, get_tags(VHost)). + +-spec to_map(vhost()) -> map(). +to_map(VHost) -> + #{ + name => get_name(VHost), + description => get_description(VHost), + tags => get_tags(VHost), + default_queue_type => get_default_queue_type(VHost), + metadata => get_metadata(VHost) + }. \ No newline at end of file diff --git a/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/export_definitions_command.ex b/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/export_definitions_command.ex index cc21e2a0a46d..8c83b9b87df0 100644 --- a/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/export_definitions_command.ex +++ b/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/export_definitions_command.ex @@ -2,7 +2,7 @@ ## License, v. 2.0. If a copy of the MPL was not distributed with this ## file, You can obtain one at https://mozilla.org/MPL/2.0/. ## -## Copyright (c) 2007-2023 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +## Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. defmodule RabbitMQ.CLI.Ctl.Commands.ExportDefinitionsCommand do alias RabbitMQ.CLI.Core.{DocGuide, ExitCodes, Helpers} diff --git a/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/list_vhosts_command.ex b/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/list_vhosts_command.ex index c9b4222e4d89..6ad98132c3e3 100644 --- a/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/list_vhosts_command.ex +++ b/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/list_vhosts_command.ex @@ -19,6 +19,8 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListVhostsCommand do use RabbitMQ.CLI.Core.AcceptsDefaultSwitchesAndTimeout def merge_defaults([], opts) do + # this default historically benefits those who script using 'rabbitmqctl list_vhosts', + # adding more fields here would break scripts but be more useful to a human reader. MK. merge_defaults(["name"], opts) end diff --git a/deps/rabbitmq_ct_helpers/src/rabbit_ct_broker_helpers.erl b/deps/rabbitmq_ct_helpers/src/rabbit_ct_broker_helpers.erl index f3d556595802..8c96d3910400 100644 --- a/deps/rabbitmq_ct_helpers/src/rabbit_ct_broker_helpers.erl +++ b/deps/rabbitmq_ct_helpers/src/rabbit_ct_broker_helpers.erl @@ -111,6 +111,7 @@ add_vhost/2, add_vhost/3, add_vhost/4, + update_vhost_metadata/3, delete_vhost/2, delete_vhost/3, delete_vhost/4, @@ -1541,6 +1542,9 @@ add_vhost(Config, Node, VHost) -> add_vhost(Config, Node, VHost, Username) -> catch rpc(Config, Node, rabbit_vhost, add, [VHost, Username]). +update_vhost_metadata(Config, VHost, Meta) -> + catch rpc(Config, 0, rabbit_vhost, update_metadata, [VHost, Meta, <<"acting-user">>]). + delete_vhost(Config, VHost) -> delete_vhost(Config, 0, VHost). diff --git a/deps/rabbitmq_management/BUILD.bazel b/deps/rabbitmq_management/BUILD.bazel index 6b560bb7059e..2f053bb609a7 100644 --- a/deps/rabbitmq_management/BUILD.bazel +++ b/deps/rabbitmq_management/BUILD.bazel @@ -167,7 +167,7 @@ rabbitmq_integration_suite( additional_beam = [ "test/rabbit_mgmt_runtime_parameters_util.beam", ], - shard_count = 6, + shard_count = 7, runtime_deps = [ "//deps/amqp10_client:erlang_app", ], diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_definitions.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_definitions.erl index 3790ca97b90c..a614bf16e930 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_definitions.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_definitions.erl @@ -58,30 +58,39 @@ all_definitions(ReqData, Context) -> Vsn = rabbit:base_product_version(), ProductName = rabbit:product_name(), ProductVersion = rabbit:product_version(), - rabbit_mgmt_util:reply( - [{rabbit_version, rabbit_data_coercion:to_binary(Vsn)}, - {rabbitmq_version, rabbit_data_coercion:to_binary(Vsn)}, - {product_name, rabbit_data_coercion:to_binary(ProductName)}, - {product_version, rabbit_data_coercion:to_binary(ProductVersion)}] ++ - retain_whitelisted( - [{users, rabbit_mgmt_wm_users:users(all)}, - {vhosts, rabbit_mgmt_wm_vhosts:basic()}, - {permissions, rabbit_mgmt_wm_permissions:permissions()}, - {topic_permissions, rabbit_mgmt_wm_topic_permissions:topic_permissions()}, - {parameters, rabbit_mgmt_wm_parameters:basic(ReqData)}, - {global_parameters, rabbit_mgmt_wm_global_parameters:basic()}, - {policies, rabbit_mgmt_wm_policies:basic(ReqData)}, - {queues, Qs}, - {exchanges, Xs}, - {bindings, Bs}]), - case rabbit_mgmt_util:qs_val(<<"download">>, ReqData) of - undefined -> ReqData; - Filename -> rabbit_mgmt_util:set_resp_header( - <<"Content-Disposition">>, - "attachment; filename=" ++ - binary_to_list(Filename), ReqData) - end, - Context). + + Contents = [ + {users, rabbit_mgmt_wm_users:users(all)}, + {vhosts, rabbit_mgmt_wm_vhosts:basic()}, + {permissions, rabbit_mgmt_wm_permissions:permissions()}, + {topic_permissions, rabbit_mgmt_wm_topic_permissions:topic_permissions()}, + {parameters, rabbit_mgmt_wm_parameters:basic(ReqData)}, + {global_parameters, rabbit_mgmt_wm_global_parameters:basic()}, + {policies, rabbit_mgmt_wm_policies:basic(ReqData)}, + {queues, Qs}, + {exchanges, Xs}, + {bindings, Bs} + ], + + TopLevelDefsAndMetadata = [ + {rabbit_version, rabbit_data_coercion:to_binary(Vsn)}, + {rabbitmq_version, rabbit_data_coercion:to_binary(Vsn)}, + {product_name, rabbit_data_coercion:to_binary(ProductName)}, + {product_version, rabbit_data_coercion:to_binary(ProductVersion)}, + {rabbitmq_definition_format, <<"cluster">>}, + {original_cluster_name, rabbit_nodes:cluster_name()}, + {explanation, rabbit_data_coercion:to_binary(io_lib:format("Definitions of cluster '~ts'", [rabbit_nodes:cluster_name()]))} + ], + Result = TopLevelDefsAndMetadata ++ retain_whitelisted(Contents), + ReqData1 = case rabbit_mgmt_util:qs_val(<<"download">>, ReqData) of + undefined -> ReqData; + Filename -> rabbit_mgmt_util:set_resp_header( + <<"Content-Disposition">>, + "attachment; filename=" ++ + binary_to_list(Filename), ReqData) + end, + + rabbit_mgmt_util:reply(Result, ReqData1, Context). accept_json(ReqData0, Context) -> BodySizeLimit = application:get_env(rabbitmq_management, max_http_body_size, ?MANAGEMENT_DEFAULT_HTTP_MAX_BODY_SIZE), @@ -94,7 +103,10 @@ accept_json(ReqData0, Context) -> accept(Body, ReqData, Context) end. -vhost_definitions(ReqData, VHost, Context) -> +vhost_definitions(ReqData, VHostName, Context) -> + %% the existence of this virtual host is verified in the called, 'to_json/2' + VHost = rabbit_vhost:lookup(VHostName), + %% rabbit_mgmt_wm_<>:basic/1 filters by VHost if it is available. %% TODO: should we stop stripping virtual host? Such files cannot be imported on boot, for example. Xs = [strip_vhost(X) || X <- rabbit_mgmt_wm_exchanges:basic(ReqData), @@ -105,25 +117,48 @@ vhost_definitions(ReqData, VHost, Context) -> %% TODO: should we stop stripping virtual host? Such files cannot be imported on boot, for example. Bs = [strip_vhost(B) || B <- rabbit_mgmt_wm_bindings:basic(ReqData), export_binding(B, QNames)], - {ok, Vsn} = application:get_key(rabbit, vsn), Parameters = [strip_vhost( rabbit_mgmt_format:parameter(P)) - || P <- rabbit_runtime_parameters:list(VHost)], - rabbit_mgmt_util:reply( - [{rabbit_version, rabbit_data_coercion:to_binary(Vsn)}] ++ - retain_whitelisted( - [{parameters, Parameters}, - {policies, [strip_vhost(P) || P <- rabbit_mgmt_wm_policies:basic(ReqData)]}, - {queues, Qs}, - {exchanges, Xs}, - {bindings, Bs}]), - case rabbit_mgmt_util:qs_val(<<"download">>, ReqData) of - undefined -> ReqData; - Filename -> - HeaderVal = "attachment; filename=" ++ binary_to_list(Filename), - rabbit_mgmt_util:set_resp_header(<<"Content-Disposition">>, HeaderVal, ReqData) - end, - Context). + || P <- rabbit_runtime_parameters:list(VHostName)], + Contents = [ + {parameters, Parameters}, + {policies, [strip_vhost(P) || P <- rabbit_mgmt_wm_policies:basic(ReqData)]}, + {queues, Qs}, + {exchanges, Xs}, + {bindings, Bs} + ], + + Vsn = rabbit:base_product_version(), + ProductName = rabbit:product_name(), + ProductVersion = rabbit:product_version(), + + DQT = rabbit_queue_type:short_alias_of(rabbit_vhost:default_queue_type(VHostName)), + %% note: the type changes to a map + VHost1 = rabbit_queue_type:inject_dqt(VHost), + Metadata = maps:get(metadata, VHost1), + + TopLevelDefsAndMetadata = [ + {rabbit_version, rabbit_data_coercion:to_binary(Vsn)}, + {rabbitmq_version, rabbit_data_coercion:to_binary(Vsn)}, + {product_name, rabbit_data_coercion:to_binary(ProductName)}, + {product_version, rabbit_data_coercion:to_binary(ProductVersion)}, + {rabbitmq_definition_format, <<"single_virtual_host">>}, + {original_vhost_name, VHostName}, + {explanation, rabbit_data_coercion:to_binary(io_lib:format("Definitions of virtual host '~ts'", [VHostName]))}, + {metadata, Metadata}, + {description, vhost:get_description(VHost)}, + {default_queue_type, DQT}, + {limits, vhost:get_limits(VHost)} + ], + Result = TopLevelDefsAndMetadata ++ retain_whitelisted(Contents), + + ReqData1 = case rabbit_mgmt_util:qs_val(<<"download">>, ReqData) of + undefined -> ReqData; + Filename -> + HeaderVal = "attachment; filename=" ++ binary_to_list(Filename), + rabbit_mgmt_util:set_resp_header(<<"Content-Disposition">>, HeaderVal, ReqData) + end, + rabbit_mgmt_util:reply(Result, ReqData1, Context). accept_multipart(ReqData0, Context) -> {Parts, ReqData} = get_all_parts(ReqData0), @@ -271,7 +306,12 @@ retain_whitelisted(Items) -> retain_whitelisted_items(Name, List, Allowed) -> {Name, [only_whitelisted_for_item(I, Allowed) || I <- List]}. -only_whitelisted_for_item(Item, Allowed) -> +only_whitelisted_for_item(Item, Allowed) when is_map(Item) -> + Map1 = maps:with(Allowed, Item), + maps:filter(fun(_Key, Val) -> + Val =/= undefined + end, Map1); +only_whitelisted_for_item(Item, Allowed) when is_list(Item) -> [{K, Fact} || {K, Fact} <- Item, lists:member(K, Allowed), Fact =/= undefined]. strip_vhost(Item) -> diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_overview.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_overview.erl index 5af3fbdd133f..1b2c5a4b92ad 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_overview.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_overview.erl @@ -52,6 +52,7 @@ to_json(ReqData, Context = #context{user = User = #user{tags = Tags}}) -> {erlang_version, erlang_version()}, {erlang_full_version, erlang_full_version()}, {disable_stats, rabbit_mgmt_util:disable_stats(ReqData)}, + {default_queue_type, rabbit_queue_type:default_alias()}, {is_op_policy_updating_enabled, not rabbit_mgmt_features:is_op_policy_updating_disabled()}, {enable_queue_totals, rabbit_mgmt_util:enable_queue_totals(ReqData)}], try diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_vhosts.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_vhosts.erl index a0779a2af36a..17b1947ea8f8 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_vhosts.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_vhosts.erl @@ -31,9 +31,12 @@ to_json(ReqData, Context = #context{user = User}) -> try Basic = [rabbit_vhost:info(V) || V <- rabbit_mgmt_util:list_visible_vhosts(User)], - Data = rabbit_mgmt_util:augment_resources(Basic, ?DEFAULT_SORT, - ?BASIC_COLUMNS, ReqData, - Context, fun augment/2), + Augmented = rabbit_mgmt_util:augment_resources(Basic, ?DEFAULT_SORT, + ?BASIC_COLUMNS, ReqData, + Context, fun augment/2), + %% inject default DQT into virtual host metadata, + %% where necessary + Data = rabbit_queue_type:vhosts_with_dqt(Augmented), rabbit_mgmt_util:reply(Data, ReqData, Context) catch {error, invalid_range_parameters, Reason} -> @@ -64,4 +67,7 @@ augmented(ReqData, #context{user = User}) -> end. basic() -> - rabbit_vhost:info_all([name, description, tags, default_queue_type, metadata]). + Maps = lists:map( + fun maps:from_list/1, + rabbit_vhost:info_all([name, description, tags, default_queue_type, metadata])), + rabbit_queue_type:vhosts_with_dqt(Maps). diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl index c2230064247b..b63828d8b729 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl @@ -52,7 +52,8 @@ all() -> {group, definitions_group1_without_prefix}, {group, definitions_group2_without_prefix}, {group, definitions_group3_without_prefix}, - {group, definitions_group4_without_prefix} + {group, definitions_group4_without_prefix}, + {group, default_queue_type_without_prefix} ]. groups() -> @@ -66,7 +67,8 @@ groups() -> {definitions_group1_without_prefix, [], definitions_group1_tests()}, {definitions_group2_without_prefix, [], definitions_group2_tests()}, {definitions_group3_without_prefix, [], definitions_group3_tests()}, - {definitions_group4_without_prefix, [], definitions_group4_tests()} + {definitions_group4_without_prefix, [], definitions_group4_tests()}, + {default_queue_type_without_prefix, [], default_queue_type_group_tests()} ]. some_tests() -> @@ -90,7 +92,8 @@ definitions_group1_tests() -> definitions_group2_tests() -> [ definitions_default_queue_type_test, - definitions_vhost_metadata_test + definitions_vhost_metadata_test, + definitions_file_metadata_test ]. definitions_group3_tests() -> @@ -104,6 +107,14 @@ definitions_group4_tests() -> definitions_vhost_test ]. +default_queue_type_group_tests() -> + [ + default_queue_type_fallback_in_overview_test, + default_queue_type_with_value_configured_in_overview_test, + default_queue_types_in_vhost_list_test, + default_queue_type_of_one_vhost_test + ]. + all_tests() -> [ cli_redirect_test, api_redirect_test, @@ -200,7 +211,7 @@ all_tests() -> [ disabled_qq_replica_opers_test, qq_status_test, list_deprecated_features_test, - list_used_deprecated_features_test, + list_used_deprecated_features_test, cluster_and_node_tags_test, version_test ]. @@ -245,11 +256,19 @@ init_per_suite(Config) -> end_per_suite(Config) -> Config. -init_per_group(all_tests_with_prefix=Group, Config0) -> +init_per_group(Group, Config0) when Group == all_tests_with_prefix -> PathConfig = {rabbitmq_management, [{path_prefix, ?PATH_PREFIX}]}, Config1 = rabbit_ct_helpers:merge_app_env(Config0, PathConfig), Config2 = finish_init(Group, Config1), start_broker(Config2); +init_per_group(Group, Config0) when Group == default_queue_type_group_tests -> + case rabbit_ct_helpers:is_mixed_versions() of + true -> + {skip, "not mixed versions compatible"}; + _ -> + Config1 = finish_init(Group, Config0), + start_broker(Config1) + end; init_per_group(Group, Config0) -> Config1 = finish_init(Group, Config0), start_broker(Config1). @@ -298,6 +317,13 @@ init_per_testcase(queues_detailed_test, Config) -> true -> Config; false -> {skip, "The detailed queues endpoint is not available."} end; +init_per_testcase(Testcase, Config) when Testcase == definitions_file_metadata_test -> + case rabbit_ct_helpers:is_mixed_versions() of + true -> + {skip, "not mixed versions compatible"}; + _ -> + rabbit_ct_helpers:testcase_started(Config, Testcase) + end; init_per_testcase(Testcase, Config) -> rabbit_ct_broker_helpers:close_all_connections(Config, 0, <<"rabbit_mgmt_SUITE:init_per_testcase">>), rabbit_ct_helpers:testcase_started(Config, Testcase). @@ -545,6 +571,8 @@ vhosts_description_test(Config) -> Expected = #{name => <<"myvhost">>, metadata => #{ description => <<"vhost description">>, + %% this is an injected DQT + default_queue_type => <<"classic">>, tags => [<<"tag1">>, <<"tag2">>] }}, assert_item(Expected, http_get(Config, "/vhosts/myvhost")), @@ -1928,7 +1956,7 @@ long_definitions_vhosts(long_definitions_multipart_test) -> definitions_vhost_metadata_test(Config) -> register_parameters_and_policy_validator(Config), - VHostName = <<"definitions-vhost-metadata-test">>, + VHostName = rabbit_data_coercion:to_binary(?FUNCTION_NAME), Desc = <<"Created by definitions_vhost_metadata_test">>, DQT = <<"quorum">>, Tags = [<<"one">>, <<"tag-two">>], @@ -1939,19 +1967,19 @@ definitions_vhost_metadata_test(Config) -> }, %% Create a test vhost - http_put(Config, "/vhosts/definitions-vhost-metadata-test", Metadata, {group, '2xx'}), + http_put(Config, io_lib:format("/vhosts/~ts", [VHostName]), Metadata, {group, '2xx'}), PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}], - http_put(Config, "/permissions/definitions-vhost-metadata-test/guest", PermArgs, {group, '2xx'}), + http_put(Config, io_lib:format("/permissions/~ts/guest", [VHostName]), PermArgs, {group, '2xx'}), %% Get the definitions Definitions = http_get(Config, "/definitions", ?OK), + ct:pal("Exported definitions:~n~tp~tn", [Definitions]), %% Check if vhost definition is correct VHosts = maps:get(vhosts, Definitions), {value, VH} = lists:search(fun(VH) -> maps:get(name, VH) =:= VHostName end, VHosts), - ct:pal("VHost: ~p", [VH]), ?assertEqual(#{ name => VHostName, description => Desc, @@ -1964,7 +1992,42 @@ definitions_vhost_metadata_test(Config) -> http_post(Config, "/definitions", Definitions, {group, '2xx'}), %% Remove the test vhost - http_delete(Config, "/vhosts/definitions-vhost-metadata-test", {group, '2xx'}), + http_delete(Config, io_lib:format("/vhosts/~ts", [VHostName]), {group, '2xx'}), + ok. + +definitions_file_metadata_test(Config) -> + register_parameters_and_policy_validator(Config), + + VHostName = rabbit_data_coercion:to_binary(?FUNCTION_NAME), + Desc = <<"Created by definitions_vhost_metadata_test">>, + DQT = <<"quorum">>, + Tags = [<<"tag-one">>, <<"tag-two">>], + Metadata = #{ + description => Desc, + default_queue_type => DQT, + tags => Tags + }, + + http_put(Config, io_lib:format("/vhosts/~ts", [VHostName]), Metadata, {group, '2xx'}), + PermArgs = [{configure, <<".*">>}, {write, <<".*">>}, {read, <<".*">>}], + http_put(Config, io_lib:format("/permissions/~ts/guest", [VHostName]), PermArgs, {group, '2xx'}), + + AllDefinitions = http_get(Config, "/definitions", ?OK), + %% verify definitions file metadata + ?assertEqual(<<"cluster">>, maps:get(rabbitmq_definition_format, AllDefinitions)), + ?assert(is_binary(maps:get(original_cluster_name, AllDefinitions))), + + %% Post the definitions back + http_post(Config, "/definitions", AllDefinitions, {group, '2xx'}), + + VHDefinitions = http_get(Config, io_lib:format("/definitions/~ts", [VHostName]), ?OK), + %% verify definitions file metadata + ?assertEqual(<<"single_virtual_host">>, maps:get(rabbitmq_definition_format, VHDefinitions)), + ?assertEqual(VHostName, (maps:get(original_vhost_name, VHDefinitions))), + ?assert(is_map_key(default_queue_type, maps:get(metadata, VHDefinitions))), + + %% Remove the test vhost + http_delete(Config, io_lib:format("/vhosts/~ts", [VHostName]), {group, '2xx'}), ok. definitions_default_queue_type_test(Config) -> @@ -3969,10 +4032,124 @@ cluster_and_node_tags_test(Config) -> ?assertEqual(ExpectedTags, NodeTags), passed. +default_queue_type_fallback_in_overview_test(Config) -> + Overview = http_get(Config, "/overview"), + DQT = maps:get(default_queue_type, Overview), + ?assertEqual(<<"classic">>, DQT). + +default_queue_type_with_value_configured_in_overview_test(Config) -> + Overview = with_default_queue_type(Config, rabbit_quorum_queue, + fun(Cfg) -> + http_get(Cfg, "/overview") + end), + + DQT = maps:get(default_queue_type, Overview), + ?assertEqual(<<"quorum">>, DQT). + +default_queue_types_in_vhost_list_test(Config) -> + TestName = rabbit_data_coercion:to_binary(?FUNCTION_NAME), + VHost1 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 1])), + VHost2 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 2])), + VHost3 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 3])), + VHost4 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 4])), + + VHosts = #{ + VHost1 => undefined, + VHost2 => <<"classic">>, + VHost3 => <<"quorum">>, + VHost4 => <<"stream">> + }, + + try + begin + lists:foreach( + fun({V, QT}) -> + rabbit_ct_broker_helpers:add_vhost(Config, V), + rabbit_ct_broker_helpers:set_full_permissions(Config, V), + rabbit_ct_broker_helpers:update_vhost_metadata(Config, V, #{ + default_queue_type => QT + }) + end, maps:to_list(VHosts)), + + List = http_get(Config, "/vhosts"), + ct:pal("GET /api/vhosts returned: ~tp", [List]), + VH1 = find_map_by_name(VHost1, List), + ?assertEqual(<<"classic">>, maps:get(default_queue_type, VH1)), + + VH2 = find_map_by_name(VHost2, List), + ?assertEqual(<<"classic">>, maps:get(default_queue_type, VH2)), + + VH3 = find_map_by_name(VHost3, List), + ?assertEqual(<<"quorum">>, maps:get(default_queue_type, VH3)), + + VH4 = find_map_by_name(VHost4, List), + ?assertEqual(<<"stream">>, maps:get(default_queue_type, VH4)) + end + after + lists:foreach( + fun(V) -> + rabbit_ct_broker_helpers:delete_vhost(Config, V) + end, maps:keys(VHosts)) + end. + +default_queue_type_of_one_vhost_test(Config) -> + TestName = rabbit_data_coercion:to_binary(?FUNCTION_NAME), + VHost1 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 1])), + VHost2 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 2])), + VHost3 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 3])), + VHost4 = rabbit_data_coercion:to_binary(io_lib:format("~ts-~b", [TestName, 4])), + + VHosts = #{ + VHost1 => undefined, + VHost2 => <<"classic">>, + VHost3 => <<"quorum">>, + VHost4 => <<"stream">> + }, + + try + begin + lists:foreach( + fun({V, QT}) -> + rabbit_ct_broker_helpers:add_vhost(Config, V), + rabbit_ct_broker_helpers:set_full_permissions(Config, V), + rabbit_ct_broker_helpers:update_vhost_metadata(Config, V, #{ + default_queue_type => QT + }) + end, maps:to_list(VHosts)), + + VH1 = http_get(Config, io_lib:format("/vhosts/~ts", [VHost1])), + ?assertEqual(<<"classic">>, maps:get(default_queue_type, VH1)), + + VH2 = http_get(Config, io_lib:format("/vhosts/~ts", [VHost2])), + ?assertEqual(<<"classic">>, maps:get(default_queue_type, VH2)), + + VH3 = http_get(Config, io_lib:format("/vhosts/~ts", [VHost3])), + ?assertEqual(<<"quorum">>, maps:get(default_queue_type, VH3)), + + VH4 = http_get(Config, io_lib:format("/vhosts/~ts", [VHost4])), + ?assertEqual(<<"stream">>, maps:get(default_queue_type, VH4)) + end + after + lists:foreach( + fun(V) -> + rabbit_ct_broker_helpers:delete_vhost(Config, V) + end, maps:keys(VHosts)) + end. + %% ------------------------------------------------------------------- %% Helpers. %% ------------------------------------------------------------------- +%% Finds a map by its <<"name">> key in an HTTP API response. +-spec find_map_by_name(binary(), [#{binary() => any()}]) -> #{binary() => any()} | undefined. +find_map_by_name(NameToFind, List) -> + case lists:filter(fun(#{name := Name}) -> + Name =:= NameToFind + end, List) of + [] -> undefined; + [Val] -> Val + end. + msg(Key, Headers, Body) -> msg(Key, Headers, Body, <<"string">>). @@ -4065,3 +4242,13 @@ await_condition(Fun) -> false end end, ?COLLECT_INTERVAL * 100). + + +clear_default_queue_type(Config) -> + rpc(Config, application, unset_env, [rabbit, default_queue_type]). + +with_default_queue_type(Config, DQT, Fun) -> + rpc(Config, application, set_env, [rabbit, default_queue_type, DQT]), + ToReturn = Fun(Config), + rpc(Config, application, unset_env, [rabbit, default_queue_type]), + ToReturn. diff --git a/selenium/test/authnz-msg-protocols/amqp10.js b/selenium/test/authnz-msg-protocols/amqp10.js index 9148c00b89b2..076746064724 100644 --- a/selenium/test/authnz-msg-protocols/amqp10.js +++ b/selenium/test/authnz-msg-protocols/amqp10.js @@ -29,10 +29,7 @@ describe('Having AMQP 1.0 protocol enabled and the following auth_backends: ' + let expectations = [] let username = process.env.RABBITMQ_AMQP_USERNAME let password = process.env.RABBITMQ_AMQP_PASSWORD -<<<<<<< HEAD -======= let amqp; ->>>>>>> 0ba194ae53 (Replace java amqp10 with javascript one) before(function () { if (backends.includes("http") && username.includes("http")) { @@ -76,7 +73,7 @@ describe('Having AMQP 1.0 protocol enabled and the following auth_backends: ' + closeAmqp(amqp.connection) } } catch (error) { - console.error("Failed to close amqp10 connection due to " + error); - } + console.error("Failed to close amqp10 connection due to " + error); + } }) })