feat(config): support deleting a config entry

This commit is contained in:
Shawn 2021-07-26 17:33:02 +08:00 committed by turtleDeng
parent 5b83193413
commit c0c5bcc698
3 changed files with 75 additions and 36 deletions

View File

@ -18,6 +18,7 @@
-compile({no_auto_import, [get/0, get/1]}).
-export([ load/0
, read_override_conf/0
, save_configs/2
, save_to_app_env/1
, save_to_emqx_config/2
@ -47,6 +48,7 @@
]).
-export([ update/2
, remove/1
]).
%% raw configs is the config that is now parsed and tranlated by hocon schema
@ -129,7 +131,11 @@ put(KeyPath, Config) ->
-spec update(emqx_map_lib:config_key_path(), update_request()) ->
ok | {error, term()}.
update(ConfKeyPath, UpdateReq) ->
emqx_config_handler:update_config(ConfKeyPath, UpdateReq, get_raw()).
emqx_config_handler:update_config(ConfKeyPath, UpdateReq).
-spec remove(emqx_map_lib:config_key_path()) -> ok | {error, term()}.
remove(ConfKeyPath) ->
emqx_config_handler:remove_config(ConfKeyPath).
-spec get_raw() -> map().
get_raw() ->
@ -164,22 +170,18 @@ load() ->
{_MappedEnvs, RichConf} = hocon_schema:map_translate(emqx_schema, RawRichConf, #{}),
ok = save_to_emqx_config(to_plainmap(RichConf), to_plainmap(RawRichConf)).
-spec save_configs(raw_config(), Opts) -> ok | {error, term()}
when Opts :: #{overridden_keys => all | [binary()]}.
save_configs(RawConf, Opts) ->
{_MappedEnvs, RichConf} = hocon_schema:map_translate(emqx_schema, to_richmap(RawConf), #{}),
save_to_emqx_config(to_plainmap(RichConf), RawConf),
-spec read_override_conf() -> raw_config().
read_override_conf() ->
load_hocon_file(emqx_override_conf_name(), map).
-spec save_configs(raw_config(), raw_config()) -> ok | {error, term()}.
save_configs(RawConf, OverrideConf) ->
{_MappedEnvs, RichConf} = hocon_schema:map_translate(emqx_schema, to_richmap(RawConf), #{}),
%% We may need also support hot config update for the apps that use application envs.
%% If that is the case uncomment the following line to update the configs to application env
%save_to_app_env(_MappedEnvs),
%% We don't save the entire config to emqx_override.conf, but only the sub configs
%% specified by RootKeys
case maps:get(overridden_keys, Opts, all) of
all -> save_to_override_conf(RawConf);
RootKeys -> save_to_override_conf(maps:with(RootKeys, RawConf))
end.
save_to_emqx_config(to_plainmap(RichConf), RawConf),
save_to_override_conf(OverrideConf).
-spec save_to_app_env([tuple()]) -> ok.
save_to_app_env(AppEnvs) ->
@ -192,13 +194,11 @@ save_to_emqx_config(Conf, RawConf) ->
emqx_config:put(emqx_map_lib:unsafe_atom_key_map(Conf)),
emqx_config:put_raw(RawConf).
-spec save_to_override_conf(config()) -> ok | {error, term()}.
save_to_override_conf(Conf) ->
-spec save_to_override_conf(raw_config()) -> ok | {error, term()}.
save_to_override_conf(RawConf) ->
FileName = emqx_override_conf_name(),
OldConf = load_hocon_file(FileName, map),
MergedConf = maps:merge(OldConf, Conf),
ok = filelib:ensure_dir(FileName),
case file:write_file(FileName, jsx:prettify(jsx:encode(MergedConf))) of
case file:write_file(FileName, jsx:prettify(jsx:encode(RawConf))) of
ok -> ok;
{error, Reason} ->
logger:error("write to ~s failed, ~p", [FileName, Reason]),
@ -221,4 +221,4 @@ to_richmap(Map) ->
RichMap.
to_plainmap(RichMap) ->
hocon_schema:richmap_to_map(RichMap).
hocon_schema:richmap_to_map(RichMap).

View File

@ -24,14 +24,11 @@
%% API functions
-export([ start_link/0
, add_handler/2
, update_config/3
, update_config/2
, remove_config/1
, merge_to_old_config/2
]).
%% emqx_config_handler callbacks
-export([ pre_config_update/2
]).
%% gen_server callbacks
-export([init/1,
handle_call/3,
@ -62,11 +59,15 @@
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, {}, []).
-spec update_config(emqx_config:config_key_path(), emqx_config:update_request(),
emqx_config:raw_config()) ->
-spec update_config(emqx_config:config_key_path(), emqx_config:update_request()) ->
ok | {error, term()}.
update_config(ConfKeyPath, UpdateReq, RawConfig) ->
gen_server:call(?MODULE, {update_config, ConfKeyPath, UpdateReq, RawConfig}).
update_config(ConfKeyPath, UpdateReq) ->
gen_server:call(?MODULE, {update_config, ConfKeyPath, UpdateReq}).
-spec remove_config(emqx_config:config_key_path()) ->
ok | {error, term()}.
remove_config(ConfKeyPath) ->
gen_server:call(?MODULE, {remove_config, ConfKeyPath}).
-spec add_handler(emqx_config:config_key_path(), handler_name()) -> ok.
add_handler(ConfKeyPath, HandlerName) ->
@ -83,11 +84,28 @@ handle_call({add_child, ConfKeyPath, HandlerName}, _From,
{reply, ok, State#{handlers =>
emqx_map_lib:deep_put(ConfKeyPath, Handlers, #{?MOD => HandlerName})}};
handle_call({update_config, ConfKeyPath, UpdateReq, RawConf}, _From,
handle_call({update_config, ConfKeyPath, UpdateReq}, _From,
#{handlers := Handlers} = State) ->
OldConf = emqx_config:get(),
try {RootKeys, Conf} = do_update_config(ConfKeyPath, Handlers, RawConf, UpdateReq),
Result = emqx_config:save_configs(Conf, #{overridden_keys => RootKeys}),
OldRawConf = emqx_config:get_raw(),
try NewRawConf = do_update_config(ConfKeyPath, Handlers, OldRawConf, UpdateReq),
OverrideConf = update_override_config(ConfKeyPath, NewRawConf),
Result = emqx_config:save_configs(NewRawConf, OverrideConf),
do_post_config_update(ConfKeyPath, Handlers, OldConf, emqx_config:get()),
{reply, Result, State}
catch
Error : Reason : ST ->
?LOG(error, "update config failed: ~p", [{Error, Reason, ST}]),
{reply, {error, Reason}, State}
end;
handle_call({remove_config, ConfKeyPath}, _From, #{handlers := Handlers} = State) ->
OldConf = emqx_config:get(),
OldRawConf = emqx_config:get_raw(),
BinKeyPath = bin_path(ConfKeyPath),
try NewRawConf = emqx_map_lib:deep_remove(BinKeyPath, OldRawConf),
OverrideConf = emqx_map_lib:deep_remove(BinKeyPath, emqx_config:read_override_conf()),
Result = emqx_config:save_configs(NewRawConf, OverrideConf),
do_post_config_update(ConfKeyPath, Handlers, OldConf, emqx_config:get()),
{reply, Result, State}
catch
@ -148,11 +166,6 @@ call_post_config_update(Handlers, OldConf, NewConf) ->
false -> ok
end.
%% callbacks for the top-level handler
pre_config_update(UpdateReq, OldConf) ->
FullRawConf = merge_to_old_config(UpdateReq, OldConf),
{maps:keys(UpdateReq), FullRawConf}.
%% The default callback of config handlers
%% the behaviour is overwriting the old config if:
%% 1. the old config is undefined
@ -163,6 +176,18 @@ merge_to_old_config(UpdateReq, RawConf) when is_map(UpdateReq), is_map(RawConf)
merge_to_old_config(UpdateReq, _RawConf) ->
UpdateReq.
update_override_config(ConfKeyPath, RawConf) ->
%% We don't save the entire config to emqx_override.conf, but only the part
%% specified by the ConfKeyPath
PartialConf = maps:with(root_keys(ConfKeyPath), RawConf),
OldConf = emqx_config:read_override_conf(),
maps:merge(OldConf, PartialConf).
root_keys([]) -> [];
root_keys([RootKey | _]) -> [bin(RootKey)].
bin_path(ConfKeyPath) -> [bin(Key) || Key <- ConfKeyPath].
bin(A) when is_atom(A) -> list_to_binary(atom_to_list(A));
bin(B) when is_binary(B) -> B;
bin(S) when is_list(S) -> list_to_binary(S).

View File

@ -19,6 +19,7 @@
, deep_get/3
, deep_find/2
, deep_put/3
, deep_remove/2
, deep_merge/2
, safe_atom_key_map/1
, unsafe_atom_key_map/1
@ -64,6 +65,19 @@ deep_put([Key | KeyPath], Map, Config) ->
SubMap = deep_put(KeyPath, maps:get(Key, Map, #{}), Config),
Map#{Key => SubMap}.
-spec deep_remove(config_key_path(), map()) -> map().
deep_remove([], Map) ->
Map;
deep_remove([Key], Map) ->
maps:remove(Key, Map);
deep_remove([Key | KeyPath], Map) ->
case maps:find(Key, Map) of
{ok, SubMap} when is_map(SubMap) ->
Map#{Key => deep_remove(KeyPath, SubMap)};
{ok, _Val} -> Map;
error -> Map
end.
%% #{a => #{b => 3, c => 2}, d => 4}
%% = deep_merge(#{a => #{b => 1, c => 2}, d => 4}, #{a => #{b => 3}}).
-spec deep_merge(map(), map()) -> map().