Skip to content

Commit b6796b7

Browse files
committed
CLI: list_deprecated_features command
Lists all or used deprecated features
1 parent f54f880 commit b6796b7

File tree

6 files changed

+408
-1
lines changed

6 files changed

+408
-1
lines changed

deps/rabbit/app.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def all_beam_files(name = "all_beam_files"):
110110
"src/rabbit_definitions_hashing.erl",
111111
"src/rabbit_definitions_import_https.erl",
112112
"src/rabbit_definitions_import_local_filesystem.erl",
113+
"src/rabbit_deprecated_feature_extra.erl",
113114
"src/rabbit_deprecated_features.erl",
114115
"src/rabbit_diagnostics.erl",
115116
"src/rabbit_direct.erl",
@@ -373,6 +374,7 @@ def all_test_beam_files(name = "all_test_beam_files"):
373374
"src/rabbit_definitions_hashing.erl",
374375
"src/rabbit_definitions_import_https.erl",
375376
"src/rabbit_definitions_import_local_filesystem.erl",
377+
"src/rabbit_deprecated_feature_extra.erl",
376378
"src/rabbit_deprecated_features.erl",
377379
"src/rabbit_diagnostics.erl",
378380
"src/rabbit_direct.erl",
@@ -652,6 +654,7 @@ def all_srcs(name = "all_srcs"):
652654
"src/rabbit_definitions_hashing.erl",
653655
"src/rabbit_definitions_import_https.erl",
654656
"src/rabbit_definitions_import_local_filesystem.erl",
657+
"src/rabbit_deprecated_feature_extra.erl",
655658
"src/rabbit_deprecated_features.erl",
656659
"src/rabbit_diagnostics.erl",
657660
"src/rabbit_direct.erl",
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
%% This Source Code Form is subject to the terms of the Mozilla Public
2+
%% License, v. 2.0. If a copy of the MPL was not distributed with this
3+
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
%%
5+
%% @copyright 2018-2023 VMware, Inc. or its affiliates.
6+
%%
7+
%% @doc
8+
%% This module provides extra functions unused by the feature flags
9+
%% subsystem core functionality.
10+
11+
-module(rabbit_deprecated_feature_extra).
12+
13+
-export([cli_info/1]).
14+
15+
-type cli_info() :: [cli_info_entry()].
16+
%% A list of deprecated feature properties, formatted for the RabbitMQ CLI.
17+
18+
-type cli_info_entry() :: [{name, rabbit_feature_flags:feature_name()} |
19+
{deprecation_phase, rabbit_deprecated_features:deprecation_phase()} |
20+
{provided_by, atom()} |
21+
{desc, string()} |
22+
{doc_url, string()}].
23+
%% A list of properties for a single deprecated feature, formatted for the
24+
%% RabbitMQ CLI.
25+
26+
-spec cli_info(Which :: all | used) -> cli_info().
27+
%% @doc
28+
%% Returns a list of all or used deprecated features properties, depending on the argument.
29+
%%
30+
%% @param Which The group of deprecated features to return: `all' or `used'.
31+
%% @returns the list of all deprecated feature properties.
32+
33+
cli_info(all) ->
34+
cli_info0(rabbit_deprecated_features:list(all));
35+
cli_info(used) ->
36+
cli_info0(rabbit_deprecated_features:list(used)).
37+
38+
-spec cli_info0(rabbit_feature_flags:feature_flags()) -> cli_info().
39+
%% @doc
40+
%% Formats a map of deprecated features and their properties into a list of
41+
%% deprecated feature properties as expected by the RabbitMQ CLI.
42+
%%
43+
%% @param DeprecatedFeatures A map of deprecated features.
44+
%% @returns the list of deprecated features properties, created from the map
45+
%% specified in arguments.
46+
47+
cli_info0(DeprecatedFeature) ->
48+
lists:foldr(
49+
fun(FeatureName, Acc) ->
50+
FeatureProps = maps:get(FeatureName, DeprecatedFeature),
51+
52+
App = maps:get(provided_by, FeatureProps),
53+
DeprecationPhase = maps:get(deprecation_phase, FeatureProps, ""),
54+
Desc = maps:get(desc, FeatureProps, ""),
55+
DocUrl = maps:get(doc_url, FeatureProps, ""),
56+
Info = [{name, FeatureName},
57+
{desc, unicode:characters_to_binary(Desc)},
58+
{deprecation_phase, DeprecationPhase},
59+
{doc_url, unicode:characters_to_binary(DocUrl)},
60+
{provided_by, App}],
61+
[Info | Acc]
62+
end, [], lists:sort(maps:keys(DeprecatedFeature))).

deps/rabbit/src/rabbit_deprecated_features.erl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
-export([extend_properties/2,
119119
should_be_permitted/2,
120120
enable_underlying_feature_flag_cb/1]).
121+
-export([list/1]).
121122

