Skip to content

Commit 67af27b

Browse files
authored
Merge pull request #12445 from rabbitmq/accept-relative-feature-flags-list-in-RABBITMQ_FEATURE_FLAGS-env
rabbit_feature_flags: Accept "+feature1,-feature2" in `$RABBITMQ_FEATURE_FLAGS`
2 parents df59a52 + 9b2c6d9 commit 67af27b

File tree

3 files changed

+116
-85
lines changed

3 files changed

+116
-85
lines changed

deps/rabbit/src/rabbit_ff_controller.erl

Lines changed: 108 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -675,48 +675,83 @@ enable_task(FeatureNames) ->
675675
end.
676676

677677
enable_default_task() ->
678-
FeatureNames = get_forced_feature_flag_names(),
679-
case FeatureNames of
680-
undefined ->
678+
case get_forced_feature_flag_names() of
679+
{ok, undefined} ->
681680
?LOG_DEBUG(
682681
"Feature flags: starting an unclustered node for the first "
683682
"time: all stable feature flags will be enabled by default",
684683
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
685684
{ok, Inventory} = collect_inventory_on_nodes([node()]),
686-
#{feature_flags := FeatureFlags} = Inventory,
687-
StableFeatureNames =
688-
maps:fold(
689-
fun(FeatureName, FeatureProps, Acc) ->
690-
Stability = rabbit_feature_flags:get_stability(
691-
FeatureProps),
692-
case Stability of
693-
stable -> [FeatureName | Acc];
694-
_ -> Acc
695-
end
696-
end, [], FeatureFlags),
685+
StableFeatureNames = get_stable_feature_flags(Inventory),
697686
enable_many(Inventory, StableFeatureNames);
698-
[] ->
687+
{ok, []} ->
699688
?LOG_DEBUG(
700689
"Feature flags: starting an unclustered node for the first "
701690
"time: all feature flags are forcibly left disabled from "
702691
"the $RABBITMQ_FEATURE_FLAGS environment variable",
703692
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
704693
ok;
705-
_ ->
694+
{ok, FeatureNames} when is_list(FeatureNames) ->
706695
?LOG_DEBUG(
707696
"Feature flags: starting an unclustered node for the first "
708697
"time: only the following feature flags specified in the "
709698
"$RABBITMQ_FEATURE_FLAGS environment variable will be enabled: "
710-
"~tp",
699+
"~0tp",
711700
[FeatureNames],
712701
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
713702
{ok, Inventory} = collect_inventory_on_nodes([node()]),
714-
enable_many(Inventory, FeatureNames)
703+
enable_many(Inventory, FeatureNames);
704+
{ok, {rel, Plus, Minus}} ->
705+
?LOG_DEBUG(
706+
"Feature flags: starting an unclustered node for the first "
707+
"time: all stable feature flags will be enabled, after "
708+
"applying changes from $RABBITMQ_FEATURE_FLAGS: adding ~0tp, "
709+
"skipping ~0tp",
710+
[Plus, Minus],
711+
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
712+
{ok, Inventory} = collect_inventory_on_nodes([node()]),
713+
StableFeatureNames = get_stable_feature_flags(Inventory),
714+
Unsupported = lists:filter(
715+
fun(FeatureName) ->
716+
not is_known_and_supported(
717+
Inventory, FeatureName)
718+
end, Minus),
719+
case Unsupported of
720+
[] ->
721+
FeatureNames = (StableFeatureNames -- Minus) ++ Plus,
722+
enable_many(Inventory, FeatureNames);
723+
_ ->
724+
?LOG_ERROR(
725+
"Feature flags: unsupported feature flags to skip in "
726+
"$RABBITMQ_FEATURE_FLAGS: ~0tp",
727+
[Unsupported],
728+
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
729+
{error, unsupported}
730+
end;
731+
{error, syntax_error_in_envvar} = Error ->
732+
?LOG_DEBUG(
733+
"Feature flags: invalid mix of `feature_flag` and "
734+
"`+/-feature_flag` in $RABBITMQ_FEATURE_FLAGS",
735+
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
736+
Error
715737
end.
716738

739+
get_stable_feature_flags(#{feature_flags := FeatureFlags}) ->
740+
maps:fold(
741+
fun(FeatureName, FeatureProps, Acc) ->
742+
Stability = rabbit_feature_flags:get_stability(FeatureProps),
743+
case Stability of
744+
stable -> [FeatureName | Acc];
745+
_ -> Acc
746+
end
747+
end, [], FeatureFlags).
748+
717749
-spec get_forced_feature_flag_names() -> Ret when
718-
Ret :: FeatureNames | undefined,
719-
FeatureNames :: [rabbit_feature_flags:feature_name()].
750+
Ret :: {ok, Abs | Rel | undefined} | {error, syntax_error_in_envvar},
751+
Abs :: [rabbit_feature_flags:feature_name()],
752+
Rel :: {rel,
753+
[rabbit_feature_flags:feature_name()],
754+
[rabbit_feature_flags:feature_name()]}.
720755
%% @doc Returns the (possibly empty) list of feature flags the user wants to
721756
%% enable out-of-the-box when starting a node for the first time.
722757
%%
@@ -737,59 +772,73 @@ enable_default_task() ->
737772
%% @private
738773

739774
get_forced_feature_flag_names() ->
740-
Ret = case get_forced_feature_flag_names_from_env() of
741-
undefined -> get_forced_feature_flag_names_from_config();
742-
List -> List
743-
end,
744-
case Ret of
745-
undefined ->
746-
ok;
747-
[] ->
748-
?LOG_INFO(
749-
"Feature flags: automatic enablement of feature flags "
750-
"disabled (i.e. none will be enabled automatically)",
751-
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS});
752-
_ ->
753-
?LOG_INFO(
754-
"Feature flags: automatic enablement of feature flags "
755-
"limited to the following list: ~tp",
756-
[Ret],
757-
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS})
758-
end,
759-
Ret.
775+
case get_forced_feature_flag_names_from_env() of
776+
{ok, undefined} -> get_forced_feature_flag_names_from_config();
777+
{ok, _} = Ret -> Ret;
778+
{error, _} = Error -> Error
779+
end.
760780

761781
-spec get_forced_feature_flag_names_from_env() -> Ret when
762-
Ret :: FeatureNames | undefined,
763-
FeatureNames :: [rabbit_feature_flags:feature_name()].
782+
Ret :: {ok, Abs | Rel | undefined} | {error, syntax_error_in_envvar},
783+
Abs :: [rabbit_feature_flags:feature_name()],
784+
Rel :: {rel,
785+
[rabbit_feature_flags:feature_name()],
786+
[rabbit_feature_flags:feature_name()]}.
764787
%% @private
765788

766789
get_forced_feature_flag_names_from_env() ->
767-
case rabbit_prelaunch:get_context() of
768-
#{forced_feature_flags_on_init := ForcedFFs}
769-
when is_list(ForcedFFs) ->
770-
ForcedFFs;
771-
_ ->
772-
undefined
790+
Value = case rabbit_prelaunch:get_context() of
791+
#{forced_feature_flags_on_init := ForcedFFs} -> ForcedFFs;
792+
_ -> undefined
793+
end,
794+
case Value of
795+
undefined ->
796+
{ok, Value};
797+
[] ->
798+
{ok, Value};
799+
[[Op | _] | _] when Op =:= $+ orelse Op =:= $- ->
800+
lists:foldr(
801+
fun
802+
([$+ | NameS], {ok, {rel, Plus, Minus}}) ->
803+
Name = list_to_atom(NameS),
804+
Plus1 = [Name | Plus],
805+
{ok, {rel, Plus1, Minus}};
806+
([$- | NameS], {ok, {rel, Plus, Minus}}) ->
807+
Name = list_to_atom(NameS),
808+
Minus1 = [Name | Minus],
809+
{ok, {rel, Plus, Minus1}};
810+
(_, {error, _} = Error) ->
811+
Error;
812+
(_, _) ->
813+
{error, syntax_error_in_envvar}
814+
end, {ok, {rel, [], []}}, Value);
815+
_ when is_list(Value) ->
816+
lists:foldr(
817+
fun
818+
(Name, {ok, Abs}) when is_atom(Name) ->
819+
{ok, [Name | Abs]};
820+
([C | _] = NameS, {ok, Abs})
821+
when C =/= $+ andalso C =/= $- ->
822+
Name = list_to_atom(NameS),
823+
{ok, [Name | Abs]};
824+
(_, {error, _} = Error) ->
825+
Error;
826+
(_, _) ->
827+
{error, syntax_error_in_envvar}
828+
end, {ok, []}, Value)
773829
end.
774830

775831
-spec get_forced_feature_flag_names_from_config() -> Ret when
776-
Ret :: FeatureNames | undefined,
832+
Ret :: {ok, FeatureNames | undefined},
777833
FeatureNames :: [rabbit_feature_flags:feature_name()].
778834
%% @private
779835

780836
get_forced_feature_flag_names_from_config() ->
781837
Value = application:get_env(
782838
rabbit, forced_feature_flags_on_init, undefined),
783839
case Value of
784-
undefined ->
785-
Value;
786-
_ when is_list(Value) ->
787-
case lists:all(fun is_atom/1, Value) of
788-
true -> Value;
789-
false -> undefined
790-
end;
791-
_ ->
792-
undefined
840+
undefined -> {ok, Value};
841+
_ when is_list(Value) -> {ok, Value}
793842
end.
794843

795844
-spec sync_cluster_task() -> Ret when
@@ -914,7 +963,7 @@ enable_if_supported(#{states_per_node := _} = Inventory, FeatureName) ->
914963
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),
915964
enable_with_registry_locked(Inventory, FeatureName);
916965
false ->
917-
?LOG_DEBUG(
966+
?LOG_ERROR(
918967
"Feature flags: `~ts`: unsupported; aborting",
919968
[FeatureName],
920969
#{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}),

deps/rabbit_common/src/rabbit_env.erl

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@
6565
"RABBITMQ_KEEP_PID_FILE_ON_EXIT",
6666
"RABBITMQ_LOG",
6767
"RABBITMQ_LOG_BASE",
68-
"RABBITMQ_LOG_FF_REGISTRY",
6968
"RABBITMQ_LOGS",
7069
"RABBITMQ_MNESIA_BASE",
7170
"RABBITMQ_MNESIA_DIR",
@@ -150,7 +149,6 @@ get_context_after_reloading_env(Context) ->
150149
fun keep_pid_file_on_exit/1,
151150
fun feature_flags_file/1,
152151
fun forced_feature_flags_on_init/1,
153-
fun log_feature_flags_registry/1,
154152
fun plugins_path/1,
155153
fun plugins_expand_dir/1,
156154
fun enabled_plugins_file/1,
@@ -999,24 +997,15 @@ forced_feature_flags_on_init(Context) ->
999997
case Value of
1000998
false ->
1001999
%% get_prefixed_env_var() considers an empty string
1002-
%% is the same as an undefined environment variable.
1003-
update_context(Context,
1004-
forced_feature_flags_on_init, undefined, default);
1000+
%% as an undefined environment variable.
1001+
update_context(
1002+
Context,
1003+
forced_feature_flags_on_init, undefined, default);
10051004
_ ->
1006-
Flags = [list_to_atom(V) || V <- string:lexemes(Value, ",")],
1007-
update_context(Context,
1008-
forced_feature_flags_on_init, Flags, environment)
1009-
end.
1010-
1011-
log_feature_flags_registry(Context) ->
1012-
case get_prefixed_env_var("RABBITMQ_LOG_FF_REGISTRY") of
1013-
false ->
1014-
update_context(Context,
1015-
log_feature_flags_registry, false, default);
1016-
Value ->
1017-
Log = value_is_yes(Value),
1018-
update_context(Context,
1019-
log_feature_flags_registry, Log, environment)
1005+
FeatureNames = string:lexemes(Value, ","),
1006+
update_context(
1007+
Context,
1008+
forced_feature_flags_on_init, FeatureNames, environment)
10201009
end.
10211010

10221011
%% -------------------------------------------------------------------

deps/rabbit_common/test/rabbit_env_SUITE.erl

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@ check_default_values(_) ->
187187
interactive_shell => default,
188188
keep_pid_file_on_exit => default,
189189
log_base_dir => default,
190-
log_feature_flags_registry => default,
191190
log_levels => default,
192191
main_config_file => default,
193192
main_log_file => default,
@@ -231,7 +230,6 @@ check_default_values(_) ->
231230
interactive_shell => false,
232231
keep_pid_file_on_exit => false,
233232
log_base_dir => "/var/log/rabbitmq",
234-
log_feature_flags_registry => false,
235233
log_levels => undefined,
236234
main_config_file => "/etc/rabbitmq/rabbitmq",
237235
main_log_file => "/var/log/rabbitmq/" ++ NodeS ++ ".log",
@@ -282,7 +280,6 @@ check_default_values(_) ->
282280
interactive_shell => false,
283281
keep_pid_file_on_exit => false,
284282
log_base_dir => "%APPDATA%/RabbitMQ/log",
285-
log_feature_flags_registry => false,
286283
log_levels => undefined,
287284
main_config_file => "%APPDATA%/RabbitMQ/rabbitmq",
288285
main_log_file => "%APPDATA%/RabbitMQ/log/" ++ NodeS ++ ".log",
@@ -408,7 +405,6 @@ check_values_from_reachable_remote_node(Config) ->
408405
interactive_shell => default,
409406
keep_pid_file_on_exit => default,
410407
log_base_dir => default,
411-
log_feature_flags_registry => default,
412408
log_levels => default,
413409
main_config_file => default,
414410
main_log_file => default,
@@ -452,7 +448,6 @@ check_values_from_reachable_remote_node(Config) ->
452448
interactive_shell => false,
453449
keep_pid_file_on_exit => false,
454450
log_base_dir => "/var/log/rabbitmq",
455-
log_feature_flags_registry => false,
456451
log_levels => undefined,
457452
main_config_file => "/etc/rabbitmq/rabbitmq",
458453
main_log_file => "/var/log/rabbitmq/" ++ NodeS ++ ".log",
@@ -540,7 +535,6 @@ check_values_from_offline_remote_node(_) ->
540535
interactive_shell => default,
541536
keep_pid_file_on_exit => default,
542537
log_base_dir => default,
543-
log_feature_flags_registry => default,
544538
log_levels => default,
545539
main_config_file => default,
546540
main_log_file => default,
@@ -584,7 +578,6 @@ check_values_from_offline_remote_node(_) ->
584578
interactive_shell => false,
585579
keep_pid_file_on_exit => false,
586580
log_base_dir => "/var/log/rabbitmq",
587-
log_feature_flags_registry => false,
588581
log_levels => undefined,
589582
main_config_file => "/etc/rabbitmq/rabbitmq",
590583
main_log_file => "/var/log/rabbitmq/" ++ NodeS ++ ".log",

0 commit comments

Comments
 (0)