Add some APIs for emqx_config (#5095)

This commit is contained in:
Shawn 2021-06-25 15:40:28 +08:00 committed by zhanghongtong
parent d77352168f
commit cc1f57ff01
3 changed files with 113 additions and 56 deletions

View File

@ -18,43 +18,95 @@
-compile({no_auto_import, [get/0, get/1]}). -compile({no_auto_import, [get/0, get/1]}).
-export([ get/0 -export([ get/0
, get/1
, get/2 , get/2
, put/1 , put/1
, put/2 , put/2
]).
-export([ update_config/2
]).
%% raw configs is the config that is now parsed and tranlated by hocon schema
-export([ get_raw/0
, get_raw/1
, get_raw/2
, put_raw/1
, put_raw/2
]).
-export([ deep_get/2
, deep_get/3 , deep_get/3
, deep_put/3 , deep_put/3
, safe_atom_key_map/1 , safe_atom_key_map/1
, unsafe_atom_key_map/1 , unsafe_atom_key_map/1
]). ]).
-spec get() -> term(). -define(CONF, ?MODULE).
get() -> -define(RAW_CONF, {?MODULE, raw}).
persistent_term:get(?MODULE, #{}).
-spec get([atom()], term()) -> term(). -export_type([update_request/0, raw_config/0, config_key/0, config_key_path/0]).
-type update_request() :: term().
-type raw_config() :: hocon:config() | undefined.
-type config_key() :: atom() | binary().
-type config_key_path() :: [config_key()].
-spec get() -> map().
get() ->
persistent_term:get(?CONF, #{}).
-spec get(config_key_path()) -> term().
get(KeyPath) ->
deep_get(KeyPath, get()).
-spec get(config_key_path(), term()) -> term().
get(KeyPath, Default) -> get(KeyPath, Default) ->
deep_get(KeyPath, get(), Default). deep_get(KeyPath, get(), Default).
-spec deep_get([atom()], map(), term()) -> term(). -spec put(map()) -> ok.
deep_get([], Map, _Default) ->
Map;
deep_get([Key | KeyPath], Map, Default) when is_map(Map) ->
case maps:find(Key, Map) of
{ok, SubMap} -> deep_get(KeyPath, SubMap, Default);
error -> Default
end;
deep_get([_Key | _KeyPath], _Map, Default) ->
Default.
-spec put(term()) -> ok.
put(Config) -> put(Config) ->
persistent_term:put(?MODULE, Config). persistent_term:put(?CONF, Config).
-spec put([atom()], term()) -> ok. -spec put(config_key_path(), term()) -> ok.
put(KeyPath, Config) -> put(KeyPath, Config) ->
put(deep_put(KeyPath, get(), Config)). put(deep_put(KeyPath, get(), Config)).
-spec deep_put([atom()], map(), term()) -> ok. -spec update_config(config_key_path(), update_request()) ->
ok | {error, term()}.
update_config(ConfKeyPath, UpdateReq) ->
emqx_config_handler:update_config(ConfKeyPath, UpdateReq, get_raw()).
-spec get_raw() -> map().
get_raw() ->
persistent_term:get(?RAW_CONF, #{}).
-spec get_raw(config_key_path()) -> term().
get_raw(KeyPath) ->
deep_get(KeyPath, get_raw()).
-spec get_raw(config_key_path(), term()) -> term().
get_raw(KeyPath, Default) ->
deep_get(KeyPath, get_raw(), Default).
-spec put_raw(map()) -> ok.
put_raw(Config) ->
persistent_term:put(?RAW_CONF, Config).
-spec put_raw(config_key_path(), term()) -> ok.
put_raw(KeyPath, Config) ->
put_raw(deep_put(KeyPath, get_raw(), Config)).
%%-----------------------------------------------------------------
-spec deep_get(config_key_path(), map()) -> term().
deep_get(ConfKeyPath, Map) ->
do_deep_get(ConfKeyPath, Map, fun(KeyPath, Data) ->
error({not_found, KeyPath, Data}) end).
-spec deep_get(config_key_path(), map(), term()) -> term().
deep_get(ConfKeyPath, Map, Default) ->
do_deep_get(ConfKeyPath, Map, fun(_, _) -> Default end).
-spec deep_put(config_key_path(), map(), term()) -> map().
deep_put([], Map, Config) when is_map(Map) -> deep_put([], Map, Config) when is_map(Map) ->
Config; Config;
deep_put([Key | KeyPath], Map, Config) -> deep_put([Key | KeyPath], Map, Config) ->
@ -67,6 +119,19 @@ unsafe_atom_key_map(Map) ->
safe_atom_key_map(Map) -> safe_atom_key_map(Map) ->
covert_keys_to_atom(Map, fun(K) -> binary_to_existing_atom(K, utf8) end). covert_keys_to_atom(Map, fun(K) -> binary_to_existing_atom(K, utf8) end).
%%---------------------------------------------------------------------------
-spec do_deep_get(config_key_path(), map(), fun((config_key(), term()) -> any())) -> term().
do_deep_get([], Map, _) ->
Map;
do_deep_get([Key | KeyPath], Map, OnNotFound) when is_map(Map) ->
case maps:find(Key, Map) of
{ok, SubMap} -> do_deep_get(KeyPath, SubMap, OnNotFound);
error -> OnNotFound(Key, Map)
end;
do_deep_get([Key | _KeyPath], Data, OnNotFound) ->
OnNotFound(Key, Data).
covert_keys_to_atom(BinKeyMap, Conv) when is_map(BinKeyMap) -> covert_keys_to_atom(BinKeyMap, Conv) when is_map(BinKeyMap) ->
maps:fold( maps:fold(
fun(K, V, Acc) when is_binary(K) -> fun(K, V, Acc) when is_binary(K) ->

View File

@ -24,8 +24,7 @@
%% API functions %% API functions
-export([ start_link/0 -export([ start_link/0
, add_handler/2 , add_handler/2
, update_config/2 , update_config/3
, get_raw_config/0
, merge_to_old_config/2 , merge_to_old_config/2
]). ]).
@ -43,60 +42,49 @@
-define(MOD, {mod}). -define(MOD, {mod}).
-type update_request() :: term().
-type raw_config() :: hocon:config() | undefined.
-type config_key() :: atom().
-type handler_name() :: module(). -type handler_name() :: module().
-type config_key_path() :: [atom()]. -type handlers() :: #{emqx_config:config_key() => handlers(), ?MOD => handler_name()}.
-type handlers() :: #{config_key() => handlers(), ?MOD => handler_name()}.
-optional_callbacks([handle_update_config/2]). -optional_callbacks([handle_update_config/2]).
-callback handle_update_config(update_request(), raw_config()) -> update_request(). -callback handle_update_config(emqx_config:update_request(), emqx_config:raw_config()) ->
emqx_config:update_request().
-type state() :: #{ -type state() :: #{
handlers := handlers(), handlers := handlers(),
raw_config := raw_config(),
atom() => term() atom() => term()
}. }.
start_link() -> start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, {}, []). gen_server:start_link({local, ?MODULE}, ?MODULE, {}, []).
-spec update_config(config_key_path(), update_request()) -> ok | {error, term()}. -spec update_config(emqx_config:config_key_path(), emqx_config:update_request(),
update_config(ConfKeyPath, UpdateReq) -> emqx_config:raw_config()) ->
gen_server:call(?MODULE, {update_config, ConfKeyPath, UpdateReq}). ok | {error, term()}.
update_config(ConfKeyPath, UpdateReq, RawConfig) ->
gen_server:call(?MODULE, {update_config, ConfKeyPath, UpdateReq, RawConfig}).
-spec add_handler(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) ->
gen_server:call(?MODULE, {add_child, ConfKeyPath, HandlerName}). gen_server:call(?MODULE, {add_child, ConfKeyPath, HandlerName}).
-spec get_raw_config() -> raw_config().
get_raw_config() ->
gen_server:call(?MODULE, get_raw_config).
%%============================================================================ %%============================================================================
-spec init(term()) -> {ok, state()}. -spec init(term()) -> {ok, state()}.
init(_) -> init(_) ->
{ok, RawConf} = hocon:load(emqx_conf_name(), #{format => richmap}), {ok, RawConf} = hocon:load(emqx_conf_name(), #{format => richmap}),
{_MappedEnvs, Conf} = hocon_schema:map_translate(emqx_schema, RawConf, #{}), {_MappedEnvs, Conf} = hocon_schema:map_translate(emqx_schema, RawConf, #{}),
ok = save_config_to_emqx_config(hocon_schema:richmap_to_map(Conf)), ok = save_config_to_emqx(to_plainmap(Conf), to_plainmap(RawConf)),
{ok, #{raw_config => hocon_schema:richmap_to_map(RawConf), {ok, #{handlers => #{?MOD => ?MODULE}}}.
handlers => #{?MOD => ?MODULE}}}.
handle_call(get_raw_config, _From, State = #{raw_config := RawConf}) ->
{reply, RawConf, State};
handle_call({add_child, ConfKeyPath, HandlerName}, _From, handle_call({add_child, ConfKeyPath, HandlerName}, _From,
State = #{handlers := Handlers}) -> State = #{handlers := Handlers}) ->
{reply, ok, State#{handlers => {reply, ok, State#{handlers =>
emqx_config:deep_put(ConfKeyPath, Handlers, #{?MOD => HandlerName})}}; emqx_config:deep_put(ConfKeyPath, Handlers, #{?MOD => HandlerName})}};
handle_call({update_config, ConfKeyPath, UpdateReq}, _From, handle_call({update_config, ConfKeyPath, UpdateReq, RawConf}, _From,
#{raw_config := RawConf, handlers := Handlers} = State) -> #{handlers := Handlers} = State) ->
try {RootKeys, Conf} = do_update_config(ConfKeyPath, Handlers, RawConf, UpdateReq), try {RootKeys, Conf} = do_update_config(ConfKeyPath, Handlers, RawConf, UpdateReq),
{reply, save_configs(RootKeys, Conf), State#{raw_config => Conf}} {reply, save_configs(RootKeys, Conf), State}
catch catch
throw: Reason -> throw: Reason ->
{reply, {error, Reason}, State}; {reply, {error, Reason}, State};
@ -158,21 +146,22 @@ merge_to_old_config(UpdateReq, RawConf) ->
maps:merge(RawConf, UpdateReq). maps:merge(RawConf, UpdateReq).
%%============================================================================ %%============================================================================
save_configs(RootKeys, Conf0) -> save_configs(RootKeys, RawConf) ->
{_MappedEnvs, Conf1} = hocon_schema:map_translate(emqx_schema, to_richmap(Conf0), #{}), {_MappedEnvs, Conf} = hocon_schema:map_translate(emqx_schema, to_richmap(RawConf), #{}),
%save_config_to_app_env(MappedEnvs), %% We may need also support hot config update for the apps that use application envs.
save_config_to_emqx_config(hocon_schema:richmap_to_map(Conf1)), %% If so uncomment the following line to update the configs to application env
save_config_to_disk(RootKeys, Conf0). %save_config_to_app_env(_MappedEnvs),
save_config_to_emqx(to_plainmap(Conf), RawConf),
save_config_to_disk(RootKeys, RawConf).
%% We may need also support hot config update for the apps that use application envs.
%% If so uncomment the following lines to update the configs to application env
% save_config_to_app_env(MappedEnvs) -> % save_config_to_app_env(MappedEnvs) ->
% lists:foreach(fun({AppName, Envs}) -> % lists:foreach(fun({AppName, Envs}) ->
% [application:set_env(AppName, Par, Val) || {Par, Val} <- Envs] % [application:set_env(AppName, Par, Val) || {Par, Val} <- Envs]
% end, MappedEnvs). % end, MappedEnvs).
save_config_to_emqx_config(Conf) -> save_config_to_emqx(Conf, RawConf) ->
emqx_config:put(emqx_config:unsafe_atom_key_map(Conf)). emqx_config:put(emqx_config:unsafe_atom_key_map(Conf)),
emqx_config:put_raw(RawConf).
save_config_to_disk(RootKeys, Conf) -> save_config_to_disk(RootKeys, Conf) ->
FileName = emqx_override_conf_name(), FileName = emqx_override_conf_name(),
@ -215,6 +204,9 @@ to_richmap(Map) ->
{ok, RichMap} = hocon:binary(jsx:encode(Map), #{format => richmap}), {ok, RichMap} = hocon:binary(jsx:encode(Map), #{format => richmap}),
RichMap. RichMap.
to_plainmap(RichMap) ->
hocon_schema:richmap_to_map(RichMap).
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).

View File

@ -60,4 +60,4 @@ config_key_path() ->
[emqx_data_bridge, bridges]. [emqx_data_bridge, bridges].
update_config(ConfigReq) -> update_config(ConfigReq) ->
emqx_config_handler:update_config(config_key_path(), ConfigReq). emqx_config:update_config(config_key_path(), ConfigReq).