Skip to content

Commit

Permalink
Merge pull request #12643 from rabbitmq/rework-feature-flags-web-ui
Browse files Browse the repository at this point in the history
rabbit_feature_flags: Rework the management UI page
  • Loading branch information
dumbbell authored Nov 6, 2024
2 parents a7281c4 + f7a740c commit 7cbf64f
Show file tree
Hide file tree
Showing 6 changed files with 524 additions and 128 deletions.
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.
%% 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
%% 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

0 comments on commit 7cbf64f

Please sign in to comment.