diff --git a/apps/emqx/src/emqx_listeners.erl b/apps/emqx/src/emqx_listeners.erl
index 2af2673d1..55c7d2715 100644
--- a/apps/emqx/src/emqx_listeners.erl
+++ b/apps/emqx/src/emqx_listeners.erl
@@ -175,9 +175,10 @@ restart_listener(Type, ListenerName, Conf) ->
restart_listener(Type, ListenerName, Conf, Conf).
restart_listener(Type, ListenerName, OldConf, NewConf) ->
- case stop_listener(Type, ListenerName, OldConf) of
+ case do_stop_listener(Type, ListenerName, OldConf) of
ok -> start_listener(Type, ListenerName, NewConf);
- Error -> Error
+ {error, not_found} -> start_listener(Type, ListenerName, NewConf);
+ {error, Reason} -> {error, Reason}
end.
%% @doc Stop all listeners.
diff --git a/apps/emqx/src/emqx_schema.erl b/apps/emqx/src/emqx_schema.erl
index f8da72ad9..8b863d062 100644
--- a/apps/emqx/src/emqx_schema.erl
+++ b/apps/emqx/src/emqx_schema.erl
@@ -157,9 +157,6 @@ roots(low) ->
, {"quota",
sc(ref("quota"),
#{})}
- , {"plugins", %% TODO: move to emqx_conf_schema
- sc(ref("plugins"),
- #{})}
, {"stats",
sc(ref("stats"),
#{})}
@@ -800,13 +797,6 @@ fields("deflate_opts") ->
}
];
-fields("plugins") ->
- [ {"expand_plugins_dir",
- sc(string(),
- #{})
- }
- ];
-
fields("broker") ->
[ {"sys_msg_interval",
sc(hoconsc:union([disabled, duration()]),
diff --git a/apps/emqx/test/emqx_common_test_helpers.erl b/apps/emqx/test/emqx_common_test_helpers.erl
index a5d80bab5..c4336b855 100644
--- a/apps/emqx/test/emqx_common_test_helpers.erl
+++ b/apps/emqx/test/emqx_common_test_helpers.erl
@@ -120,6 +120,7 @@ all(Suite) ->
string:substr(atom_to_list(F), 1, 2) == "t_"
]).
+%% set emqx app boot modules
-spec(boot_modules(all|list(atom())) -> ok).
boot_modules(Mods) ->
application:set_env(emqx, boot_modules, Mods).
@@ -162,8 +163,7 @@ app_schema(App) ->
mustache_vars(App) ->
[{platform_data_dir, app_path(App, "data")},
{platform_etc_dir, app_path(App, "etc")},
- {platform_log_dir, app_path(App, "log")},
- {platform_plugins_dir, app_path(App, "plugins")}
+ {platform_log_dir, app_path(App, "log")}
].
start_app(App, Schema, ConfigFile, SpecAppConfig) ->
diff --git a/apps/emqx/test/emqx_listeners_SUITE.erl b/apps/emqx/test/emqx_listeners_SUITE.erl
index a9ee0d7d9..7411ca1fd 100644
--- a/apps/emqx/test/emqx_listeners_SUITE.erl
+++ b/apps/emqx/test/emqx_listeners_SUITE.erl
@@ -132,8 +132,7 @@ render_config_file() ->
mustache_vars() ->
[{platform_data_dir, local_path(["data"])},
{platform_etc_dir, local_path(["etc"])},
- {platform_log_dir, local_path(["log"])},
- {platform_plugins_dir, local_path(["plugins"])}
+ {platform_log_dir, local_path(["log"])}
].
generate_config() ->
@@ -144,10 +143,6 @@ generate_config() ->
set_app_env({App, Lists}) ->
lists:foreach(fun({authz_file, _Var}) ->
application:set_env(App, authz_file, local_path(["etc", "authz.conf"]));
- ({plugins_loaded_file, _Var}) ->
- application:set_env(App,
- plugins_loaded_file,
- local_path(["test", "emqx_SUITE_data","loaded_plugins"]));
({Par, Var}) ->
application:set_env(App, Par, Var)
end, Lists).
diff --git a/apps/emqx/test/emqx_persistent_session_SUITE.erl b/apps/emqx/test/emqx_persistent_session_SUITE.erl
index 0fa73ebe2..cf7579ba4 100644
--- a/apps/emqx/test/emqx_persistent_session_SUITE.erl
+++ b/apps/emqx/test/emqx_persistent_session_SUITE.erl
@@ -160,9 +160,6 @@ init_per_group(gc_tests, Config) ->
init_per_suite(Config) ->
Config.
-set_special_confs(emqx) ->
- Path = emqx_common_test_helpers:deps_path(emqx, "test/emqx_SUITE_data/loaded_plugins"),
- application:set_env(emqx, plugins_loaded_file, Path);
set_special_confs(_) ->
ok.
diff --git a/apps/emqx_conf/src/emqx_conf_schema.erl b/apps/emqx_conf/src/emqx_conf_schema.erl
index 5511a6dd0..1ea368826 100644
--- a/apps/emqx_conf/src/emqx_conf_schema.erl
+++ b/apps/emqx_conf/src/emqx_conf_schema.erl
@@ -50,6 +50,7 @@
, emqx_authz_schema
, emqx_auto_subscribe_schema
, emqx_modules_schema
+ , emqx_plugins_schema
, emqx_dashboard_schema
, emqx_gateway_schema
, emqx_prometheus_schema
diff --git a/apps/emqx_plugins/etc/emqx_plugins.conf b/apps/emqx_plugins/etc/emqx_plugins.conf
new file mode 100644
index 000000000..0a1dfb72d
--- /dev/null
+++ b/apps/emqx_plugins/etc/emqx_plugins.conf
@@ -0,0 +1,7 @@
+plugins {
+ prebuilt {
+ }
+ external {
+ }
+ install_dir = "{{ platform_plugins_dir }}"
+}
diff --git a/apps/emqx_plugins/src/emqx_plugins.app.src b/apps/emqx_plugins/src/emqx_plugins.app.src
new file mode 100644
index 000000000..a772f219f
--- /dev/null
+++ b/apps/emqx_plugins/src/emqx_plugins.app.src
@@ -0,0 +1,9 @@
+%% -*- mode: erlang -*-
+{application, emqx_plugins,
+ [{description, "EMQ X Plugin Management"},
+ {vsn, "0.1.0"},
+ {modules, []},
+ {mod, {emqx_plugins_app,[]}},
+ {applications, [kernel,stdlib,emqx]},
+ {env, []}
+ ]}.
diff --git a/apps/emqx_plugins/src/emqx_plugins.appup.src b/apps/emqx_plugins/src/emqx_plugins.appup.src
new file mode 100644
index 000000000..f9474dd33
--- /dev/null
+++ b/apps/emqx_plugins/src/emqx_plugins.appup.src
@@ -0,0 +1,8 @@
+%% -*- mode: erlang -*-
+{"0.1.0",
+ [ {<<".*">>, []}
+ ],
+ [
+ {<<".*">>, []}
+ ]
+}.
diff --git a/apps/emqx/src/emqx_plugins.erl b/apps/emqx_plugins/src/emqx_plugins.erl
similarity index 98%
rename from apps/emqx/src/emqx_plugins.erl
rename to apps/emqx_plugins/src/emqx_plugins.erl
index c28ead717..003ca7ec3 100644
--- a/apps/emqx/src/emqx_plugins.erl
+++ b/apps/emqx_plugins/src/emqx_plugins.erl
@@ -16,9 +16,8 @@
-module(emqx_plugins).
--include("emqx.hrl").
--include("logger.hrl").
-
+-include_lib("emqx/include/emqx.hrl").
+-include_lib("emqx/include/logger.hrl").
-export([ load/0
, load/1
@@ -41,7 +40,7 @@
%% @doc Load all plugins when the broker started.
-spec(load() -> ok | ignore | {error, term()}).
load() ->
- ok = load_ext_plugins(emqx:get_config([plugins, expand_plugins_dir], undefined)).
+ ok = load_ext_plugins(emqx:get_config([plugins, install_dir], undefined)).
%% @doc Load a Plugin
-spec(load(atom()) -> ok | {error, term()}).
diff --git a/apps/emqx_plugins/src/emqx_plugins_app.erl b/apps/emqx_plugins/src/emqx_plugins_app.erl
new file mode 100644
index 000000000..c04fbb445
--- /dev/null
+++ b/apps/emqx_plugins/src/emqx_plugins_app.erl
@@ -0,0 +1,30 @@
+%%--------------------------------------------------------------------
+%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%--------------------------------------------------------------------
+
+-module(emqx_plugins_app).
+
+-behaviour(application).
+
+-export([ start/2
+ , stop/1
+ ]).
+
+start(_Type, _Args) ->
+ {ok, Sup} = emqx_plugins_sup:start_link(),
+ {ok, Sup}.
+
+stop(_State) ->
+ ok.
diff --git a/apps/emqx_plugins/src/emqx_plugins_schema.erl b/apps/emqx_plugins/src/emqx_plugins_schema.erl
new file mode 100644
index 000000000..8d04923ff
--- /dev/null
+++ b/apps/emqx_plugins/src/emqx_plugins_schema.erl
@@ -0,0 +1,99 @@
+%%--------------------------------------------------------------------
+%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%--------------------------------------------------------------------
+
+-module(emqx_plugins_schema).
+
+-behaviour(hocon_schema).
+
+-export([ roots/0
+ , fields/1
+ ]).
+
+-include_lib("typerefl/include/types.hrl").
+
+roots() -> ["plugins"].
+
+fields("plugins") ->
+ #{fields => fields(),
+ desc => """
+Manage EMQ X plugins.
+
+Plugins can be pre-built as a part of EMQ X package,
+or installed as a standalone package in a location specified by
+install_dir
config key
+
+The standalone-installed plugins are referred to as 'external' plugins.
+"""
+ }.
+
+fields() ->
+ [ {prebuilt, fun prebuilt/1}
+ , {external, fun external/1}
+ , {install_dir, fun install_dir/1}
+ ].
+
+prebuilt(type) -> hoconsc:map("name", boolean());
+prebuilt(nullable) -> true;
+prebuilt(T) when T=/= desc -> undefined;
+prebuilt(desc) -> """
+A map() from plugin name to a boolean (true | false) flag to indicate
+whether or not to enable the prebuilt plugin.
+
+Most of the prebuilt plugins from 4.x are converted into features since 5.0.
+""" ++ prebuilt_plugins() ++
+"""
+
+Enabled plugins are loaded (started) as a part of EMQ X node's boot sequence.
+Plugins can be loaded on the fly, and enabled from dashbaord UI and/or CLI.
+
+Example config: {emqx_foo_bar: true, emqx_bazz: false}
+""".
+
+external(type) -> hoconsc:map("name", string());
+external(nullable) -> true;
+external(T) when T =/= desc -> undefined;
+external(desc) ->
+"""
+A map from plugin name to a version number string for enabled ones.
+To disable an external plugin, set the value to 'false'.
+
+Enabled plugins are loaded (started) as a part of EMQ X node's boot sequence.
+Plugins can be loaded on the fly, and enabled from dashbaord UI and/or CLI.
+
+Example config: {emqx_extplug1: \"0.1.0\", emqx_extplug2: false}
+""".
+
+install_dir(type) -> string();
+install_dir(nullable) -> true;
+install_dir(default) -> "plugins"; %% runner's root dir
+install_dir(T) when T =/= desc -> undefined;
+install_dir(desc) -> """
+In which directory are the external plugins installed.
+The plugin beam files and configuration files should reside in
+the sub-directory named as emqx_foo_bar-0.1.0
.
+
+NOTE: For security reasons, this directory should **NOT** be writable
+by anyone expect for emqx
(or any user which runs EMQ X)
+""".
+
+%% TODO: when we have some prebuilt plugins, change this function to:
+%% """
+%% The names should be one of
+%% - name1
+%% - name2
+%% """
+prebuilt_plugins() ->
+ "So far, we do not have any prebuilt plugins".
diff --git a/apps/emqx_plugins/src/emqx_plugins_sup.erl b/apps/emqx_plugins/src/emqx_plugins_sup.erl
new file mode 100644
index 000000000..c1e26752e
--- /dev/null
+++ b/apps/emqx_plugins/src/emqx_plugins_sup.erl
@@ -0,0 +1,30 @@
+%%--------------------------------------------------------------------
+%% Copyright (c) 2021 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%--------------------------------------------------------------------
+
+-module(emqx_plugins_sup).
+
+-behaviour(supervisor).
+
+-export([start_link/0]).
+
+-export([init/1]).
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+init([]) ->
+ Children = [],
+ {ok, {{one_for_one, 10, 10}, Children}}.
diff --git a/apps/emqx/test/emqx_plugins_SUITE.erl b/apps/emqx_plugins/test/emqx_plugins_SUITE.erl
similarity index 95%
rename from apps/emqx/test/emqx_plugins_SUITE.erl
rename to apps/emqx_plugins/test/emqx_plugins_SUITE.erl
index 3aba5a997..013544e32 100644
--- a/apps/emqx/test/emqx_plugins_SUITE.erl
+++ b/apps/emqx_plugins/test/emqx_plugins_SUITE.erl
@@ -42,12 +42,14 @@ init_per_suite(Config) ->
emqx_common_test_helpers:boot_modules([]),
emqx_common_test_helpers:start_apps([]),
- emqx_config:put([plugins, expand_plugins_dir], DataPath),
+ emqx_config:put([plugins, install_dir], DataPath),
?assertEqual(ok, emqx_plugins:load()),
Config.
end_per_suite(_Config) ->
- emqx_common_test_helpers:stop_apps([]).
+ emqx_common_test_helpers:boot_modules(all),
+ emqx_common_test_helpers:stop_apps([]),
+ emqx_config:erase(plugins).
t_load(_) ->
?assertEqual(ok, emqx_plugins:load()),
@@ -57,7 +59,7 @@ t_load(_) ->
?assertEqual({error, not_started}, emqx_plugins:unload(emqx_mini_plugin)),
?assertEqual({error, not_started}, emqx_plugins:unload(emqx_hocon_plugin)),
- emqx_config:put([plugins, expand_plugins_dir], undefined).
+ emqx_config:erase(plugins).
t_load_ext_plugin(_) ->
?assertError({plugin_app_file_not_found, _},
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/Makefile b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/Makefile
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/Makefile
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/Makefile
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/etc/emqx_hocon_plugin.conf b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/etc/emqx_hocon_plugin.conf
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/etc/emqx_hocon_plugin.conf
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/etc/emqx_hocon_plugin.conf
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/rebar.config b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/rebar.config
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/rebar.config
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/rebar.config
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin.app.src b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin.app.src
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin.app.src
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin.app.src
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin_app.erl b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin_app.erl
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin_app.erl
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin_app.erl
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin_schema.erl b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin_schema.erl
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin_schema.erl
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_hocon_plugin/src/emqx_hocon_plugin_schema.erl
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/Makefile b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/Makefile
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/Makefile
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/Makefile
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/etc/emqx_mini_plugin.conf b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/etc/emqx_mini_plugin.conf
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/etc/emqx_mini_plugin.conf
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/etc/emqx_mini_plugin.conf
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/priv/emqx_mini_plugin.schema b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/priv/emqx_mini_plugin.schema
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/priv/emqx_mini_plugin.schema
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/priv/emqx_mini_plugin.schema
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/rebar.config b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/rebar.config
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/rebar.config
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/rebar.config
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/src/emqx_mini_plugin.app.src b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/src/emqx_mini_plugin.app.src
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/src/emqx_mini_plugin.app.src
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/src/emqx_mini_plugin.app.src
diff --git a/apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/src/emqx_mini_plugin_app.erl b/apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/src/emqx_mini_plugin_app.erl
similarity index 100%
rename from apps/emqx/test/emqx_plugins_SUITE_data/emqx_mini_plugin/src/emqx_mini_plugin_app.erl
rename to apps/emqx_plugins/test/emqx_plugins_SUITE_data/emqx_mini_plugin/src/emqx_mini_plugin_app.erl
diff --git a/apps/emqx_retainer/test/emqx_retainer_SUITE.erl b/apps/emqx_retainer/test/emqx_retainer_SUITE.erl
index 5596e9539..7191bacc0 100644
--- a/apps/emqx_retainer/test/emqx_retainer_SUITE.erl
+++ b/apps/emqx_retainer/test/emqx_retainer_SUITE.erl
@@ -55,6 +55,7 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
emqx_common_test_helpers:stop_apps([emqx_retainer]).
+
%%--------------------------------------------------------------------
%% Test Cases
%%--------------------------------------------------------------------
diff --git a/rebar.config.erl b/rebar.config.erl
index a97d77333..b73a7a740 100644
--- a/rebar.config.erl
+++ b/rebar.config.erl
@@ -257,7 +257,7 @@ overlay_vars_pkg(pkg) ->
, {platform_etc_dir, "/etc/emqx"}
, {platform_lib_dir, ""}
, {platform_log_dir, "/var/log/emqx"}
- , {platform_plugins_dir, "/var/lib/enqx/plugins"}
+ , {platform_plugins_dir, "/var/lib/emqx/plugins"}
, {runner_root_dir, "/usr/lib/emqx"}
, {runner_bin_dir, "/usr/bin"}
, {runner_etc_dir, "/etc/emqx"}
@@ -306,6 +306,7 @@ relx_apps(ReleaseType, Edition) ->
, emqx_prometheus
, emqx_psk
, emqx_slow_subs
+ , emqx_plugins
]
++ [quicer || is_quicer_supported()]
%++ [emqx_license || is_enterprise(Edition)]