Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optionally block enabling certain feature flags via Management UI/API #10682

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -646,3 +646,10 @@ end}.
{datatype, {enum, [true, false]}},
{include_default, false}
]}.

%% Block enabling certain feature flags over API

{mapping, "management.restrictions.feature_flag_blocked.$name", "rabbitmq_management.restrictions.feature_flag_blocked", [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about renaming this key to management.restrictions.blocked_feature_flags.$name (in plural with the adjective coming first, as it would in a piece of prose in English)?

{datatype, [string, {enum, [false]}]},
{include_default, false}
]}.
8 changes: 8 additions & 0 deletions deps/rabbitmq_management/src/rabbit_mgmt_features.erl
Expand Up @@ -9,6 +9,7 @@

-export([is_op_policy_updating_disabled/0,
is_qq_replica_operations_disabled/0,
is_feature_flag_blocked/1,
are_stats_enabled/0]).

is_qq_replica_operations_disabled() ->
Expand All @@ -20,6 +21,13 @@ is_op_policy_updating_disabled() ->
_ -> false
end.

-spec is_feature_flag_blocked(rabbit_feature_flags:feature_name()) -> {true, string()} | false.
is_feature_flag_blocked(FeatureFlag) ->
case get_restriction([feature_flag_blocked, FeatureFlag]) of
Msg when is_list(Msg) -> {true, Msg};
_ -> false
end.

are_stats_enabled() ->
DisabledFromConf = application:get_env(
rabbitmq_management, disable_management_stats, false),
Expand Down
Expand Up @@ -13,7 +13,6 @@
-export([variances/2]).

-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
-include_lib("rabbit_common/include/rabbit.hrl").

%%--------------------------------------------------------------------

Expand All @@ -38,13 +37,18 @@ accept_content(ReqData, #context{} = Context) ->
NameS = rabbit_mgmt_util:id(name, ReqData),
try
Name = list_to_existing_atom(binary_to_list(NameS)),
case rabbit_feature_flags:enable(Name) of
ok ->
{true, ReqData, Context};
{error, Reason1} ->
FormattedReason1 = rabbit_ff_extra:format_error(Reason1),
rabbit_mgmt_util:bad_request(
list_to_binary(FormattedReason1), ReqData, Context)
case rabbit_mgmt_features:is_feature_flag_blocked(Name) of
{true, Message} ->
rabbit_mgmt_util:method_not_allowed(Message, ReqData, Context);
false ->
case rabbit_feature_flags:enable(Name) of
ok ->
{true, ReqData, Context};
{error, Reason1} ->
FormattedReason1 = rabbit_ff_extra:format_error(Reason1),
rabbit_mgmt_util:bad_request(
list_to_binary(FormattedReason1), ReqData, Context)
end
end
catch
_:badarg ->
Expand Down
28 changes: 28 additions & 0 deletions deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl
Expand Up @@ -164,6 +164,7 @@ all_tests() -> [
user_limit_set_test,
config_environment_test,
disabled_qq_replica_opers_test,
feature_flag_blocked_test,
list_deprecated_features_test,
list_used_deprecated_features_test
].
Expand Down Expand Up @@ -242,6 +243,21 @@ init_per_testcase(Testcase = disabled_qq_replica_opers_test, Config) ->
rabbit_ct_broker_helpers:rpc_all(Config,
application, set_env, [rabbitmq_management, restrictions, Restrictions]),
rabbit_ct_helpers:testcase_started(Config, Testcase);
init_per_testcase(Testcase = feature_flag_blocked_test, Config) ->
Desc = "This is an experimental feature",
DocUrl = "https://rabbitmq.com/",
FeatureFlags = #{Testcase =>
#{provided_by => ?MODULE,
desc => Desc,
doc_url => DocUrl}},
ok = rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_feature_flags,
inject_test_feature_flags,
[FeatureFlags]),

Restrictions = [{feature_flag_blocked, [{Testcase, "This feature flag is blocked"}]}],
rabbit_ct_broker_helpers:rpc_all(Config,
application, set_env, [rabbitmq_management, restrictions, Restrictions]),
rabbit_ct_helpers:testcase_started(Config, Testcase);
init_per_testcase(queues_detailed_test, Config) ->
IsEnabled = rabbit_ct_broker_helpers:is_feature_flag_enabled(
Config, detailed_queues_endpoint),
Expand Down Expand Up @@ -308,6 +324,13 @@ end_per_testcase0(disabled_operator_policy_test, Config) ->
end_per_testcase0(disabled_qq_replica_opers_test, Config) ->
rpc(Config, application, unset_env, [rabbitmq_management, restrictions]),
Config;
end_per_testcase0(feature_flag_blocked_test, Config) ->
rabbit_ct_broker_helpers:rpc(Config, 0, application, unset_env,
[rabbitmq_management, restrictions]),
ok = rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_feature_flags,
clear_injected_test_feature_flags,
[]),
Config;
end_per_testcase0(Testcase, Config)
when Testcase == list_deprecated_features_test;
Testcase == list_used_deprecated_features_test ->
Expand Down Expand Up @@ -3770,6 +3793,11 @@ disabled_qq_replica_opers_test(Config) ->
http_delete(Config, "/queues/quorum/replicas/on/" ++ Nodename ++ "/shrink", ?METHOD_NOT_ALLOWED),
passed.

feature_flag_blocked_test(Config) ->
Body = "",
http_put(Config, "/feature-flags/" ++ atom_to_list(?FUNCTION_NAME) ++ "/enable", Body, ?METHOD_NOT_ALLOWED),
passed.

list_deprecated_features_test(Config) ->
Desc = "This is a deprecated feature",
DocUrl = "https://rabbitmq.com/",
Expand Down