Преглед изворни кода

chore(emqx_machine): refactor injecting runtime deps

Co-authored-by: Thales Macedo Garitezi <thalesmg@gmail.com>
Ilya Averyanov пре 2 година
родитељ
комит
6a092aeb69

+ 17 - 40
apps/emqx_machine/src/emqx_machine_boot.erl

@@ -166,9 +166,8 @@ is_app(Name) ->
 sorted_reboot_apps() ->
 sorted_reboot_apps() ->
     RebootApps = reboot_apps(),
     RebootApps = reboot_apps(),
     Apps0 = [{App, app_deps(App, RebootApps)} || App <- RebootApps],
     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) ->
 app_deps(App, RebootApps) ->
     case application:get_key(App, applications) of
     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)
         {ok, List} -> lists:filter(fun(A) -> lists:member(A, RebootApps) end, List)
     end.
     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) ->
 sorted_reboot_apps(Apps) ->
     G = digraph:new(),
     G = digraph:new(),

+ 53 - 0
apps/emqx_machine/src/emqx_machine_boot_runtime_deps.erl

@@ -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).