Merge pull request #12507 from keynslug/fix/gw-app-no-scan

fix(gateway): avoid scanning modules of known gateway apps
This commit is contained in:
Andrew Mayorov 2024-02-15 17:11:49 +01:00 committed by GitHub
commit 4484578c1f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 82 additions and 110 deletions

View File

@ -20,6 +20,17 @@
-include("emqx_gateway.hrl"). -include("emqx_gateway.hrl").
-include_lib("emqx/include/logger.hrl"). -include_lib("emqx/include/logger.hrl").
-define(GATEWAYS, [
emqx_gateway_coap,
emqx_gateway_exproto,
emqx_gateway_gbt32960,
emqx_gateway_jt808,
emqx_gateway_lwm2m,
emqx_gateway_mqttsn,
emqx_gateway_ocpp,
emqx_gateway_stomp
]).
-export([ -export([
childspec/2, childspec/2,
childspec/3, childspec/3,
@ -679,32 +690,28 @@ default_subopts() ->
-spec find_gateway_definitions() -> list(gateway_def()). -spec find_gateway_definitions() -> list(gateway_def()).
find_gateway_definitions() -> find_gateway_definitions() ->
ensure_gateway_loaded(), lists:flatmap(
lists:flatten(
lists:map(
fun(App) -> fun(App) ->
gateways(find_attrs(App, gateway)) lists:flatmap(fun gateways/1, find_attrs(App, gateway))
end, end,
ignore_lib_apps(application:loaded_applications()) ?GATEWAYS
)
). ).
-spec find_gateway_definition(atom()) -> {ok, map()} | {error, term()}. -spec find_gateway_definition(atom()) -> {ok, map()} | {error, term()}.
find_gateway_definition(Name) -> find_gateway_definition(Name) ->
ensure_gateway_loaded(), find_gateway_definition(Name, ?GATEWAYS).
find_gateway_definition(Name, ignore_lib_apps(application:loaded_applications())).
-dialyzer({no_match, [find_gateway_definition/2]}). -dialyzer({no_match, [find_gateway_definition/2]}).
find_gateway_definition(Name, [App | T]) -> find_gateway_definition(Name, [App | T]) ->
Attrs = find_attrs(App, gateway), Attrs = find_attrs(App, gateway),
SearchFun = fun({_App, _Mod, #{name := GwName}}) -> SearchFun = fun(#{name := GwName}) ->
GwName =:= Name GwName =:= Name
end, end,
case lists:search(SearchFun, Attrs) of case lists:search(SearchFun, Attrs) of
{value, {_App, _Mod, Defination}} -> {value, Definition} ->
case check_gateway_edition(Defination) of case check_gateway_edition(Definition) of
true -> true ->
{ok, Defination}; {ok, Definition};
_ -> _ ->
{error, invalid_edition} {error, invalid_edition}
end; end;
@ -715,23 +722,18 @@ find_gateway_definition(_Name, []) ->
{error, not_found}. {error, not_found}.
-dialyzer({no_match, [gateways/1]}). -dialyzer({no_match, [gateways/1]}).
gateways([]) -> gateways(
[]; Definition = #{
gateways([
{_App, _Mod,
Defination =
#{
name := Name, name := Name,
callback_module := CbMod, callback_module := CbMod,
config_schema_module := SchemaMod config_schema_module := SchemaMod
}} }
| More ) when is_atom(Name), is_atom(CbMod), is_atom(SchemaMod) ->
]) when is_atom(Name), is_atom(CbMod), is_atom(SchemaMod) -> case check_gateway_edition(Definition) of
case check_gateway_edition(Defination) of
true -> true ->
[Defination | gateways(More)]; [Definition];
_ -> _ ->
gateways(More) []
end. end.
-if(?EMQX_RELEASE_EDITION == ee). -if(?EMQX_RELEASE_EDITION == ee).
@ -742,12 +744,10 @@ check_gateway_edition(Defination) ->
ce == maps:get(edition, Defination, ce). ce == maps:get(edition, Defination, ce).
-endif. -endif.
find_attrs(App, Def) -> find_attrs(AppMod, Def) ->
[ [
{App, Mod, Attr} Attr
|| {ok, Modules} <- [application:get_key(App, modules)], || {Name, Attrs} <- module_attributes(AppMod),
Mod <- Modules,
{Name, Attrs} <- module_attributes(Mod),
Name =:= Def, Name =:= Def,
Attr <- Attrs Attr <- Attrs
]. ].
@ -759,43 +759,6 @@ module_attributes(Module) ->
error:undef -> [] error:undef -> []
end. end.
ignore_lib_apps(Apps) ->
LibApps = [
kernel,
stdlib,
sasl,
appmon,
eldap,
erts,
syntax_tools,
ssl,
crypto,
mnesia,
os_mon,
inets,
goldrush,
gproc,
runtime_tools,
snmp,
otp_mibs,
public_key,
asn1,
ssh,
hipe,
common_test,
observer,
webtool,
xmerl,
tools,
test_server,
compiler,
debugger,
eunit,
et,
wx
],
[AppName || {AppName, _, _} <- Apps, not lists:member(AppName, LibApps)].
-spec plus_max_connections(non_neg_integer() | infinity, non_neg_integer() | infinity) -> -spec plus_max_connections(non_neg_integer() | infinity, non_neg_integer() | infinity) ->
pos_integer() | infinity. pos_integer() | infinity.
plus_max_connections(_, infinity) -> plus_max_connections(_, infinity) ->
@ -805,20 +768,5 @@ plus_max_connections(infinity, _) ->
plus_max_connections(A, B) when is_integer(A) andalso is_integer(B) -> plus_max_connections(A, B) when is_integer(A) andalso is_integer(B) ->
A + B. A + B.
%% we need to load all gateway applications before generate doc from cli
ensure_gateway_loaded() ->
lists:foreach(
fun application:load/1,
[
emqx_gateway_exproto,
emqx_gateway_stomp,
emqx_gateway_coap,
emqx_gateway_lwm2m,
emqx_gateway_mqttsn,
emqx_gateway_gbt32960,
emqx_gateway_ocpp
]
).
random_clientid(GwName) when is_atom(GwName) -> random_clientid(GwName) when is_atom(GwName) ->
iolist_to_binary([atom_to_list(GwName), "-", emqx_utils:gen_id()]). iolist_to_binary([atom_to_list(GwName), "-", emqx_utils:gen_id()]).

View File

@ -74,7 +74,10 @@ end_per_testcase(_TestCase, _Config) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
t_registered_gateway(_) -> t_registered_gateway(_) ->
[{coap, #{cbkmod := emqx_gateway_coap}} | _] = emqx_gateway:registered_gateway(). ?assertMatch(
[{coap, #{cbkmod := emqx_gateway_coap}} | _],
lists:sort(emqx_gateway:registered_gateway())
).
t_load_unload_list_lookup(_) -> t_load_unload_list_lookup(_) ->
{ok, _} = emqx_gateway:load(?GWNAME, #{idle_timeout => 1000}), {ok, _} = emqx_gateway:load(?GWNAME, #{idle_timeout => 1000}),

View File

@ -45,17 +45,24 @@
all() -> emqx_common_test_helpers:all(?MODULE). all() -> emqx_common_test_helpers:all(?MODULE).
init_per_suite(Conf) -> init_per_suite(Conf) ->
application:load(emqx), Apps = emqx_cth_suite:start(
emqx_gateway_test_utils:load_all_gateway_apps(), [
emqx_config:delete_override_conf_files(), emqx_conf,
emqx_config:erase(gateway), emqx_auth,
emqx_common_test_helpers:load_config(emqx_gateway_schema, ?CONF_DEFAULT), emqx_auth_mnesia,
emqx_mgmt_api_test_util:init_suite([grpc, emqx_conf, emqx_auth, emqx_auth_mnesia, emqx_gateway]), emqx_management,
Conf. {emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"},
{emqx_gateway, ?CONF_DEFAULT}
| emqx_gateway_test_utils:all_gateway_apps()
],
#{work_dir => emqx_cth_suite:work_dir(Conf)}
),
_ = emqx_common_test_http:create_default_app(),
[{suite_apps, Apps} | Conf].
end_per_suite(Conf) -> end_per_suite(Conf) ->
emqx_mgmt_api_test_util:end_suite([emqx_gateway, emqx_auth_mnesia, emqx_auth, emqx_conf, grpc]), _ = emqx_common_test_http:delete_default_app(),
Conf. ok = emqx_cth_suite:stop(proplists:get_value(suite_apps, Conf)).
init_per_testcase(t_gateway_fail, Config) -> init_per_testcase(t_gateway_fail, Config) ->
meck:expect( meck:expect(

View File

@ -8,10 +8,13 @@
-include_lib("hocon/include/hoconsc.hrl"). -include_lib("hocon/include/hoconsc.hrl").
-include_lib("typerefl/include/types.hrl"). -include_lib("typerefl/include/types.hrl").
-export([fields/1, desc/1]). -behaviour(hocon_schema).
-export([namespace/0, fields/1, desc/1]).
-define(NOT_EMPTY(MSG), emqx_resource_validator:not_empty(MSG)). -define(NOT_EMPTY(MSG), emqx_resource_validator:not_empty(MSG)).
namespace() -> gateway.
fields(jt808) -> fields(jt808) ->
[ [
{frame, sc(ref(jt808_frame))}, {frame, sc(ref(jt808_frame))},
@ -51,9 +54,8 @@ fields(jt808_proto) ->
[ [
{auth, {auth,
sc( sc(
hoconsc:union([ hoconsc:union([ref(anonymous_true), ref(anonymous_false)]),
ref(anonymous_true), ref(anonymous_false) #{desc => ?DESC(jt808_auth)}
])
)}, )},
{up_topic, fun up_topic/1}, {up_topic, fun up_topic/1},
{dn_topic, fun dn_topic/1} {dn_topic, fun dn_topic/1}
@ -61,12 +63,18 @@ fields(jt808_proto) ->
fields(anonymous_true) -> fields(anonymous_true) ->
[ [
{allow_anonymous, {allow_anonymous,
sc(hoconsc:union([true]), #{desc => ?DESC(allow_anonymous), required => true})} sc(
hoconsc:union([true]),
#{desc => ?DESC(jt808_allow_anonymous), required => true}
)}
] ++ fields_reg_auth_required(false); ] ++ fields_reg_auth_required(false);
fields(anonymous_false) -> fields(anonymous_false) ->
[ [
{allow_anonymous, {allow_anonymous,
sc(hoconsc:union([false]), #{desc => ?DESC(allow_anonymous), required => true})} sc(
hoconsc:union([false]),
#{desc => ?DESC(jt808_allow_anonymous), required => true}
)}
] ++ fields_reg_auth_required(true). ] ++ fields_reg_auth_required(true).
fields_reg_auth_required(Required) -> fields_reg_auth_required(Required) ->
@ -102,14 +110,14 @@ jt808_frame_max_length(_) ->
undefined. undefined.
up_topic(type) -> binary(); up_topic(type) -> binary();
up_topic(desc) -> ?DESC(?FUNCTION_NAME); up_topic(desc) -> ?DESC(jt808_up_topic);
up_topic(default) -> ?DEFAULT_UP_TOPIC; up_topic(default) -> ?DEFAULT_UP_TOPIC;
up_topic(validator) -> [?NOT_EMPTY("the value of the field 'up_topic' cannot be empty")]; up_topic(validator) -> [?NOT_EMPTY("the value of the field 'up_topic' cannot be empty")];
up_topic(required) -> true; up_topic(required) -> true;
up_topic(_) -> undefined. up_topic(_) -> undefined.
dn_topic(type) -> binary(); dn_topic(type) -> binary();
dn_topic(desc) -> ?DESC(?FUNCTION_NAME); dn_topic(desc) -> ?DESC(jt808_dn_topic);
dn_topic(default) -> ?DEFAULT_DN_TOPIC; dn_topic(default) -> ?DEFAULT_DN_TOPIC;
dn_topic(validator) -> [?NOT_EMPTY("the value of the field 'dn_topic' cannot be empty")]; dn_topic(validator) -> [?NOT_EMPTY("the value of the field 'dn_topic' cannot be empty")];
dn_topic(required) -> true; dn_topic(required) -> true;
@ -121,6 +129,10 @@ desc(jt808_frame) ->
"Limits for the JT/T 808 frames."; "Limits for the JT/T 808 frames.";
desc(jt808_proto) -> desc(jt808_proto) ->
"The JT/T 808 protocol options."; "The JT/T 808 protocol options.";
desc(anonymous_false) ->
?DESC(jt808_allow_anonymous);
desc(anonymous_true) ->
?DESC(jt808_allow_anonymous);
desc(_) -> desc(_) ->
undefined. undefined.

View File

@ -101,10 +101,9 @@ end_per_testcase(_Case, Config) ->
ok. ok.
boot_apps(Case, JT808Conf, Config) -> boot_apps(Case, JT808Conf, Config) ->
application:load(emqx_gateway_jt808),
Apps = emqx_cth_suite:start( Apps = emqx_cth_suite:start(
[ [
cowboy, emqx,
{emqx_conf, JT808Conf}, {emqx_conf, JT808Conf},
emqx_gateway emqx_gateway
], ],

View File

@ -3,19 +3,22 @@ emqx_jt808_schema {
jt808_frame_max_length.desc: jt808_frame_max_length.desc:
"""The maximum length of the JT/T 808 frame.""" """The maximum length of the JT/T 808 frame."""
jt808_auth.desc:
"""Authentication settings of the JT/T 808 Gateway."""
jt808_allow_anonymous.desc: jt808_allow_anonymous.desc:
"""Allow anonymous access to the JT/T 808 Gateway.""" """Allow anonymous access to the JT/T 808 Gateway."""
registry_url.desc registry_url.desc:
"""The JT/T 808 device registry central URL.""" """The JT/T 808 device registry central URL."""
authentication_url.desc authentication_url.desc:
"""The JT/T 808 device authentication central URL.""" """The JT/T 808 device authentication central URL."""
jt808_up_topic.desc jt808_up_topic.desc:
"""The topic of the JT/T 808 protocol upstream message.""" """The topic of the JT/T 808 protocol upstream message."""
jt808_dn_topic.desc jt808_dn_topic.desc:
"""The topic of the JT/T 808 protocol downstream message.""" """The topic of the JT/T 808 protocol downstream message."""
retry_interval.desc: retry_interval.desc: