feat(dashboard): support bind specific ip(port or ip:port).

This commit is contained in:
zhongwencool 2022-01-28 16:21:45 +08:00
parent 0cde9e6ecf
commit ec5d1b1463
6 changed files with 88 additions and 27 deletions

View File

@ -14,7 +14,7 @@ dashboard {
protocol = http
num_acceptors = 4
max_connections = 512
port = 18083
bind = 18083
backlog = 512
send_timeout = 5s
inet6 = false
@ -23,7 +23,7 @@ dashboard {
# ,
# {
# protocol = https
# port = 18084
# bind = "127.0.0.1:18084"
# num_acceptors = 2
# backlog = 512
# send_timeout = 5s

View File

@ -61,15 +61,26 @@ start_listeners() ->
dispatch => Dispatch,
middlewares => [cowboy_router, ?EMQX_MIDDLE, cowboy_handler]
},
[begin
Minirest = maps:put(protocol, Protocol, BaseMinirest),
{ok, _} = minirest:start(Name, RanchOptions, Minirest),
?ULOG("Start listener ~ts on ~p successfully.~n", [Name, Port])
end || {Name, Protocol, Port, RanchOptions} <- listeners()].
Res =
lists:foldl(fun({Name, Protocol, Port, RanchOptions}, Acc) ->
Minirest = BaseMinirest#{protocol => Protocol},
case minirest:start(Name, RanchOptions, Minirest) of
{ok, _} ->
?ULOG("Start listener ~ts on ~p successfully.~n", [Name, Port]),
Acc;
{error, _Reason} ->
%% Don't record the reason because minirest already does(too much logs noise).
[Name | Acc]
end
end, [], listeners()),
case Res of
[] -> ok;
_ -> {error, Res}
end.
stop_listeners() ->
[begin
ok = minirest:stop(Name),
_ = minirest:stop(Name),
?ULOG("Stop listener ~ts on ~p successfully.~n", [Name, Port])
end || {Name, _, Port, _} <- listeners()].
@ -85,12 +96,19 @@ apps() ->
listeners() ->
[begin
Protocol = maps:get(protocol, ListenerOptions, http),
Port = maps:get(port, ListenerOptions, 18083),
Name = listener_name(Protocol, Port),
RanchOptions = ranch_opts(maps:without([protocol], ListenerOptions)),
Protocol = maps:get(protocol, ListenerOption0, http),
{ListenerOption, Port} = ip_port(ListenerOption0),
Name = listener_name(Protocol, ListenerOption),
RanchOptions = ranch_opts(maps:without([protocol], ListenerOption)),
{Name, Protocol, Port, RanchOptions}
end || ListenerOptions <- emqx_conf:get([dashboard, listeners], [])].
end || ListenerOption0 <- emqx_conf:get([dashboard, listeners], [])].
ip_port(Opts) -> ip_port(maps:take(bind, Opts), Opts).
ip_port(error, Opts) -> {Opts#{port => 18083}, 18083};
ip_port({Port, Opts}, _) when is_integer(Port) -> {Opts#{port => Port}, Port};
ip_port({{IP, Port}, Opts}, _) -> {Opts#{port => Port, ip => IP}, Port}.
ranch_opts(RanchOptions) ->
Keys = [ {ack_timeout, handshake_timeout}
@ -119,8 +137,16 @@ key_only(K , true , S) -> [K | S];
key_only(_K, false, S) -> S;
key_only(K , V , S) -> [{K, V} | S].
listener_name(Protocol, Port) ->
Name = "dashboard:" ++ atom_to_list(Protocol) ++ ":" ++ integer_to_list(Port),
listener_name(Protocol, #{port := Port, ip := IP}) ->
Name = "dashboard:"
++ atom_to_list(Protocol) ++ ":"
++ inet:ntoa(IP) ++ ":"
++ integer_to_list(Port),
list_to_atom(Name);
listener_name(Protocol, #{port := Port}) ->
Name = "dashboard:"
++ atom_to_list(Protocol) ++ ":"
++ integer_to_list(Port),
list_to_atom(Name).
authorize(Req) ->

View File

@ -27,10 +27,13 @@
start(_StartType, _StartArgs) ->
{ok, Sup} = emqx_dashboard_sup:start_link(),
ok = mria_rlog:wait_for_shards([?DASHBOARD_SHARD], infinity),
_ = emqx_dashboard:start_listeners(),
case emqx_dashboard:start_listeners() of
ok ->
emqx_dashboard_cli:load(),
{ok, _Result} = emqx_dashboard_admin:add_default_user(),
{ok, Sup}.
{ok, Sup};
{error, Reason} -> {error, Reason}
end.
stop(_State) ->
emqx_dashboard_cli:unload(),

View File

@ -25,8 +25,18 @@ namespace() -> <<"dashboard">>.
roots() -> ["dashboard"].
fields("dashboard") ->
[ {listeners, hoconsc:array(hoconsc:union([hoconsc:ref(?MODULE, "http"),
hoconsc:ref(?MODULE, "https")]))}
[ {listeners,
sc(hoconsc:array(hoconsc:union([hoconsc:ref(?MODULE, "http"),
hoconsc:ref(?MODULE, "https")])),
#{ desc =>
"""HTTP(s) listeners identified by their protocol type,
is used to serve dashboard UI and restful HTTP API.<br>
Listeners must have a unique combination of port number and IP address.<br>
For example, an HTTP listener can listen on all configured IP addresses
on a given port for a machine by specifying the IP address 0.0.0.0.<br>
Alternatively, the HTTP listener can specify a unique IP address for each listener,
but use the same port.
""" })}
, {default_username, fun default_username/1}
, {default_password, fun default_password/1}
, {sample_interval, sc(emqx_schema:duration_s(), #{default => "10s"})}
@ -35,11 +45,23 @@ fields("dashboard") ->
];
fields("http") ->
[ {"protocol", hoconsc:enum([http, https])}
, {"port", hoconsc:mk(integer(), #{default => 18083})}
, {"num_acceptors", sc(integer(), #{default => 4})}
[ {"protocol", sc(
hoconsc:enum([http, https]),
#{ desc => "HTTP/HTTPS protocol."
, nullable => false
, default => http})}
, {"bind", fun bind/1}
, {"num_acceptors", sc(
integer(),
#{ default => 4
, desc => "Socket acceptor pool for TCP protocols."
})}
, {"max_connections", sc(integer(), #{default => 512})}
, {"backlog", sc(integer(), #{default => 1024})}
, {"backlog", sc(
integer(),
#{ default => 1024
, desc => "Defines the maximum length that the queue of pending connections can grow to."
})}
, {"send_timeout", sc(emqx_schema:duration(), #{default => "5s"})}
, {"inet6", sc(boolean(), #{default => false})}
, {"ipv6_v6only", sc(boolean(), #{default => false})}
@ -50,6 +72,12 @@ fields("https") ->
proplists:delete("fail_if_no_peer_cert",
emqx_schema:server_ssl_opts_schema(#{}, true)).
bind(type) -> hoconsc:union([non_neg_integer(), emqx_schema:ip_port()]);
bind(default) -> 18083;
bind(nullable) -> false;
bind(desc) -> "Port without IP(18083) or port with specified IP(127.0.0.1:18083).";
bind(_) -> undefined.
default_username(type) -> string();
default_username(default) -> "admin";
default_username(nullable) -> false;
@ -67,6 +95,10 @@ default_password(_) -> undefined.
cors(type) -> boolean();
cors(default) -> false;
cors(nullable) -> true;
cors(desc) ->
"""Support Cross-Origin Resource Sharing (CORS).
Allows a server to indicate any origins (domain, scheme, or port) other than
its own from which a browser should permit loading resources.""";
cors(_) -> undefined.
sc(Type, Meta) -> hoconsc:mk(Type, Meta).

View File

@ -5,7 +5,7 @@
%% FIXME: tag this as v3.1.3
{prometheus, {git, "https://github.com/emqx/prometheus.erl", {ref, "9994c76adca40d91a2545102230ccce2423fd8a7"}}},
{hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.23.0"}}},
{minirest, {git, "https://github.com/emqx/minirest", {tag, "1.2.10"}}}
{minirest, {git, "https://github.com/emqx/minirest", {tag, "1.2.11"}}}
]}.
{edoc_opts, [{preprocess, true}]}.

View File

@ -56,7 +56,7 @@
, {mria, {git, "https://github.com/emqx/mria", {tag, "0.1.5"}}}
, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.11.3"}}}
, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.8.0"}}}
, {minirest, {git, "https://github.com/emqx/minirest", {tag, "1.2.10"}}}
, {minirest, {git, "https://github.com/emqx/minirest", {tag, "1.2.11"}}}
, {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "0.5.2"}}}
, {replayq, "0.3.3"}
, {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {tag, "2.0.4"}}}