feat(config): support deleting a config entry
This commit is contained in:
parent
5b83193413
commit
c0c5bcc698
|
@ -18,6 +18,7 @@
|
||||||
-compile({no_auto_import, [get/0, get/1]}).
|
-compile({no_auto_import, [get/0, get/1]}).
|
||||||
|
|
||||||
-export([ load/0
|
-export([ load/0
|
||||||
|
, read_override_conf/0
|
||||||
, save_configs/2
|
, save_configs/2
|
||||||
, save_to_app_env/1
|
, save_to_app_env/1
|
||||||
, save_to_emqx_config/2
|
, save_to_emqx_config/2
|
||||||
|
@ -47,6 +48,7 @@
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([ update/2
|
-export([ update/2
|
||||||
|
, remove/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% raw configs is the config that is now parsed and tranlated by hocon schema
|
%% 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()) ->
|
-spec update(emqx_map_lib:config_key_path(), update_request()) ->
|
||||||
ok | {error, term()}.
|
ok | {error, term()}.
|
||||||
update(ConfKeyPath, UpdateReq) ->
|
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().
|
-spec get_raw() -> map().
|
||||||
get_raw() ->
|
get_raw() ->
|
||||||
|
@ -164,22 +170,18 @@ load() ->
|
||||||
{_MappedEnvs, RichConf} = hocon_schema:map_translate(emqx_schema, RawRichConf, #{}),
|
{_MappedEnvs, RichConf} = hocon_schema:map_translate(emqx_schema, RawRichConf, #{}),
|
||||||
ok = save_to_emqx_config(to_plainmap(RichConf), to_plainmap(RawRichConf)).
|
ok = save_to_emqx_config(to_plainmap(RichConf), to_plainmap(RawRichConf)).
|
||||||
|
|
||||||
-spec save_configs(raw_config(), Opts) -> ok | {error, term()}
|
-spec read_override_conf() -> raw_config().
|
||||||
when Opts :: #{overridden_keys => all | [binary()]}.
|
read_override_conf() ->
|
||||||
save_configs(RawConf, Opts) ->
|
load_hocon_file(emqx_override_conf_name(), map).
|
||||||
{_MappedEnvs, RichConf} = hocon_schema:map_translate(emqx_schema, to_richmap(RawConf), #{}),
|
|
||||||
save_to_emqx_config(to_plainmap(RichConf), RawConf),
|
|
||||||
|
|
||||||
|
-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.
|
%% 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
|
%% If that is the case uncomment the following line to update the configs to application env
|
||||||
%save_to_app_env(_MappedEnvs),
|
%save_to_app_env(_MappedEnvs),
|
||||||
|
save_to_emqx_config(to_plainmap(RichConf), RawConf),
|
||||||
%% We don't save the entire config to emqx_override.conf, but only the sub configs
|
save_to_override_conf(OverrideConf).
|
||||||
%% 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.
|
|
||||||
|
|
||||||
-spec save_to_app_env([tuple()]) -> ok.
|
-spec save_to_app_env([tuple()]) -> ok.
|
||||||
save_to_app_env(AppEnvs) ->
|
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(emqx_map_lib:unsafe_atom_key_map(Conf)),
|
||||||
emqx_config:put_raw(RawConf).
|
emqx_config:put_raw(RawConf).
|
||||||
|
|
||||||
-spec save_to_override_conf(config()) -> ok | {error, term()}.
|
-spec save_to_override_conf(raw_config()) -> ok | {error, term()}.
|
||||||
save_to_override_conf(Conf) ->
|
save_to_override_conf(RawConf) ->
|
||||||
FileName = emqx_override_conf_name(),
|
FileName = emqx_override_conf_name(),
|
||||||
OldConf = load_hocon_file(FileName, map),
|
|
||||||
MergedConf = maps:merge(OldConf, Conf),
|
|
||||||
ok = filelib:ensure_dir(FileName),
|
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;
|
ok -> ok;
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
logger:error("write to ~s failed, ~p", [FileName, Reason]),
|
logger:error("write to ~s failed, ~p", [FileName, Reason]),
|
||||||
|
|
|
@ -24,14 +24,11 @@
|
||||||
%% API functions
|
%% API functions
|
||||||
-export([ start_link/0
|
-export([ start_link/0
|
||||||
, add_handler/2
|
, add_handler/2
|
||||||
, update_config/3
|
, update_config/2
|
||||||
|
, remove_config/1
|
||||||
, merge_to_old_config/2
|
, merge_to_old_config/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% emqx_config_handler callbacks
|
|
||||||
-export([ pre_config_update/2
|
|
||||||
]).
|
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1,
|
-export([init/1,
|
||||||
handle_call/3,
|
handle_call/3,
|
||||||
|
@ -62,11 +59,15 @@
|
||||||
start_link() ->
|
start_link() ->
|
||||||
gen_server:start_link({local, ?MODULE}, ?MODULE, {}, []).
|
gen_server:start_link({local, ?MODULE}, ?MODULE, {}, []).
|
||||||
|
|
||||||
-spec update_config(emqx_config:config_key_path(), emqx_config:update_request(),
|
-spec update_config(emqx_config:config_key_path(), emqx_config:update_request()) ->
|
||||||
emqx_config:raw_config()) ->
|
|
||||||
ok | {error, term()}.
|
ok | {error, term()}.
|
||||||
update_config(ConfKeyPath, UpdateReq, RawConfig) ->
|
update_config(ConfKeyPath, UpdateReq) ->
|
||||||
gen_server:call(?MODULE, {update_config, ConfKeyPath, UpdateReq, RawConfig}).
|
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.
|
-spec add_handler(emqx_config:config_key_path(), handler_name()) -> ok.
|
||||||
add_handler(ConfKeyPath, HandlerName) ->
|
add_handler(ConfKeyPath, HandlerName) ->
|
||||||
|
@ -83,11 +84,28 @@ handle_call({add_child, ConfKeyPath, HandlerName}, _From,
|
||||||
{reply, ok, State#{handlers =>
|
{reply, ok, State#{handlers =>
|
||||||
emqx_map_lib:deep_put(ConfKeyPath, Handlers, #{?MOD => HandlerName})}};
|
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) ->
|
#{handlers := Handlers} = State) ->
|
||||||
OldConf = emqx_config:get(),
|
OldConf = emqx_config:get(),
|
||||||
try {RootKeys, Conf} = do_update_config(ConfKeyPath, Handlers, RawConf, UpdateReq),
|
OldRawConf = emqx_config:get_raw(),
|
||||||
Result = emqx_config:save_configs(Conf, #{overridden_keys => RootKeys}),
|
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()),
|
do_post_config_update(ConfKeyPath, Handlers, OldConf, emqx_config:get()),
|
||||||
{reply, Result, State}
|
{reply, Result, State}
|
||||||
catch
|
catch
|
||||||
|
@ -148,11 +166,6 @@ call_post_config_update(Handlers, OldConf, NewConf) ->
|
||||||
false -> ok
|
false -> ok
|
||||||
end.
|
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 default callback of config handlers
|
||||||
%% the behaviour is overwriting the old config if:
|
%% the behaviour is overwriting the old config if:
|
||||||
%% 1. the old config is undefined
|
%% 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) ->
|
merge_to_old_config(UpdateReq, _RawConf) ->
|
||||||
UpdateReq.
|
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(A) when is_atom(A) -> list_to_binary(atom_to_list(A));
|
||||||
bin(B) when is_binary(B) -> B;
|
bin(B) when is_binary(B) -> B;
|
||||||
bin(S) when is_list(S) -> list_to_binary(S).
|
bin(S) when is_list(S) -> list_to_binary(S).
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
, deep_get/3
|
, deep_get/3
|
||||||
, deep_find/2
|
, deep_find/2
|
||||||
, deep_put/3
|
, deep_put/3
|
||||||
|
, deep_remove/2
|
||||||
, deep_merge/2
|
, deep_merge/2
|
||||||
, safe_atom_key_map/1
|
, safe_atom_key_map/1
|
||||||
, unsafe_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),
|
SubMap = deep_put(KeyPath, maps:get(Key, Map, #{}), Config),
|
||||||
Map#{Key => SubMap}.
|
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}
|
%% #{a => #{b => 3, c => 2}, d => 4}
|
||||||
%% = deep_merge(#{a => #{b => 1, c => 2}, d => 4}, #{a => #{b => 3}}).
|
%% = deep_merge(#{a => #{b => 1, c => 2}, d => 4}, #{a => #{b => 3}}).
|
||||||
-spec deep_merge(map(), map()) -> map().
|
-spec deep_merge(map(), map()) -> map().
|
||||||
|
|
Loading…
Reference in New Issue