Repository: rabbitmq/rabbitmq-server (language: javascript)

Issue:
Introduce definition file type markers
This is a continuation to https://github.com/rabbitmq/rabbitmq-server/issues/10078.

Per discussions of https://github.com/rabbitmq/rabbitmq-server/pull/12821 and #10078, several members of the core team concluded that our definition files need a "type" (single virtual host vs. cluster-wide export) metadata field.

It then can be used for filtering out single virtual host files at definition boot, with a sensible error message.


Discussion / hints:

Fix patch:
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/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).


Test patch:
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/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);
+    }
   })
 })