From ca1d24a98d21ac54c48b6b92dc86005c21568113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Gro=C3=9Fe?= Date: Sat, 13 Feb 2021 14:26:12 +0100 Subject: [PATCH] feature(mgmt): restart a listener example: ``` emqx_ctl listener restart mqtt:ssl:external ``` or ``` PUT /api/v4/listeners/mqtt:ssl:external/restart ``` thank you @zmstone for providing the listener-identifier apis :) --- .../emqx_management/src/emqx_mgmt.erl | 8 +++++ .../src/emqx_mgmt_api_listeners.erl | 29 ++++++++++++++++++- .../emqx_management/src/emqx_mgmt_cli.erl | 11 ++++++- .../emqx_management/test/emqx_mgmt_SUITE.erl | 12 ++++++++ src/emqx_listeners.erl | 20 +++++++++---- 5 files changed, 73 insertions(+), 7 deletions(-) diff --git a/lib-opensource/emqx_management/src/emqx_mgmt.erl b/lib-opensource/emqx_management/src/emqx_mgmt.erl index 587e4152f..19474e5a1 100644 --- a/lib-opensource/emqx_management/src/emqx_mgmt.erl +++ b/lib-opensource/emqx_management/src/emqx_mgmt.erl @@ -102,6 +102,7 @@ %% Listeners -export([ list_listeners/0 , list_listeners/1 + , restart_listener/2 ]). %% Alarms @@ -560,6 +561,12 @@ list_listeners(Node) when Node =:= node() -> list_listeners(Node) -> rpc_call(Node, list_listeners, [Node]). +restart_listener(Node, Identifier) when Node =:= node() -> + emqx_listeners:restart_listener(Identifier); + +restart_listener(Node, Identifier) -> + rpc_call(Node, restart_listener, [Node, Identifier]). + %%-------------------------------------------------------------------- %% Get Alarms %%-------------------------------------------------------------------- @@ -978,3 +985,4 @@ action_to_prop_list({action_instance, ActionInstId, Name, FallbackActions, Args} {name, Name}, {fallbacks, actions_to_prop_list(FallbackActions)}, {args, Args}]. + diff --git a/lib-opensource/emqx_management/src/emqx_mgmt_api_listeners.erl b/lib-opensource/emqx_management/src/emqx_mgmt_api_listeners.erl index 153d20d38..7acbe8107 100644 --- a/lib-opensource/emqx_management/src/emqx_mgmt_api_listeners.erl +++ b/lib-opensource/emqx_management/src/emqx_mgmt_api_listeners.erl @@ -30,7 +30,19 @@ func => list, descr => "A list of listeners on the node"}). --export([list/2]). +-rest_api(#{name => restart_listener, + method => 'PUT', + path => "/listeners/:bin:identifier/restart", + func => restart, + descr => "Restart a listener in the cluster"}). + +-rest_api(#{name => restart_node_listener, + method => 'PUT', + path => "/nodes/:atom:node/listeners/:bin:identifier/restart", + func => restart, + descr => "Restart a listener on a node"}). + +-export([list/2, restart/2]). %% List listeners on a node. list(#{node := Node}, _Params) -> @@ -41,6 +53,21 @@ list(_Binding, _Params) -> return({ok, [#{node => Node, listeners => format(Listeners)} || {Node, Listeners} <- emqx_mgmt:list_listeners()]}). +%% Restart listeners on a node. +restart(#{node := Node, identifier := Identifier}, _Params) -> + case emqx_mgmt:restart_listener(Node, Identifier) of + ok -> return({ok, "Listener restarted."}); + {error, Error} -> return({error, Error}) + end; + +%% Restart listeners in the cluster. +restart(#{identifier := Identifier}, _Params) -> + Results = [{Node, emqx_mgmt:restart_listener(Node, Identifier)} || {Node, _Info} <- emqx_mgmt:list_nodes()], + case lists:filter(fun({_, Result}) -> Result =/= ok end, Results) of + [] -> return(ok); + Errors -> return({error, Errors}) + end. + format(Listeners) when is_list(Listeners) -> [ Info#{listen_on => list_to_binary(esockd:to_string(ListenOn))} || Info = #{listen_on := ListenOn} <- Listeners ]; diff --git a/lib-opensource/emqx_management/src/emqx_mgmt_cli.erl b/lib-opensource/emqx_management/src/emqx_mgmt_cli.erl index db05cafb8..8f3a09026 100644 --- a/lib-opensource/emqx_management/src/emqx_mgmt_cli.erl +++ b/lib-opensource/emqx_management/src/emqx_mgmt_cli.erl @@ -550,10 +550,19 @@ listeners(["stop", _Proto, ListenOn]) -> end, stop_listener(emqx_listeners:find_by_listen_on(ListenOn1), ListenOn1); +listeners(["restart", Identifier]) -> + case emqx_listeners:restart_listener(Identifier) of + ok -> + emqx_ctl:print("Restarted ~s listener successfully.~n", [Identifier]); + {error, Error} -> + emqx_ctl:print("Failed to restart ~s listener: ~0p~n", [Identifier, Error]) + end; + listeners(_) -> emqx_ctl:usage([{"listeners", "List listeners"}, {"listeners stop ", "Stop a listener"}, - {"listeners stop ", "Stop a listener"} + {"listeners stop ", "Stop a listener"}, + {"listeners restart ", "Restart a listener"} ]). stop_listener(false, Input) -> diff --git a/lib-opensource/emqx_management/test/emqx_mgmt_SUITE.erl b/lib-opensource/emqx_management/test/emqx_mgmt_SUITE.erl index d8cb91dab..56db2d118 100644 --- a/lib-opensource/emqx_management/test/emqx_mgmt_SUITE.erl +++ b/lib-opensource/emqx_management/test/emqx_mgmt_SUITE.erl @@ -294,6 +294,18 @@ t_listeners_cmd_new(_) -> "Stop mqtt:wss:external listener on 0.0.0.0:8084 successfully.\n", emqx_mgmt_cli:listeners(["stop", "mqtt:wss:external"]) ), + ?assertEqual( + emqx_mgmt_cli:listeners(["restart", "mqtt:tcp:external"]), + "Restarted mqtt:tcp:external listener successfully.\n" + ), + ?assertEqual( + emqx_mgmt_cli:listeners(["restart", "mqtt:ssl:external"]), + "Restarted mqtt:ssl:external listener successfully.\n" + ), + ?assertEqual( + emqx_mgmt_cli:listeners(["restart", "bad:listener:identifier"]), + "Failed to restart bad:listener:identifier listener: {no_such_listener,\"bad:listener:identifier\"}\n" + ), unmock_print(). t_plugins_cmd(_) -> diff --git a/src/emqx_listeners.erl b/src/emqx_listeners.erl index eadc93b97..651be0e8e 100644 --- a/src/emqx_listeners.erl +++ b/src/emqx_listeners.erl @@ -173,24 +173,34 @@ with_port({Addr, Port}, Opts = #{socket_opts := SocketOption}) -> restart() -> lists:foreach(fun restart_listener/1, emqx:get_env(listeners, [])). --spec(restart_listener(listener()) -> any()). +-spec(restart_listener(listener() | string() | binary()) -> ok | {error, any()}). restart_listener(#{proto := Proto, listen_on := ListenOn, opts := Options}) -> - restart_listener(Proto, ListenOn, Options). + restart_listener(Proto, ListenOn, Options); +restart_listener(Identifier) -> + case emqx_listeners:find_by_id(Identifier) of + false -> {error, {no_such_listener, Identifier}}; + Listener -> restart_listener(Listener) + end. --spec(restart_listener(esockd:proto(), esockd:listen_on(), [esockd:option()]) -> any()). +-spec(restart_listener(esockd:proto(), esockd:listen_on(), [esockd:option()]) -> + ok | {error, any()}). restart_listener(tcp, ListenOn, _Options) -> esockd:reopen('mqtt:tcp', ListenOn); restart_listener(Proto, ListenOn, _Options) when Proto == ssl; Proto == tls -> esockd:reopen('mqtt:ssl', ListenOn); restart_listener(Proto, ListenOn, Options) when Proto == http; Proto == ws -> _ = cowboy:stop_listener(ws_name('mqtt:ws', ListenOn)), - start_listener(Proto, ListenOn, Options); + ok(start_listener(Proto, ListenOn, Options)); restart_listener(Proto, ListenOn, Options) when Proto == https; Proto == wss -> _ = cowboy:stop_listener(ws_name('mqtt:wss', ListenOn)), - start_listener(Proto, ListenOn, Options); + ok(start_listener(Proto, ListenOn, Options)); restart_listener(Proto, ListenOn, _Opts) -> esockd:reopen(Proto, ListenOn). +ok(ok) -> ok; +ok({ok, _}) -> ok; +ok(Error) -> Error. + %% @doc Stop all listeners. -spec(stop() -> ok). stop() ->