From 22835c729c7f5d3099dd85474b01f773d66287a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-S=C3=A9bastien=20P=C3=A9dron?= Date: Thu, 31 Oct 2024 09:26:45 +0100 Subject: [PATCH] rabbit_feature_flags: Rework the management UI page [Why] The "Feature flags" admin section had several issues: * It was not designed for experimental feature flags. What was done for RabbitMQ 4.0.0 was still unclear as to what a user should expect for experimental feature flags. * The UI uses synchronous requests from the browser main thread. It means that for a feature flag that has a long running migration callback, the browser tab could freeze for a very long time. [How] The feature flags table is reworked and now displays: * a series of icons to highlight the following: * a feature flag that has a migration function and thus that can take time to be enabled * a feature flag that is experimental * whether this experimental feature flag is supported or not * a toggle to quickly show if a feature flag is enabled or not and let the user enable it at the same time. For stable feature flags, when a user click on the toggle, the toggle goes into an intermediate state while waiting for the response from the broker. If the response is successful, the toggle is green. Otherwise it goes back to red and the error is displayed in a popup as before. For experimental feature flags, when a user click on the toggle, a popup is displayed to let the user know of the possible constraints and consequences, with one or two required checkboxes to tick so the user confirms they understand the message. The feature flag is enabled only after the user validates the popup. The displayed message and the checkboxes depend on if the experimental feature flag is supported or not (it is a new attribute of experimental feature flags). The request to enable feature flags now uses the modern `fetch()` API. Therefore it uses Javascript promises and does not block the main thread: the UI remains responsive while a migration callback runs. Finally, an "Enable all stable feature flags" button has been added to the warning that tells the user some stable feature flags are still disabled. --- .../rabbitmq_management/priv/www/css/main.css | 57 ++- .../priv/www/js/tmpl/feature-flags.ejs | 448 +++++++++++++----- 2 files changed, 386 insertions(+), 119 deletions(-) diff --git a/deps/rabbitmq_management/priv/www/css/main.css b/deps/rabbitmq_management/priv/www/css/main.css index a3bcaae5d5f5..b3e404b794b8 100644 --- a/deps/rabbitmq_management/priv/www/css/main.css +++ b/deps/rabbitmq_management/priv/www/css/main.css @@ -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%; } @@ -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; } @@ -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%); +} diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs index 070acdb39420..42b8ad2b4f90 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs @@ -1,145 +1,303 @@ +

Feature Flags

<% - var needs_enabling = false; + var nonreq_feature_flags = []; for (var i = 0; i < feature_flags.length; i++) { - var feature_flag = feature_flags[i]; - if (feature_flag.state == "disabled" && feature_flag.stability != "experimental") { - needs_enabling = true; - } + if (feature_flags[i].stability == 'required') + continue; + nonreq_feature_flags.push(feature_flags[i]); } - if (needs_enabling) { %> -

- All stable feature flags must be enabled after completing an upgrade. Without enabling all flags, upgrading to future minor or major versions of RabbitMQ may not be possible. [Learn more] -

- <% } %> + %> +

Feature Flags

-<%= filter_ui(feature_flags) %> -
-<% if (feature_flags.length > 0) { %> - - - - - - - - - - <% - for (var i = 0; i < feature_flags.length; i++) { - var feature_flag = feature_flags[i]; - if (feature_flag.stability == "required") { - /* Hide required feature flags. There is nothing the user can do - * about them and they just add noise to the UI. */ - continue; - } - if (feature_flag.stability == "experimental") { - continue; - } - var state_color = "grey"; - if (feature_flag.state == "enabled") { - state_color = "green"; - } else if (feature_flag.state == "disabled") { - state_color = "yellow"; - } else if (feature_flag.state == "unsupported") { - state_color = "red"; - } - %> - > - - - - - <% } %> - -
<%= fmt_sort('Name', 'name') %><%= fmt_sort('State', 'state') %>Description
<%= fmt_string(feature_flag.name) %> - <% if (feature_flag.stability == "experimental") { %> - Experimental - <% } else if (feature_flag.stability == "stable" && feature_flag.state == "disabled") { %> -

Disabled!

- <% } %> - <% if (feature_flag.state == "disabled") { %> -
- - -
- <% } else { %> - - <%= fmt_string(feature_flag.state) %> - - <% } %> -
-

<%= fmt_string(feature_flag.desc) %>

- <% if (feature_flag.doc_url) { %> -

[Learn more]

- <% } %> -
-<% } else { %> -

... no feature_flags ...

-<% } %> -
-
-
+<%= filter_ui(nonreq_feature_flags) %> +
+<% if (nonreq_feature_flags.length > 0) { %> + + + -These flags can be enabled in production deployments after an appropriate amount of testing in non-production environments. -

+ <% - for (var i = 0; i < feature_flags.length; i++) { - var feature_flag = feature_flags[i]; - if (feature_flag.stability != "experimental") { - continue; - } - var state_color = "grey"; - if (feature_flag.state == "enabled") { - state_color = "green"; - } else if (feature_flag.state == "disabled") { - state_color = "yellow"; - } else if (feature_flag.state == "unsupported") { - state_color = "red"; - } + for (var i = 0; i < nonreq_feature_flags.length; i++) { + var feature_flag = nonreq_feature_flags[i]; %> > - +
<%= fmt_sort('Name', 'name') %>Specificities <%= fmt_sort('State', 'state') %> Description
<%= fmt_string(feature_flag.name) %> - <% if (feature_flag.state == "disabled") { %> -
- -
-
-
- - -
- - <% } else { %> - - <%= fmt_string(feature_flag.state) %> - +
+ <% if (feature_flag.callbacks.includes('enable')) { %> + + This feature flags has a migration function which might take some time and consume resources. + + + <% } %> + <% if (feature_flag.stability == 'experimental') { %> + + This is an experimental feature flag + + <% } %> + <% if (feature_flag.experiment_level == 'unsupported') { %> + + This experimental feature flag is not yet supported at this stage and an upgrade path is not guarantied + + + <% } %> + + + checked disabled + <% } %> + <% if (feature_flag.state == 'state_changing') { %> + disabled + <% } %> + onchange='handle_feature_flag(this, "<%= feature_flag.name %>");'/> +

<%= fmt_string(feature_flag.desc) %>

@@ -151,6 +309,62 @@ These flags can be enabled in production deployments after an appropriate amount <% } %>
+ + + + +

Enabling an experimental feature flag

+

+ The feature flag is experimental. + This means the functionality behind it is still a work in progress. Here + are a few important things to keep in mind: +

+
    +
  1. +

    + Before enabling it, make sure to try it in a test environment + first before enabling it in production. +

    +

    + The feature flag is supported even though it is still experimental. + Therefore, upgrades to a later version of RabbitMQ with this feature flag + enabled are supported. +

    +

    + + +

    +
  2. +
  3. +

    + This development of this feature is at an early stage. Support is not + provided and enabling it in production is not recommended. +

    +

    + Once it is enabled, upgrades to a future version of RabbitMQ is not + guarantied! If there is no upgrade path, you will have to use a + blue-green migration + to upgrade RabbitMQ. +

    +

    + + +

    +

    + + +

    +
  4. +
  5. + If you enable it, + please give feedback, + this will help the RabbitMQ team polish it and make it stable as soon as + possible. +
  6. +
+ + +
<% } else { %>

... no feature_flags ...

<% } %>