diff --git a/apps/emqx_bridge_mysql/test/emqx_bridge_mysql_SUITE.erl b/apps/emqx_bridge_mysql/test/emqx_bridge_mysql_SUITE.erl index 96fcf6d24..61847a6b0 100644 --- a/apps/emqx_bridge_mysql/test/emqx_bridge_mysql_SUITE.erl +++ b/apps/emqx_bridge_mysql/test/emqx_bridge_mysql_SUITE.erl @@ -112,8 +112,6 @@ end_per_group(_Group, _Config) -> ok. init_per_suite(Config) -> - emqx_common_test_helpers:clear_screen(), - Config. end_per_suite(_Config) -> diff --git a/apps/emqx_mysql/src/emqx_mysql.app.src b/apps/emqx_mysql/src/emqx_mysql.app.src index e9f7f6f98..9ae3234cc 100644 --- a/apps/emqx_mysql/src/emqx_mysql.app.src +++ b/apps/emqx_mysql/src/emqx_mysql.app.src @@ -1,6 +1,6 @@ {application, emqx_mysql, [ {description, "EMQX MySQL Database Connector"}, - {vsn, "0.1.5"}, + {vsn, "0.1.6"}, {registered, []}, {applications, [ kernel, diff --git a/apps/emqx_mysql/src/emqx_mysql.erl b/apps/emqx_mysql/src/emqx_mysql.erl index a9b132570..d33f313bc 100644 --- a/apps/emqx_mysql/src/emqx_mysql.erl +++ b/apps/emqx_mysql/src/emqx_mysql.erl @@ -289,10 +289,17 @@ do_check_prepares(_NoTemplates) -> connect(Options) -> %% TODO: teach `tdengine` to accept 0-arity closures as passwords. - {value, {password, Secret}, Rest} = lists:keytake(password, 1, Options), - NOptions = [{password, emqx_secret:unwrap(Secret)} | Rest], + NOptions = init_connect_opts(Options), mysql:start_link(NOptions). +init_connect_opts(Options) -> + case lists:keytake(password, 1, Options) of + {value, {password, Secret}, Rest} -> + [{password, emqx_secret:unwrap(Secret)} | Rest]; + false -> + Options + end. + init_prepare(State = #{query_templates := Templates}) -> case maps:size(Templates) of 0 -> diff --git a/apps/emqx_mysql/test/emqx_mysql_SUITE.erl b/apps/emqx_mysql/test/emqx_mysql_SUITE.erl index b8886f1cd..25e9cce5a 100644 --- a/apps/emqx_mysql/test/emqx_mysql_SUITE.erl +++ b/apps/emqx_mysql/test/emqx_mysql_SUITE.erl @@ -1,17 +1,17 @@ -% %%-------------------------------------------------------------------- -% %% Copyright (c) 2020-2023 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. -% %%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- +%% Copyright (c) 2020-2023 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_mysql_SUITE). @@ -19,40 +19,30 @@ -compile(export_all). -include_lib("emqx_connector/include/emqx_connector.hrl"). --include_lib("eunit/include/eunit.hrl"). --include_lib("emqx/include/emqx.hrl"). -include_lib("stdlib/include/assert.hrl"). -define(MYSQL_HOST, "mysql"). +-define(MYSQL_USER, "root"). +-define(MYSQL_PASSWORD, "public"). -define(MYSQL_RESOURCE_MOD, emqx_mysql). all() -> emqx_common_test_helpers:all(?MODULE). -groups() -> - []. - init_per_suite(Config) -> case emqx_common_test_helpers:is_tcp_server_available(?MYSQL_HOST, ?MYSQL_DEFAULT_PORT) of true -> - ok = emqx_common_test_helpers:start_apps([emqx_conf]), - ok = emqx_connector_test_helpers:start_apps([emqx_resource]), - {ok, _} = application:ensure_all_started(emqx_connector), - Config; + Apps = emqx_cth_suite:start( + [emqx_conf, emqx_connector], + #{work_dir => emqx_cth_suite:work_dir(Config)} + ), + [{apps, Apps} | Config]; false -> {skip, no_mysql} end. -end_per_suite(_Config) -> - ok = emqx_common_test_helpers:stop_apps([emqx_conf]), - ok = emqx_connector_test_helpers:stop_apps([emqx_resource]), - _ = application:stop(emqx_connector). - -init_per_testcase(_, Config) -> - Config. - -end_per_testcase(_, _Config) -> - ok. +end_per_suite(Config) -> + ok = emqx_cth_suite:stop(proplists:get_value(apps, Config)). % %%------------------------------------------------------------------------------ % %% Testcases @@ -64,6 +54,12 @@ t_lifecycle(_Config) -> mysql_config() ). +t_lifecycle_passwordless(_Config) -> + perform_lifecycle_check( + <<"emqx_mysql_SUITE:passwordless">>, + mysql_config(passwordless) + ). + perform_lifecycle_check(ResourceId, InitialConfig) -> {ok, #{config := CheckedConfig}} = emqx_resource:check_config(?MYSQL_RESOURCE_MOD, InitialConfig), @@ -136,25 +132,55 @@ perform_lifecycle_check(ResourceId, InitialConfig) -> % %%------------------------------------------------------------------------------ mysql_config() -> - RawConfig = list_to_binary( - io_lib:format( - "" - "\n" - " auto_reconnect = true\n" - " database = mqtt\n" - " username= root\n" - " password = public\n" - " pool_size = 8\n" - " server = \"~s:~b\"\n" - " " - "", - [?MYSQL_HOST, ?MYSQL_DEFAULT_PORT] - ) - ), + mysql_config(default). - {ok, Config} = hocon:binary(RawConfig), +mysql_config(default) -> + parse_mysql_config( + "\n auto_reconnect = true" + "\n database = mqtt" + "\n username = ~p" + "\n password = ~p" + "\n pool_size = 8" + "\n server = \"~s:~b\"" + "\n", + [?MYSQL_USER, ?MYSQL_PASSWORD, ?MYSQL_HOST, ?MYSQL_DEFAULT_PORT] + ); +mysql_config(passwordless) -> + ok = run_admin_query("CREATE USER IF NOT EXISTS 'nopwd'@'%'"), + ok = run_admin_query("GRANT ALL ON mqtt.* TO 'nopwd'@'%'"), + parse_mysql_config( + "\n auto_reconnect = true" + "\n database = mqtt" + "\n username = nopwd" + "\n pool_size = 8" + "\n server = \"~s:~b\"" + "\n", + [?MYSQL_HOST, ?MYSQL_DEFAULT_PORT] + ). + +parse_mysql_config(FormatString, Args) -> + {ok, Config} = hocon:binary(io_lib:format(FormatString, Args)), #{<<"config">> => Config}. +run_admin_query(Query) -> + Pid = connect_mysql(), + try + mysql:query(Pid, Query) + after + mysql:stop(Pid) + end. + +connect_mysql() -> + Opts = [ + {host, ?MYSQL_HOST}, + {port, ?MYSQL_DEFAULT_PORT}, + {user, ?MYSQL_USER}, + {password, ?MYSQL_PASSWORD}, + {database, "mysql"} + ], + {ok, Pid} = mysql:start_link(Opts), + Pid. + test_query_no_params() -> {sql, <<"SELECT 1">>}. diff --git a/changes/ee/fix-12256.en.md b/changes/ee/fix-12256.en.md new file mode 100644 index 000000000..5db28dd68 --- /dev/null +++ b/changes/ee/fix-12256.en.md @@ -0,0 +1 @@ +Fix an issue where connections to passwordless MySQL resources could not be established.