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

rabbit_feature_flags: Rework the management UI page #12643

Merged
merged 4 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion deps/rabbit/src/rabbit_core_ff.erl
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,10 @@

-rabbit_feature_flag(
{khepri_db,
#{desc => "New Raft-based metadata store. Fully supported as of RabbitMQ 4.0",
#{desc => "New Raft-based metadata store.",
doc_url => "https://www.rabbitmq.com/docs/next/metadata-store",
stability => experimental,
experiment_level => supported,
depends_on => [feature_flags_v2,
direct_exchange_routing_v2,
maintenance_mode_status,
Expand Down
98 changes: 90 additions & 8 deletions deps/rabbit/src/rabbit_feature_flags.erl
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
get_state/1,
get_stability/1,
get_require_level/1,
get_experiment_level/1,
check_node_compatibility/1, check_node_compatibility/2,
sync_feature_flags_with_cluster/2,
refresh_feature_flags_after_app_load/0,
Expand Down Expand Up @@ -149,6 +150,7 @@
doc_url => string(),
stability => stability(),
require_level => require_level(),
experiment_level => experiment_level(),
depends_on => [feature_name()],
callbacks =>
#{callback_name() => callback_fun_name()}}.
Expand Down Expand Up @@ -186,6 +188,7 @@
doc_url => string(),
stability => stability(),
require_level => require_level(),
experiment_level => experiment_level(),
depends_on => [feature_name()],
callbacks =>
#{callback_name() => callback_fun_name()},
Expand Down Expand Up @@ -219,6 +222,24 @@
%% A soft required feature flag will be automatically enabled when a RabbitMQ
%% node is upgraded to a version where it is required.

-type experiment_level() :: unsupported | supported.
%% The level of support of an experimental feature flag.
%%
%% At first, an experimental feature flag is offered to give a chance to users
%% to try it and give feedback as part of the design and development of the
%% feature. At this stage, it is unsupported: it must not be enabled in a
%% production environment and upgrade to a later version of RabbitMQ while
%% this experimental feature flag is enabled is not supported.
%%
%% Then, the experimental feature flag becomes supported. At this point, it is
%% stable enough that upgrading is guarantied and help will be provided.
dumbbell marked this conversation as resolved.
Show resolved Hide resolved
%% However it is not mature enough to be marked as stable (which would make it
%% enabled by default in a new deployment or when running `rabbitmqctl
%% enable_feature_flag all'.
%%
%% The next step is to change its stability to `stable'. Once done, the
%% `experiment_level()' field is irrelevant.

-type callback_fun_name() :: {Module :: module(), Function :: atom()}.
%% The name of the module and function to call when changing the state of
%% the feature flag.
Expand Down Expand Up @@ -327,6 +348,8 @@
feature_state/0,
feature_states/0,
stability/0,
require_level/0,
experiment_level/0,
callback_fun_name/0,
callbacks/0,
callback_name/0,
Expand Down Expand Up @@ -696,30 +719,38 @@ info() ->
info(Options) when is_map(Options) ->
rabbit_ff_extra:info(Options).

-spec get_state(feature_name()) -> enabled | disabled | unavailable.
-spec get_state(feature_name()) -> enabled |
state_changing |
disabled |
unavailable.
%% @doc
%% Returns the state of a feature flag.
%%
%% The possible states are:
%% <ul>
%% <li>`enabled': the feature flag is enabled.</li>
%% <li>`state_changing': the feature flag is being enabled.</li>
%% <li>`disabled': the feature flag is supported by all nodes in the
%% cluster but currently disabled.</li>
%% <li>`unavailable': the feature flag is unsupported by at least one
%% node in the cluster and can not be enabled for now.</li>
%% </ul>
%%
%% @param FeatureName The name of the feature flag to check.
%% @returns `enabled', `disabled' or `unavailable'.
%% @returns `enabled', `state_changing', `disabled' or `unavailable'.

get_state(FeatureName) when is_atom(FeatureName) ->
IsEnabled = is_enabled(FeatureName),
IsEnabled = is_enabled(FeatureName, non_blocking),
case IsEnabled of
true -> enabled;
false -> case is_supported(FeatureName) of
true -> disabled;
false -> unavailable
end
true ->
enabled;
state_changing ->
state_changing;
false ->
case is_supported(FeatureName) of
true -> disabled;
false -> unavailable
end
end.

-spec get_stability
Expand Down Expand Up @@ -809,6 +840,45 @@ get_require_level(FeatureProps) when ?IS_DEPRECATION(FeatureProps) ->
_ -> none
end.

-spec get_experiment_level
(FeatureName) -> ExperimentLevel | undefined when
FeatureName :: feature_name(),
ExperimentLevel :: experiment_level() | none;
(FeatureProps) -> ExperimentLevel when
FeatureProps ::
feature_props_extended() |
rabbit_deprecated_features:feature_props_extended(),
ExperimentLevel :: experiment_level() | none.
%% @doc
%% Returns the experimental level of an experimental feature flag.
%%
%% The possible experiment levels are:
%% <ul>
%% <li>`unsupported': the experimental feature flag must not be enabled in
%% production and upgrades with it enabled is unsupported.</li>
%% <li>`supported': the experimental feature flag is not yet stable enough but
%% upgrades are guarantied to be possible. This is returned too if the
dumbbell marked this conversation as resolved.
Show resolved Hide resolved
%% feature flag is stable or required.</li>
%% </ul>
%%
%% @param FeatureName The name of the feature flag to check.
%% @param FeatureProps A feature flag properties map.
%% @returns `unsupported', `supported', or `undefined' if the given feature
%% flag name doesn't correspond to a known feature flag.

get_experiment_level(FeatureName) when is_atom(FeatureName) ->
case rabbit_ff_registry_wrapper:get(FeatureName) of
undefined -> undefined;
FeatureProps -> get_experiment_level(FeatureProps)
end;
get_experiment_level(FeatureProps) when ?IS_FEATURE_FLAG(FeatureProps) ->
case get_stability(FeatureProps) of
experimental -> maps:get(experiment_level, FeatureProps, unsupported);
_ -> supported
end;
get_experiment_level(FeatureProps) when ?IS_DEPRECATION(FeatureProps) ->
supported.

%% -------------------------------------------------------------------
%% Feature flags registry.
%% -------------------------------------------------------------------
Expand Down Expand Up @@ -968,6 +1038,7 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) ->
doc_url,
stability,
require_level,
experiment_level,
depends_on,
callbacks],
?assertEqual([], maps:keys(FeatureProps) -- ValidProps),
Expand All @@ -979,6 +1050,17 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) ->
?assert(Stability =:= stable orelse
Stability =:= experimental orelse
Stability =:= required),
?assert(Stability =:= experimental orelse
not maps:is_key(experiment_level, FeatureProps)),
?assert(Stability =:= required orelse
not maps:is_key(require_level, FeatureProps)),
RequireLevel = maps:get(require_level, FeatureProps, soft),
?assert(RequireLevel =:= hard orelse RequireLevel =:= soft),
ExperimentLevel = maps:get(
experiment_level, FeatureProps,
unsupported),
?assert(ExperimentLevel =:= unsupported orelse
ExperimentLevel =:= supported),
?assertNot(maps:is_key(migration_fun, FeatureProps)),
?assertNot(maps:is_key(warning, FeatureProps)),
case FeatureProps of
Expand Down
16 changes: 16 additions & 0 deletions deps/rabbit/src/rabbit_ff_extra.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
-type cli_info_entry() :: [{name, rabbit_feature_flags:feature_name()} |
{state, enabled | disabled | unavailable} |
{stability, rabbit_feature_flags:stability()} |
{require_level,
rabbit_feature_flags:require_level()} |
{experiment_level,
rabbit_feature_flags:experiment_level()} |
{callbacks,
[rabbit_feature_flags:callback_name()]} |
{provided_by, atom()} |
{desc, string()} |
{doc_url, string()}].
Expand Down Expand Up @@ -61,6 +67,11 @@ cli_info(FeatureFlags) ->
FeatureProps = maps:get(FeatureName, FeatureFlags),
State = rabbit_feature_flags:get_state(FeatureName),
Stability = rabbit_feature_flags:get_stability(FeatureProps),
RequireLevel = rabbit_feature_flags:get_require_level(
FeatureProps),
ExperimentLevel = rabbit_feature_flags:get_experiment_level(
FeatureProps),
Callbacks = maps:keys(maps:get(callbacks, FeatureProps, #{})),
App = maps:get(provided_by, FeatureProps),
Desc = maps:get(desc, FeatureProps, ""),
DocUrl = maps:get(doc_url, FeatureProps, ""),
Expand All @@ -69,6 +80,9 @@ cli_info(FeatureFlags) ->
{doc_url, unicode:characters_to_binary(DocUrl)},
{state, State},
{stability, Stability},
{require_level, RequireLevel},
{experiment_level, ExperimentLevel},
{callbacks, Callbacks},
{provided_by, App}],
[FFInfo | Acc]
end, [], lists:sort(maps:keys(FeatureFlags))).
Expand Down Expand Up @@ -160,6 +174,8 @@ info(FeatureFlags, Options) ->
{State, Color} = case State0 of
enabled ->
{"Enabled", Green};
state_changing ->
{"(Changing)", Yellow};
disabled ->
{"Disabled", Yellow};
unavailable ->
Expand Down
57 changes: 55 additions & 2 deletions deps/rabbitmq_management/priv/www/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ div.form-popup-help {
width: 500px;
z-index: 2;
}
p.warning, div.form-popup-warn { background: #FF9; }
div.warning, p.warning, div.form-popup-warn { background: #FF9; }

div.form-popup-options { z-index: 3; overflow:auto; max-height:95%; }

Expand All @@ -255,7 +255,14 @@ div.form-popup-options span:hover {
cursor: pointer;
}

p.warning { padding: 15px; border-radius: 5px; -moz-border-radius: 5px; text-align: center; }
div.warning, p.warning { padding: 15px; border-radius: 5px; -moz-border-radius: 5px; text-align: center; }
div.warning {
margin: 15px 0;
}

div.warning button {
margin: auto;
}

.highlight { min-width: 120px; font-size: 120%; text-align:center; padding:10px; background-color: #ddd; margin: 0 20px 0 0; color: #888; border-radius: 5px; -moz-border-radius: 5px; }
.highlight strong { font-size: 2em; display: block; color: #444; font-weight: normal; }
Expand Down Expand Up @@ -367,3 +374,49 @@ div.bindings-wrapper p.arrow { font-size: 200%; }
}

table.dynamic-shovels td label {width: 200px; margin-right:10px;padding: 4px 0px 5px 0px}

input[type=checkbox].toggle {
display: none;
}

label.toggle {
cursor: pointer;
text-indent: -9999px;
width: 32px;
height: 16px;
background: #ff5630;
display: block;
border-radius: 16px;
position: relative;
margin: auto;
}

label.toggle:after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 12px;
height: 12px;
background: #fff;
border-radius: 12px;
transition: 0.3s;
}

input.toggle:indeterminate + label.toggle {
background: #ffab00;
}

input.toggle:checked + label.toggle {
background: #36b37e;
}

input.toggle:indeterminate + label.toggle:after {
left: calc(50%);
transform: translateX(-50%);
}

input.toggle:checked + label.toggle:after {
left: calc(100% - 2px);
transform: translateX(-100%);
}
17 changes: 17 additions & 0 deletions deps/rabbitmq_management/priv/www/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,23 @@ function reset_timer() {
}
}

function pause_auto_refresh() {
if (typeof globalThis.rmq_webui_auto_refresh_paused == 'undefined')
globalThis.rmq_webui_auto_refresh_paused = 0;

globalThis.rmq_webui_auto_refresh_paused++;
if (timer != null) {
clearInterval(timer);
}
}

function resume_auto_refresh() {
globalThis.rmq_webui_auto_refresh_paused--;
if (globalThis.rmq_webui_auto_refresh_paused == 0) {
reset_timer();
}
}

function update_manual(div, query) {
var path;
var template;
Expand Down
Loading
Loading