diff options
Diffstat (limited to 'deps/rabbit/src/rabbit_ff_extra.erl')
-rw-r--r-- | deps/rabbit/src/rabbit_ff_extra.erl | 244 |
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]). |