feat: support emqx_conf:update([exhook],Conf)

This commit is contained in:
某文 2023-05-28 23:16:37 +08:00
parent c1d935fd34
commit bd29433997
5 changed files with 243 additions and 29 deletions

View File

@ -1,7 +1,7 @@
%% -*- mode: erlang -*- %% -*- mode: erlang -*-
{application, emqx_exhook, [ {application, emqx_exhook, [
{description, "EMQX Extension for Hook"}, {description, "EMQX Extension for Hook"},
{vsn, "5.0.12"}, {vsn, "5.0.13"},
{modules, []}, {modules, []},
{registered, []}, {registered, []},
{mod, {emqx_exhook_app, []}}, {mod, {emqx_exhook_app, []}},

View File

@ -22,8 +22,7 @@
-export([ -export([
start/2, start/2,
stop/1, stop/1
prep_stop/1
]). ]).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -34,10 +33,6 @@ start(_StartType, _StartArgs) ->
{ok, Sup} = emqx_exhook_sup:start_link(), {ok, Sup} = emqx_exhook_sup:start_link(),
{ok, Sup}. {ok, Sup}.
prep_stop(State) ->
emqx_ctl:unregister_command(exhook),
State.
stop(_State) -> stop(_State) ->
ok. ok.

View File

@ -181,10 +181,14 @@ pre_config_update(_, {enable, Name, Enable}, OldConf) ->
of of
not_found -> throw(not_found); not_found -> throw(not_found);
NewConf -> {ok, lists:map(fun maybe_write_certs/1, NewConf)} NewConf -> {ok, lists:map(fun maybe_write_certs/1, NewConf)}
end. end;
pre_config_update(_, NewConf, _OldConf) when NewConf =:= #{} ->
{ok, NewConf#{<<"servers">> => []}};
pre_config_update(_, NewConf = #{<<"servers">> := Servers}, _OldConf) ->
{ok, NewConf#{<<"servers">> => lists:map(fun maybe_write_certs/1, Servers)}}.
post_config_update(_KeyPath, UpdateReq, NewConf, OldConf, _AppEnvs) -> post_config_update(_KeyPath, UpdateReq, NewConf, OldConf, _AppEnvs) ->
Result = call({update_config, UpdateReq, NewConf}), Result = call({update_config, UpdateReq, NewConf, OldConf}),
try_clear_ssl_files(UpdateReq, NewConf, OldConf), try_clear_ssl_files(UpdateReq, NewConf, OldConf),
{ok, Result}. {ok, Result}.
@ -197,6 +201,7 @@ post_config_update(_KeyPath, UpdateReq, NewConf, OldConf, _AppEnvs) ->
init([]) -> init([]) ->
process_flag(trap_exit, true), process_flag(trap_exit, true),
emqx_conf:add_handler([exhook, servers], ?MODULE), emqx_conf:add_handler([exhook, servers], ?MODULE),
emqx_conf:add_handler([exhook], ?MODULE),
ServerL = emqx:get_config([exhook, servers]), ServerL = emqx:get_config([exhook, servers]),
Servers = load_all_servers(ServerL), Servers = load_all_servers(ServerL),
Servers2 = reorder(ServerL, Servers), Servers2 = reorder(ServerL, Servers),
@ -222,22 +227,16 @@ handle_call(
OrderServers = sort_name_by_order(Infos, Servers), OrderServers = sort_name_by_order(Infos, Servers),
{reply, OrderServers, State}; {reply, OrderServers, State};
handle_call( handle_call(
{update_config, {move, _Name, _Position}, NewConfL}, {update_config, {move, _Name, _Position}, NewConfL, _},
_From, _From,
#{servers := Servers} = State #{servers := Servers} = State
) -> ) ->
Servers2 = reorder(NewConfL, Servers), Servers2 = reorder(NewConfL, Servers),
{reply, ok, State#{servers := Servers2}}; {reply, ok, State#{servers := Servers2}};
handle_call({update_config, {delete, ToDelete}, _}, _From, State) -> handle_call({update_config, {delete, ToDelete}, _, _}, _From, State) ->
emqx_exhook_metrics:on_server_deleted(ToDelete), {reply, ok, remove_server(ToDelete, State)};
#{servers := Servers} = State2 = do_unload_server(ToDelete, State),
Servers2 = maps:remove(ToDelete, Servers),
{reply, ok, update_servers(Servers2, State2)};
handle_call( handle_call(
{update_config, {add, RawConf}, NewConfL}, {update_config, {add, RawConf}, NewConfL, _},
_From, _From,
#{servers := Servers} = State #{servers := Servers} = State
) -> ) ->
@ -246,14 +245,68 @@ handle_call(
Servers2 = Servers#{Name => Server}, Servers2 = Servers#{Name => Server},
Servers3 = reorder(NewConfL, Servers2), Servers3 = reorder(NewConfL, Servers2),
{reply, Result, State#{servers := Servers3}}; {reply, Result, State#{servers := Servers3}};
handle_call({update_config, {update, Name, _Conf}, NewConfL, _}, _From, State) ->
{Result, State2} = restart_server(Name, NewConfL, State),
{reply, Result, State2};
handle_call({update_config, {enable, Name, _Enable}, NewConfL, _}, _From, State) ->
{Result, State2} = restart_server(Name, NewConfL, State),
{reply, Result, State2};
handle_call({update_config, _, ConfL, ConfL}, _From, State) ->
{reply, ok, State};
handle_call({update_config, _, #{servers := NewConfL}, #{servers := OldConfL}}, _From, State) ->
#{
removed := Removed,
added := Added,
changed := Updated
} = emqx_utils:diff_lists(NewConfL, OldConfL, fun(#{name := Name}) -> Name end),
%% remove servers
State2 = lists:foldl(
fun(Conf, Acc) ->
ToDelete = maps:get(name, Conf),
remove_server(ToDelete, Acc)
end,
State,
Removed
),
%% update servers
{UpdateRes, State3} =
lists:foldl(
fun({_Old, Conf}, {ResAcc, StateAcc}) ->
Name = maps:get(name, Conf),
case restart_server(Name, NewConfL, StateAcc) of
{ok, StateAcc1} -> {ResAcc, StateAcc1};
{Err, StateAcc1} -> {[Err | ResAcc], StateAcc1}
end
end,
{[], State2},
Updated
),
%% Add servers
{AddRes, State4} =
lists:foldl(
fun(Conf, {ResAcc, StateAcc}) ->
case do_load_server(options_to_server(Conf)) of
{ok, Server} ->
#{servers := Servers} = StateAcc,
Name = maps:get(name, Conf),
Servers2 = Servers#{Name => Server},
{ResAcc, update_servers(Servers2, StateAcc)};
{Err, StateAcc1} ->
{[Err | ResAcc], StateAcc1}
end
end,
{[], State3},
Added
),
%% update order
#{servers := Servers4} = State4,
State5 = State4#{servers => reorder(NewConfL, Servers4)},
case lists:append([UpdateRes, AddRes]) of
[] -> {reply, ok, State5};
_ -> {reply, {error, #{added => AddRes, updated => UpdateRes}}, State5}
end;
handle_call({lookup, Name}, _From, State) -> handle_call({lookup, Name}, _From, State) ->
{reply, where_is_server(Name, State), State}; {reply, where_is_server(Name, State), State};
handle_call({update_config, {update, Name, _Conf}, NewConfL}, _From, State) ->
{Result, State2} = restart_server(Name, NewConfL, State),
{reply, Result, State2};
handle_call({update_config, {enable, Name, _Enable}, NewConfL}, _From, State) ->
{Result, State2} = restart_server(Name, NewConfL, State),
{reply, Result, State2};
handle_call({server_info, Name}, _From, State) -> handle_call({server_info, Name}, _From, State) ->
case where_is_server(Name, State) of case where_is_server(Name, State) of
not_found -> not_found ->
@ -287,6 +340,12 @@ handle_call(_Request, _From, State) ->
Reply = ok, Reply = ok,
{reply, Reply, State}. {reply, Reply, State}.
remove_server(ToDelete, State) ->
emqx_exhook_metrics:on_server_deleted(ToDelete),
#{servers := Servers} = State2 = do_unload_server(ToDelete, State),
Servers2 = maps:remove(ToDelete, Servers),
update_servers(Servers2, State2).
handle_cast(_Msg, State) -> handle_cast(_Msg, State) ->
{noreply, State}. {noreply, State}.
@ -310,6 +369,8 @@ terminate(Reason, State = #{servers := Servers}) ->
Servers Servers
), ),
?tp(info, exhook_mgr_terminated, #{reason => Reason, servers => Servers}), ?tp(info, exhook_mgr_terminated, #{reason => Reason, servers => Servers}),
emqx_conf:remove_handler([exhook, servers]),
emqx_conf:remove_handler([exhook]),
ok. ok.
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
@ -612,8 +673,16 @@ try_clear_ssl_files({Op, Name, _}, NewConfs, OldConfs) when
NewSSL = find_server_ssl_cfg(Name, NewConfs), NewSSL = find_server_ssl_cfg(Name, NewConfs),
OldSSL = find_server_ssl_cfg(Name, OldConfs), OldSSL = find_server_ssl_cfg(Name, OldConfs),
emqx_tls_lib:delete_ssl_files(ssl_file_path(Name), NewSSL, OldSSL); emqx_tls_lib:delete_ssl_files(ssl_file_path(Name), NewSSL, OldSSL);
try_clear_ssl_files(_Req, _NewConf, _OldConf) -> %% replace the whole config from the file
ok. try_clear_ssl_files(_Req, #{servers := NewServers}, #{servers := OldServers}) ->
lists:foreach(
fun(#{name := Name} = Conf) ->
NewSSL = find_server_ssl_cfg(Name, NewServers),
OldSSL = maps:get(ssl, Conf, undefined),
emqx_tls_lib:delete_ssl_files(ssl_file_path(Name), NewSSL, OldSSL)
end,
OldServers
).
search_server_cfg(Name, Confs) -> search_server_cfg(Name, Confs) ->
lists:search( lists:search(

View File

@ -2,7 +2,7 @@
{application, emqx_utils, [ {application, emqx_utils, [
{description, "Miscellaneous utilities for EMQX apps"}, {description, "Miscellaneous utilities for EMQX apps"},
% strict semver, bump manually! % strict semver, bump manually!
{vsn, "5.0.2"}, {vsn, "5.0.3"},
{modules, [ {modules, [
emqx_utils, emqx_utils,
emqx_utils_api, emqx_utils_api,

View File

@ -54,7 +54,8 @@
safe_to_existing_atom/1, safe_to_existing_atom/1,
safe_to_existing_atom/2, safe_to_existing_atom/2,
pub_props_to_packet/1, pub_props_to_packet/1,
safe_filename/1 safe_filename/1,
diff_lists/3
]). ]).
-export([ -export([
@ -748,3 +749,152 @@ safe_filename(Filename) when is_binary(Filename) ->
binary:replace(Filename, <<":">>, <<"-">>, [global]); binary:replace(Filename, <<":">>, <<"-">>, [global]);
safe_filename(Filename) when is_list(Filename) -> safe_filename(Filename) when is_list(Filename) ->
lists:flatten(string:replace(Filename, ":", "-", all)). lists:flatten(string:replace(Filename, ":", "-", all)).
%% @doc Compares two lists of maps and returns the differences between them in a
%% map containing four keys 'removed', 'added', 'identical', and 'changed'
%% each holding a list of maps. Elements are compared using key function KeyFunc
%% to extract the comparison key used for matching.
%%
%% The return value is a map with the following keys and the list of maps as its values:
%% * 'removed' a list of maps that were present in the Old list, but not found in the New list.
%% * 'added' a list of maps that were present in the New list, but not found in the Old list.
%% * 'identical' a list of maps that were present in both lists and have the same comparison key value.
%% * 'changed' a list of pairs of maps representing the changes between maps present in the New and Old lists.
%% The first map in the pair represents the map in the Old list, and the second map
%% represents the potential modification in the New list.
%% The KeyFunc parameter is a function that extracts the comparison key used
%% for matching from each map. The function should return a comparable term,
%% such as an atom, a number, or a string. This is used to determine if each
%% element is the same in both lists.
-spec diff_lists(list(T), list(T), Func) ->
#{
added := list(T),
identical := list(T),
removed := list(T),
changed := list({Old :: T, New :: T})
}
when
Func :: fun((T) -> any()),
T :: any().
diff_lists(New, Old, KeyFunc) when is_list(New) andalso is_list(Old) ->
Removed =
lists:foldl(
fun(E, RemovedAcc) ->
case search(KeyFunc(E), KeyFunc, New) of
false -> [E | RemovedAcc];
_ -> RemovedAcc
end
end,
[],
Old
),
{Added, Identical, Changed} =
lists:foldl(
fun(E, Acc) ->
{Added0, Identical0, Changed0} = Acc,
case search(KeyFunc(E), KeyFunc, Old) of
false ->
{[E | Added0], Identical0, Changed0};
E ->
{Added0, [E | Identical0], Changed0};
E1 ->
{Added0, Identical0, [{E1, E} | Changed0]}
end
end,
{[], [], []},
New
),
#{
removed => lists:reverse(Removed),
added => lists:reverse(Added),
identical => lists:reverse(Identical),
changed => lists:reverse(Changed)
}.
search(_ExpectValue, _KeyFunc, []) ->
false;
search(ExpectValue, KeyFunc, [Item | List]) ->
case KeyFunc(Item) =:= ExpectValue of
true -> Item;
false -> search(ExpectValue, KeyFunc, List)
end.
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
diff_lists_test() ->
KeyFunc = fun(#{name := Name}) -> Name end,
?assertEqual(
#{
removed => [],
added => [],
identical => [],
changed => []
},
diff_lists([], [], KeyFunc)
),
%% test removed list
?assertEqual(
#{
removed => [#{name => a, value => 1}],
added => [],
identical => [],
changed => []
},
diff_lists([], [#{name => a, value => 1}], KeyFunc)
),
%% test added list
?assertEqual(
#{
removed => [],
added => [#{name => a, value => 1}],
identical => [],
changed => []
},
diff_lists([#{name => a, value => 1}], [], KeyFunc)
),
%% test identical list
?assertEqual(
#{
removed => [],
added => [],
identical => [#{name => a, value => 1}],
changed => []
},
diff_lists([#{name => a, value => 1}], [#{name => a, value => 1}], KeyFunc)
),
Old = [
#{name => a, value => 1},
#{name => b, value => 4},
#{name => e, value => 2},
#{name => d, value => 4}
],
New = [
#{name => a, value => 1},
#{name => b, value => 2},
#{name => e, value => 2},
#{name => c, value => 3}
],
Diff = diff_lists(New, Old, KeyFunc),
?assertEqual(
#{
added => [
#{name => c, value => 3}
],
identical => [
#{name => a, value => 1},
#{name => e, value => 2}
],
removed => [
#{name => d, value => 4}
],
changed => [{#{name => b, value => 4}, #{name => b, value => 2}}]
},
Diff
),
ok.
-endif.