Merge pull request #10422 from thalesmg/fix-plugin-sync-single-node-v50

fix(plugins): attempt to extract plugin from current node on startup
This commit is contained in:
Thales Macedo Garitezi 2023-04-18 14:39:02 -03:00 committed by GitHub
commit 6a1ef5e68a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 106 additions and 12 deletions

View File

@ -764,6 +764,7 @@ setup_node(Node, Opts) when is_map(Opts) ->
load_apps => LoadApps, load_apps => LoadApps,
apps => Apps, apps => Apps,
env => Env, env => Env,
join_to => JoinTo,
start_apps => StartApps start_apps => StartApps
} }
] ]

View File

@ -1,7 +1,7 @@
%% -*- mode: erlang -*- %% -*- mode: erlang -*-
{application, emqx_plugins, [ {application, emqx_plugins, [
{description, "EMQX Plugin Management"}, {description, "EMQX Plugin Management"},
{vsn, "0.1.3"}, {vsn, "0.1.4"},
{modules, []}, {modules, []},
{mod, {emqx_plugins_app, []}}, {mod, {emqx_plugins_app, []}},
{applications, [kernel, stdlib, emqx]}, {applications, [kernel, stdlib, emqx]},

View File

@ -479,22 +479,39 @@ ensure_exists_and_installed(NameVsn) ->
case filelib:is_dir(dir(NameVsn)) of case filelib:is_dir(dir(NameVsn)) of
true -> true ->
ok; ok;
_ -> false ->
Nodes = [N || N <- mria:running_nodes(), N /= node()], %% Do we have the package, but it's not extracted yet?
case get_from_any_node(Nodes, NameVsn, []) of case get_tar(NameVsn) of
{ok, TarContent} -> {ok, TarContent} ->
ok = file:write_file(pkg_file(NameVsn), TarContent), ok = file:write_file(pkg_file(NameVsn), TarContent),
ok = do_ensure_installed(NameVsn); ok = do_ensure_installed(NameVsn);
{error, NodeErrors} -> _ ->
?SLOG(error, #{ %% If not, try to get it from the cluster.
msg => "failed_to_copy_plugin_from_other_nodes", do_get_from_cluster(NameVsn)
name_vsn => NameVsn,
node_errors => NodeErrors
}),
{error, plugin_not_found}
end end
end. end.
do_get_from_cluster(NameVsn) ->
Nodes = [N || N <- mria:running_nodes(), N /= node()],
case get_from_any_node(Nodes, NameVsn, []) of
{ok, TarContent} ->
ok = file:write_file(pkg_file(NameVsn), TarContent),
ok = do_ensure_installed(NameVsn);
{error, NodeErrors} when Nodes =/= [] ->
?SLOG(error, #{
msg => "failed_to_copy_plugin_from_other_nodes",
name_vsn => NameVsn,
node_errors => NodeErrors
}),
{error, plugin_not_found};
{error, _} ->
?SLOG(error, #{
msg => "no_nodes_to_copy_plugin_from",
name_vsn => NameVsn
}),
{error, plugin_not_found}
end.
get_from_any_node([], _NameVsn, Errors) -> get_from_any_node([], _NameVsn, Errors) ->
{error, Errors}; {error, Errors};
get_from_any_node([Node | T], NameVsn, Errors) -> get_from_any_node([Node | T], NameVsn, Errors) ->

View File

@ -45,7 +45,10 @@ all() ->
groups() -> groups() ->
[ [
{copy_plugin, [sequence], [group_t_copy_plugin_to_a_new_node]}, {copy_plugin, [sequence], [
group_t_copy_plugin_to_a_new_node,
group_t_copy_plugin_to_a_new_node_single_node
]},
{create_tar_copy_plugin, [sequence], [group_t_copy_plugin_to_a_new_node]} {create_tar_copy_plugin, [sequence], [group_t_copy_plugin_to_a_new_node]}
]. ].
@ -601,6 +604,78 @@ group_t_copy_plugin_to_a_new_node(Config) ->
rpc:call(CopyToNode, emqx_plugins, describe, [NameVsn]) rpc:call(CopyToNode, emqx_plugins, describe, [NameVsn])
). ).
%% checks that we can start a cluster with a lone node.
group_t_copy_plugin_to_a_new_node_single_node({init, Config}) ->
PrivDataDir = ?config(priv_dir, Config),
ToInstallDir = filename:join(PrivDataDir, "plugins_copy_to"),
file:del_dir_r(ToInstallDir),
ok = filelib:ensure_path(ToInstallDir),
#{package := Package, release_name := PluginName} = get_demo_plugin_package(ToInstallDir),
NameVsn = filename:basename(Package, ?PACKAGE_SUFFIX),
[{CopyTo, CopyToOpts}] =
emqx_common_test_helpers:emqx_cluster(
[
{core, plugins_copy_to}
],
#{
apps => [emqx_conf, emqx_plugins],
env => [
{emqx, init_config_load_done, false},
{emqx, boot_modules, []}
],
env_handler => fun
(emqx_plugins) ->
ok = emqx_plugins:put_config(install_dir, ToInstallDir),
%% this is to simulate an user setting the state
%% via environment variables before starting the node
ok = emqx_plugins:put_config(
states,
[#{name_vsn => NameVsn, enable => true}]
),
ok;
(_) ->
ok
end,
priv_data_dir => PrivDataDir,
schema_mod => emqx_conf_schema,
peer_mod => slave,
load_schema => true
}
),
[
{to_install_dir, ToInstallDir},
{copy_to_node_name, CopyTo},
{copy_to_opts, CopyToOpts},
{name_vsn, NameVsn},
{plugin_name, PluginName}
| Config
];
group_t_copy_plugin_to_a_new_node_single_node({'end', Config}) ->
CopyToNode = proplists:get_value(copy_to_node, Config),
ok = emqx_common_test_helpers:stop_slave(CopyToNode),
ok = file:del_dir_r(proplists:get_value(to_install_dir, Config)),
ok;
group_t_copy_plugin_to_a_new_node_single_node(Config) ->
CopyTo = ?config(copy_to_node_name, Config),
CopyToOpts = ?config(copy_to_opts, Config),
ToInstallDir = ?config(to_install_dir, Config),
NameVsn = proplists:get_value(name_vsn, Config),
%% Start the node for the first time. The plugin should start
%% successfully even if it's not extracted yet. Simply starting
%% the node would crash if not working properly.
CopyToNode = emqx_common_test_helpers:start_slave(CopyTo, CopyToOpts),
ct:pal("~p config:\n ~p", [
CopyToNode, erpc:call(CopyToNode, emqx_plugins, get_config, [[], #{}])
]),
ct:pal("~p install_dir:\n ~p", [
CopyToNode, erpc:call(CopyToNode, file, list_dir, [ToInstallDir])
]),
?assertMatch(
{ok, #{running_status := running, config_status := enabled}},
rpc:call(CopyToNode, emqx_plugins, describe, [NameVsn])
),
ok.
make_tar(Cwd, NameWithVsn) -> make_tar(Cwd, NameWithVsn) ->
make_tar(Cwd, NameWithVsn, NameWithVsn). make_tar(Cwd, NameWithVsn, NameWithVsn).

View File

@ -0,0 +1 @@
Fixed a bug where external plugins could not be configured via environment variables in a lone-node cluster.