122123
-type deprecated_feature_modattr() :: {rabbit_feature_flags:feature_name(),
123124
feature_props()}.
@@ -346,6 +347,25 @@ get_warning(FeatureProps, Permitted) when is_map(FeatureProps) ->
346347
maps:get(when_removed, Msgs)
347348
end.
348349

350+
-spec list(Which :: all | used) -> rabbit_feature_flags:feature_flags().
351+
%% @doc
352+
%% Lists all or used deprecated features, depending on the argument.
353+
%%
354+
%% @param Which The group of deprecated features to return: `all' or `used'.
355+
%% @returns A map of selected deprecated features.
356+
357+
list(all) ->
358+
maps:filter(
359+
fun(_, FeatureProps) -> ?IS_DEPRECATION(FeatureProps) end,
360+
rabbit_ff_registry_wrapper:list(all));
361+
list(used) ->
362+
maps:filter(
363+
fun(_, FeatureProps) -> ?IS_DEPRECATION(FeatureProps)
364+
and
365+
is_deprecated_feature_in_use(FeatureProps)
366+
end,
367+
rabbit_ff_registry_wrapper:list(all)).
368+
349369
%% -------------------------------------------------------------------
350370
%% Internal functions.
351371
%% -------------------------------------------------------------------
@@ -602,3 +622,8 @@ enable_underlying_feature_flag_cb(
602622
_ ->
603623
ok
604624
end.
625+
626+
is_deprecated_feature_in_use(#{callbacks := #{is_feature_used := {CallbackMod, CallbackFun}}}) ->
627+
erlang:apply(CallbackMod, CallbackFun, [#{}]);
628+
is_deprecated_feature_in_use(_) ->
629+
false.

deps/rabbit/test/deprecated_features_SUITE.erl

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
get_appropriate_warning_when_disconnected/1,
3737
get_appropriate_warning_when_removed/1,
3838
deprecated_feature_enabled_if_feature_flag_depends_on_it/1,
39+
list_all_deprecated_features/1,
40+
list_used_deprecated_features/1,
3941

4042
feature_is_unused/1,
4143
feature_is_used/1
@@ -67,7 +69,9 @@ groups() ->
6769
get_appropriate_warning_when_denied,
6870
get_appropriate_warning_when_disconnected,
6971
get_appropriate_warning_when_removed,
70-
deprecated_feature_enabled_if_feature_flag_depends_on_it
72+
deprecated_feature_enabled_if_feature_flag_depends_on_it,
73+
list_all_deprecated_features,
74+
list_used_deprecated_features
7175
],
7276
[
7377
{cluster_size_1, [], Tests},
@@ -726,3 +730,50 @@ deprecated_feature_enabled_if_feature_flag_depends_on_it(Config) ->
726730
ok
727731
end )
728732
|| Node <- AllNodes].
733+
734+
list_all_deprecated_features(Config) ->
735+
[FirstNode | _] = AllNodes = ?config(nodes, Config),
736+
feature_flags_v2_SUITE:connect_nodes(AllNodes),
737+
feature_flags_v2_SUITE:override_running_nodes(AllNodes),
738+
739+
FeatureName = ?FUNCTION_NAME,
740+
FeatureFlags = #{FeatureName =>
741+
#{provided_by => rabbit,
742+
deprecation_phase => permitted_by_default}},
743+
?assertEqual(
744+
ok,
745+
feature_flags_v2_SUITE:inject_on_nodes(AllNodes, FeatureFlags)),
746+
747+
feature_flags_v2_SUITE:run_on_node(
748+
FirstNode,
749+
fun() ->
750+
Map = rabbit_deprecated_features:list(all),
751+
?assert(maps:is_key(FeatureName, Map))
752+
end).
753+
754+
list_used_deprecated_features(Config) ->
755+
[FirstNode | _] = AllNodes = ?config(nodes, Config),
756+
feature_flags_v2_SUITE:connect_nodes(AllNodes),
757+
feature_flags_v2_SUITE:override_running_nodes(AllNodes),
758+
759+
UsedFeatureName = used_deprecated_feature,
760+
UnusedFeatureName = unused_deprecated_feature,
761+
FeatureFlags = #{UsedFeatureName =>
762+
#{provided_by => rabbit,
763+
deprecation_phase => permitted_by_default,
764+
callbacks => #{is_feature_used => {?MODULE, feature_is_used}}},
765+
UnusedFeatureName =>
766+
#{provided_by => rabbit,
767+
deprecation_phase => permitted_by_default,
768+
callbacks => #{is_feature_used => {?MODULE, feature_is_unused}}}},
769+
?assertEqual(
770+
ok,
771+
feature_flags_v2_SUITE:inject_on_nodes(AllNodes, FeatureFlags)),
772+
773+
feature_flags_v2_SUITE:run_on_node(
774+
FirstNode,
775+
fun() ->
776+
Map = rabbit_deprecated_features:list(used),
777+
?assertNot(maps:is_key(UnusedFeatureName, Map)),
778+
?assert(maps:is_key(UsedFeatureName, Map))
779+
end).
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
## This Source Code Form is subject to the terms of the Mozilla Public
2+
## License, v. 2.0. If a copy of the MPL was not distributed with this
3+
## file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
##
5+
## Copyright (c) 2018-2023 VMware, Inc. or its affiliates. All rights reserved.
6+
7+
defmodule RabbitMQ.CLI.Ctl.Commands.ListDeprecatedFeaturesCommand do
8+
alias RabbitMQ.CLI.Core.{DocGuide, Validators}
9+
alias RabbitMQ.CLI.Ctl.InfoKeys
10+
11+
@behaviour RabbitMQ.CLI.CommandBehaviour
12+
use RabbitMQ.CLI.DefaultOutput
13+
14+
def formatter(), do: RabbitMQ.CLI.Formatters.Table
15+
16+
@info_keys ~w(name deprecation_phase provided_by desc doc_url)a
17+
18+
def info_keys(), do: @info_keys
19+
20+
def scopes(), do: [:ctl, :diagnostics]
21+
22+
def switches(), do: [used: :boolean]
23+
24+
def merge_defaults([], opts) do
25+
{["name", "deprecation_phase"], Map.merge(%{used: false}, opts)}
26+
end
27+
28+
def merge_defaults(args, opts) do
29+
{args, Map.merge(%{used: false}, opts)}
30+
end
31+
32+
def validate(args, _) do
33+
case InfoKeys.validate_info_keys(args, @info_keys) do
34+
{:ok, _} -> :ok
35+
err -> err
36+
end
37+
end
38+
39+
def validate_execution_environment(args, opts) do
40+
Validators.chain(
41+
[
42+
&Validators.rabbit_is_loaded/2,
43+
&Validators.rabbit_is_running/2
44+
],
45+
[args, opts]
46+
)
47+
end
48+
49+
def run([_ | _] = args, %{node: node_name, timeout: timeout, used: false}) do
50+
case :rabbit_misc.rpc_call(
51+
node_name,
52+
:rabbit_deprecated_feature_extra,
53+
:cli_info,
54+
[:all],
55+
timeout
56+
) do
57+
# Server does not support deprecated features, consider none are available.
58+
{:badrpc, {:EXIT, {:undef, _}}} -> []
59+
{:badrpc, _} = err -> err
60+
val -> filter_by_arg(val, args)
61+
end
62+
end
63+
64+
def run([_ | _] = args, %{node: node_name, timeout: timeout, used: true}) do
65+
case :rabbit_misc.rpc_call(
66+
node_name,
67+
:rabbit_deprecated_feature_extra,
68+
:cli_info,
69+
[:used],
70+
timeout
71+
) do
72+
# Server does not support deprecated features, consider none are available.
73+
{:badrpc, {:EXIT, {:undef, _}}} -> []
74+
{:badrpc, _} = err -> err
75+
val -> filter_by_arg(val, args)
76+
end
77+
end
78+
79+
def banner(_, %{used: false}), do: "Listing deprecated features ..."
80+
def banner(_, %{used: true}), do: "Listing deprecated features in use ..."
81+
82+
def usage, do: "list_deprecated_features [--used] [<column> ...]"
83+
84+
def usage_additional() do
85+
[
86+
["<column>", "must be one of " <> Enum.join(Enum.sort(@info_keys), ", ")],
87+
["--used", "returns deprecated features in use"]
88+
]
89+
end
90+
91+
def usage_doc_guides() do
92+
[
93+
DocGuide.feature_flags()
94+
]
95+
end
96+
97+
def help_section(), do: :feature_flags
98+
99+
def description(), do: "Lists deprecated features"
100+
101+
#
102+
# Implementation
103+
#
104+
105+
defp filter_by_arg(ff_info, _) when is_tuple(ff_info) do
106+
# tuple means unexpected data
107+
ff_info
108+
end
109+
110+
defp filter_by_arg(ff_info, [_ | _] = args) when is_list(ff_info) do
111+
symbol_args = InfoKeys.prepare_info_keys(args)
112+
113+
Enum.map(
114+
ff_info,
115+
fn ff ->
116+
symbol_args
117+
|> Enum.filter(fn arg -> ff[arg] != nil end)
118+
|> Enum.map(fn arg -> {arg, ff[arg]} end)
119+
end
120+
)
121+
end
122+
end

0 commit comments

Comments
 (0)