Skip to content

Commit 7cbf64f

Browse files
authored
Merge pull request #12643 from rabbitmq/rework-feature-flags-web-ui
rabbit_feature_flags: Rework the management UI page
2 parents a7281c4 + f7a740c commit 7cbf64f

File tree

6 files changed

+524
-128
lines changed

6 files changed

+524
-128
lines changed

deps/rabbit/src/rabbit_core_ff.erl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,10 @@
146146

147147
-rabbit_feature_flag(
148148
{khepri_db,
149-
#{desc => "New Raft-based metadata store. Fully supported as of RabbitMQ 4.0",
149+
#{desc => "New Raft-based metadata store.",
150150
doc_url => "https://www.rabbitmq.com/docs/next/metadata-store",
151151
stability => experimental,
152+
experiment_level => supported,
152153
depends_on => [feature_flags_v2,
153154
direct_exchange_routing_v2,
154155
maintenance_mode_status,

deps/rabbit/src/rabbit_feature_flags.erl

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
get_state/1,
107107
get_stability/1,
108108
get_require_level/1,
109+
get_experiment_level/1,
109110
check_node_compatibility/1, check_node_compatibility/2,
110111
sync_feature_flags_with_cluster/2,
111112
refresh_feature_flags_after_app_load/0,
@@ -149,6 +150,7 @@
149150
doc_url => string(),
150151
stability => stability(),
151152
require_level => require_level(),
153+
experiment_level => experiment_level(),
152154
depends_on => [feature_name()],
153155
callbacks =>
154156
#{callback_name() => callback_fun_name()}}.
@@ -186,6 +188,7 @@
186188
doc_url => string(),
187189
stability => stability(),
188190
require_level => require_level(),
191+
experiment_level => experiment_level(),
189192
depends_on => [feature_name()],
190193
callbacks =>
191194
#{callback_name() => callback_fun_name()},
@@ -219,6 +222,24 @@
219222
%% A soft required feature flag will be automatically enabled when a RabbitMQ
220223
%% node is upgraded to a version where it is required.
221224

225+
-type experiment_level() :: unsupported | supported.
226+
%% The level of support of an experimental feature flag.
227+
%%
228+
%% At first, an experimental feature flag is offered to give a chance to users
229+
%% to try it and give feedback as part of the design and development of the
230+
%% feature. At this stage, it is unsupported: it must not be enabled in a
231+
%% production environment and upgrade to a later version of RabbitMQ while
232+
%% this experimental feature flag is enabled is not supported.
233+
%%
234+
%% Then, the experimental feature flag becomes supported. At this point, it is
235+
%% stable enough that upgrading is guarantied and help will be provided.
236+
%% However it is not mature enough to be marked as stable (which would make it
237+
%% enabled by default in a new deployment or when running `rabbitmqctl
238+
%% enable_feature_flag all'.
239+
%%
240+
%% The next step is to change its stability to `stable'. Once done, the
241+
%% `experiment_level()' field is irrelevant.
242+
222243
-type callback_fun_name() :: {Module :: module(), Function :: atom()}.
223244
%% The name of the module and function to call when changing the state of
224245
%% the feature flag.
@@ -327,6 +348,8 @@
327348
feature_state/0,
328349
feature_states/0,
329350
stability/0,
351+
require_level/0,
352+
experiment_level/0,
330353
callback_fun_name/0,
331354
callbacks/0,
332355
callback_name/0,
@@ -696,30 +719,38 @@ info() ->
696719
info(Options) when is_map(Options) ->
697720
rabbit_ff_extra:info(Options).
698721

699-
-spec get_state(feature_name()) -> enabled | disabled | unavailable.
722+
-spec get_state(feature_name()) -> enabled |
723+
state_changing |
724+
disabled |
725+
unavailable.
700726
%% @doc
701727
%% Returns the state of a feature flag.
702728
%%
703729
%% The possible states are:
704730
%% <ul>
705731
%% <li>`enabled': the feature flag is enabled.</li>
732+
%% <li>`state_changing': the feature flag is being enabled.</li>
706733
%% <li>`disabled': the feature flag is supported by all nodes in the
707734
%% cluster but currently disabled.</li>
708735
%% <li>`unavailable': the feature flag is unsupported by at least one
709736
%% node in the cluster and can not be enabled for now.</li>
710737
%% </ul>
711738
%%
712739
%% @param FeatureName The name of the feature flag to check.
713-
%% @returns `enabled', `disabled' or `unavailable'.
740+
%% @returns `enabled', `state_changing', `disabled' or `unavailable'.
714741

715742
get_state(FeatureName) when is_atom(FeatureName) ->
716-
IsEnabled = is_enabled(FeatureName),
743+
IsEnabled = is_enabled(FeatureName, non_blocking),
717744
case IsEnabled of
718-
true -> enabled;
719-
false -> case is_supported(FeatureName) of
720-
true -> disabled;
721-
false -> unavailable
722-
end
745+
true ->
746+
enabled;
747+
state_changing ->
748+
state_changing;
749+
false ->
750+
case is_supported(FeatureName) of
751+
true -> disabled;
752+
false -> unavailable
753+
end
723754
end.
724755

725756
-spec get_stability
@@ -809,6 +840,45 @@ get_require_level(FeatureProps) when ?IS_DEPRECATION(FeatureProps) ->
809840
_ -> none
810841
end.
811842

843+
-spec get_experiment_level
844+
(FeatureName) -> ExperimentLevel | undefined when
845+
FeatureName :: feature_name(),
846+
ExperimentLevel :: experiment_level() | none;
847+
(FeatureProps) -> ExperimentLevel when
848+
FeatureProps ::
849+
feature_props_extended() |
850+
rabbit_deprecated_features:feature_props_extended(),
851+
ExperimentLevel :: experiment_level() | none.
852+
%% @doc
853+
%% Returns the experimental level of an experimental feature flag.
854+
%%
855+
%% The possible experiment levels are:
856+
%% <ul>
857+
%% <li>`unsupported': the experimental feature flag must not be enabled in
858+
%% production and upgrades with it enabled is unsupported.</li>
859+
%% <li>`supported': the experimental feature flag is not yet stable enough but
860+
%% upgrades are guarantied to be possible. This is returned too if the
861+
%% feature flag is stable or required.</li>
862+
%% </ul>
863+
%%
864+
%% @param FeatureName The name of the feature flag to check.
865+
%% @param FeatureProps A feature flag properties map.
866+
%% @returns `unsupported', `supported', or `undefined' if the given feature
867+
%% flag name doesn't correspond to a known feature flag.
868+
869+
get_experiment_level(FeatureName) when is_atom(FeatureName) ->
870+
case rabbit_ff_registry_wrapper:get(FeatureName) of
871+
undefined -> undefined;
872+
FeatureProps -> get_experiment_level(FeatureProps)
873+
end;
874+
get_experiment_level(FeatureProps) when ?IS_FEATURE_FLAG(FeatureProps) ->
875+
case get_stability(FeatureProps) of
876+
experimental -> maps:get(experiment_level, FeatureProps, unsupported);
877+
_ -> supported
878+
end;
879+
get_experiment_level(FeatureProps) when ?IS_DEPRECATION(FeatureProps) ->
880+
supported.
881+
812882
%% -------------------------------------------------------------------
813883
%% Feature flags registry.
814884
%% -------------------------------------------------------------------
@@ -968,6 +1038,7 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) ->
9681038
doc_url,
9691039
stability,
9701040
require_level,
1041+
experiment_level,
9711042
depends_on,
9721043
callbacks],
9731044
?assertEqual([], maps:keys(FeatureProps) -- ValidProps),
@@ -979,6 +1050,17 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) ->
9791050
?assert(Stability =:= stable orelse
9801051
Stability =:= experimental orelse
9811052
Stability =:= required),
1053+
?assert(Stability =:= experimental orelse
1054+
not maps:is_key(experiment_level, FeatureProps)),
1055+
?assert(Stability =:= required orelse
1056+
not maps:is_key(require_level, FeatureProps)),
1057+
RequireLevel = maps:get(require_level, FeatureProps, soft),
1058+
?assert(RequireLevel =:= hard orelse RequireLevel =:= soft),
1059+
ExperimentLevel = maps:get(
1060+
experiment_level, FeatureProps,
1061+
unsupported),
1062+
?assert(ExperimentLevel =:= unsupported orelse
1063+
ExperimentLevel =:= supported),
9821064
?assertNot(maps:is_key(migration_fun, FeatureProps)),
9831065
?assertNot(maps:is_key(warning, FeatureProps)),
9841066
case FeatureProps of

deps/rabbit/src/rabbit_ff_extra.erl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424
-type cli_info_entry() :: [{name, rabbit_feature_flags:feature_name()} |
2525
{state, enabled | disabled | unavailable} |
2626
{stability, rabbit_feature_flags:stability()} |
27+
{require_level,
28+
rabbit_feature_flags:require_level()} |
29+
{experiment_level,
30+
rabbit_feature_flags:experiment_level()} |
31+
{callbacks,
32+
[rabbit_feature_flags:callback_name()]} |
2733
{provided_by, atom()} |
2834
{desc, string()} |
2935
{doc_url, string()}].
@@ -61,6 +67,11 @@ cli_info(FeatureFlags) ->
6167
FeatureProps = maps:get(FeatureName, FeatureFlags),
6268
State = rabbit_feature_flags:get_state(FeatureName),
6369
Stability = rabbit_feature_flags:get_stability(FeatureProps),
70+
RequireLevel = rabbit_feature_flags:get_require_level(
71+
FeatureProps),
72+
ExperimentLevel = rabbit_feature_flags:get_experiment_level(
73+
FeatureProps),
74+
Callbacks = maps:keys(maps:get(callbacks, FeatureProps, #{})),
6475
App = maps:get(provided_by, FeatureProps),
6576
Desc = maps:get(desc, FeatureProps, ""),
6677
DocUrl = maps:get(doc_url, FeatureProps, ""),
@@ -69,6 +80,9 @@ cli_info(FeatureFlags) ->
6980
{doc_url, unicode:characters_to_binary(DocUrl)},
7081
{state, State},
7182
{stability, Stability},
83+
{require_level, RequireLevel},
84+
{experiment_level, ExperimentLevel},
85+
{callbacks, Callbacks},
7286
{provided_by, App}],
7387
[FFInfo | Acc]
7488
end, [], lists:sort(maps:keys(FeatureFlags))).
@@ -160,6 +174,8 @@ info(FeatureFlags, Options) ->
160174
{State, Color} = case State0 of
161175
enabled ->
162176
{"Enabled", Green};
177+
state_changing ->
178+
{"(Changing)", Yellow};
163179
disabled ->
164180
{"Disabled", Yellow};
165181
unavailable ->

deps/rabbitmq_management/priv/www/css/main.css

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ div.form-popup-help {
232232
width: 500px;
233233
z-index: 2;
234234
}
235-
p.warning, div.form-popup-warn { background: #FF9; }
235+
div.warning, p.warning, div.form-popup-warn { background: #FF9; }
236236

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

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

258-
p.warning { padding: 15px; border-radius: 5px; -moz-border-radius: 5px; text-align: center; }
258+
div.warning, p.warning { padding: 15px; border-radius: 5px; -moz-border-radius: 5px; text-align: center; }
259+
div.warning {
260+
margin: 15px 0;
261+
}
262+
263+
div.warning button {
264+
margin: auto;
265+
}
259266

260267
.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; }
261268
.highlight strong { font-size: 2em; display: block; color: #444; font-weight: normal; }
@@ -367,3 +374,49 @@ div.bindings-wrapper p.arrow { font-size: 200%; }
367374
}
368375

369376
table.dynamic-shovels td label {width: 200px; margin-right:10px;padding: 4px 0px 5px 0px}
377+
378+
input[type=checkbox].toggle {
379+
display: none;
380+
}
381+
382+
label.toggle {
383+
cursor: pointer;
384+
text-indent: -9999px;
385+
width: 32px;
386+
height: 16px;
387+
background: #ff5630;
388+
display: block;
389+
border-radius: 16px;
390+
position: relative;
391+
margin: auto;
392+
}
393+
394+
label.toggle:after {
395+
content: '';
396+
position: absolute;
397+
top: 2px;
398+
left: 2px;
399+
width: 12px;
400+
height: 12px;
401+
background: #fff;
402+
border-radius: 12px;
403+
transition: 0.3s;
404+
}
405+
406+
input.toggle:indeterminate + label.toggle {
407+
background: #ffab00;
408+
}
409+
410+
input.toggle:checked + label.toggle {
411+
background: #36b37e;
412+
}
413+
414+
input.toggle:indeterminate + label.toggle:after {
415+
left: calc(50%);
416+
transform: translateX(-50%);
417+
}
418+
419+
input.toggle:checked + label.toggle:after {
420+
left: calc(100% - 2px);
421+
transform: translateX(-100%);
422+
}

deps/rabbitmq_management/priv/www/js/main.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,23 @@ function reset_timer() {
303303
}
304304
}
305305

306+
function pause_auto_refresh() {
307+
if (typeof globalThis.rmq_webui_auto_refresh_paused == 'undefined')
308+
globalThis.rmq_webui_auto_refresh_paused = 0;
309+
310+
globalThis.rmq_webui_auto_refresh_paused++;
311+
if (timer != null) {
312+
clearInterval(timer);
313+
}
314+
}
315+
316+
function resume_auto_refresh() {
317+
globalThis.rmq_webui_auto_refresh_paused--;
318+
if (globalThis.rmq_webui_auto_refresh_paused == 0) {
319+
reset_timer();
320+
}
321+
}
322+
306323
function update_manual(div, query) {
307324
var path;
308325
var template;

0 commit comments

Comments
 (0)