Skip to content

Commit fe1b7cb

Browse files
authored
Merge pull request #7305 from rabbitmq/support-required-ff-in-plugins
rabbit_feature_flags: Support required feature flags in plugins
2 parents 2dc45e8 + a4e8cdd commit fe1b7cb

File tree

5 files changed

+162
-53
lines changed

5 files changed

+162
-53
lines changed

deps/rabbit/src/rabbit_ff_controller.erl

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -747,11 +747,12 @@ check_required_and_enable(
747747
%% Required feature flags vs. virgin nodes.
748748
FeatureProps = maps:get(FeatureName, FeatureFlags),
749749
Stability = rabbit_feature_flags:get_stability(FeatureProps),
750+
ProvidedBy = maps:get(provided_by, FeatureProps),
750751
NodesWhereDisabled = list_nodes_where_feature_flag_is_disabled(
751752
Inventory, FeatureName),
752753

753754
MarkDirectly = case Stability of
754-
required ->
755+
required when ProvidedBy =:= rabbit ->
755756
?LOG_DEBUG(
756757
"Feature flags: `~s`: the feature flag is "
757758
"required on some nodes; list virgin nodes "
@@ -770,6 +771,25 @@ check_required_and_enable(
770771
end
771772
end, NodesWhereDisabled),
772773
VirginNodesWhereDisabled =:= NodesWhereDisabled;
774+
required when ProvidedBy =/= rabbit ->
775+
%% A plugin can be enabled/disabled at runtime and
776+
%% between restarts. Thus we have no way to
777+
%% distinguish a newly enabled plugin from a plugin
778+
%% which was enabled in the past.
779+
%%
780+
%% Therefore, we always mark required feature flags
781+
%% from plugins directly as enabled. However, the
782+
%% plugin is responsible for checking that its
783+
%% possibly existing data is as it expects it or
784+
%% perform any cleanup/conversion!
785+
?LOG_DEBUG(
786+
"Feature flags: `~s`: the feature flag is "
787+
"required on some nodes; it comes from a "
788+
"plugin which can be enabled at runtime, "
789+
"so it can be marked as enabled",
790+
[FeatureName],
791+
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
792+
true;
773793
_ ->
774794
false
775795
end,

deps/rabbit/src/rabbit_ff_registry_factory.erl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ maybe_initialize_registry(NewSupportedFeatureFlags,
247247
fun(FeatureName, FeatureProps) ->
248248
Stability = maps:get(
249249
stability, FeatureProps, stable),
250+
ProvidedBy = maps:get(
251+
provided_by, FeatureProps),
250252
State = case FeatureStates0 of
251253
#{FeatureName := FeatureState} ->
252254
FeatureState;
@@ -264,6 +266,9 @@ maybe_initialize_registry(NewSupportedFeatureFlags,
264266
%% feature flag as enabled.
265267
?assertNotEqual(state_changing, State),
266268
true;
269+
required when ProvidedBy =/= rabbit ->
270+
?assertNotEqual(state_changing, State),
271+
true;
267272
required ->
268273
%% This is not a new node and the
269274
%% required feature flag is disabled.

deps/rabbit/test/feature_flags_SUITE.erl

Lines changed: 122 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
enable_feature_flag_with_a_network_partition/1,
3232
mark_feature_flag_as_enabled_with_a_network_partition/1,
3333
required_feature_flag_enabled_by_default/1,
34+
required_plugin_feature_flag_enabled_by_default/1,
35+
required_plugin_feature_flag_enabled_after_activation/1,
3436
plugin_stable_ff_enabled_on_initial_node_start/1,
3537

3638
clustering_ok_with_ff_disabled_everywhere/1,
@@ -62,6 +64,8 @@ groups() ->
6264
enable_feature_flag_in_a_healthy_situation,
6365
enable_unsupported_feature_flag_in_a_healthy_situation,
6466
required_feature_flag_enabled_by_default,
67+
required_plugin_feature_flag_enabled_by_default,
68+
required_plugin_feature_flag_enabled_after_activation,
6569
plugin_stable_ff_enabled_on_initial_node_start
6670
]},
6771
{enabling_in_cluster, [],
@@ -70,7 +74,9 @@ groups() ->
7074
enable_unsupported_feature_flag_in_a_healthy_situation,
7175
enable_feature_flag_with_a_network_partition,
7276
mark_feature_flag_as_enabled_with_a_network_partition,
73-
required_feature_flag_enabled_by_default
77+
required_feature_flag_enabled_by_default,
78+
required_plugin_feature_flag_enabled_by_default,
79+
required_plugin_feature_flag_enabled_after_activation
7480
]},
7581
{clustering, [],
7682
[
@@ -168,9 +174,10 @@ init_per_group(enabling_on_single_node, Config) ->
168174
[{rmq_nodes_count, 1}]),
169175
rabbit_ct_helpers:run_setup_steps(Config1, [fun prepare_my_plugin/1]);
170176
init_per_group(enabling_in_cluster, Config) ->
171-
rabbit_ct_helpers:set_config(
172-
Config,
173-
[{rmq_nodes_count, 5}]);
177+
Config1 = rabbit_ct_helpers:set_config(
178+
Config,
179+
[{rmq_nodes_count, 5}]),
180+
rabbit_ct_helpers:run_setup_steps(Config1, [fun prepare_my_plugin/1]);
174181
init_per_group(clustering, Config) ->
175182
Config1 = rabbit_ct_helpers:set_config(
176183
Config,
@@ -194,81 +201,89 @@ end_per_group(_, Config) ->
194201
init_per_testcase(Testcase, Config) ->
195202
rabbit_ct_helpers:testcase_started(Config, Testcase),
196203
TestNumber = rabbit_ct_helpers:testcase_number(Config, ?MODULE, Testcase),
197-
case ?config(tc_group_properties, Config) of
204+
Config1 = case Testcase of
205+
required_plugin_feature_flag_enabled_after_activation ->
206+
rabbit_ct_helpers:set_config(
207+
Config,
208+
[{start_rmq_with_plugins_disabled, true}]);
209+
_ ->
210+
Config
211+
end,
212+
case ?config(tc_group_properties, Config1) of
198213
[{name, registry} | _] ->
199214
logger:set_primary_config(level, debug),
200-
FeatureFlagsFile = filename:join(?config(priv_dir, Config),
215+
FeatureFlagsFile = filename:join(?config(priv_dir, Config1),
201216
rabbit_misc:format(
202217
"feature_flags-~ts",
203218
[Testcase])),
204219
application:set_env(rabbit, feature_flags_file, FeatureFlagsFile),
205220
rabbit_ct_helpers:set_config(
206-
Config, {feature_flags_file, FeatureFlagsFile});
221+
Config1, {feature_flags_file, FeatureFlagsFile});
207222
[{name, Name} | _]
208223
when Name =:= enabling_on_single_node orelse
209224
Name =:= clustering orelse
210225
Name =:= activating_plugin ->
211-
ClusterSize = ?config(rmq_nodes_count, Config),
212-
Config1 = rabbit_ct_helpers:set_config(
213-
Config,
226+
ClusterSize = ?config(rmq_nodes_count, Config1),
227+
Config2 = rabbit_ct_helpers:set_config(
228+
Config1,
214229
[{rmq_nodename_suffix, Testcase},
215230
{tcp_ports_base, {skip_n_nodes,
216231
TestNumber * ClusterSize}}
217232
]),
218-
Config2 = rabbit_ct_helpers:merge_app_env(
219-
Config1,
233+
Config3 = rabbit_ct_helpers:merge_app_env(
234+
Config2,
220235
{rabbit,
221236
[{log, [{file, [{level, debug}]}]}]}),
222-
Config3 = rabbit_ct_helpers:run_steps(
223-
Config2,
237+
Config4 = rabbit_ct_helpers:run_steps(
238+
Config3,
224239
rabbit_ct_broker_helpers:setup_steps() ++
225240
rabbit_ct_client_helpers:setup_steps()),
226-
case Config3 of
241+
case Config4 of
227242
{skip, _} ->
228-
Config3;
243+
Config4;
229244
_ ->
230-
case is_feature_flag_subsystem_available(Config3) of
245+
case is_feature_flag_subsystem_available(Config4) of
231246
true ->
232247
%% We can declare a new feature flag at
233248
%% runtime. All of them are supported but
234249
%% still disabled.
235-
declare_arbitrary_feature_flag(Config3),
236-
Config3;
250+
declare_arbitrary_feature_flag(Config4),
251+
Config4;
237252
false ->
238-
end_per_testcase(Testcase, Config3),
253+
end_per_testcase(Testcase, Config4),
239254
{skip, "Feature flags subsystem unavailable"}
240255
end
241256
end;
242257
[{name, enabling_in_cluster} | _] ->
243-
ClusterSize = ?config(rmq_nodes_count, Config),
244-
Config1 = rabbit_ct_helpers:set_config(
245-
Config,
258+
ClusterSize = ?config(rmq_nodes_count, Config1),
259+
Config2 = rabbit_ct_helpers:set_config(
260+
Config1,
246261
[{rmq_nodename_suffix, Testcase},
247262
{tcp_ports_base, {skip_n_nodes,
248263
TestNumber * ClusterSize}},
249264
{net_ticktime, 5}
250265
]),
251-
Config2 = rabbit_ct_helpers:merge_app_env(
252-
Config1,
266+
Config3 = rabbit_ct_helpers:merge_app_env(
267+
Config2,
253268
{rabbit,
254269
[{log, [{file, [{level, debug}]}]}]}),
255-
Config3 = rabbit_ct_helpers:run_steps(
256-
Config2,
270+
Config4 = rabbit_ct_helpers:run_steps(
271+
Config3,
257272
rabbit_ct_broker_helpers:setup_steps() ++
258273
rabbit_ct_client_helpers:setup_steps()),
259-
case Config3 of
274+
case Config4 of
260275
{skip, _} ->
261-
Config3;
276+
Config4;
262277
_ ->
263-
case is_feature_flag_subsystem_available(Config3) of
278+
case is_feature_flag_subsystem_available(Config4) of
264279
true ->
265280
%% We can declare a new feature flag at
266281
%% runtime. All of them are supported but
267282
%% still disabled.
268-
declare_arbitrary_feature_flag(Config3),
269-
Config3;
283+
declare_arbitrary_feature_flag(Config4),
284+
Config4;
270285
false ->
271-
end_per_testcase(Testcase, Config3),
286+
end_per_testcase(Testcase, Config4),
272287
{skip, "Feature flags subsystem unavailable"}
273288
end
274289
end
@@ -996,14 +1011,6 @@ required_feature_flag_enabled_by_default(Config) ->
9961011
True = lists:duplicate(ClusterSize, true),
9971012
False = lists:duplicate(ClusterSize, false),
9981013

999-
%% Restart the first node to make sure it evaluates again the list of
1000-
%% required flags. Indeed, the testsuite injects its required feature
1001-
%% flag after the nodes booted and we don't want to verify a
1002-
%% testsuite-specific handling.
1003-
%[Node | _] = rabbit_ct_broker_helpers:get_node_configs(Config, nodename),
1004-
%?assertEqual(ok, rabbit_ct_broker_helpers:stop_node(Config, Node)),
1005-
%?assertEqual(ok, rabbit_ct_broker_helpers:start_node(Config, Node)),
1006-
10071014
%% The stable feature flag is supported but disabled.
10081015
?assertEqual(
10091016
True,
@@ -1020,6 +1027,80 @@ required_feature_flag_enabled_by_default(Config) ->
10201027
True,
10211028
is_feature_flag_enabled(Config, RequiredFName)).
10221029

1030+
required_plugin_feature_flag_enabled_by_default(Config) ->
1031+
StableFName = plugin_ff,
1032+
RequiredFName = required_plugin_ff,
1033+
ClusterSize = ?config(rmq_nodes_count, Config),
1034+
True = lists:duplicate(ClusterSize, true),
1035+
1036+
%% The stable feature flag is supported and enabled because the plugin is
1037+
%% enabled during startup.
1038+
?assertEqual(
1039+
True,
1040+
is_feature_flag_supported(Config, StableFName)),
1041+
?assertEqual(
1042+
True,
1043+
is_feature_flag_enabled(Config, StableFName)),
1044+
1045+
%% Likewise for the required feature flag.
1046+
?assertEqual(
1047+
True,
1048+
is_feature_flag_supported(Config, RequiredFName)),
1049+
?assertEqual(
1050+
True,
1051+
is_feature_flag_enabled(Config, RequiredFName)).
1052+
1053+
required_plugin_feature_flag_enabled_after_activation(Config) ->
1054+
StableFName = plugin_ff,
1055+
RequiredFName = required_plugin_ff,
1056+
ClusterSize = ?config(rmq_nodes_count, Config),
1057+
True = lists:duplicate(ClusterSize, true),
1058+
False = lists:duplicate(ClusterSize, false),
1059+
1060+
?assertEqual(true, ?config(start_rmq_with_plugins_disabled, Config)),
1061+
1062+
%% The stable feature flag is unsupported.
1063+
?assertEqual(
1064+
False,
1065+
is_feature_flag_supported(Config, StableFName)),
1066+
?assertEqual(
1067+
False,
1068+
is_feature_flag_enabled(Config, StableFName)),
1069+
1070+
%% The required feature flag is unsupported.
1071+
?assertEqual(
1072+
False,
1073+
is_feature_flag_supported(Config, RequiredFName)),
1074+
?assertEqual(
1075+
False,
1076+
is_feature_flag_enabled(Config, RequiredFName)),
1077+
1078+
rabbit_ct_broker_helpers:enable_plugin(Config, 0, "my_plugin"),
1079+
1080+
LastI = ClusterSize - 1,
1081+
rabbit_ct_broker_helpers:enable_plugin(Config, LastI, "my_plugin"),
1082+
TrueWhereEnabled = lists:map(
1083+
fun
1084+
(I) when I =:= 0 orelse I =:= LastI -> true;
1085+
(_) -> false
1086+
end, lists:seq(0, LastI)),
1087+
1088+
%% The stable feature flag is supported but disabled.
1089+
?assertEqual(
1090+
True,
1091+
is_feature_flag_supported(Config, StableFName)),
1092+
?assertEqual(
1093+
False,
1094+
is_feature_flag_enabled(Config, StableFName)),
1095+
1096+
%% The required feature flag is supported and enabled.
1097+
?assertEqual(
1098+
True,
1099+
is_feature_flag_supported(Config, RequiredFName)),
1100+
?assertEqual(
1101+
TrueWhereEnabled,
1102+
is_feature_flag_enabled(Config, RequiredFName)).
1103+
10231104
plugin_stable_ff_enabled_on_initial_node_start(Config) ->
10241105
?assertEqual([true], is_feature_flag_supported(Config, plugin_ff)),
10251106
?assertEqual([true], is_feature_flag_enabled(Config, plugin_ff)),

deps/rabbit/test/feature_flags_SUITE_data/my_plugin/src/my_plugin.erl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@
88
-module(my_plugin).
99

1010
-rabbit_feature_flag({plugin_ff, #{desc => "Plugin's feature flag A"}}).
11+
12+
-rabbit_feature_flag({required_plugin_ff, #{desc => "Plugin's feature flag B",
13+
stability => required}}).

0 commit comments

Comments
 (0)