chore(emqx_machine): refactor injecting runtime deps

Co-authored-by: Thales Macedo Garitezi <thalesmg@gmail.com>
This commit is contained in:
Ilya Averyanov 2024-01-30 16:23:27 +03:00
parent db3f285054
commit 6a092aeb69
2 changed files with 70 additions and 40 deletions

View File

@ -166,9 +166,8 @@ is_app(Name) ->
sorted_reboot_apps() ->
RebootApps = reboot_apps(),
Apps0 = [{App, app_deps(App, RebootApps)} || App <- RebootApps],
Apps1 = inject_bridge_deps(Apps0),
Apps2 = inject_dashboard_deps(Apps1),
sorted_reboot_apps(Apps2).
Apps = emqx_machine_boot_runtime_deps:inject(Apps0, runtime_deps()),
sorted_reboot_apps(Apps).
app_deps(App, RebootApps) ->
case application:get_key(App, applications) of
@ -176,43 +175,21 @@ app_deps(App, RebootApps) ->
{ok, List} -> lists:filter(fun(A) -> lists:member(A, RebootApps) end, List)
end.
%% `emqx_bridge' is special in that it needs all the bridges apps to
%% be started before it, so that, when it loads the bridges from
%% configuration, the bridge app and its dependencies need to be up.
%%
%% `emqx_connector' also needs to start all connector dependencies for the same reason.
%% Since standalone apps like `emqx_mongodb' are already dependencies of `emqx_bridge_*'
%% apps, we may apply the same tactic for `emqx_connector' and inject individual bridges
%% as its dependencies.
inject_bridge_deps(RebootAppDeps) ->
BridgeApps = [
App
|| {App, _Deps} <- RebootAppDeps,
lists:prefix("emqx_bridge_", atom_to_list(App))
],
lists:map(
fun
({emqx_bridge, Deps0}) when is_list(Deps0) ->
{emqx_bridge, Deps0 ++ BridgeApps};
({emqx_connector, Deps0}) when is_list(Deps0) ->
{emqx_connector, Deps0 ++ BridgeApps};
(App) ->
App
end,
RebootAppDeps
).
inject_dashboard_deps(Reboots) ->
Apps = [emqx_license],
Deps = lists:filter(fun(App) -> lists:keymember(App, 1, Reboots) end, Apps),
lists:map(
fun
({emqx_dashboard, Deps0}) when is_list(Deps0) ->
{emqx_dashboard, Deps0 ++ Deps};
(App) ->
App
end,
Reboots
).
runtime_deps() ->
[
%% `emqx_bridge' is special in that it needs all the bridges apps to
%% be started before it, so that, when it loads the bridges from
%% configuration, the bridge app and its dependencies need to be up.
{emqx_bridge, fun(App) -> lists:prefix("emqx_bridge_", atom_to_list(App)) end},
%% `emqx_connector' also needs to start all connector dependencies for the same reason.
%% Since standalone apps like `emqx_mongodb' are already dependencies of `emqx_bridge_*'
%% apps, we may apply the same tactic for `emqx_connector' and inject individual bridges
%% as its dependencies.
{emqx_connector, fun(App) -> lists:prefix("emqx_bridge_", atom_to_list(App)) end},
%% emqx_fdb is an EE app
{emqx_durable_storage, emqx_fdb},
{emqx_dashboard, emqx_license}
].
sorted_reboot_apps(Apps) ->
G = digraph:new(),

View File

@ -0,0 +1,53 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2024 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_machine_boot_runtime_deps).
-export([inject/2]).
-type app_name() :: atom().
-type app_deps() :: {app_name(), [app_name()]}.
-type app_selector() :: app_name() | fun((app_name()) -> boolean()).
-type runtime_dep() :: {_WhatDepends :: app_name(), _OnWhat :: app_selector()}.
-spec inject([app_deps()], [runtime_dep()]) -> [app_deps()].
inject(AppDepList, DepSpecs) ->
AppDep0 = maps:from_list(AppDepList),
AppDep1 = lists:foldl(
fun(DepSpec, AppDepAcc) ->
inject_one_dep(AppDepAcc, DepSpec)
end,
AppDep0,
DepSpecs
),
maps:to_list(AppDep1).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
inject_one_dep(AppDep, {WhatDepends, OnWhatSelector}) ->
OnWhat = select_apps(OnWhatSelector, maps:keys(AppDep)),
case AppDep of
#{WhatDepends := Deps} when is_list(Deps) ->
AppDep#{WhatDepends => lists:usort(Deps ++ OnWhat)};
_ ->
AppDep
end.
select_apps(AppName, AppNames) when is_atom(AppName) ->
lists:filter(fun(App) -> App =:= AppName end, AppNames);
select_apps(AppSelector, AppNames) when is_function(AppSelector, 1) ->
lists:filter(AppSelector, AppNames).