summaryrefslogtreecommitdiff
path: root/deps/rabbit/src/rabbit_ff_extra.erl
diff options
context:
space:
mode:
Diffstat (limited to 'deps/rabbit/src/rabbit_ff_extra.erl')
-rw-r--r--deps/rabbit/src/rabbit_ff_extra.erl244
1 files changed, 244 insertions, 0 deletions
diff --git a/deps/rabbit/src/rabbit_ff_extra.erl b/deps/rabbit/src/rabbit_ff_extra.erl
new file mode 100644
index 0000000000..f0728d491e
--- /dev/null
+++ b/deps/rabbit/src/rabbit_ff_extra.erl
@@ -0,0 +1,244 @@
+%% This Source Code Form is subject to the terms of the Mozilla Public
+%% License, v. 2.0. If a copy of the MPL was not distributed with this
+%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
+%%
+%% @copyright 2018-2020 VMware, Inc. or its affiliates.
+%%
+%% @doc
+%% This module provides extra functions unused by the feature flags
+%% subsystem core functionality.
+
+-module(rabbit_ff_extra).
+
+-include_lib("stdout_formatter/include/stdout_formatter.hrl").
+
+-export([cli_info/0,
+ info/1,
+ info/2,
+ format_error/1]).
+
+-type cli_info() :: [cli_info_entry()].
+%% A list of feature flags properties, formatted for the RabbitMQ CLI.
+
+-type cli_info_entry() :: [{name, rabbit_feature_flags:feature_name()} |
+ {state, enabled | disabled | unavailable} |
+ {stability, rabbit_feature_flags:stability()} |
+ {provided_by, atom()} |
+ {desc, string()} |
+ {doc_url, string()}].
+%% A list of properties for a single feature flag, formatted for the
+%% RabbitMQ CLI.
+
+-type info_options() :: #{colors => boolean(),
+ lines => boolean(),
+ verbose => non_neg_integer()}.
+%% Options accepted by {@link info/1} and {@link info/2}.
+
+-export_type([info_options/0]).
+
+-spec cli_info() -> cli_info().
+%% @doc
+%% Returns a list of all feature flags properties.
+%%
+%% @returns the list of all feature flags properties.
+
+cli_info() ->
+ cli_info(rabbit_feature_flags:list(all)).
+
+-spec cli_info(rabbit_feature_flags:feature_flags()) -> cli_info().
+%% @doc
+%% Formats a map of feature flags and their properties into a list of
+%% feature flags properties as expected by the RabbitMQ CLI.
+%%
+%% @param FeatureFlags A map of feature flags.
+%% @returns the list of feature flags properties, created from the map
+%% specified in arguments.
+
+cli_info(FeatureFlags) ->
+ lists:foldr(
+ fun(FeatureName, Acc) ->
+ FeatureProps = maps:get(FeatureName, FeatureFlags),
+ State = rabbit_feature_flags:get_state(FeatureName),
+ Stability = rabbit_feature_flags:get_stability(FeatureProps),
+ App = maps:get(provided_by, FeatureProps),
+ Desc = maps:get(desc, FeatureProps, ""),
+ DocUrl = maps:get(doc_url, FeatureProps, ""),
+ FFInfo = [{name, FeatureName},
+ {desc, unicode:characters_to_binary(Desc)},
+ {doc_url, unicode:characters_to_binary(DocUrl)},
+ {state, State},
+ {stability, Stability},
+ {provided_by, App}],
+ [FFInfo | Acc]
+ end, [], lists:sort(maps:keys(FeatureFlags))).
+
+-spec info(info_options()) -> ok.
+%% @doc
+%% Displays an array of all supported feature flags and their properties
+%% on `stdout'.
+%%
+%% @param Options Options to tune what is displayed and how.
+
+info(Options) ->
+ %% Two tables: one for stable feature flags, one for experimental ones.
+ StableFF = rabbit_feature_flags:list(all, stable),
+ case maps:size(StableFF) of
+ 0 ->
+ ok;
+ _ ->
+ stdout_formatter:display(
+ #paragraph{content = "\n## Stable feature flags:",
+ props = #{bold => true}}),
+ info(StableFF, Options)
+ end,
+ ExpFF = rabbit_feature_flags:list(all, experimental),
+ case maps:size(ExpFF) of
+ 0 ->
+ ok;
+ _ ->
+ stdout_formatter:display(
+ #paragraph{content = "\n## Experimental feature flags:",
+ props = #{bold => true}}),
+ info(ExpFF, Options)
+ end,
+ case maps:size(StableFF) + maps:size(ExpFF) of
+ 0 -> ok;
+ _ -> state_legend(Options)
+ end.
+
+-spec info(rabbit_feature_flags:feature_flags(), info_options()) -> ok.
+%% @doc
+%% Displays an array of feature flags and their properties on `stdout',
+%% based on the specified feature flags map.
+%%
+%% @param FeatureFlags Map of the feature flags to display.
+%% @param Options Options to tune what is displayed and how.
+
+info(FeatureFlags, Options) ->
+ Verbose = maps:get(verbose, Options, 0),
+ UseColors = use_colors(Options),
+ UseLines = use_lines(Options),
+ Title = case UseColors of
+ true -> #{title => true};
+ false -> #{}
+ end,
+ Bold = case UseColors of
+ true -> #{bold => true};
+ false -> #{}
+ end,
+ {Green, Yellow, Red} = case UseColors of
+ true ->
+ {#{fg => green},
+ #{fg => yellow},
+ #{bold => true,
+ bg => red}};
+ false ->
+ {#{}, #{}, #{}}
+ end,
+ Border = case UseLines of
+ true -> #{border_drawing => ansi};
+ false -> #{border_drawing => ascii}
+ end,
+ %% Table columns:
+ %% | Name | State | Provided by | Description
+ %%
+ %% where:
+ %% State = Enabled | Disabled | Unavailable (if a node doesn't
+ %% support it).
+ TableHeader = #row{cells = ["Name",
+ "State",
+ "Provided",
+ "Description"],
+ props = Title},
+ Nodes = lists:sort([node() | rabbit_feature_flags:remote_nodes()]),
+ Rows = lists:map(
+ fun(FeatureName) ->
+ FeatureProps = maps:get(FeatureName, FeatureFlags),
+ State0 = rabbit_feature_flags:get_state(FeatureName),
+ {State, Color} = case State0 of
+ enabled ->
+ {"Enabled", Green};
+ disabled ->
+ {"Disabled", Yellow};
+ unavailable ->
+ {"Unavailable", Red}
+ end,
+ App = maps:get(provided_by, FeatureProps),
+ Desc = maps:get(desc, FeatureProps, ""),
+ VFun = fun(Node) ->
+ Supported =
+ rabbit_feature_flags:does_node_support(
+ Node, [FeatureName], 60000),
+ {Label, LabelColor} =
+ case Supported of
+ true -> {"supported", #{}};
+ false -> {"unsupported", Red}
+ end,
+ #paragraph{content =
+ [rabbit_misc:format(" ~s: ",
+ [Node]),
+ #paragraph{content = Label,
+ props = LabelColor}]}
+ end,
+ ExtraLines = if
+ Verbose > 0 ->
+ NodesList = lists:join(
+ "\n",
+ lists:map(
+ VFun, Nodes)),
+ ["\n\n",
+ "Per-node support level:\n"
+ | NodesList];
+ true ->
+ []
+ end,
+ [#paragraph{content = FeatureName,
+ props = Bold},
+ #paragraph{content = State,
+ props = Color},
+ #paragraph{content = App},
+ #paragraph{content = [Desc | ExtraLines]}]
+ end, lists:sort(maps:keys(FeatureFlags))),
+ io:format("~n", []),
+ stdout_formatter:display(#table{rows = [TableHeader | Rows],
+ props = Border#{cell_padding => {0, 1}}}).
+
+use_colors(Options) ->
+ maps:get(colors, Options, true).
+
+use_lines(Options) ->
+ maps:get(lines, Options, true).
+
+state_legend(Options) ->
+ UseColors = use_colors(Options),
+ {Green, Yellow, Red} = case UseColors of
+ true ->
+ {#{fg => green},
+ #{fg => yellow},
+ #{bold => true,
+ bg => red}};
+ false ->
+ {#{}, #{}, #{}}
+ end,
+ Enabled = #paragraph{content = "Enabled", props = Green},
+ Disabled = #paragraph{content = "Disabled", props = Yellow},
+ Unavailable = #paragraph{content = "Unavailable", props = Red},
+ stdout_formatter:display(
+ #paragraph{
+ content =
+ ["\n",
+ "Possible states:\n",
+ " ", Enabled, ": The feature flag is enabled on all nodes\n",
+ " ", Disabled, ": The feature flag is disabled on all nodes\n",
+ " ", Unavailable, ": The feature flag cannot be enabled because"
+ " one or more nodes do not support it\n"]}).
+
+-spec format_error(any()) -> string().
+%% @doc
+%% Formats the error reason term so it can be presented to human beings.
+%%
+%% @param Reason The term in the `{error, Reason}' tuple.
+%% @returns the formatted error reason.
+
+format_error(Reason) ->
+ rabbit_misc:format("~p", [Reason]).