| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- %%--------------------------------------------------------------------
- %% Copyright (c) 2019-2022 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_plugins_SUITE).
- -compile(export_all).
- -compile(nowarn_export_all).
- -include_lib("emqx/include/emqx.hrl").
- -include_lib("eunit/include/eunit.hrl").
- -define(EMQX_PLUGIN_TEMPLATE_VSN, "5.0-rc.1").
- -define(PACKAGE_SUFFIX, ".tar.gz").
- all() -> emqx_common_test_helpers:all(?MODULE).
- init_per_suite(Config) ->
- WorkDir = proplists:get_value(data_dir, Config),
- OrigInstallDir = emqx_plugins:get_config(install_dir, undefined),
- emqx_plugins:put_config(install_dir, WorkDir),
- emqx_common_test_helpers:start_apps([]),
- [{orig_install_dir, OrigInstallDir} | Config].
- end_per_suite(Config) ->
- emqx_common_test_helpers:boot_modules(all),
- emqx_common_test_helpers:stop_apps([]),
- emqx_config:erase(plugins),
- %% restore config
- case proplists:get_value(orig_install_dir, Config) of
- undefined -> ok;
- OrigInstallDir -> emqx_plugins:put_config(install_dir, OrigInstallDir)
- end.
- init_per_testcase(TestCase, Config) ->
- emqx_plugins:put_configured([]),
- lists:foreach(fun(#{<<"name">> := Name, <<"rel_vsn">> := Vsn}) ->
- emqx_plugins:purge(bin([Name, "-", Vsn]))
- end, emqx_plugins:list()),
- ?MODULE:TestCase({init, Config}).
- end_per_testcase(TestCase, Config) ->
- emqx_plugins:put_configured([]),
- ?MODULE:TestCase({'end', Config}).
- build_demo_plugin_package() ->
- WorkDir = emqx_plugins:install_dir(),
- BuildSh = filename:join([WorkDir, "build-demo-plugin.sh"]),
- case emqx_run_sh:do(BuildSh ++ " " ++ ?EMQX_PLUGIN_TEMPLATE_VSN,
- [{cd, WorkDir}]) of
- {ok, _} ->
- Pkg = filename:join([WorkDir, "emqx_plugin_template-" ++
- ?EMQX_PLUGIN_TEMPLATE_VSN ++
- ?PACKAGE_SUFFIX]),
- case filelib:is_regular(Pkg) of
- true -> Pkg;
- false -> error(#{reason => unexpected_build_result, not_found => Pkg})
- end;
- {error, {Rc, Output}} ->
- io:format(user, "failed_to_build_demo_plugin, Exit = ~p, Output:~n~ts\n", [Rc, Output]),
- error(failed_to_build_demo_plugin)
- end.
- bin(A) when is_atom(A) -> atom_to_binary(A, utf8);
- bin(L) when is_list(L) -> unicode:characters_to_binary(L, utf8);
- bin(B) when is_binary(B) -> B.
- t_demo_install_start_stop_uninstall({init, Config}) ->
- Package = build_demo_plugin_package(),
- NameVsn = filename:basename(Package, ?PACKAGE_SUFFIX),
- [{name_vsn, NameVsn} | Config];
- t_demo_install_start_stop_uninstall({'end', _Config}) -> ok;
- t_demo_install_start_stop_uninstall(Config) ->
- NameVsn = proplists:get_value(name_vsn, Config),
- ok = emqx_plugins:ensure_installed(NameVsn),
- %% idempotent
- ok = emqx_plugins:ensure_installed(NameVsn),
- {ok, Info} = emqx_plugins:read_plugin(NameVsn),
- ?assertEqual([Info], emqx_plugins:list()),
- %% start
- ok = emqx_plugins:ensure_started(NameVsn),
- ok = assert_app_running(emqx_plugin_template, true),
- ok = assert_app_running(map_sets, true),
- %% start (idempotent)
- ok = emqx_plugins:ensure_started(bin(NameVsn)),
- ok = assert_app_running(emqx_plugin_template, true),
- ok = assert_app_running(map_sets, true),
- %% running app can not be un-installed
- ?assertMatch({error, _},
- emqx_plugins:ensure_uninstalled(NameVsn)),
- %% stop
- ok = emqx_plugins:ensure_stopped(NameVsn),
- ok = assert_app_running(emqx_plugin_template, false),
- ok = assert_app_running(map_sets, false),
- %% stop (idempotent)
- ok = emqx_plugins:ensure_stopped(bin(NameVsn)),
- ok = assert_app_running(emqx_plugin_template, false),
- ok = assert_app_running(map_sets, false),
- %% still listed after stopped
- ?assertMatch([#{<<"name">> := <<"emqx_plugin_template">>,
- <<"rel_vsn">> := <<?EMQX_PLUGIN_TEMPLATE_VSN>>
- }], emqx_plugins:list()),
- ok = emqx_plugins:ensure_uninstalled(NameVsn),
- ?assertEqual([], emqx_plugins:list()),
- ok.
- %% help funtion to create a info file.
- %% The file is in JSON format when built
- %% but since we are using hocon:load to load it
- %% ad-hoc test files can be in hocon format
- write_info_file(Config, NameVsn, Content) ->
- WorkDir = proplists:get_value(data_dir, Config),
- InfoFile = filename:join([WorkDir, NameVsn, "release.json"]),
- ok = filelib:ensure_dir(InfoFile),
- ok = file:write_file(InfoFile, Content).
- t_start_restart_and_stop({init, Config}) ->
- Package = build_demo_plugin_package(),
- NameVsn = filename:basename(Package, ?PACKAGE_SUFFIX),
- [{name_vsn, NameVsn} | Config];
- t_start_restart_and_stop({'end', _Config}) -> ok;
- t_start_restart_and_stop(Config) ->
- NameVsn = proplists:get_value(name_vsn, Config),
- ok = emqx_plugins:ensure_installed(NameVsn),
- ok = emqx_plugins:ensure_enabled(NameVsn),
- FakeInfo = "name=bar, rel_vsn=\"2\", rel_apps=[\"bar-9\"],"
- "description=\"desc bar\"",
- Bar2 = <<"bar-2">>,
- ok = write_info_file(Config, Bar2, FakeInfo),
- %% fake a disabled plugin in config
- ok = emqx_plugins:ensure_state(Bar2, front, false),
- assert_app_running(emqx_plugin_template, false),
- ok = emqx_plugins:ensure_started(),
- assert_app_running(emqx_plugin_template, true),
- %% fake enable bar-2
- ok = emqx_plugins:ensure_state(Bar2, rear, true),
- %% should cause an error
- ?assertError(#{function := _, errors := [_ | _]},
- emqx_plugins:ensure_started()),
- %% but demo plugin should still be running
- assert_app_running(emqx_plugin_template, true),
- %% stop all
- ok = emqx_plugins:ensure_stopped(),
- assert_app_running(emqx_plugin_template, false),
- ok = emqx_plugins:ensure_state(Bar2, rear, false),
- ok = emqx_plugins:restart(NameVsn),
- assert_app_running(emqx_plugin_template, true),
- %% repeat
- ok = emqx_plugins:restart(NameVsn),
- assert_app_running(emqx_plugin_template, true),
- ok = emqx_plugins:ensure_stopped(),
- ok = emqx_plugins:ensure_disabled(NameVsn),
- ok = emqx_plugins:ensure_uninstalled(NameVsn),
- ok = emqx_plugins:ensure_uninstalled(Bar2),
- ?assertEqual([], emqx_plugins:list()),
- ok.
- t_enable_disable({init, Config}) ->
- Package = build_demo_plugin_package(),
- NameVsn = filename:basename(Package, ?PACKAGE_SUFFIX),
- [{name_vsn, NameVsn} | Config];
- t_enable_disable({'end', Config}) ->
- ok = emqx_plugins:ensure_uninstalled(proplists:get_value(name_vsn, Config));
- t_enable_disable(Config) ->
- NameVsn = proplists:get_value(name_vsn, Config),
- ok = emqx_plugins:ensure_installed(NameVsn),
- ?assertEqual([], emqx_plugins:configured()),
- ok = emqx_plugins:ensure_enabled(NameVsn),
- ?assertEqual([#{name_vsn => NameVsn, enable => true}], emqx_plugins:configured()),
- ok = emqx_plugins:ensure_disabled(NameVsn),
- ?assertEqual([#{name_vsn => NameVsn, enable => false}], emqx_plugins:configured()),
- ok = emqx_plugins:ensure_enabled(bin(NameVsn)),
- ?assertEqual([#{name_vsn => NameVsn, enable => true}], emqx_plugins:configured()),
- ?assertMatch({error, #{reason := "bad_plugin_config_status",
- hint := "disable_the_plugin_first"
- }}, emqx_plugins:ensure_uninstalled(NameVsn)),
- ok = emqx_plugins:ensure_disabled(bin(NameVsn)),
- ok = emqx_plugins:ensure_uninstalled(NameVsn),
- ?assertMatch({error, _}, emqx_plugins:ensure_enabled(NameVsn)),
- ?assertMatch({error, _}, emqx_plugins:ensure_disabled(NameVsn)),
- ok.
- assert_app_running(Name, true) ->
- AllApps = application:which_applications(),
- ?assertMatch({Name, _, _}, lists:keyfind(Name, 1, AllApps));
- assert_app_running(Name, false) ->
- AllApps = application:which_applications(),
- ?assertEqual(false, lists:keyfind(Name, 1, AllApps)).
- t_bad_tar_gz({init, Config}) -> Config;
- t_bad_tar_gz({'end', _Config}) -> ok;
- t_bad_tar_gz(Config) ->
- WorkDir = proplists:get_value(data_dir, Config),
- FakeTarTz = filename:join([WorkDir, "fake-vsn.tar.gz"]),
- ok = file:write_file(FakeTarTz, "a\n"),
- ?assertMatch({error, #{reason := "bad_plugin_package",
- return := eof
- }},
- emqx_plugins:ensure_installed("fake-vsn")),
- ?assertMatch({error, #{reason := "failed_to_extract_plugin_package",
- return := not_found
- }},
- emqx_plugins:ensure_installed("nonexisting")),
- ?assertEqual([], emqx_plugins:list()),
- ok = emqx_plugins:delete_package("fake-vsn"),
- %% idempotent
- ok = emqx_plugins:delete_package("fake-vsn").
- %% create a corrupted .tar.gz
- %% failed install attempts should not leave behind extracted dir
- t_bad_tar_gz2({init, Config}) -> Config;
- t_bad_tar_gz2({'end', _Config}) -> ok;
- t_bad_tar_gz2(Config) ->
- WorkDir = proplists:get_value(data_dir, Config),
- NameVsn = "foo-0.2",
- %% this an invalid info file content
- BadInfo = "name=foo, rel_vsn=\"0.2\", rel_apps=[foo]",
- ok = write_info_file(Config, NameVsn, BadInfo),
- TarGz = filename:join([WorkDir, NameVsn ++ ".tar.gz"]),
- ok = make_tar(WorkDir, NameVsn),
- ?assert(filelib:is_regular(TarGz)),
- %% failed to install, it also cleans up the bad .tar.gz file
- ?assertMatch({error, _}, emqx_plugins:ensure_installed(NameVsn)),
- %% the tar.gz file is still around
- ?assert(filelib:is_regular(TarGz)),
- ?assertEqual({error, enoent}, file:read_file_info(emqx_plugins:dir(NameVsn))),
- ok = emqx_plugins:delete_package(NameVsn).
- t_bad_info_json({init, Config}) -> Config;
- t_bad_info_json({'end', _}) -> ok;
- t_bad_info_json(Config) ->
- NameVsn = "test-2",
- ok = write_info_file(Config, NameVsn, "bad-syntax"),
- ?assertMatch({error, #{error := "bad_info_file",
- return := {parse_error, _}
- }},
- emqx_plugins:read_plugin(NameVsn)),
- ok = write_info_file(Config, NameVsn, "{\"bad\": \"obj\"}"),
- ?assertMatch({error, #{error := "bad_info_file_content",
- mandatory_fields := _
- }},
- emqx_plugins:read_plugin(NameVsn)),
- ?assertEqual([], emqx_plugins:list()),
- emqx_plugins:purge(NameVsn),
- ok.
- make_tar(Cwd, NameWithVsn) ->
- {ok, OriginalCwd} = file:get_cwd(),
- ok = file:set_cwd(Cwd),
- try
- Files = filelib:wildcard(NameWithVsn ++ "/**"),
- TarFile = NameWithVsn ++ ".tar.gz",
- ok = erl_tar:create(TarFile, Files, [compressed])
- after
- file:set_cwd(OriginalCwd)
- end.
|