fix: ensure default values for `loaded_{plugins,modules}`

Fixes #7455 .

This tries to populate `loaded_{plugins,modules}` files with default
values before loading them, in case they don't exist.
This commit is contained in:
Thales Macedo Garitezi 2022-04-05 12:03:10 -03:00
parent bbda2e4dfa
commit aa19283ff2
No known key found for this signature in database
GPG Key ID: DD279F8152A9B6DD
4 changed files with 133 additions and 4 deletions

View File

@ -43,6 +43,7 @@ load() ->
case emqx:get_env(modules_loaded_file) of
undefined -> ok;
File ->
ensure_loaded_modules_file(File),
load_modules(File)
end.
@ -58,6 +59,31 @@ load(ModuleName) ->
emqx_modules:load_module(ModuleName, true)
end.
%% @doc Creates a `loaded_modules' file with default values if one
%% doesn't exist.
-spec ensure_loaded_modules_file(file:filename()) -> ok.
ensure_loaded_modules_file(Filepath) ->
case filelib:is_regular(Filepath) of
true ->
ok;
false ->
do_ensure_loaded_modules_file(Filepath)
end.
do_ensure_loaded_modules_file(Filepath) ->
DefaultModules = [emqx_mod_acl_internal, emqx_mod_presence],
Res = file:write_file(Filepath,
[io_lib:format("{~p, true}.~n", [Mod])
|| Mod <- DefaultModules]),
case Res of
ok ->
ok;
{error, Reason} ->
?LOG(error, "Could not write default loaded_modules file ~p ; Error: ~p",
[Filepath, Reason]),
ok
end.
%% @doc Unload all the extended modules.
-spec(unload() -> ok).
unload() ->
@ -175,8 +201,10 @@ write_loaded(false) -> ok.
%%--------------------------------------------------------------------
%% @doc Modules Command
%%--------------------------------------------------------------------
cli(["list"]) ->
lists:foreach(fun({Name, Active}) ->
lists:foreach(fun({Name, Active}) ->
emqx_ctl:print("Module(~s, description=~s, active=~s)~n",
[Name, Name:description(), Active])
end, emqx_modules:list());

View File

@ -20,6 +20,7 @@
-compile(nowarn_export_all).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
-define(CONTENT_TYPE, "application/x-www-form-urlencoded").
@ -44,6 +45,32 @@ end_per_suite(_Config) ->
emqx_ct_http:delete_default_app(),
emqx_ct_helpers:stop_apps([emqx_modules, emqx_management]).
init_per_testcase(t_ensure_default_loaded_modules_file, Config) ->
LoadedModulesFilepath = application:get_env(emqx, modules_loaded_file),
ok = application:stop(emqx_modules),
TmpFilepath = filename:join(["/", "tmp", "loaded_modules_tmp"]),
case file:delete(TmpFilepath) of
ok -> ok;
{error, enoent} -> ok
end,
application:set_env(emqx, modules_loaded_file, TmpFilepath),
[ {loaded_modules_filepath, LoadedModulesFilepath}
, {tmp_filepath, TmpFilepath}
| Config];
init_per_testcase(_TestCase, Config) ->
Config.
end_per_testcase(t_ensure_default_loaded_modules_file, Config) ->
LoadedModulesFilepath = ?config(loaded_modules_filepath, Config),
TmpFilepath = ?config(tmp_filepath, Config),
file:delete(TmpFilepath),
ok = application:stop(emqx_modules),
application:set_env(emqx, modules_loaded_file, LoadedModulesFilepath),
ok = application:start(emqx_modules),
ok;
end_per_testcase(_TestCase, _Config) ->
ok.
t_load(_) ->
?assertEqual(ok, emqx_modules:unload()),
?assertEqual(ok, emqx_modules:load()),
@ -52,6 +79,19 @@ t_load(_) ->
?assertEqual(ignore, emqx_modules:reload(emqx_mod_rewrite)),
?assertEqual(ok, emqx_modules:reload(emqx_mod_acl_internal)).
t_ensure_default_loaded_modules_file(_Config) ->
ok = application:start(emqx_modules),
?assertEqual(
[ {emqx_mod_acl_internal,true}
, {emqx_mod_delayed,false}
, {emqx_mod_presence,true}
, {emqx_mod_rewrite,false}
, {emqx_mod_subscription,false}
, {emqx_mod_topic_metrics,false}
],
lists:sort(emqx_modules:list())),
ok.
t_list(_) ->
?assertMatch([{_, _} | _ ], emqx_modules:list()).

View File

@ -215,7 +215,21 @@ load_plugin_conf(AppName, PluginDir) ->
end, AppsEnv).
ensure_file(File) ->
case filelib:is_file(File) of false -> write_loaded([]); true -> ok end.
case filelib:is_file(File) of
false ->
DefaultPlugins = [ {emqx_management, true}
, {emqx_dashboard, true}
, {emqx_modules, false}
, {emqx_recon, true}
, {emqx_retainer, true}
, {emqx_telemetry, true}
, {emqx_rule_engine, true}
, {emqx_bridge_mqtt, false}
],
write_loaded(DefaultPlugins);
true ->
ok
end.
with_loaded_file(File, SuccFun) ->
case read_loaded(File) of

View File

@ -21,11 +21,11 @@
-include_lib("emqx/include/emqx.hrl").
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
all() -> emqx_ct:all(?MODULE).
init_per_suite(Config) ->
%% Compile extra plugin code
DataPath = proplists:get_value(data_dir, Config),
@ -47,7 +47,35 @@ set_special_cfg(PluginsDir) ->
ok.
end_per_suite(_Config) ->
emqx_ct_helpers:stop_apps([]).
emqx_ct_helpers:stop_apps([]),
file:delete(get(loaded_file)).
init_per_testcase(t_ensure_default_loaded_plugins_file, Config) ->
{ok, LoadedPluginsFilepath} = application:get_env(emqx, plugins_loaded_file),
TmpFilepath = filename:join(["/", "tmp", "loaded_plugins_tmp"]),
case file:delete(TmpFilepath) of
ok -> ok;
{error, enoent} -> ok
end,
application:set_env(emqx, plugins_loaded_file, TmpFilepath),
[ {loaded_plugins_filepath, LoadedPluginsFilepath}
, {tmp_filepath, TmpFilepath}
| Config];
init_per_testcase(_TestCase, Config) ->
Config.
end_per_testcase(t_ensure_default_loaded_plugins_file, Config) ->
LoadedPluginsFilepath = ?config(loaded_plugins_filepath, Config),
TmpFilepath = ?config(tmp_filepath, Config),
file:delete(TmpFilepath),
emqx_plugins:unload(),
application:set_env(emqx, plugins_loaded_file, LoadedPluginsFilepath),
%% need to purge the plugin to avoid inter-testcase dependencies.
code:purge(emqx_mini_plugin_app),
ok;
end_per_testcase(_TestCase, _Config) ->
emqx_plugins:unload(),
ok.
t_load(_) ->
?assertEqual(ok, emqx_plugins:load()),
@ -61,6 +89,25 @@ t_load(_) ->
?assertEqual(ignore, emqx_plugins:load()),
?assertEqual(ignore, emqx_plugins:unload()).
t_ensure_default_loaded_plugins_file(Config) ->
%% this will trigger it to write the default plugins to the
%% inexistent file; but it won't truly load them in this test
%% because there are no config files in `expand_plugins_dir'.
TmpFilepath = ?config(tmp_filepath, Config),
ok = emqx_plugins:load(),
{ok, Contents} = file:consult(TmpFilepath),
?assertEqual(
[ {emqx_bridge_mqtt, false}
, {emqx_dashboard, true}
, {emqx_management, true}
, {emqx_modules, false}
, {emqx_recon, true}
, {emqx_retainer, true}
, {emqx_rule_engine, true}
, {emqx_telemetry, true}
],
lists:sort(Contents)),
ok.
t_init_config(_) ->
ConfFile = "emqx_mini_plugin.config",