From 3c34cb3b6a58de1c791b41a481065fd81f1b01b1 Mon Sep 17 00:00:00 2001 From: DDDHuang <44492639+DDDHuang@users.noreply.github.com> Date: Thu, 7 Apr 2022 18:04:15 +0800 Subject: [PATCH] fix: start auth & acl mongo with availability check --- .../src/emqx_auth_mongo.app.src | 2 +- .../src/emqx_auth_mongo.appup.src | 20 +++++-- apps/emqx_auth_mongo/src/emqx_auth_mongo.erl | 54 ++++++++++++++++++ .../src/emqx_auth_mongo_app.erl | 57 ++++++++++++++++--- 4 files changed, 117 insertions(+), 16 deletions(-) diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src b/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src index 9eb14fd7d..3090656f7 100644 --- a/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src +++ b/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src @@ -1,6 +1,6 @@ {application, emqx_auth_mongo, [{description, "EMQ X Authentication/ACL with MongoDB"}, - {vsn, "4.3.2"}, % strict semver, bump manually! + {vsn, "4.3.3"}, % strict semver, bump manually! {modules, []}, {registered, [emqx_auth_mongo_sup]}, {applications, [kernel,stdlib,mongodb,ecpool]}, diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo.appup.src b/apps/emqx_auth_mongo/src/emqx_auth_mongo.appup.src index 449125709..a12cb37b1 100644 --- a/apps/emqx_auth_mongo/src/emqx_auth_mongo.appup.src +++ b/apps/emqx_auth_mongo/src/emqx_auth_mongo.appup.src @@ -1,17 +1,25 @@ %% -*- mode: erlang -*- +%% Unless you know what you are doing, DO NOT edit manually!! {VSN, - [{"4.3.1", - [{load_module,emqx_auth_mongo,brutal_purge,soft_purge,[]}]}, + [{"4.3.2", + [{load_module,emqx_auth_mongo_app,brutal_purge,soft_purge,[]}, + {load_module,emqx_auth_mongo,brutal_purge,soft_purge,[]}]}, + {"4.3.1", + [{load_module,emqx_auth_mongo_app,brutal_purge,soft_purge,[]}, + {load_module,emqx_auth_mongo,brutal_purge,soft_purge,[]}]}, {"4.3.0", [{load_module,emqx_auth_mongo_app,brutal_purge,soft_purge,[]}, {load_module,emqx_auth_mongo,brutal_purge,soft_purge,[]}, {load_module,emqx_acl_mongo,brutal_purge,soft_purge,[]}]}, {<<".*">>,[]}], - [{"4.3.1", - [{load_module,emqx_auth_mongo,brutal_purge,soft_purge,[]}]}, + [{"4.3.2", + [{load_module,emqx_auth_mongo_app,brutal_purge,soft_purge,[]}, + {load_module,emqx_auth_mongo,brutal_purge,soft_purge,[]}]}, + {"4.3.1", + [{load_module,emqx_auth_mongo_app,brutal_purge,soft_purge,[]}, + {load_module,emqx_auth_mongo,brutal_purge,soft_purge,[]}]}, {"4.3.0", [{load_module,emqx_auth_mongo_app,brutal_purge,soft_purge,[]}, {load_module,emqx_auth_mongo,brutal_purge,soft_purge,[]}, {load_module,emqx_acl_mongo,brutal_purge,soft_purge,[]}]}, - {<<".*">>,[]}] -}. + {<<".*">>,[]}]}. diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo.erl b/apps/emqx_auth_mongo/src/emqx_auth_mongo.erl index 307aa3f7f..df70b6dbe 100644 --- a/apps/emqx_auth_mongo/src/emqx_auth_mongo.erl +++ b/apps/emqx_auth_mongo/src/emqx_auth_mongo.erl @@ -35,6 +35,10 @@ , query_multi/3 ]). +-export([ available/2 + , available/3 + ]). + -spec(register_metrics() -> ok). register_metrics() -> lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). @@ -97,6 +101,56 @@ is_superuser(Pool, #superquery{collection = Coll, field = Field, selector = Sele end end. +%%-------------------------------------------------------------------- +%% Availability Test +%%-------------------------------------------------------------------- + +available(Pool, #superquery{collection = Collection, selector = Selector}) -> + available(Pool, Collection, maps:from_list(replvars(Selector, test_client_info()))); +available(Pool, #authquery{collection = Collection, selector = Selector}) -> + available(Pool, Collection, maps:from_list(replvars(Selector, test_client_info()))); +available(Pool, #aclquery{collection = Collection, selector = Selectors}) -> + Fun = + fun(Selector) -> + maps:from_list(emqx_auth_mongo:replvars(Selector, test_client_info())) + end, + available(Pool, Collection, lists:map(Fun, Selectors), fun query_multi/3). + +available(Pool, Collection, Query) -> + available(Pool, Collection, Query, fun query/3). + +available(Pool, Collection, Query, Fun) -> + try Fun(Pool, Collection, Query) of + {error, Reason} -> + ?LOG(error, "[MongoDB] ~p availability test error: ~0p", [Collection, Reason]), + {error, Reason}; + Error = #{<<"code">> := Code} -> + CodeName = maps:get(<<"codeName">>, Error, undefined), + ErrorMessage = maps:get(<<"errmsg">>, Error, undefined), + ?LOG(error, "[MongoDB] ~p availability test error, code: ~p Name: ~0p Message: ~0p", + [Collection, Code, CodeName, ErrorMessage]), + {error, {mongo_error, Code}}; + _Return -> + %% Any success result is fine. + ok + catch E:R:S -> + ?LOG(error, "[MongoDB] ~p availability test error, ~p: ~0p: ~0p", [Collection, E, R, S]), + {error, R} + end. + +%% Test client info +test_client_info() -> + #{ + clientid => <<"EMQX_availability_test_client">>, + username => <<"EMQX_availability_test_username">>, + cn => <<"EMQX_availability_test_cn">>, + dn => <<"EMQX_availability_test_dn">> + }. + +%%-------------------------------------------------------------------- +%% Internal func +%%-------------------------------------------------------------------- + replvars(VarList, ClientInfo) -> lists:map(fun(Var) -> replvar(Var, ClientInfo) end, VarList). diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo_app.erl b/apps/emqx_auth_mongo/src/emqx_auth_mongo_app.erl index 3e05e3411..8c3d1f968 100644 --- a/apps/emqx_auth_mongo/src/emqx_auth_mongo_app.erl +++ b/apps/emqx_auth_mongo/src/emqx_auth_mongo_app.erl @@ -36,26 +36,65 @@ start(_StartType, _StartArgs) -> {ok, Sup} = emqx_auth_mongo_sup:start_link(), - with_env(auth_query, fun reg_authmod/1), - with_env(acl_query, fun reg_aclmod/1), + ok = safe_start(), {ok, Sup}. prep_stop(State) -> - ok = emqx:unhook('client.authenticate', fun emqx_auth_mongo:check/3), - ok = emqx:unhook('client.check_acl', fun emqx_acl_mongo:check_acl/5), + ok = unload_hook(), + _ = stop_pool(), State. stop(_State) -> ok. +unload_hook() -> + ok = emqx:unhook('client.authenticate', fun emqx_auth_mongo:check/3), + ok = emqx:unhook('client.check_acl', fun emqx_acl_mongo:check_acl/5). + +stop_pool() -> + ecpool:stop_sup_pool(?APP). + +safe_start() -> + try + ok = with_env(auth_query, fun reg_authmod/1), + ok = with_env(acl_query, fun reg_aclmod/1), + ok + catch _E:R:_S -> + unload_hook(), + _ = stop_pool(), + {error, R} + end. + reg_authmod(AuthQuery) -> - emqx_auth_mongo:register_metrics(), - SuperQuery = r(super_query, application:get_env(?APP, super_query, undefined)), - ok = emqx:hook('client.authenticate', fun emqx_auth_mongo:check/3, - [#{authquery => AuthQuery, superquery => SuperQuery, pool => ?APP}]). + case emqx_auth_mongo:available(?APP, AuthQuery) of + ok -> + emqx_auth_mongo:register_metrics(), + HookFun = fun emqx_auth_mongo:check/3, + HookOptions = #{authquery => AuthQuery, superquery => undefined, pool => ?APP}, + case r(super_query, application:get_env(?APP, super_query, undefined)) of + undefined -> + ok = emqx:hook('client.authenticate', HookFun, [HookOptions]); + SuperQuery -> + case emqx_auth_mongo:available(?APP, SuperQuery) of + ok -> + ok = emqx:hook('client.authenticate', HookFun, + [HookOptions#{superquery => SuperQuery}]); + {error, Reason} -> + {error, Reason} + end + end; + {error, Reason} -> + {error, Reason} + end. reg_aclmod(AclQuery) -> - ok = emqx:hook('client.check_acl', fun emqx_acl_mongo:check_acl/5, [#{aclquery => AclQuery, pool => ?APP}]). + case emqx_auth_mongo:available(?APP, AclQuery) of + ok -> + ok = emqx:hook('client.check_acl', fun emqx_acl_mongo:check_acl/5, + [#{aclquery => AclQuery, pool => ?APP}]); + {error, Reason} -> + {error, Reason} + end. %%-------------------------------------------------------------------- %% Internal functions