feat(conf): merge all conf to emqx.conf

This commit is contained in:
Turtle 2021-07-02 14:12:48 +08:00 committed by turtleDeng
parent fb2f2741a4
commit 918a26e921
47 changed files with 375 additions and 882 deletions

View File

@ -39,7 +39,7 @@ emqx_test(){
unzip -q "${PACKAGE_PATH}/${packagename}"
export EMQX_ZONE__EXTERNAL__SERVER_KEEPALIVE=60 \
EMQX_MQTT__MAX_TOPIC_ALIAS=10
sed -i '/emqx_telemetry/d' "${PACKAGE_PATH}"/emqx/data/loaded_plugins
# sed -i '/emqx_telemetry/d' "${PACKAGE_PATH}"/emqx/data/loaded_plugins
echo "running ${packagename} start"
if ! "${PACKAGE_PATH}"/emqx/bin/emqx start; then
@ -115,7 +115,7 @@ emqx_test(){
running_test(){
export EMQX_ZONE__EXTERNAL__SERVER_KEEPALIVE=60 \
EMQX_MQTT__MAX_TOPIC_ALIAS=10
sed -i '/emqx_telemetry/d' /var/lib/emqx/loaded_plugins
# sed -i '/emqx_telemetry/d' /var/lib/emqx/loaded_plugins
if ! emqx start; then
cat /var/log/emqx/erlang.log.1 || true

View File

@ -38,7 +38,7 @@ services:
- -c
- |
sed -i "s 127.0.0.1 $$(ip route show |grep "link" |awk '{print $$1}') g" /opt/emqx/etc/acl.conf
sed -i '/emqx_telemetry/d' /opt/emqx/data/loaded_plugins
# sed -i '/emqx_telemetry/d' /opt/emqx/data/loaded_plugins
/opt/emqx/bin/emqx foreground
healthcheck:
test: ["CMD", "/opt/emqx/bin/emqx_ctl", "status"]
@ -62,7 +62,7 @@ services:
- -c
- |
sed -i "s 127.0.0.1 $$(ip route show |grep "link" |awk '{print $$1}') g" /opt/emqx/etc/acl.conf
sed -i '/emqx_telemetry/d' /opt/emqx/data/loaded_plugins
# sed -i '/emqx_telemetry/d' /opt/emqx/data/loaded_plugins
/opt/emqx/bin/emqx foreground
healthcheck:
test: ["CMD", "/opt/emqx/bin/emqx", "ping"]

View File

@ -179,7 +179,7 @@ jobs:
cd source
pkg_name=$(basename _packages/${{ matrix.profile }}/${{ matrix.profile }}-*.zip)
unzip -q _packages/${{ matrix.profile }}/$pkg_name
gsed -i '/emqx_telemetry/d' ./emqx/data/loaded_plugins
# gsed -i '/emqx_telemetry/d' ./emqx/data/loaded_plugins
./emqx/bin/emqx start || cat emqx/log/erlang.log.1
ready='no'
for i in {1..10}; do

View File

@ -108,7 +108,7 @@ jobs:
run: |
pkg_name=$(basename _packages/${EMQX_NAME}/emqx-*.zip)
unzip -q _packages/${EMQX_NAME}/$pkg_name
gsed -i '/emqx_telemetry/d' ./emqx/data/loaded_plugins
# gsed -i '/emqx_telemetry/d' ./emqx/data/loaded_plugins
./emqx/bin/emqx start || cat emqx/log/erlang.log.1
ready='no'
for i in {1..10}; do

View File

@ -48,13 +48,13 @@ jobs:
echo "['$(date -u +"%Y-%m-%dT%H:%M:%SZ")']:waiting emqx";
sleep 5;
done
- name: verify EMQX_LOADED_PLUGINS override working
run: |
expected="{emqx_sn, true}."
output=$(docker exec -i node1.emqx.io bash -c "cat data/loaded_plugins" | tail -n1)
if [ "$expected" != "$output" ]; then
exit 1
fi
# - name: verify EMQX_LOADED_PLUGINS override working
# run: |
# expected="{emqx_sn, true}."
# output=$(docker exec -i node1.emqx.io bash -c "cat data/loaded_plugins" | tail -n1)
# if [ "$expected" != "$output" ]; then
# exit 1
# fi
- name: make paho tests
run: |
if ! docker exec -i python /scripts/pytest.sh; then

2
.gitignore vendored
View File

@ -45,6 +45,6 @@ emqx_dialyzer_*_plt
*/emqx_dashboard/priv/www
dist.zip
scripts/git-token
etc/*.seg
apps/*/etc/*.all
_upgrade_base/
TAGS

View File

@ -73,7 +73,8 @@ coveralls: $(REBAR)
@ENABLE_COVER_COMPILE=1 $(REBAR) as test coveralls send
.PHONY: $(REL_PROFILES)
$(REL_PROFILES:%=%): $(REBAR) get-dashboard
$(REL_PROFILES:%=%): $(REBAR) get-dashboard conf-segs
@$(REBAR) as $(@) do compile,release
## Not calling rebar3 clean because
@ -111,7 +112,7 @@ xref: $(REBAR)
dialyzer: $(REBAR)
@$(REBAR) as check dialyzer
COMMON_DEPS := $(REBAR) get-dashboard $(CONF_SEGS)
COMMON_DEPS := $(REBAR) get-dashboard conf-segs
## rel target is to create release package without relup
.PHONY: $(REL_PROFILES:%=%-rel) $(PKG_PROFILES:%=%-rel)
@ -152,3 +153,6 @@ quickrun:
./_build/$(PROFILE)/rel/emqx/bin/emqx console
include docker.mk
conf-segs:
@scripts/merge-config.escript

View File

@ -1,26 +0,0 @@
%%--------------------------------------------------------------------
%% [ACL](https://docs.emqx.io/broker/v3/en/config.html)
%%
%% -type(who() :: all | binary() |
%% {ipaddr, esockd_access:cidr()} |
%% {client, binary()} |
%% {user, binary()}).
%%
%% -type(access() :: subscribe | publish | pubsub).
%%
%% -type(topic() :: binary()).
%%
%% -type(rule() :: {allow, all} |
%% {allow, who(), access(), list(topic())} |
%% {deny, all} |
%% {deny, who(), access(), list(topic())}).
%%--------------------------------------------------------------------
{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}.
{allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}.
{deny, all, subscribe, ["$SYS/#", {eq, "#"}]}.
{allow, all}.

View File

@ -1,14 +0,0 @@
%%--------------------------------------------------------------------
%% For paho interoperability test cases
%%--------------------------------------------------------------------
{deny, {client, "myclientid"}, subscribe, ["test/nosubscribe"]}.
{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}.
{allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}.
{deny, all, subscribe, ["$SYS/#", {eq, "#"}]}.
{allow, all}.

View File

@ -108,8 +108,7 @@
descr :: string(),
vendor :: string() | undefined,
active = false :: boolean(),
info = #{} :: map(),
type :: atom()
info = #{} :: map()
}).
%%--------------------------------------------------------------------

View File

@ -1,111 +0,0 @@
%% -*- mode: erlang -*-
{VSN,
[
{"4.3.4",
[{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]}
]},
{"4.3.3",
[{load_module,emqx_packet,brutal_purge,soft_purge,[]},
{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]},
{load_module,emqx_ws_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]}
]},
{"4.3.2",
[{load_module,emqx_packet,brutal_purge,soft_purge,[]},
{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]},
{load_module,emqx_ws_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_http_lib,brutal_purge,soft_purge,[]},
{load_module,emqx_channel,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]},
{load_module,emqx_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_cm,brutal_purge,soft_purge,[]}
]},
{"4.3.1",
[{load_module,emqx_packet,brutal_purge,soft_purge,[]},
{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]},
{load_module,emqx_ws_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_frame,brutal_purge,soft_purge,[]},
{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_congestion,brutal_purge,soft_purge,[]},
{load_module,emqx_node_dump,brutal_purge,soft_purge,[]},
{load_module,emqx_channel,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]},
{load_module,emqx_plugins,brutal_purge,soft_purge,[]},
{load_module,emqx_logger_textfmt,brutal_purge,soft_purge,[]},
{load_module,emqx_http_lib,brutal_purge,soft_purge,[]}]},
{"4.3.0",
[{load_module,emqx_packet,brutal_purge,soft_purge,[]},
{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]},
{load_module,emqx_logger_jsonfmt,brutal_purge,soft_purge,[]},
{load_module,emqx_ws_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_congestion,brutal_purge,soft_purge,[]},
{load_module,emqx_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_frame,brutal_purge,soft_purge,[]},
{load_module,emqx_trie,brutal_purge,soft_purge,[]},
{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_node_dump,brutal_purge,soft_purge,[]},
{load_module,emqx_channel,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]},
{load_module,emqx_plugins,brutal_purge,soft_purge,[]},
{load_module,emqx_logger_textfmt,brutal_purge,soft_purge,[]},
{load_module,emqx_metrics,brutal_purge,soft_purge,[]},
{apply,{emqx_metrics,upgrade_retained_delayed_counter_type,[]}},
{load_module,emqx_http_lib,brutal_purge,soft_purge,[]}]},
{<<".*">>,[]}],
[
{"4.3.4",
[{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]}
]},
{"4.3.3",
[{load_module,emqx_packet,brutal_purge,soft_purge,[]},
{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]},
{load_module,emqx_ws_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]}
]},
{"4.3.2",
[{load_module,emqx_packet,brutal_purge,soft_purge,[]},
{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]},
{load_module,emqx_ws_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_http_lib,brutal_purge,soft_purge,[]},
{load_module,emqx_channel,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]},
{load_module,emqx_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_cm,brutal_purge,soft_purge,[]}
]},
{"4.3.1",
[{load_module,emqx_packet,brutal_purge,soft_purge,[]},
{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]},
{load_module,emqx_ws_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_frame,brutal_purge,soft_purge,[]},
{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_congestion,brutal_purge,soft_purge,[]},
{load_module,emqx_node_dump,brutal_purge,soft_purge,[]},
{load_module,emqx_channel,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]},
{load_module,emqx_plugins,brutal_purge,soft_purge,[]},
{load_module,emqx_logger_textfmt,brutal_purge,soft_purge,[]},
{load_module,emqx_http_lib,brutal_purge,soft_purge,[]}]},
{"4.3.0",
[{load_module,emqx_packet,brutal_purge,soft_purge,[]},
{load_module,emqx_shared_sub,brutal_purge,soft_purge,[]},
{load_module,emqx_logger_jsonfmt,brutal_purge,soft_purge,[]},
{load_module,emqx_ws_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_connection,brutal_purge,soft_purge,[]},
{load_module,emqx_congestion,brutal_purge,soft_purge,[]},
{load_module,emqx_frame,brutal_purge,soft_purge,[]},
{load_module,emqx_trie,brutal_purge,soft_purge,[]},
{load_module,emqx_cm,brutal_purge,soft_purge,[]},
{load_module,emqx_node_dump,brutal_purge,soft_purge,[]},
{load_module,emqx_channel,brutal_purge,soft_purge,[]},
{load_module,emqx_app,brutal_purge,soft_purge,[]},
{load_module,emqx_plugins,brutal_purge,soft_purge,[]},
{load_module,emqx_logger_textfmt,brutal_purge,soft_purge,[]},
{load_module,emqx_metrics,brutal_purge,soft_purge,[]},
{load_module,emqx_http_lib,brutal_purge,soft_purge,[]}]},
{<<".*">>,[]}]}.

View File

@ -227,7 +227,6 @@ shutdown() ->
shutdown(Reason) ->
?LOG(critical, "emqx shutdown for ~s", [Reason]),
_ = emqx_alarm_handler:unload(),
_ = emqx_plugins:unload(),
lists:foreach(fun application:stop/1
, lists:reverse(default_started_applications())
).

View File

@ -51,7 +51,7 @@ start(_Type, _Args) ->
ok = ekka_rlog:wait_for_shards(?EMQX_SHARDS, infinity),
{ok, Sup} = emqx_sup:start_link(),
ok = start_autocluster(),
ok = emqx_plugins:init(),
% ok = emqx_plugins:init(),
_ = emqx_plugins:load(),
_ = start_ce_modules(),
emqx_boot:is_enabled(listeners) andalso (ok = emqx_listeners:start()),

View File

@ -21,8 +21,6 @@
-logger_header("[Plugins]").
-export([init/0]).
-export([ load/0
, load/1
, unload/0
@ -30,8 +28,6 @@
, reload/1
, list/0
, find_plugin/1
, generate_configs/1
, apply_configs/1
]).
-export([funlog/2]).
@ -41,35 +37,14 @@
-compile(nowarn_export_all).
-endif.
-dialyzer({no_match, [ plugin_loaded/2
, plugin_unloaded/2
]}).
%%--------------------------------------------------------------------
%% APIs
%%--------------------------------------------------------------------
%% @doc Init plugins' config
-spec(init() -> ok).
init() ->
case emqx:get_env(plugins_etc_dir) of
undefined -> ok;
PluginsEtc ->
CfgFiles = [filename:join(PluginsEtc, File) ||
File <- filelib:wildcard("*.config", PluginsEtc)],
lists:foreach(fun init_config/1, CfgFiles)
end.
%% @doc Load all plugins when the broker started.
-spec(load() -> ok | ignore | {error, term()}).
load() ->
ok = load_ext_plugins(emqx:get_env(expand_plugins_dir)),
case emqx:get_env(plugins_loaded_file) of
undefined -> ignore; %% No plugins available
File ->
_ = ensure_file(File),
with_loaded_file(File, fun(Names) -> load_plugins(Names, false) end)
end.
ok = load_ext_plugins(emqx:get_env(expand_plugins_dir)).
%% @doc Load a Plugin
-spec(load(atom()) -> ok | {error, term()}).
@ -82,17 +57,13 @@ load(PluginName) when is_atom(PluginName) ->
?LOG(notice, "Plugin ~s is already started", [PluginName]),
{error, already_started};
{_, false} ->
load_plugin(PluginName, true)
load_plugin(PluginName)
end.
%% @doc Unload all plugins before broker stopped.
-spec(unload() -> list() | {error, term()}).
-spec(unload() -> ok).
unload() ->
case emqx:get_env(plugins_loaded_file) of
undefined -> ignore;
File ->
with_loaded_file(File, fun stop_plugins/1)
end.
stop_plugins(list()).
%% @doc UnLoad a Plugin
-spec(unload(atom()) -> ok | {error, term()}).
@ -105,7 +76,7 @@ unload(PluginName) when is_atom(PluginName) ->
?LOG(error, "Plugin ~s is not started", [PluginName]),
{error, not_started};
{_, _} ->
unload_plugin(PluginName, true)
unload_plugin(PluginName)
end.
reload(PluginName) when is_atom(PluginName)->
@ -126,8 +97,8 @@ reload(PluginName) when is_atom(PluginName)->
-spec(list() -> [emqx_types:plugin()]).
list() ->
StartedApps = names(started_app),
lists:map(fun({Name, _, [Type| _]}) ->
Plugin = plugin(Name, Type),
lists:map(fun({Name, _, _}) ->
Plugin = plugin(Name),
case lists:member(Name, StartedApps) of
true -> Plugin#plugin{active = true};
false -> Plugin
@ -144,12 +115,6 @@ find_plugin(Name, Plugins) ->
%% Internal functions
%%--------------------------------------------------------------------
init_config(CfgFile) ->
{ok, [AppsEnv]} = file:consult(CfgFile),
lists:foreach(fun({App, Envs}) ->
[application:set_env(App, Par, Val) || {Par, Val} <- Envs]
end, AppsEnv).
%% load external plugins which are placed in etc/plugins dir
load_ext_plugins(undefined) -> ok;
load_ext_plugins(Dir) ->
@ -173,15 +138,15 @@ load_ext_plugin(PluginDir) ->
?LOG(alert, "plugin_app_file_not_found: ~s", [AppFile]),
error({plugin_app_file_not_found, AppFile})
end,
ok = load_plugin_app(AppName, Ebin),
try
ok = generate_configs(AppName, PluginDir)
catch
throw : {conf_file_not_found, ConfFile} ->
%% this is maybe a dependency of an external plugin
?LOG(debug, "config_load_error_ignored for app=~p, path=~s", [AppName, ConfFile]),
ok
end.
ok = load_plugin_app(AppName, Ebin).
% try
% ok = generate_configs(AppName, PluginDir)
% catch
% throw : {conf_file_not_found, ConfFile} ->
% %% this is maybe a dependency of an external plugin
% ?LOG(debug, "config_load_error_ignored for app=~p, path=~s", [AppName, ConfFile]),
% ok
% end.
load_plugin_app(AppName, Ebin) ->
_ = code:add_patha(Ebin),
@ -199,57 +164,24 @@ load_plugin_app(AppName, Ebin) ->
{error, {already_loaded, _}} -> ok
end.
ensure_file(File) ->
case filelib:is_file(File) of false -> write_loaded([]); true -> ok end.
with_loaded_file(File, SuccFun) ->
case read_loaded(File) of
{ok, Names0} ->
Names = filter_plugins(Names0),
SuccFun(Names);
{error, Error} ->
?LOG(alert, "Failed to read: ~p, error: ~p", [File, Error]),
{error, Error}
end.
filter_plugins(Names) ->
lists:filtermap(fun(Name1) when is_atom(Name1) -> {true, Name1};
({Name1, true}) -> {true, Name1};
({_Name1, false}) -> false
end, Names).
load_plugins(Names, Persistent) ->
Plugins = list(),
NotFound = Names -- names(Plugins),
case NotFound of
[] -> ok;
NotFound -> ?LOG(alert, "cannot_find_plugins: ~p", [NotFound])
end,
NeedToLoad = Names -- NotFound -- names(started_app),
lists:foreach(fun(Name) ->
Plugin = find_plugin(Name, Plugins),
load_plugin(Plugin#plugin.name, Persistent)
end, NeedToLoad).
%% Stop plugins
stop_plugins(Names) ->
_ = [stop_app(App) || App <- Names],
stop_plugins(Plugins) ->
_ = [stop_app(Plugin#plugin.name) || Plugin <- Plugins],
ok.
plugin(AppName, Type) ->
plugin(AppName) ->
case application:get_all_key(AppName) of
{ok, Attrs} ->
Descr = proplists:get_value(description, Attrs, ""),
#plugin{name = AppName, descr = Descr, type = plugin_type(Type)};
#plugin{name = AppName, descr = Descr};
undefined -> error({plugin_not_found, AppName})
end.
load_plugin(Name, Persistent) ->
load_plugin(Name) ->
try
ok = ?MODULE:generate_configs(Name),
case load_app(Name) of
ok ->
start_app(Name, fun(App) -> plugin_loaded(App, Persistent) end);
start_app(Name);
{error, Error0} ->
{error, Error0}
end
@ -268,22 +200,21 @@ load_app(App) ->
{error, Error}
end.
start_app(App, SuccFun) ->
start_app(App) ->
case application:ensure_all_started(App) of
{ok, Started} ->
?LOG(info, "Started plugins: ~p", [Started]),
?LOG(info, "Load plugin ~s successfully", [App]),
_ = SuccFun(App),
ok;
{error, {ErrApp, Reason}} ->
?LOG(error, "Load plugin ~s failed, cannot start plugin ~s for ~0p", [App, ErrApp, Reason]),
{error, {ErrApp, Reason}}
end.
unload_plugin(App, Persistent) ->
unload_plugin(App) ->
case stop_app(App) of
ok ->
_ = plugin_unloaded(App, Persistent), ok;
ok;
{error, Reason} ->
{error, Reason}
end.
@ -307,133 +238,5 @@ names(started_app) ->
names(Plugins) ->
[Name || #plugin{name = Name} <- Plugins].
plugin_loaded(_Name, false) ->
ok;
plugin_loaded(Name, true) ->
case read_loaded() of
{ok, Names} ->
case lists:member(Name, Names) of
false ->
%% write file if plugin is loaded
write_loaded(lists:append(Names, [{Name, true}]));
true ->
ignore
end;
{error, Error} ->
?LOG(error, "Cannot read loaded plugins: ~p", [Error])
end.
plugin_unloaded(_Name, false) ->
ok;
plugin_unloaded(Name, true) ->
case read_loaded() of
{ok, Names0} ->
Names = filter_plugins(Names0),
case lists:member(Name, Names) of
true ->
write_loaded(lists:delete(Name, Names));
false ->
?LOG(error, "Cannot find ~s in loaded_file", [Name])
end;
{error, Error} ->
?LOG(error, "Cannot read loaded_plugins: ~p", [Error])
end.
read_loaded() ->
case emqx:get_env(plugins_loaded_file) of
undefined -> {error, not_found};
File -> read_loaded(File)
end.
read_loaded(File) -> file:consult(File).
write_loaded(AppNames) ->
FilePath = emqx:get_env(plugins_loaded_file),
case file:write_file(FilePath, [io_lib:format("~p.~n", [Name]) || Name <- AppNames]) of
ok -> ok;
{error, Error} ->
?LOG(error, "Write File ~p Error: ~p", [FilePath, Error]),
{error, Error}
end.
plugin_type(auth) -> auth;
plugin_type(protocol) -> protocol;
plugin_type(backend) -> backend;
plugin_type(bridge) -> bridge;
plugin_type(_) -> feature.
funlog(Key, Value) ->
?LOG(info, "~s = ~p", [string:join(Key, "."), Value]).
generate_configs(App) ->
PluginConfDir = emqx:get_env(plugins_etc_dir),
PluginSchemaDir = code:priv_dir(App),
generate_configs(App, PluginConfDir, PluginSchemaDir).
generate_configs(App, PluginDir) ->
PluginConfDir = filename:join([PluginDir, "etc"]),
PluginSchemaDir = filename:join([PluginDir, "priv"]),
generate_configs(App, PluginConfDir, PluginSchemaDir).
generate_configs(App, PluginConfDir, PluginSchemaDir) ->
ConfigFile = filename:join([PluginConfDir, App]) ++ ".config",
case filelib:is_file(ConfigFile) of
true ->
{ok, [Configs]} = file:consult(ConfigFile),
apply_configs(Configs);
false ->
SchemaFile = filename:join([PluginSchemaDir, App]) ++ ".schema",
case filelib:is_file(SchemaFile) of
true ->
AppsEnv = do_generate_configs(App),
apply_configs(AppsEnv);
false ->
SchemaMod = lists:concat([App, "_schema"]),
ConfName = filename:join([PluginConfDir, App]) ++ ".conf",
SchemaFile1 = filename:join([code:lib_dir(App), "ebin", SchemaMod]) ++ ".beam",
do_generate_hocon_configs(App, ConfName, SchemaFile1)
end
end.
do_generate_configs(App) ->
Name1 = filename:join([emqx:get_env(plugins_etc_dir), App]) ++ ".conf",
Name2 = filename:join([code:lib_dir(App), "etc", App]) ++ ".conf",
ConfFile = case {filelib:is_file(Name1), filelib:is_file(Name2)} of
{true, _} -> Name1;
{false, true} -> Name2;
{false, false} -> error({config_not_found, [Name1, Name2]})
end,
SchemaFile = filename:join([code:priv_dir(App), App]) ++ ".schema",
case filelib:is_file(SchemaFile) of
true ->
Schema = cuttlefish_schema:files([SchemaFile]),
Conf = cuttlefish_conf:file(ConfFile),
cuttlefish_generator:map(Schema, Conf, undefined, fun ?MODULE:funlog/2);
false ->
error({schema_not_found, SchemaFile})
end.
do_generate_hocon_configs(App, ConfName, SchemaFile) ->
SchemaMod = lists:concat([App, "_schema"]),
case {filelib:is_file(ConfName), filelib:is_file(SchemaFile)} of
{true, true} ->
{ok, RawConfig} = hocon:load(ConfName, #{format => richmap}),
_ = hocon_schema:check(list_to_atom(SchemaMod), RawConfig, #{atom_key => true,
return_plain => true}),
ok;
% emqx_config:update_config([App], Config);
{true, false} ->
error({schema_not_found, [SchemaFile]});
{false, true} ->
error({config_not_found, [ConfName]});
{false, false} ->
error({conf_and_schema_not_found, [ConfName, SchemaFile]})
end.
apply_configs([]) ->
ok;
apply_configs([{App, Config} | More]) ->
lists:foreach(fun({Key, _}) -> application:unset_env(App, Key) end, application:get_all_env(App)),
lists:foreach(fun({Key, Val}) -> application:set_env(App, Key, Val) end, Config),
apply_configs(More).

View File

@ -65,6 +65,12 @@ includes() ->
[ "emqx_data_bridge"
, "emqx_telemetry"
, "emqx_retainer"
, "emqx_statsd"
, "emqx_authn"
, "emqx_authz"
, "emqx_bridge_mqtt"
, "emqx_modules"
, "emqx_management"
].
-endif.

View File

@ -63,64 +63,28 @@ t_load(_) ->
?assertEqual({error, not_started}, emqx_plugins:unload(emqx_hocon_plugin)),
application:set_env(emqx, expand_plugins_dir, undefined),
application:set_env(emqx, plugins_loaded_file, undefined),
?assertEqual(ignore, emqx_plugins:load()),
?assertEqual(ignore, emqx_plugins:unload()).
t_init_config(_) ->
ConfFile = "emqx_mini_plugin.config",
Data = "[{emqx_mini_plugin,[{mininame ,test}]}].",
file:write_file(ConfFile, list_to_binary(Data)),
?assertEqual(ok, emqx_plugins:init_config(ConfFile)),
file:delete(ConfFile),
?assertEqual({ok,test}, application:get_env(emqx_mini_plugin, mininame)).
application:set_env(emqx, plugins_loaded_file, undefined).
t_load_ext_plugin(_) ->
?assertError({plugin_app_file_not_found, _},
emqx_plugins:load_ext_plugin("./not_existed_path/")).
t_list(_) ->
?assertMatch([{plugin, _, _, _, _, _, _, _} | _ ], emqx_plugins:list()).
?assertMatch([{plugin, _, _, _, _, _, _} | _ ], emqx_plugins:list()).
t_find_plugin(_) ->
?assertMatch({plugin, emqx_mini_plugin, _, _, _, _, _, _}, emqx_plugins:find_plugin(emqx_mini_plugin)),
?assertMatch({plugin, emqx_hocon_plugin, _, _, _, _, _, _}, emqx_plugins:find_plugin(emqx_hocon_plugin)).
t_plugin_type(_) ->
?assertEqual(auth, emqx_plugins:plugin_type(auth)),
?assertEqual(protocol, emqx_plugins:plugin_type(protocol)),
?assertEqual(backend, emqx_plugins:plugin_type(backend)),
?assertEqual(bridge, emqx_plugins:plugin_type(bridge)),
?assertEqual(feature, emqx_plugins:plugin_type(undefined)).
t_with_loaded_file(_) ->
?assertMatch({error, _}, emqx_plugins:with_loaded_file("./not_existed_path/", fun(_) -> ok end)).
t_plugin_loaded(_) ->
?assertEqual(ok, emqx_plugins:plugin_loaded(emqx_mini_plugin, false)),
?assertEqual(ok, emqx_plugins:plugin_loaded(emqx_mini_plugin, true)),
?assertEqual(ok, emqx_plugins:plugin_loaded(emqx_hocon_plugin, false)),
?assertEqual(ok, emqx_plugins:plugin_loaded(emqx_hocon_plugin, true)).
t_plugin_unloaded(_) ->
?assertEqual(ok, emqx_plugins:plugin_unloaded(emqx_mini_plugin, false)),
?assertEqual(ok, emqx_plugins:plugin_unloaded(emqx_mini_plugin, true)),
?assertEqual(ok, emqx_plugins:plugin_unloaded(emqx_hocon_plugin, false)),
?assertEqual(ok, emqx_plugins:plugin_unloaded(emqx_hocon_plugin, true)).
?assertMatch({plugin, emqx_mini_plugin, _, _, _, _, _}, emqx_plugins:find_plugin(emqx_mini_plugin)),
?assertMatch({plugin, emqx_hocon_plugin, _, _, _, _, _}, emqx_plugins:find_plugin(emqx_hocon_plugin)).
t_plugin(_) ->
try
emqx_plugins:plugin(not_existed_plugin, undefined)
emqx_plugins:plugin(not_existed_plugin)
catch
_Error:Reason:_Stacktrace ->
?assertEqual({plugin_not_found,not_existed_plugin}, Reason)
end,
?assertMatch({plugin, emqx_mini_plugin, _, _, _, _, _, _}, emqx_plugins:plugin(emqx_mini_plugin, undefined)),
?assertMatch({plugin, emqx_hocon_plugin, _, _, _, _, _, _}, emqx_plugins:plugin(emqx_hocon_plugin, undefined)).
t_filter_plugins(_) ->
?assertEqual([name1, name2], emqx_plugins:filter_plugins([name1, {name2,true}, {name3, false}])).
?assertMatch({plugin, emqx_mini_plugin, _, _, _, _, _}, emqx_plugins:plugin(emqx_mini_plugin)),
?assertMatch({plugin, emqx_hocon_plugin, _, _, _, _, _}, emqx_plugins:plugin(emqx_hocon_plugin)).
t_load_plugin(_) ->
ok = meck:new(application, [unstick, non_strict, passthrough, no_history]),
@ -133,9 +97,9 @@ t_load_plugin(_) ->
ok = meck:new(emqx_plugins, [unstick, non_strict, passthrough, no_history]),
ok = meck:expect(emqx_plugins, generate_configs, fun(_) -> ok end),
ok = meck:expect(emqx_plugins, apply_configs, fun(_) -> ok end),
?assertMatch({error, _}, emqx_plugins:load_plugin(already_loaded_app, true)),
?assertMatch(ok, emqx_plugins:load_plugin(normal, true)),
?assertMatch({error,_}, emqx_plugins:load_plugin(error_app, true)),
?assertMatch({error, _}, emqx_plugins:load_plugin(already_loaded_app)),
?assertMatch(ok, emqx_plugins:load_plugin(normal)),
?assertMatch({error,_}, emqx_plugins:load_plugin(error_app)),
ok = meck:unload(emqx_plugins),
ok = meck:unload(application).
@ -146,8 +110,8 @@ t_unload_plugin(_) ->
(error_app) -> {error, error};
(_) -> ok end),
?assertEqual(ok, emqx_plugins:unload_plugin(not_started_app, true)),
?assertEqual(ok, emqx_plugins:unload_plugin(normal, true)),
?assertEqual({error,error}, emqx_plugins:unload_plugin(error_app, true)),
?assertEqual(ok, emqx_plugins:unload_plugin(not_started_app)),
?assertEqual(ok, emqx_plugins:unload_plugin(normal)),
?assertEqual({error,error}, emqx_plugins:unload_plugin(error_app)),
ok = meck:unload(application).

View File

@ -1,4 +1,4 @@
authn: {
emqx_authn: {
chains: [
# {
# id: "chain1"

View File

@ -38,11 +38,8 @@ stop(_State) ->
ok.
initialize() ->
ConfFile = filename:join([emqx:get_env(plugins_etc_dir), ?APP]) ++ ".conf",
{ok, RawConfig} = hocon:load(ConfFile),
#{authn := #{chains := Chains,
bindings := Bindings}}
= hocon_schema:check_plain(emqx_authn_schema, RawConfig, #{atom_key => true, nullable => true}),
#{chains := Chains,
bindings := Bindings} = emqx_config:get([authn], #{chains => [], bindings => []}),
initialize_chains(Chains),
initialize_bindings(Bindings).

View File

@ -27,9 +27,9 @@
, authenticator_name/0
]).
structs() -> [authn].
structs() -> ["emqx_authn"].
fields(authn) ->
fields("emqx_authn") ->
[ {chains, fun chains/1}
, {bindings, fun bindings/1}];
@ -80,7 +80,7 @@ fields(pgsql) ->
, {config, hoconsc:t(hoconsc:ref(emqx_authn_pgsql, config))}
].
chains(type) -> hoconsc:array({union, [hoconsc:ref('simple-chain')]});
chains(type) -> hoconsc:array({union, [hoconsc:ref(?MODULE, 'simple-chain')]});
chains(default) -> [];
chains(_) -> undefined.
@ -89,10 +89,10 @@ chain_id(nullable) -> false;
chain_id(_) -> undefined.
simple_authenticators(type) ->
hoconsc:array({union, [ hoconsc:ref('built-in-database')
, hoconsc:ref(jwt)
, hoconsc:ref(mysql)
, hoconsc:ref(pgsql)]});
hoconsc:array({union, [ hoconsc:ref(?MODULE, 'built-in-database')
, hoconsc:ref(?MODULE, jwt)
, hoconsc:ref(?MODULE, mysql)
, hoconsc:ref(?MODULE, pgsql)]});
simple_authenticators(default) -> [];
simple_authenticators(_) -> undefined.
@ -105,7 +105,7 @@ authenticator_name(type) -> authenticator_name();
authenticator_name(nullable) -> false;
authenticator_name(_) -> undefined.
bindings(type) -> hoconsc:array(hoconsc:ref(binding));
bindings(type) -> hoconsc:array(hoconsc:ref(?MODULE, binding));
bindings(default) -> [];
bindings(_) -> undefined.

View File

@ -36,11 +36,7 @@ register_metrics() ->
init() ->
ok = register_metrics(),
Conf = filename:join(emqx:get_env(plugins_etc_dir), 'authz.conf'),
{ok, RawConf} = hocon:load(Conf),
#{emqx_authz := #{rules := Rules}} = hocon_schema:check_plain(emqx_authz_schema, RawConf, #{atom_key => true}),
emqx_config:put([emqx_authz], #{rules => Rules}),
% Rules = emqx_config:get([emqx_authz, rules], []),
Rules = emqx_config:get([emqx_authz, rules], []),
NRules = [compile(Rule) || Rule <- Rules],
ok = emqx_hooks:add('client.authorize', {?MODULE, authorize, [NRules]}, -1).

View File

@ -41,11 +41,7 @@ set_special_configs(emqx) ->
application:set_env(emqx, enable_acl_cache, false),
ok;
set_special_configs(emqx_authz) ->
application:set_env(emqx, plugins_etc_dir,
emqx_ct_helpers:deps_path(emqx_authz, "test")),
Conf = #{<<"emqx_authz">> => #{<<"rules">> => []}},
ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'authz.conf'), jsx:encode(Conf)),
% emqx_config:put([emqx_authz], #{rules => []}),
emqx_config:put([emqx_authz], #{rules => []}),
ok;
set_special_configs(_App) ->
ok.

View File

@ -55,22 +55,13 @@ set_special_configs(emqx) ->
application:set_env(emqx, enable_acl_cache, false),
ok;
set_special_configs(emqx_authz) ->
application:set_env(emqx, plugins_etc_dir,
emqx_ct_helpers:deps_path(emqx_authz, "test")),
Conf = #{<<"emqx_authz">> => #{<<"rules">> => []}},
ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'authz.conf'), jsx:encode(Conf)),
% emqx_config:put([emqx_authz], #{rules => []}),
emqx_config:put([emqx_authz], #{rules => []}),
ok;
set_special_configs(emqx_management) ->
application:set_env(emqx, plugins_etc_dir,
emqx_ct_helpers:deps_path(emqx_management, "test")),
Conf = #{<<"emqx_management">> => #{
<<"listeners">> => [#{
<<"protocol">> => <<"http">>
}]}
},
ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'emqx_management.conf'), jsx:encode(Conf)),
emqx_config:put([emqx_management], #{listeners => [#{protocol => "http", port => 8081}],
default_application_id => <<"admin">>,
default_application_secret => <<"public">>}),
ok;
set_special_configs(_App) ->

View File

@ -47,22 +47,12 @@ set_special_configs(emqx) ->
emqx_ct_helpers:deps_path(emqx, "test/loaded_plguins")),
ok;
set_special_configs(emqx_authz) ->
application:set_env(emqx, plugins_etc_dir,
emqx_ct_helpers:deps_path(emqx_authz, "test")),
Conf = #{<<"emqx_authz">> =>
#{<<"rules">> =>
[#{<<"config">> =>#{<<"meck">> => <<"fake">>},
<<"principal">> => all,
<<"sql">> => <<"fake sql">>,
<<"type">> => mysql}
]}},
ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'authz.conf'), jsx:encode(Conf)),
% Rules = [#{config =>#{<<"meck">> => <<"fake">>},
% principal => all,
% sql => <<"fake sql">>,
% type => mysql}
% ],
% emqx_config:put([emqx_authz], #{rules => Rules}),
Rules = [#{config =>#{<<"meck">> => <<"fake">>},
principal => all,
sql => <<"fake sql">>,
type => mysql}
],
emqx_config:put([emqx_authz], #{rules => Rules}),
ok;
set_special_configs(_App) ->
ok.

View File

@ -47,22 +47,12 @@ set_special_configs(emqx) ->
emqx_ct_helpers:deps_path(emqx, "test/loaded_plguins")),
ok;
set_special_configs(emqx_authz) ->
application:set_env(emqx, plugins_etc_dir,
emqx_ct_helpers:deps_path(emqx_authz, "test")),
Conf = #{<<"emqx_authz">> =>
#{<<"rules">> =>
[#{<<"config">> =>#{<<"meck">> => <<"fake">>},
<<"principal">> => all,
<<"sql">> => <<"fake sql">>,
<<"type">> => pgsql}
]}},
ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'authz.conf'), jsx:encode(Conf)),
% Rules = [#{config =>#{<<"meck">> => <<"fake">>},
% principal => all,
% sql => <<"fake sql">>,
% type => pgsql}
% ],
% emqx_config:put([emqx_authz], #{rules => Rules}),
Rules = [#{config =>#{<<"meck">> => <<"fake">>},
principal => all,
sql => <<"fake sql">>,
type => pgsql}
],
emqx_config:put([emqx_authz], #{rules => Rules}),
ok;
set_special_configs(_App) ->
ok.

View File

@ -47,28 +47,12 @@ set_special_configs(emqx) ->
emqx_ct_helpers:deps_path(emqx, "test/loaded_plguins")),
ok;
set_special_configs(emqx_authz) ->
application:set_env(emqx, plugins_etc_dir,
emqx_ct_helpers:deps_path(emqx_authz, "test")),
Conf = #{<<"emqx_authz">> =>
#{<<"rules">> =>
[#{<<"config">> =>#{
<<"server">> => <<"127.0.0.1:6379">>,
<<"password">> => <<"public">>,
<<"pool_size">> => 1,
<<"auto_reconnect">> => true,
<<"ssl">> => #{<<"enable">> => false}
},
<<"principal">> => all,
<<"cmd">> => <<"fake cmd">>,
<<"type">> => redis}
]}},
ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'authz.conf'), jsx:encode(Conf)),
% Rules = [#{config =>#{<<"meck">> => <<"fake">>},
% principal => all,
% cmd => <<"fake cmd">>,
% type => redis}
% ],
% emqx_config:put([emqx_authz], #{rules => Rules}),
Rules = [#{config =>#{<<"meck">> => <<"fake">>},
principal => all,
cmd => <<"fake cmd">>,
type => redis}
],
emqx_config:put([emqx_authz], #{rules => Rules}),
ok;
set_special_configs(_App) ->
ok.

View File

@ -3,54 +3,56 @@
##====================================================================
emqx_bridge_mqtt:{
bridges:[{
name: "mqtt1"
start_type: auto
forwards: ["test/#"],
forward_mountpoint: ""
reconnect_interval: "30s"
batch_size: 100
queue:{
replayq_dir: false
bridges:[
# {
# name: "mqtt1"
# start_type: auto
# forwards: ["test/#"],
# forward_mountpoint: ""
# reconnect_interval: "30s"
# batch_size: 100
# queue:{
# replayq_dir: "{{ platform_data_dir }}/replayq/bridge_mqtt/"
# replayq_seg_bytes: "100MB"
# replayq_offload_mode: false
# replayq_max_total_bytes: "1GB"
},
config:{
conn_type: mqtt
address: "127.0.0.1:1883"
proto_ver: v4
bridge_mode: true
clientid: "client1"
clean_start: true
username: "username1"
password: ""
keepalive: 300
subscriptions: [{
topic: "t/#"
qos: 1
}]
receive_mountpoint: ""
retry_interval: "30s"
max_inflight: 32
}
},
{
name: "rpc1"
start_type: auto
forwards: ["test/#"],
forward_mountpoint: ""
reconnect_interval: "30s"
batch_size: 100
queue:{
replayq_dir: "{{ platform_data_dir }}/replayq/bridge_mqtt/"
replayq_seg_bytes: "100MB"
replayq_offload_mode: false
replayq_max_total_bytes: "1GB"
},
config:{
conn_type: rpc
node: "emqx@127.0.0.1"
}
}]
# },
# config:{
# conn_type: mqtt
# address: "127.0.0.1:1883"
# proto_ver: v4
# bridge_mode: true
# clientid: "client1"
# clean_start: true
# username: "username1"
# password: ""
# keepalive: 300
# subscriptions: [{
# topic: "t/#"
# qos: 1
# }]
# receive_mountpoint: ""
# retry_interval: "30s"
# max_inflight: 32
# }
# },
# {
# name: "rpc1"
# start_type: auto
# forwards: ["test/#"],
# forward_mountpoint: ""
# reconnect_interval: "30s"
# batch_size: 100
# queue:{
# replayq_dir: "{{ platform_data_dir }}/replayq/bridge_mqtt/"
# replayq_seg_bytes: "100MB"
# replayq_offload_mode: false
# replayq_max_total_bytes: "1GB"
# },
# config:{
# conn_type: rpc
# node: "emqx@127.0.0.1"
# }
# }
]
}

View File

@ -26,7 +26,7 @@
structs() -> ["emqx_bridge_mqtt"].
fields("emqx_bridge_mqtt") ->
[ {bridges, hoconsc:array("bridges")}
[ {bridges, hoconsc:array(hoconsc:ref(?MODULE, "bridges"))}
];
fields("bridges") ->
@ -36,8 +36,8 @@ fields("bridges") ->
, {forward_mountpoint, emqx_schema:t(string())}
, {reconnect_interval, emqx_schema:t(emqx_schema:duration_ms(), undefined, "30s")}
, {batch_size, emqx_schema:t(integer(), undefined, 100)}
, {queue, emqx_schema:t(hoconsc:ref("queue"))}
, {config, hoconsc:union([hoconsc:ref("mqtt"), hoconsc:ref("rpc")])}
, {queue, emqx_schema:t(hoconsc:ref(?MODULE, "queue"))}
, {config, hoconsc:union([hoconsc:ref(?MODULE, "mqtt"), hoconsc:ref(?MODULE, "rpc")])}
];
fields("mqtt") ->

View File

@ -40,18 +40,7 @@
-define(OVERVIEWS, ['alarms/activated', 'alarms/deactivated', banned, brokers, stats, metrics, listeners, clients, subscriptions, routes, plugins]).
all() ->
[{group, overview},
{group, admins},
{group, rest},
{group, cli}
].
groups() ->
[{overview, [sequence], [t_overview]},
{admins, [sequence], [t_admins_add_delete]},
{rest, [sequence], [t_rest_api]},
{cli, [sequence], [t_cli]}
].
emqx_ct:all(?MODULE).
init_per_suite(Config) ->
emqx_ct_helpers:start_apps([emqx_management, emqx_dashboard],fun set_special_configs/1),
@ -62,29 +51,27 @@ end_per_suite(_Config) ->
ekka_mnesia:ensure_stopped().
set_special_configs(emqx_management) ->
application:set_env(emqx, plugins_etc_dir,
emqx_ct_helpers:deps_path(emqx_management, "test")),
Conf = #{<<"emqx_management">> => #{
<<"listeners">> => [#{
<<"protocol">> => <<"http">>
}]}
},
ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'emqx_management.conf'), jsx:encode(Conf)),
emqx_config:put([emqx_management], #{listeners => [#{protocol => "http", port => 8081}],
default_application_id => <<"admin">>,
default_application_secret => <<"public">>}),
ok;
set_special_configs(_) ->
ok.
t_overview(_) ->
mnesia:clear_table(mqtt_admin),
emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, <<"tag">>),
[?assert(request_dashboard(get, api_path(erlang:atom_to_list(Overview)), auth_header_()))|| Overview <- ?OVERVIEWS].
t_admins_add_delete(_) ->
mnesia:clear_table(mqtt_admin),
ok = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, <<"tag">>),
ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, <<"tag1">>),
Admins = emqx_dashboard_admin:all_users(),
?assertEqual(3, length(Admins)),
?assertEqual(2, length(Admins)),
ok = emqx_dashboard_admin:remove_user(<<"username1">>),
Users = emqx_dashboard_admin:all_users(),
?assertEqual(2, length(Users)),
?assertEqual(1, length(Users)),
ok = emqx_dashboard_admin:change_password(<<"username">>, <<"password">>, <<"pwd">>),
timer:sleep(10),
?assert(request_dashboard(get, api_path("brokers"), auth_header_("username", "pwd"))),
@ -93,6 +80,8 @@ t_admins_add_delete(_) ->
?assertNotEqual(true, request_dashboard(get, api_path("brokers"), auth_header_("username", "pwd"))).
t_rest_api(_Config) ->
mnesia:clear_table(mqtt_admin),
emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, <<"administrator">>),
{ok, Res0} = http_get("users"),
?assertEqual([#{<<"username">> => <<"admin">>,

View File

@ -2,125 +2,128 @@
## EMQ X Bridge Plugin
##--------------------------------------------------------------------
emqx_data_bridge.bridges: [
# {name: "mysql_bridge_1"
# type: mysql
# config: {
# server: "192.168.0.172:3306"
# database: mqtt
# pool_size: 1
# username: root
# password: public
# auto_reconnect: true
# ssl: false
# }
# }
# , {name: "pgsql_bridge_1"
# type: pgsql
# config: {
# server: "192.168.0.172:5432"
# database: mqtt
# pool_size: 1
# username: root
# password: public
# auto_reconnect: true
# ssl: false
# }
# }
# , {name: "mongodb_bridge_single"
# type: mongo
# config: {
# servers: "192.168.0.172:27017"
# mongo_type: single
# pool_size: 1
# login: root
# password: public
# auth_source: mqtt
# database: mqtt
# ssl: false
# }
# }
# ,{name: "mongodb_bridge_rs"
# type: mongo
# config: {
# servers: "127.0.0.1:27017"
# mongo_type: rs
# rs_set_name: rs_name
# pool_size: 1
# login: root
# password: public
# auth_source: mqtt
# database: mqtt
# ssl: false
# }
# }
# ,{name: "mongodb_bridge_shared"
# type: mongo
# config: {
# servers: "127.0.0.1:27017"
# mongo_type: shared
# pool_size: 1
# login: root
# password: public
# auth_source: mqtt
# database: mqtt
# ssl: false
# max_overflow: 1
# overflow_ttl:
# overflow_check_period: 10s
# local_threshold_ms: 10s
# connect_timeout_ms: 10s
# socket_timeout_ms: 10s
# server_selection_timeout_ms: 10s
# wait_queue_timeout_ms: 10s
# heartbeat_frequency_ms: 10s
# min_heartbeat_frequency_ms: 10s
# }
# }
# , {name: "redis_bridge_single"
# type: redis
# config: {
# servers: "192.168.0.172:6379"
# redis_type: single
# pool_size: 1
# database: 0
# password: public
# auto_reconnect: true
# ssl: false
# }
# }
# ,{name: "redis_bridge_sentinel"
# type: redis
# config: {
# servers: "127.0.0.1:6379, 127.0.0.2:6379, 127.0.0.3:6379"
# redis_type: sentinel
# sentinel_name: mymaster
# pool_size: 1
# database: 0
# ssl: false
# }
# }
# ,{name: "redis_bridge_cluster"
# type: redis
# config: {
# servers: "127.0.0.1:6379, 127.0.0.2:6379, 127.0.0.3:6379"
# redis_type: cluster
# pool_size: 1
# database: 0
# password: "public"
# ssl: false
# }
# }
# , {name: "ldap_bridge_1"
# type: ldap
# config: {
# servers: "192.168.0.172"
# port: 389
# bind_dn: "cn=root,dc=emqx,dc=io"
# bind_password: "public"
# timeout: 30s
# pool_size: 1
# ssl: false
# }
# }
]
emqx_data_bridge:{
bridges:[
# {name: "mysql_bridge_1"
# type: mysql
# config: {
# server: "192.168.0.172:3306"
# database: mqtt
# pool_size: 1
# username: root
# password: public
# auto_reconnect: true
# ssl: false
# }
# }
# , {name: "pgsql_bridge_1"
# type: pgsql
# config: {
# server: "192.168.0.172:5432"
# database: mqtt
# pool_size: 1
# username: root
# password: public
# auto_reconnect: true
# ssl: false
# }
# }
# , {name: "mongodb_bridge_single"
# type: mongo
# config: {
# servers: "192.168.0.172:27017"
# mongo_type: single
# pool_size: 1
# login: root
# password: public
# auth_source: mqtt
# database: mqtt
# ssl: false
# }
# }
# ,{name: "mongodb_bridge_rs"
# type: mongo
# config: {
# servers: "127.0.0.1:27017"
# mongo_type: rs
# rs_set_name: rs_name
# pool_size: 1
# login: root
# password: public
# auth_source: mqtt
# database: mqtt
# ssl: false
# }
# }
# ,{name: "mongodb_bridge_shared"
# type: mongo
# config: {
# servers: "127.0.0.1:27017"
# mongo_type: shared
# pool_size: 1
# login: root
# password: public
# auth_source: mqtt
# database: mqtt
# ssl: false
# max_overflow: 1
# overflow_ttl:
# overflow_check_period: 10s
# local_threshold_ms: 10s
# connect_timeout_ms: 10s
# socket_timeout_ms: 10s
# server_selection_timeout_ms: 10s
# wait_queue_timeout_ms: 10s
# heartbeat_frequency_ms: 10s
# min_heartbeat_frequency_ms: 10s
# }
# }
# , {name: "redis_bridge_single"
# type: redis
# config: {
# servers: "192.168.0.172:6379"
# redis_type: single
# pool_size: 1
# database: 0
# password: public
# auto_reconnect: true
# ssl: false
# }
# }
# ,{name: "redis_bridge_sentinel"
# type: redis
# config: {
# servers: "127.0.0.1:6379, 127.0.0.2:6379, 127.0.0.3:6379"
# redis_type: sentinel
# sentinel_name: mymaster
# pool_size: 1
# database: 0
# ssl: false
# }
# }
# ,{name: "redis_bridge_cluster"
# type: redis
# config: {
# servers: "127.0.0.1:6379, 127.0.0.2:6379, 127.0.0.3:6379"
# redis_type: cluster
# pool_size: 1
# database: 0
# password: "public"
# ssl: false
# }
# }
# , {name: "ldap_bridge_1"
# type: ldap
# config: {
# servers: "192.168.0.172"
# port: 389
# bind_dn: "cn=root,dc=emqx,dc=io"
# bind_password: "public"
# timeout: 30s
# pool_size: 1
# ssl: false
# }
# }
]
}

View File

@ -1,6 +1,6 @@
{application, emqx_management,
[{description, "EMQ X Management API and CLI"},
{vsn, "4.4.0"}, % strict semver, bump manually!
{vsn, "5.0.0"}, % strict semver, bump manually!
{modules, []},
{registered, [emqx_management_sup]},
{applications, [kernel,stdlib,minirest]},

View File

@ -1,13 +0,0 @@
%% -*- mode: erlang -*-
{VSN,
[ {<<"4.3.[0-2]">>,
[ {restart_application, emqx_management}
]},
{<<".*">>, []}
],
[ {<<"4.3.[0-2]">>,
[ {restart_application, emqx_management}
]},
{<<".*">>, []}
]
}.

View File

@ -28,7 +28,7 @@ fields("emqx_management") ->
[ {default_application_id, fun default_application_id/1}
, {default_application_secret, fun default_application_secret/1}
, {max_row_limit, fun max_row_limit/1}
, {listeners, hoconsc:array(hoconsc:union([hoconsc:ref("http"), hoconsc:ref("https")]))}
, {listeners, hoconsc:array(hoconsc:union([hoconsc:ref(?MODULE, "http"), hoconsc:ref(?MODULE, "https")]))}
];
fields("http") ->

View File

@ -525,7 +525,7 @@ check_row_limit([Tab|Tables], Limit) ->
end.
max_row_limit() ->
application:get_env(?APP, max_row_limit, ?MAX_ROW_LIMIT).
emqx_config:get([?APP, max_row_limit], ?MAX_ROW_LIMIT).
table_size(Tab) -> ets:info(Tab, size).

View File

@ -106,10 +106,8 @@ format({Node, Plugins}) ->
format(#plugin{name = Name,
descr = Descr,
active = Active,
type = Type}) ->
active = Active}) ->
#{name => Name,
description => iolist_to_binary(Descr),
active => Active,
type => Type}.
active => Active}.

View File

@ -29,11 +29,6 @@
-include("emqx_mgmt.hrl").
start(_Type, _Args) ->
Conf = filename:join(emqx:get_env(plugins_etc_dir), 'emqx_management.conf'),
{ok, RawConf} = hocon:load(Conf),
#{emqx_management := Config} =
hocon_schema:check_plain(emqx_management_schema, RawConf, #{atom_key => true}),
[application:set_env(?APP, Key, maps:get(Key, Config)) || Key <- maps:keys(Config)],
{ok, Sup} = emqx_mgmt_sup:start_link(),
ok = ekka_rlog:wait_for_shards([?MANAGEMENT_SHARD], infinity),
_ = emqx_mgmt_auth:add_default_app(),

View File

@ -68,14 +68,14 @@ mnesia(copy) ->
%%--------------------------------------------------------------------
-spec(add_default_app() -> ok | {ok, appsecret()} | {error, term()}).
add_default_app() ->
AppId = application:get_env(?APP, default_application_id, undefined),
AppSecret = application:get_env(?APP, default_application_secret, undefined),
AppId = emqx_config:get([?APP, default_application_id], undefined),
AppSecret = emqx_config:get([?APP, default_application_secret], undefined),
case {AppId, AppSecret} of
{undefined, _} -> ok;
{_, undefined} -> ok;
{_, _} ->
AppId1 = erlang:list_to_binary(AppId),
AppSecret1 = erlang:list_to_binary(AppSecret),
AppId1 = to_binary(AppId),
AppSecret1 = to_binary(AppSecret),
add_app(AppId1, <<"Default">>, AppSecret1, <<"Application user">>, true, undefined)
end.
@ -210,3 +210,6 @@ is_authorized(AppId, AppSecret) ->
is_expired(undefined) -> true;
is_expired(Expired) -> Expired >= erlang:system_time(second).
to_binary(L) when is_list(L) -> list_to_binary(L);
to_binary(B) when is_binary(B) -> B.

View File

@ -80,7 +80,7 @@ stop_listener({Proto, Port, _}) ->
listeners() ->
[{list_to_atom(Protocol), Port, maps:to_list(maps:without([protocol, port], Map))}
|| Map = #{protocol := Protocol,port := Port}
<- application:get_env(?APP, listeners, [])].
<- emqx_config:get([emqx_management, listeners], [])].
listener_name(Proto) ->
list_to_atom(atom_to_list(Proto) ++ ":management").

View File

@ -29,50 +29,19 @@
-define(LOG_HANDLER_ID, [file, default]).
all() ->
[{group, manage_apps},
{group, check_cli}].
groups() ->
[{manage_apps, [sequence],
[t_app
]},
{check_cli, [sequence],
[t_cli,
t_log_cmd,
t_mgmt_cmd,
t_status_cmd,
t_clients_cmd,
t_vm_cmd,
t_plugins_cmd,
t_trace_cmd,
t_broker_cmd,
t_router_cmd,
t_subscriptions_cmd,
t_listeners_cmd_old,
t_listeners_cmd_new
]}].
apps() ->
[emqx_management, emqx_retainer].
emqx_ct:all(?MODULE).
init_per_suite(Config) ->
ekka_mnesia:start(),
emqx_mgmt_auth:mnesia(boot),
emqx_ct_helpers:start_apps(apps(), fun set_special_configs/1),
emqx_ct_helpers:start_apps([emqx_retainer, emqx_management], fun set_special_configs/1),
Config.
end_per_suite(_Config) ->
emqx_ct_helpers:stop_apps(apps()).
emqx_ct_helpers:stop_apps([emqx_management, emqx_retainer]).
set_special_configs(emqx_management) ->
application:set_env(emqx, plugins_etc_dir,
emqx_ct_helpers:deps_path(emqx_management, "test")),
Conf = #{<<"emqx_management">> => #{
<<"listeners">> => [#{
<<"protocol">> => <<"http">>
}]}
},
ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'emqx_management.conf'), jsx:encode(Conf)),
emqx_config:put([emqx_management], #{listeners => [#{protocol => "http", port => 8081}]}),
ok;
set_special_configs(_App) ->
ok.

View File

@ -51,14 +51,9 @@ end_per_testcase(_, Config) ->
Config.
set_special_configs(emqx_management) ->
application:set_env(emqx, plugins_etc_dir,
emqx_ct_helpers:deps_path(emqx_management, "test")),
Conf = #{<<"emqx_management">> => #{
<<"listeners">> => [#{
<<"protocol">> => <<"http">>
}]}
},
ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'emqx_management.conf'), jsx:encode(Conf)),
emqx_config:put([emqx_management], #{listeners => [#{protocol => "http", port => 8081}],
default_application_id => <<"admin">>,
default_application_secret => <<"public">>}),
ok;
set_special_configs(_App) ->
ok.

View File

@ -143,6 +143,8 @@ cli(_) ->
{"modules reload <Module>", "Reload module"}
]).
name(Name) when is_binary(Name) ->
name(binary_to_atom(Name, utf8));
name(delayed) -> emqx_mod_delayed;
name(presence) -> emqx_mod_presence;
name(recon) -> emqx_mod_recon;

View File

@ -37,15 +37,9 @@ init_per_suite(Config) ->
Config.
set_special_configs(emqx_management) ->
application:set_env(emqx, modules_loaded_file, emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/loaded_modules")),
application:set_env(emqx, plugins_etc_dir,
emqx_ct_helpers:deps_path(emqx_management, "test")),
Conf = #{<<"emqx_management">> => #{
<<"listeners">> => [#{
<<"protocol">> => <<"http">>
}]}
},
ok = file:write_file(filename:join(emqx:get_env(plugins_etc_dir), 'emqx_management.conf'), jsx:encode(Conf)),
emqx_config:put([emqx_management], #{listeners => [#{protocol => "http", port => 8081}],
default_application_id => <<"admin">>,
default_application_secret => <<"public">>}),
ok;
set_special_configs(_) ->
ok.

View File

@ -5,4 +5,4 @@
{platform_etc_dir, "etc"}.
{platform_lib_dir, "lib"}.
{platform_log_dir, "log"}.
{platform_plugins_dir, "plugins"}.
{platform_plugins_dir, "data/plugins"}.

View File

@ -1,3 +0,0 @@
{emqx_management, true}.
{emqx_dashboard, true}.
{emqx_retainer, {{enable_plugin_emqx_retainer}}}.

View File

@ -127,8 +127,9 @@ prod_compile_opts() ->
prod_overrides() ->
[{add, [ {erl_opts, [deterministic]}]}].
relup_deps(Profile) ->
{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", compile, "scripts/inject-deps.escript " ++ atom_to_list(Profile)}]}.
relup_deps(_Profile) ->
% {post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", compile, "scripts/inject-deps.escript " ++ atom_to_list(Profile)}]}.
{post_hooks, []}.
profiles() ->
Vsn = get_vsn(),
@ -192,9 +193,8 @@ overlay_vars_rel(RelType) ->
cloud -> "vm.args";
edge -> "vm.args.edge"
end,
[
{enable_plugin_emqx_retainer, true}
, {vm_args_file, VmArgs}
[ {vm_args_file, VmArgs}
].
%% vars per packaging type, bin(zip/tar.gz/docker) or pkg(rpm/deb)
@ -204,7 +204,7 @@ overlay_vars_pkg(bin) ->
, {platform_etc_dir, "etc"}
, {platform_lib_dir, "lib"}
, {platform_log_dir, "log"}
, {platform_plugins_dir, "etc/plugins"}
, {platform_plugins_dir, "plugins"}
, {runner_root_dir, "$(cd $(dirname $(readlink $0 || echo $0))/..; pwd -P)"}
, {runner_bin_dir, "$RUNNER_ROOT_DIR/bin"}
, {runner_etc_dir, "$RUNNER_ROOT_DIR/etc"}
@ -219,7 +219,7 @@ overlay_vars_pkg(pkg) ->
, {platform_etc_dir, "/etc/emqx"}
, {platform_lib_dir, ""}
, {platform_log_dir, "/var/log/emqx"}
, {platform_plugins_dir, "/var/lib/emqx/plugins"}
, {platform_plugins_dir, "/var/lib/enqx/plugins"}
, {runner_root_dir, "/usr/lib/emqx"}
, {runner_bin_dir, "/usr/bin"}
, {runner_etc_dir, "/etc/emqx"}
@ -255,8 +255,12 @@ relx_apps(ReleaseType) ->
, emqx_connector
, emqx_data_bridge
, emqx_rule_engine
, emqx_rule_actions
, emqx_bridge_mqtt
, emqx_modules
, emqx_management
, emqx_retainer
, emqx_statsd
]
++ [emqx_telemetry || not is_enterprise()]
++ [emqx_license || is_enterprise()]
@ -279,25 +283,13 @@ is_app(Name) ->
end.
relx_plugin_apps(ReleaseType) ->
[ emqx_retainer
, emqx_management
, emqx_dashboard
, emqx_sn
, emqx_coap
, emqx_stomp
, emqx_statsd
, emqx_rule_actions
]
[]
++ relx_plugin_apps_per_rel(ReleaseType)
++ relx_plugin_apps_enterprise(is_enterprise())
++ relx_plugin_apps_extra().
relx_plugin_apps_per_rel(cloud) ->
[ emqx_lwm2m
, emqx_exhook
, emqx_exproto
, emqx_prometheus
];
[];
relx_plugin_apps_per_rel(edge) ->
[].
@ -313,11 +305,11 @@ relx_plugin_apps_extra() ->
relx_overlay(ReleaseType) ->
[ {mkdir, "log/"}
, {mkdir, "data/"}
, {mkdir, "plugins"}
, {mkdir, "data/mnesia"}
, {mkdir, "data/configs"}
, {mkdir, "data/patches"}
, {mkdir, "data/scripts"}
, {template, "data/loaded_plugins.tmpl", "data/loaded_plugins"}
, {template, "data/emqx_vars", "releases/emqx_vars"}
, {template, "data/BUILT_ON", "releases/{{release_version}}/BUILT_ON"}
, {copy, "bin/emqx", "bin/emqx"}
@ -337,12 +329,8 @@ relx_overlay(ReleaseType) ->
end.
etc_overlay(ReleaseType) ->
PluginApps = relx_plugin_apps(ReleaseType),
Templates = emqx_etc_overlay(ReleaseType) ++
lists:append([plugin_etc_overlays(App) || App <- PluginApps]) ++
[community_plugin_etc_overlays(App) || App <- relx_plugin_apps_extra()],
Templates = emqx_etc_overlay(ReleaseType),
[ {mkdir, "etc/"}
, {mkdir, "etc/plugins"}
, {copy, "{{base_dir}}/lib/emqx/etc/certs","etc/"}
] ++
lists:map(
@ -352,7 +340,7 @@ etc_overlay(ReleaseType) ->
++ extra_overlay(ReleaseType).
extra_overlay(cloud) ->
[ {copy,"{{base_dir}}/lib/emqx_lwm2m/lwm2m_xml","etc/"}
[
];
extra_overlay(edge) ->
[].
@ -366,42 +354,9 @@ emqx_etc_overlay(edge) ->
].
emqx_etc_overlay_common() ->
[{"{{base_dir}}/lib/emqx/etc/acl.conf", "etc/acl.conf"},
{"{{base_dir}}/lib/emqx/etc/emqx.conf", "etc/emqx.conf"},
{"{{base_dir}}/lib/emqx/etc/ssl_dist.conf", "etc/ssl_dist.conf"},
{"{{base_dir}}/lib/emqx_data_bridge/etc/emqx_data_bridge.conf", "etc/plugins/emqx_data_bridge.conf"},
{"{{base_dir}}/lib/emqx_telemetry/etc/emqx_telemetry.conf", "etc/plugins/emqx_telemetry.conf"},
{"{{base_dir}}/lib/emqx_authn/etc/emqx_authn.conf", "etc/plugins/emqx_authn.conf"},
{"{{base_dir}}/lib/emqx_authz/etc/emqx_authz.conf", "etc/plugins/authz.conf"},
{"{{base_dir}}/lib/emqx_rule_engine/etc/emqx_rule_engine.conf", "etc/plugins/emqx_rule_engine.conf"},
{"{{base_dir}}/lib/emqx_bridge_mqtt/etc/emqx_bridge_mqtt.conf", "etc/plugins/emqx_bridge_mqtt.conf"},
{"{{base_dir}}/lib/emqx_modules/etc/emqx_modules.conf", "etc/plugins/emqx_modules.conf"},
%% TODO: check why it has to end with .paho
%% and why it is put to etc/plugins dir
{"{{base_dir}}/lib/emqx/etc/acl.conf.paho", "etc/plugins/acl.conf.paho"}].
plugin_etc_overlays(App0) ->
App = atom_to_list(App0),
ConfFiles = find_conf_files(App),
%% NOTE: not filename:join here since relx translates it for windows
[{"{{base_dir}}/lib/"++ App ++"/etc/" ++ F, "etc/plugins/" ++ F}
|| F <- ConfFiles].
community_plugin_etc_overlays(App0) ->
App = atom_to_list(App0),
{"{{base_dir}}/lib/"++ App ++"/etc/" ++ App ++ ".conf", "etc/plugins/" ++ App ++ ".conf"}.
%% NOTE: for apps fetched as rebar dependency (there is so far no such an app)
%% the overlay should be hand-coded but not to rely on build-time wildcards.
find_conf_files(App) ->
Dir1 = filename:join(["apps", App, "etc"]),
filelib:wildcard("*.conf", Dir1) ++
case is_enterprise() of
true ->
Dir2 = filename:join(["lib-ee", App, "etc"]),
filelib:wildcard("*.conf", Dir2);
false -> []
end.
[ {"{{base_dir}}/lib/emqx/etc/emqx.conf.all", "etc/emqx.conf"}
, {"{{base_dir}}/lib/emqx/etc/ssl_dist.conf", "etc/ssl_dist.conf"}
].
get_vsn() ->
PkgVsn = os:cmd("./pkg-vsn.sh"),

View File

@ -7,7 +7,7 @@ cd -P -- "$(dirname -- "$0")/.."
find_app() {
local appdir="$1"
find "${appdir}" -mindepth 1 -maxdepth 1 -type d | grep -vE "emqx_exhook|emqx_exproto|emqx_lwm2m|emqx_sn|emqx_coap|emqx_stomp"
find "${appdir}" -mindepth 1 -maxdepth 1 -type d | grep -vE "emqx_exhook|emqx_exproto|emqx_lwm2m|emqx_sn|emqx_coap|emqx_stomp|emqx_dashboard"
}
find_app 'apps'

36
scripts/merge-config.escript Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env escript
%% This script reads up emqx.conf and split the sections
%% and dump sections to separate files.
%% Sections are grouped between CONFIG_SECTION_BGN and
%% CONFIG_SECTION_END pairs
%%
%% NOTE: this feature is so far not used in opensource
%% edition due to backward-compatibility reasons.
-mode(compile).
main(_) ->
BaseConf = "apps/emqx/etc/emqx.conf",
{ok, Bin} = file:read_file(BaseConf),
Apps = filelib:wildcard("emqx_*", "apps/"),
Conf = lists:foldl(fun(App, Acc) ->
case lists:member(App, ["emqx_exhook",
"emqx_exproto",
"emqx_lwm2m",
"emqx_sn",
"emqx_coap",
"emqx_stomp",
"emqx_dashboard"]) of
true -> Acc;
false ->
Filename = filename:join([apps, App, "etc", App]) ++ ".conf",
case filelib:is_regular(Filename) of
true ->
{ok, Bin1} = file:read_file(Filename),
<<Acc/binary, "\r\n", Bin1/binary>>;
false -> Acc
end
end
end, Bin, Apps),
ok = file:write_file("apps/emqx/etc/emqx.conf.all", Conf).