Просмотр исходного кода

feat(config): support deleting a config entry

Shawn 4 лет назад
Родитель
Сommit
c0c5bcc698
3 измененных файлов с 75 добавлено и 36 удалено
  1. 19 19
      apps/emqx/src/emqx_config.erl
  2. 42 17
      apps/emqx/src/emqx_config_handler.erl
  3. 14 0
      apps/emqx/src/emqx_map_lib.erl

+ 19 - 19
apps/emqx/src/emqx_config.erl

@@ -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).

+ 42 - 17
apps/emqx/src/emqx_config_handler.erl

@@ -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).

+ 14 - 0
apps/emqx/src/emqx_map_lib.erl

@@ -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().