Allow to disable Classic Mirrored Queues via configuration
PR #7390 introduces deprecated features and their lifecycle management. One of the first applications should be kick-starting the process of Classic Mirrored Queues deprecation. But that would be to big of a change to be backported to any of the current releases, so this commit introduces a simplified version of that deprecation. To disable CMQs one need to add the following line to the config: ``` permit_deprecated_features.classic_mirrored_queues = false ``` What it does when CMQs are disabled via configuration: - Doesn't allow to create user/operator policies that enable mirroring ("ha-mode") - Prevent RabbitMQ startup if such a policy was previously configured Differences from the final implementation that will be using deprecated features system: - No warnings are issued when CMQs are not disabled, but being used - It's not possible to set `permit_deprecated_features` option to the `true` value This differences ensure that one only enables this feature when they are absolutely sure what they are doing, but in a way that won't interfere with a subsequent phased deprecation process.
This commit is contained in:
parent
557c23b753
commit
83f2f34c38
|
@ -2088,6 +2088,32 @@ end}.
|
||||||
{datatype, integer}
|
{datatype, integer}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% Feature flags and deprecated features
|
||||||
|
%% =====================================
|
||||||
|
%%
|
||||||
|
|
||||||
|
%% NOTE: `true` is intentionally omitted - add it back when mirrored
|
||||||
|
%% queue deprecation is converted to use deprecated features system.
|
||||||
|
{mapping,
|
||||||
|
"permit_deprecated_features.$name", "rabbit.permit_deprecated_features",
|
||||||
|
[{datatype, {enum, [false]}}]
|
||||||
|
}.
|
||||||
|
|
||||||
|
%% This converts:
|
||||||
|
%% permit_deprecated_features.my_feature = true
|
||||||
|
%% to:
|
||||||
|
%% {rabbit, [{permit_deprecated_features, #{my_feature => true}}]}.
|
||||||
|
{translation, "rabbit.permit_deprecated_features",
|
||||||
|
fun(Conf) ->
|
||||||
|
Settings = cuttlefish_variable:filter_by_prefix(
|
||||||
|
"permit_deprecated_features", Conf),
|
||||||
|
maps:from_list(
|
||||||
|
[{list_to_atom(FeatureName), State}
|
||||||
|
|| {["permit_deprecated_features", FeatureName], State}
|
||||||
|
<- Settings])
|
||||||
|
end}.
|
||||||
|
|
||||||
% ==========================
|
% ==========================
|
||||||
% Kernel section
|
% Kernel section
|
||||||
% ==========================
|
% ==========================
|
||||||
|
|
|
@ -193,6 +193,12 @@
|
||||||
[{description, "core initialized"},
|
[{description, "core initialized"},
|
||||||
{requires, kernel_ready}]}).
|
{requires, kernel_ready}]}).
|
||||||
|
|
||||||
|
-rabbit_boot_step({deprecate_cmqs,
|
||||||
|
[{description, "checks whether mirrored queues are disabled"},
|
||||||
|
{mfa, {rabbit_mirror_queue_misc, prevent_startup_when_mirroring_is_disabled_but_configured, []}},
|
||||||
|
{requires, [database]},
|
||||||
|
{enables, [recovery]}]}).
|
||||||
|
|
||||||
-rabbit_boot_step({recovery,
|
-rabbit_boot_step({recovery,
|
||||||
[{description, "exchange, queue and binding recovery"},
|
[{description, "exchange, queue and binding recovery"},
|
||||||
{mfa, {rabbit, recover, []}},
|
{mfa, {rabbit, recover, []}},
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
|
|
||||||
-export([get_replicas/1, transfer_leadership/2, migrate_leadership_to_existing_replica/2]).
|
-export([get_replicas/1, transfer_leadership/2, migrate_leadership_to_existing_replica/2]).
|
||||||
|
|
||||||
|
-export([prevent_startup_when_mirroring_is_disabled_but_configured/0]).
|
||||||
|
|
||||||
%% for testing only
|
%% for testing only
|
||||||
-export([module/1]).
|
-export([module/1]).
|
||||||
|
|
||||||
|
@ -724,6 +726,63 @@ maybe_drop_master_after_sync(Q) when ?is_amqqueue(Q) ->
|
||||||
|
|
||||||
%%----------------------------------------------------------------------------
|
%%----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
mirroring_policies() ->
|
||||||
|
Policies = rabbit_policy:list_as_maps(),
|
||||||
|
OpPolicies = rabbit_policy:list_op_as_maps(),
|
||||||
|
IsMirroringPolicy = fun
|
||||||
|
(#{definition := #{<<"ha-mode">> := _}}) ->
|
||||||
|
true;
|
||||||
|
(_) ->
|
||||||
|
false
|
||||||
|
end,
|
||||||
|
{lists:filter(IsMirroringPolicy, Policies), lists:filter(IsMirroringPolicy, OpPolicies)}.
|
||||||
|
|
||||||
|
report_vhosts_using_mirroring(MirrorPolicies, PolicyType) ->
|
||||||
|
PerVhost = lists:foldr(
|
||||||
|
fun (#{vhost := VHost, name := Name}, Acc) ->
|
||||||
|
maps:update_with(
|
||||||
|
VHost,
|
||||||
|
fun (Message) -> <<Name/binary, ", ", Message/binary>> end,
|
||||||
|
<<"Virtual host ", VHost/binary, " has ", PolicyType/binary,
|
||||||
|
" policies that configure mirroring: ", Name/binary>>,
|
||||||
|
Acc)
|
||||||
|
end,
|
||||||
|
#{}, MirrorPolicies),
|
||||||
|
lists:foreach(
|
||||||
|
fun (Msg) -> rabbit_log:error("~ts", [Msg]) end,
|
||||||
|
maps:values(PerVhost)).
|
||||||
|
|
||||||
|
prevent_startup_when_mirroring_is_disabled_but_configured() ->
|
||||||
|
case are_cmqs_permitted() of
|
||||||
|
true ->
|
||||||
|
ok;
|
||||||
|
false ->
|
||||||
|
Error = {error, {failed_to_deny_deprecated_features, [classic_mirrored_queues]}},
|
||||||
|
case mirroring_policies() of
|
||||||
|
{[], []} ->
|
||||||
|
ok;
|
||||||
|
{Pols, []} ->
|
||||||
|
report_vhosts_using_mirroring(Pols, <<"user">>),
|
||||||
|
exit(Error);
|
||||||
|
{[], OpPols} ->
|
||||||
|
report_vhosts_using_mirroring(OpPols, <<"operator">>),
|
||||||
|
exit(Error);
|
||||||
|
{Pols, OpPols} ->
|
||||||
|
report_vhosts_using_mirroring(Pols, <<"user">>),
|
||||||
|
report_vhosts_using_mirroring(OpPols, <<"operator">>),
|
||||||
|
exit(Error)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
are_cmqs_permitted() ->
|
||||||
|
%% FeatureName = classic_mirrored_queues,
|
||||||
|
%% rabbit_deprecated_features:is_permitted(FeatureName).
|
||||||
|
case application:get_env(rabbit, permit_deprecated_features) of
|
||||||
|
{ok, #{classic_mirrored_queues := false}} ->
|
||||||
|
false;
|
||||||
|
_ -> true
|
||||||
|
end.
|
||||||
|
|
||||||
validate_policy(KeyList) ->
|
validate_policy(KeyList) ->
|
||||||
Mode = proplists:get_value(<<"ha-mode">>, KeyList, none),
|
Mode = proplists:get_value(<<"ha-mode">>, KeyList, none),
|
||||||
Params = proplists:get_value(<<"ha-params">>, KeyList, none),
|
Params = proplists:get_value(<<"ha-params">>, KeyList, none),
|
||||||
|
@ -734,10 +793,12 @@ validate_policy(KeyList) ->
|
||||||
<<"ha-promote-on-shutdown">>, KeyList, none),
|
<<"ha-promote-on-shutdown">>, KeyList, none),
|
||||||
PromoteOnFailure = proplists:get_value(
|
PromoteOnFailure = proplists:get_value(
|
||||||
<<"ha-promote-on-failure">>, KeyList, none),
|
<<"ha-promote-on-failure">>, KeyList, none),
|
||||||
case {Mode, Params, SyncMode, SyncBatchSize, PromoteOnShutdown, PromoteOnFailure} of
|
case {are_cmqs_permitted(), Mode, Params, SyncMode, SyncBatchSize, PromoteOnShutdown, PromoteOnFailure} of
|
||||||
{none, none, none, none, none, none} ->
|
{_, none, none, none, none, none, none} ->
|
||||||
ok;
|
ok;
|
||||||
{none, _, _, _, _, _} ->
|
{false, _, _, _, _, _, _} ->
|
||||||
|
{error, "Mirrored queues disabled via configuration", []};
|
||||||
|
{_, none, _, _, _, _, _} ->
|
||||||
{error, "ha-mode must be specified to specify ha-params, "
|
{error, "ha-mode must be specified to specify ha-params, "
|
||||||
"ha-sync-mode or ha-promote-on-shutdown", []};
|
"ha-sync-mode or ha-promote-on-shutdown", []};
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
-export([parse_set_op/7, set_op/7, delete_op/3, lookup_op/2, list_op/0, list_op/1, list_op/2,
|
-export([parse_set_op/7, set_op/7, delete_op/3, lookup_op/2, list_op/0, list_op/1, list_op/2,
|
||||||
list_formatted_op/1, list_formatted_op/3,
|
list_formatted_op/1, list_formatted_op/3,
|
||||||
match_all/2, match_as_map/1, match_op_as_map/1, definition_keys/1,
|
match_all/2, match_as_map/1, match_op_as_map/1, definition_keys/1,
|
||||||
list_in/1, list_in/2, list_as_maps/0, list_as_maps/1, list_op_as_maps/1
|
list_in/1, list_in/2, list_as_maps/0, list_as_maps/1, list_op_as_maps/0, list_op_as_maps/1
|
||||||
]).
|
]).
|
||||||
-export([sort_by_priority/1]).
|
-export([sort_by_priority/1]).
|
||||||
|
|
||||||
|
@ -126,6 +126,9 @@ list_as_maps() ->
|
||||||
list_as_maps(VHost) ->
|
list_as_maps(VHost) ->
|
||||||
[maps:from_list(PL) || PL <- sort_by_priority(list0(VHost, fun maps:from_list/1))].
|
[maps:from_list(PL) || PL <- sort_by_priority(list0(VHost, fun maps:from_list/1))].
|
||||||
|
|
||||||
|
list_op_as_maps() ->
|
||||||
|
list_op_as_maps('_').
|
||||||
|
|
||||||
list_op_as_maps(VHost) ->
|
list_op_as_maps(VHost) ->
|
||||||
[maps:from_list(PL) || PL <- sort_by_priority(list0_op(VHost, fun maps:from_list/1))].
|
[maps:from_list(PL) || PL <- sort_by_priority(list0_op(VHost, fun maps:from_list/1))].
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue