diff --git a/apps/emqx_plugins/src/emqx_plugins.app.src b/apps/emqx_plugins/src/emqx_plugins.app.src index d5c16ea59..368a1ad46 100644 --- a/apps/emqx_plugins/src/emqx_plugins.app.src +++ b/apps/emqx_plugins/src/emqx_plugins.app.src @@ -1,7 +1,7 @@ %% -*- mode: erlang -*- {application, emqx_plugins, [ {description, "EMQX Plugin Management"}, - {vsn, "0.1.4"}, + {vsn, "0.1.5"}, {modules, []}, {mod, {emqx_plugins_app, []}}, {applications, [kernel, stdlib, emqx]}, diff --git a/apps/emqx_plugins/src/emqx_plugins.erl b/apps/emqx_plugins/src/emqx_plugins.erl index 04faa44e9..5181000de 100644 --- a/apps/emqx_plugins/src/emqx_plugins.erl +++ b/apps/emqx_plugins/src/emqx_plugins.erl @@ -51,6 +51,11 @@ get_tar/1 ]). +%% `emqx_config_handler' API +-export([ + post_config_update/5 +]). + %% internal -export([do_ensure_started/1]). -export([ @@ -857,3 +862,27 @@ running_apps() -> end, application:which_applications(infinity) ). + +%%-------------------------------------------------------------------- +%% `emqx_config_handler' API +%%-------------------------------------------------------------------- + +post_config_update([?CONF_ROOT], _Req, #{states := NewStates}, #{states := OldStates}, _Envs) -> + NewStatesIndex = maps:from_list([{NV, S} || S = #{name_vsn := NV} <- NewStates]), + OldStatesIndex = maps:from_list([{NV, S} || S = #{name_vsn := NV} <- OldStates]), + #{changed := Changed} = emqx_utils_maps:diff_maps(NewStatesIndex, OldStatesIndex), + maps:foreach(fun enable_disable_plugin/2, Changed), + ok; +post_config_update(_Path, _Req, _NewConf, _OldConf, _Envs) -> + ok. + +enable_disable_plugin(NameVsn, {#{enable := true}, #{enable := false}}) -> + %% errors are already logged in this fn + _ = ensure_stopped(NameVsn), + ok; +enable_disable_plugin(NameVsn, {#{enable := false}, #{enable := true}}) -> + %% errors are already logged in this fn + _ = ensure_started(NameVsn), + ok; +enable_disable_plugin(_NameVsn, _Diff) -> + ok. diff --git a/apps/emqx_plugins/src/emqx_plugins_app.erl b/apps/emqx_plugins/src/emqx_plugins_app.erl index c42936d56..f75089144 100644 --- a/apps/emqx_plugins/src/emqx_plugins_app.erl +++ b/apps/emqx_plugins/src/emqx_plugins_app.erl @@ -18,6 +18,8 @@ -behaviour(application). +-include("emqx_plugins.hrl"). + -export([ start/2, stop/1 @@ -27,7 +29,9 @@ start(_Type, _Args) -> %% load all pre-configured ok = emqx_plugins:ensure_started(), {ok, Sup} = emqx_plugins_sup:start_link(), + ok = emqx_config_handler:add_handler([?CONF_ROOT], emqx_plugins), {ok, Sup}. stop(_State) -> + ok = emqx_config_handler:remove_handler([?CONF_ROOT]), ok. diff --git a/apps/emqx_plugins/test/emqx_plugins_SUITE.erl b/apps/emqx_plugins/test/emqx_plugins_SUITE.erl index d6dee2c1e..9bb3f5e72 100644 --- a/apps/emqx_plugins/test/emqx_plugins_SUITE.erl +++ b/apps/emqx_plugins/test/emqx_plugins_SUITE.erl @@ -65,7 +65,7 @@ init_per_suite(Config) -> WorkDir = proplists:get_value(data_dir, Config), filelib:ensure_path(WorkDir), OrigInstallDir = emqx_plugins:get_config(install_dir, undefined), - emqx_common_test_helpers:start_apps([emqx_conf]), + emqx_common_test_helpers:start_apps([emqx_conf, emqx_plugins]), emqx_plugins:put_config(install_dir, WorkDir), [{orig_install_dir, OrigInstallDir} | Config]. @@ -77,7 +77,7 @@ end_per_suite(Config) -> undefined -> ok; OrigInstallDir -> emqx_plugins:put_config(install_dir, OrigInstallDir) end, - emqx_common_test_helpers:stop_apps([emqx_conf]), + emqx_common_test_helpers:stop_apps([emqx_plugins, emqx_conf]), ok. init_per_testcase(TestCase, Config) -> @@ -505,6 +505,65 @@ t_elixir_plugin(Config) -> ?assertEqual([], emqx_plugins:list()), ok. +t_load_config_from_cli({init, Config}) -> + #{package := Package} = get_demo_plugin_package(), + NameVsn = filename:basename(Package, ?PACKAGE_SUFFIX), + [{name_vsn, NameVsn} | Config]; +t_load_config_from_cli({'end', Config}) -> + NameVsn = ?config(name_vsn, Config), + ok = emqx_plugins:ensure_stopped(NameVsn), + ok = emqx_plugins:ensure_uninstalled(NameVsn), + ok; +t_load_config_from_cli(Config) when is_list(Config) -> + NameVsn = ?config(name_vsn, Config), + ok = emqx_plugins:ensure_installed(NameVsn), + ?assertEqual([], emqx_plugins:configured()), + ok = emqx_plugins:ensure_enabled(NameVsn), + ok = emqx_plugins:ensure_started(NameVsn), + Params0 = unused, + ?assertMatch( + {200, [#{running_status := [#{status := running}]}]}, + emqx_mgmt_api_plugins:list_plugins(get, Params0) + ), + + %% Now we disable it via CLI loading + Conf0 = emqx_config:get([plugins]), + ?assertMatch( + #{states := [#{enable := true}]}, + Conf0 + ), + #{states := [Plugin0]} = Conf0, + Conf1 = Conf0#{states := [Plugin0#{enable := false}]}, + Filename = filename:join(["/tmp", [?FUNCTION_NAME, ".hocon"]]), + ok = file:write_file(Filename, hocon_pp:do(#{plugins => Conf1}, #{})), + ok = emqx_conf_cli:conf(["load", Filename]), + + Conf2 = emqx_config:get([plugins]), + ?assertMatch( + #{states := [#{enable := false}]}, + Conf2 + ), + ?assertMatch( + {200, [#{running_status := [#{status := stopped}]}]}, + emqx_mgmt_api_plugins:list_plugins(get, Params0) + ), + + %% Re-enable it via CLI loading + ok = file:write_file(Filename, hocon_pp:do(#{plugins => Conf0}, #{})), + ok = emqx_conf_cli:conf(["load", Filename]), + + Conf3 = emqx_config:get([plugins]), + ?assertMatch( + #{states := [#{enable := true}]}, + Conf3 + ), + ?assertMatch( + {200, [#{running_status := [#{status := running}]}]}, + emqx_mgmt_api_plugins:list_plugins(get, Params0) + ), + + ok. + group_t_copy_plugin_to_a_new_node({init, Config}) -> WorkDir = proplists:get_value(data_dir, Config), FromInstallDir = filename:join(WorkDir, atom_to_list(plugins_copy_from)), diff --git a/changes/ce/fix-11229.en.md b/changes/ce/fix-11229.en.md new file mode 100644 index 000000000..864f545fe --- /dev/null +++ b/changes/ce/fix-11229.en.md @@ -0,0 +1 @@ +Fixed an issue preventing plugins from starting/stopping after changing configuration via `emqx ctl conf load`.