فهرست منبع

Merge pull request #7797 from zmstone/0427-5.0-dashboard-add-listener-enable-disable-config

feat(dashboard): add listener enable/disable config toggle
zhongwencool 3 سال پیش
والد
کامیت
061d9198ce

+ 13 - 3
apps/emqx_dashboard/i18n/emqx_dashboard_i18n.conf

@@ -74,8 +74,8 @@ Note: `sample_interval` should be a divisor of 60."""
   }
   inet6 {
     desc {
-      en: "Enable IPv6 support."
-      zh: "启用IPv6"
+      en: "Enable IPv6 support, default is false, which means IPv4 only."
+      zh: "启用IPv6, 如果机器不支持IPv6,请关闭此选项,否则会导致仪表盘无法使用。"
     }
     label {
       en: "IPv6"
@@ -85,7 +85,7 @@ Note: `sample_interval` should be a divisor of 60."""
   ipv6_v6only {
     desc {
       en: "Disable IPv4-to-IPv6 mapping for the listener."
-      zh: "禁用IPv4-to-IPv6映射"
+      zh: "当开启 inet6 功能的同时禁用 IPv4-to-IPv6 映射。该配置仅在 inet6 功能开启时有效。"
     }
     label {
       en: "IPv6 only"
@@ -132,6 +132,16 @@ Note: `sample_interval` should be a divisor of 60."""
       zh: "HTTPS"
     }
   }
+  listener_enable {
+    desc {
+        en: "Ignore or enable this listener"
+        zh: "忽略或启用该监听器配置"
+    }
+    label {
+        en: "Enable"
+        zh: "启用"
+    }
+  }
   bind {
     desc {
       en: "Port without IP(18083) or port with specified IP(127.0.0.1:18083)."

+ 25 - 22
apps/emqx_dashboard/src/emqx_dashboard.erl

@@ -153,10 +153,13 @@ apps() ->
     ].
 
 listeners(Listeners) ->
-    lists:map(
+    lists:filtermap(
         fun({Protocol, Conf}) ->
-            {Conf1, Bind} = ip_port(Conf),
-            {listener_name(Protocol, Conf1), Protocol, Bind, ranch_opts(Conf1)}
+            maps:get(enable, Conf) andalso
+                begin
+                    {Conf1, Bind} = ip_port(Conf),
+                    {true, {listener_name(Protocol, Conf1), Protocol, Bind, ranch_opts(Conf1)}}
+                end
         end,
         maps:to_list(Listeners)
     ).
@@ -172,34 +175,34 @@ init_i18n() ->
     Lang = emqx_conf:get([dashboard, i18n_lang], en),
     init_i18n(File, Lang).
 
-ranch_opts(RanchOptions) ->
+ranch_opts(Options) ->
     Keys = [
-        {ack_timeout, handshake_timeout},
+        handshake_timeout,
         connection_type,
         max_connections,
         num_acceptors,
         shutdown,
         socket
     ],
-    {S, R} = lists:foldl(fun key_take/2, {RanchOptions, #{}}, Keys),
-    R#{socket_opts => maps:fold(fun key_only/3, [], S)}.
-
-key_take(Key, {All, R}) ->
-    {K, KX} =
-        case Key of
-            {K1, K2} -> {K1, K2};
-            _ -> {Key, Key}
+    RanchOpts = maps:with(Keys, Options),
+    SocketOpts = maps:fold(
+        fun filter_false/3,
+        [],
+        maps:without([enable, inet6, ipv6_v6only | Keys], Options)
+    ),
+    InetOpts =
+        case Options of
+            #{inet6 := true, ipv6_v6only := true} ->
+                [inet6, {ipv6_v6only, true}];
+            #{inet6 := true, ipv6_v6only := false} ->
+                [inet6];
+            _ ->
+                [inet]
         end,
-    case maps:get(K, All, undefined) of
-        undefined ->
-            {All, R};
-        V ->
-            {maps:remove(K, All), R#{KX => V}}
-    end.
+    RanchOpts#{socket_opts => InetOpts ++ SocketOpts}.
 
-key_only(K, true, S) -> [K | S];
-key_only(_K, false, S) -> S;
-key_only(K, V, S) -> [{K, V} | S].
+filter_false(_K, false, S) -> S;
+filter_false(K, V, S) -> [{K, V} | S].
 
 listener_name(Protocol, #{port := Port, ip := IP}) ->
     Name =

+ 32 - 12
apps/emqx_dashboard/src/emqx_dashboard_config.erl

@@ -63,22 +63,42 @@ remove_handler() ->
     ok.
 
 pre_config_update(_Path, UpdateConf0, RawConf) ->
-    UpdateConf =
-        case UpdateConf0 of
-            #{<<"default_password">> := <<"******">>} ->
-                maps:remove(<<"default_password">>, UpdateConf0);
-            _ ->
-                UpdateConf0
-        end,
+    UpdateConf = remove_sensitive_data(UpdateConf0),
     NewConf = emqx_map_lib:deep_merge(RawConf, UpdateConf),
     {ok, NewConf}.
 
+-define(SENSITIVE_PASSWORD, <<"******">>).
+
+remove_sensitive_data(Conf0) ->
+    Conf1 =
+        case Conf0 of
+            #{<<"default_password">> := ?SENSITIVE_PASSWORD} ->
+                maps:remove(<<"default_password">>, Conf0);
+            _ ->
+                Conf0
+        end,
+    case Conf1 of
+        #{<<"listeners">> := #{<<"https">> := #{<<"password">> := ?SENSITIVE_PASSWORD}}} ->
+            emqx_map_lib:deep_remove([<<"listeners">>, <<"https">>, <<"password">>], Conf1);
+        _ ->
+            Conf1
+    end.
+
 post_config_update(_, _Req, NewConf, OldConf, _AppEnvs) ->
-    #{listeners := NewListeners} = NewConf,
-    #{listeners := OldListeners} = OldConf,
+    #{listeners := #{http := NewHttp, https := NewHttps}} = NewConf,
+    #{listeners := #{http := OldHttp, https := OldHttps}} = OldConf,
     _ =
-        case NewListeners =:= OldListeners of
-            true -> ok;
-            false -> erlang:send_after(500, ?MODULE, {update_listeners, OldListeners, NewListeners})
+        case diff_listeners(OldHttp, NewHttp, OldHttps, NewHttps) of
+            identical -> ok;
+            {Stop, Start} -> erlang:send_after(500, ?MODULE, {update_listeners, Stop, Start})
         end,
     ok.
+
+diff_listeners(Http, Http, Https, Https) ->
+    identical;
+diff_listeners(OldHttp, NewHttp, Https, Https) ->
+    {#{http => OldHttp}, #{http => NewHttp}};
+diff_listeners(Http, Http, OldHttps, NewHttps) ->
+    {#{https => OldHttps}, #{https => NewHttps}};
+diff_listeners(OldHttp, NewHttp, OldHttps, NewHttps) ->
+    {#{http => OldHttp, https => OldHttps}, #{http => NewHttp, https => NewHttps}}.

+ 60 - 20
apps/emqx_dashboard/src/emqx_dashboard_schema.erl

@@ -77,7 +77,32 @@ fields("listeners") ->
     ];
 fields("http") ->
     [
-        {"bind", fun bind/1},
+        enable(true),
+        bind(18803)
+        | common_listener_fields()
+    ];
+fields("https") ->
+    [
+        enable(false),
+        bind(18804)
+        | common_listener_fields() ++
+            exclude_fields(
+                ["enable", "fail_if_no_peer_cert"],
+                emqx_schema:server_ssl_opts_schema(#{}, true)
+            )
+    ].
+
+exclude_fields([], Fields) ->
+    Fields;
+exclude_fields([FieldName | Rest], Fields) ->
+    %% assert field exists
+    case lists:keytake(FieldName, 1, Fields) of
+        {value, _, New} -> exclude_fields(Rest, New);
+        false -> error({FieldName, Fields})
+    end.
+
+common_listener_fields() ->
+    [
         {"num_acceptors",
             sc(
                 integer(),
@@ -126,25 +151,40 @@ fields("http") ->
                     desc => ?DESC(ipv6_v6only)
                 }
             )}
-    ];
-fields("https") ->
-    fields("http") ++
-        proplists:delete(
-            "fail_if_no_peer_cert",
-            emqx_schema:server_ssl_opts_schema(#{}, true)
-        ).
-
-desc("dashboard") -> ?DESC(desc_dashboard);
-desc("listeners") -> ?DESC(desc_listeners);
-desc("http") -> ?DESC(desc_http);
-desc("https") -> ?DESC(desc_https);
-desc(_) -> undefined.
-
-bind(type) -> hoconsc:union([non_neg_integer(), emqx_schema:ip_port()]);
-bind(default) -> 18083;
-bind(required) -> true;
-bind(desc) -> ?DESC(bind);
-bind(_) -> undefined.
+    ].
+
+enable(Bool) ->
+    {"enable",
+        sc(
+            boolean(),
+            #{
+                default => Bool,
+                required => true,
+                desc => ?DESC(listener_enable)
+            }
+        )}.
+
+bind(Port) ->
+    {"bind",
+        sc(
+            hoconsc:union([non_neg_integer(), emqx_schema:ip_port()]),
+            #{
+                default => Port,
+                required => true,
+                desc => ?DESC(bind)
+            }
+        )}.
+
+desc("dashboard") ->
+    ?DESC(desc_dashboard);
+desc("listeners") ->
+    ?DESC(desc_listeners);
+desc("http") ->
+    ?DESC(desc_http);
+desc("https") ->
+    ?DESC(desc_https);
+desc(_) ->
+    undefined.
 
 default_username(type) -> binary();
 default_username(default) -> "admin";

+ 1 - 0
apps/emqx_dashboard/test/emqx_dashboard_api_test_helpers.erl

@@ -37,6 +37,7 @@ set_default_config(DefaultUsername) ->
     Config = #{
         listeners => #{
             http => #{
+                enable => true,
                 port => 18083
             }
         },

+ 1 - 1
mix.exs

@@ -48,7 +48,7 @@ defmodule EMQXUmbrella.MixProject do
     [
       {:lc, github: "emqx/lc", tag: "0.2.1"},
       {:redbug, "2.0.7"},
-      {:typerefl, github: "ieQu1/typerefl", tag: "0.9.0", override: true},
+      {:typerefl, github: "ieQu1/typerefl", tag: "0.9.1", override: true},
       {:ehttpc, github: "emqx/ehttpc", tag: "0.1.12"},
       {:gproc, github: "uwiger/gproc", tag: "0.8.0", override: true},
       {:jiffy, github: "emqx/jiffy", tag: "1.0.5", override: true},

+ 1 - 1
rebar.config

@@ -47,7 +47,7 @@
     [ {lc, {git, "https://github.com/emqx/lc.git", {tag, "0.2.1"}}}
     , {redbug, "2.0.7"}
     , {gpb, "4.11.2"} %% gpb only used to build, but not for release, pin it here to avoid fetching a wrong version due to rebar plugins scattered in all the deps
-    , {typerefl, {git, "https://github.com/ieQu1/typerefl", {tag, "0.9.0"}}}
+    , {typerefl, {git, "https://github.com/ieQu1/typerefl", {tag, "0.9.1"}}}
     , {ehttpc, {git, "https://github.com/emqx/ehttpc", {tag, "0.1.12"}}}
     , {gproc, {git, "https://github.com/uwiger/gproc", {tag, "0.8.0"}}}
     , {jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}}