238 lines
9.1 KiB
Erlang
238 lines
9.1 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% Copyright (c) 2023-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_conf_cli_SUITE).
|
|
|
|
-compile(nowarn_export_all).
|
|
-compile(export_all).
|
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
|
-include("emqx_conf.hrl").
|
|
-import(emqx_config_SUITE, [prepare_conf_file/3]).
|
|
|
|
all() ->
|
|
emqx_common_test_helpers:all(?MODULE).
|
|
|
|
init_per_suite(Config) ->
|
|
emqx_mgmt_api_test_util:init_suite([emqx_conf, emqx_auth, emqx_auth_redis]),
|
|
Config.
|
|
|
|
end_per_suite(_Config) ->
|
|
emqx_mgmt_api_test_util:end_suite([emqx_conf, emqx_auth, emqx_auth_redis]).
|
|
|
|
t_load_config(Config) ->
|
|
Authz = authorization,
|
|
Conf = emqx_conf:get_raw([Authz]),
|
|
%% set sources to []
|
|
ConfBin = hocon_pp:do(#{<<"authorization">> => #{<<"sources">> => []}}, #{}),
|
|
ConfFile = prepare_conf_file(?FUNCTION_NAME, ConfBin, Config),
|
|
ok = emqx_conf_cli:conf(["load", "--replace", ConfFile]),
|
|
?assertMatch(#{<<"sources">> := []}, emqx_conf:get_raw([Authz])),
|
|
|
|
ConfBin0 = hocon_pp:do(#{<<"authorization">> => Conf#{<<"sources">> => []}}, #{}),
|
|
ConfFile0 = prepare_conf_file(?FUNCTION_NAME, ConfBin0, Config),
|
|
ok = emqx_conf_cli:conf(["load", "--replace", ConfFile0]),
|
|
?assertEqual(Conf#{<<"sources">> => []}, emqx_conf:get_raw([Authz])),
|
|
|
|
%% remove sources, it will reset to default file source.
|
|
ConfBin1 = hocon_pp:do(#{<<"authorization">> => maps:remove(<<"sources">>, Conf)}, #{}),
|
|
ConfFile1 = prepare_conf_file(?FUNCTION_NAME, ConfBin1, Config),
|
|
ok = emqx_conf_cli:conf(["load", "--replace", ConfFile1]),
|
|
Default = [emqx_authz_schema:default_authz()],
|
|
?assertEqual(Conf#{<<"sources">> => Default}, emqx_conf:get_raw([Authz])),
|
|
%% reset
|
|
ConfBin2 = hocon_pp:do(#{<<"authorization">> => Conf}, #{}),
|
|
ConfFile2 = prepare_conf_file(?FUNCTION_NAME, ConfBin2, Config),
|
|
ok = emqx_conf_cli:conf(["load", "--replace", ConfFile2]),
|
|
?assertEqual(
|
|
Conf#{<<"sources">> => [emqx_authz_schema:default_authz()]},
|
|
emqx_conf:get_raw([Authz])
|
|
),
|
|
?assertEqual({error, empty_hocon_file}, emqx_conf_cli:conf(["load", "non-exist-file"])),
|
|
ok.
|
|
|
|
t_conflict_mix_conf(Config) ->
|
|
case emqx_release:edition() of
|
|
ce ->
|
|
%% Don't fail if the test is run with emqx profile
|
|
ok;
|
|
ee ->
|
|
AuthNInit = emqx_conf:get_raw([authentication]),
|
|
Redis = #{
|
|
<<"backend">> => <<"redis">>,
|
|
<<"database">> => 0,
|
|
<<"password_hash_algorithm">> =>
|
|
#{<<"name">> => <<"sha256">>, <<"salt_position">> => <<"prefix">>},
|
|
<<"pool_size">> => 8,
|
|
<<"cmd">> => <<"HMGET mqtt_user:${username} password_hash salt">>,
|
|
<<"enable">> => false,
|
|
<<"mechanism">> => <<"password_based">>,
|
|
%% password_hash_algorithm {name = sha256, salt_position = suffix}
|
|
<<"redis_type">> => <<"single">>,
|
|
<<"server">> => <<"127.0.0.1:6379">>
|
|
},
|
|
AuthN = #{<<"authentication">> => [Redis]},
|
|
ConfBin = hocon_pp:do(AuthN, #{}),
|
|
ConfFile = prepare_conf_file(?FUNCTION_NAME, ConfBin, Config),
|
|
%% init with redis sources
|
|
ok = emqx_conf_cli:conf(["load", "--replace", ConfFile]),
|
|
[RedisRaw] = emqx_conf:get_raw([authentication]),
|
|
?assertEqual(
|
|
maps:to_list(Redis),
|
|
maps:to_list(maps:remove(<<"ssl">>, RedisRaw)),
|
|
{Redis, RedisRaw}
|
|
),
|
|
%% change redis type from single to cluster
|
|
%% the server field will become servers field
|
|
RedisCluster = maps:without([<<"server">>, <<"database">>], Redis#{
|
|
<<"redis_type">> => cluster,
|
|
<<"servers">> => [<<"127.0.0.1:6379">>]
|
|
}),
|
|
AuthN1 = AuthN#{<<"authentication">> => [RedisCluster]},
|
|
ConfBin1 = hocon_pp:do(AuthN1, #{}),
|
|
ConfFile1 = prepare_conf_file(?FUNCTION_NAME, ConfBin1, Config),
|
|
{error, Reason} = emqx_conf_cli:conf(["load", "--merge", ConfFile1]),
|
|
?assertNotEqual(
|
|
nomatch,
|
|
binary:match(
|
|
Reason,
|
|
[<<"Tips: There may be some conflicts in the new configuration under">>]
|
|
),
|
|
Reason
|
|
),
|
|
%% use replace to change redis type from single to cluster
|
|
?assertMatch(ok, emqx_conf_cli:conf(["load", "--replace", ConfFile1])),
|
|
%% clean up
|
|
ConfBinInit = hocon_pp:do(#{<<"authentication">> => AuthNInit}, #{}),
|
|
ConfFileInit = prepare_conf_file(?FUNCTION_NAME, ConfBinInit, Config),
|
|
ok = emqx_conf_cli:conf(["load", "--replace", ConfFileInit]),
|
|
ok
|
|
end.
|
|
|
|
t_config_handler_hook_failed(Config) ->
|
|
Listeners =
|
|
#{
|
|
<<"listeners">> => #{
|
|
<<"ssl">> => #{
|
|
<<"default">> => #{
|
|
<<"ssl_options">> => #{
|
|
<<"keyfile">> => <<"">>
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
ConfBin = hocon_pp:do(Listeners, #{}),
|
|
ConfFile = prepare_conf_file(?FUNCTION_NAME, ConfBin, Config),
|
|
{error, Reason} = emqx_conf_cli:conf(["load", "--merge", ConfFile]),
|
|
%% the hook failed with empty keyfile
|
|
?assertEqual(
|
|
nomatch,
|
|
binary:match(Reason, [
|
|
<<"Tips: There may be some conflicts in the new configuration under">>
|
|
]),
|
|
Reason
|
|
),
|
|
?assertNotEqual(
|
|
nomatch,
|
|
binary:match(Reason, [
|
|
<<"{bad_ssl_config,#{reason => pem_file_path_or_string_is_required">>
|
|
]),
|
|
Reason
|
|
),
|
|
ok.
|
|
|
|
t_load_readonly(Config) ->
|
|
Base0 = base_conf(),
|
|
Mqtt = #{<<"mqtt">> => emqx_conf:get_raw([mqtt])},
|
|
lists:foreach(
|
|
fun(Key) ->
|
|
KeyBin = atom_to_binary(Key),
|
|
Conf = emqx_conf:get_raw([Key]),
|
|
ConfBin0 = hocon_pp:do(maps:merge(Mqtt, #{KeyBin => Conf}), #{}),
|
|
ConfFile0 = prepare_conf_file(?FUNCTION_NAME, ConfBin0, Config),
|
|
Msg = iolist_to_binary(
|
|
io_lib:format(
|
|
"Cannot update read-only key '~s'.", [KeyBin]
|
|
)
|
|
),
|
|
?assertEqual(
|
|
{error, Msg},
|
|
emqx_conf_cli:conf(["load", ConfFile0]),
|
|
ConfFile0
|
|
),
|
|
%% reload etc/emqx.conf changed readonly keys
|
|
Base1 = maps:merge(Base0, Mqtt),
|
|
ConfBin1 = hocon_pp:do(Base1#{KeyBin => changed(Key)}, #{}),
|
|
ConfFile1 = prepare_conf_file(?FUNCTION_NAME, ConfBin1, Config),
|
|
application:set_env(emqx, config_files, [ConfFile1]),
|
|
?assertMatch(ok, emqx_conf_cli:conf(["reload"])),
|
|
%% Don't update readonly key
|
|
?assertEqual(Conf, emqx_conf:get_raw([Key]))
|
|
end,
|
|
?READONLY_KEYS
|
|
),
|
|
ok.
|
|
|
|
t_error_schema_check(Config) ->
|
|
Base = #{
|
|
%% bad multiplier
|
|
<<"mqtt">> => #{<<"keepalive_multiplier">> => -1},
|
|
<<"zones">> => #{<<"my-zone">> => #{<<"mqtt">> => #{<<"keepalive_multiplier">> => 10}}}
|
|
},
|
|
ConfBin0 = hocon_pp:do(Base, #{}),
|
|
ConfFile0 = prepare_conf_file(?FUNCTION_NAME, ConfBin0, Config),
|
|
?assertMatch({error, _}, emqx_conf_cli:conf(["load", ConfFile0])),
|
|
%% zones is not updated because of error
|
|
?assertEqual(#{}, emqx_config:get_raw([zones])),
|
|
ok.
|
|
|
|
t_reload_etc_emqx_conf_not_persistent(Config) ->
|
|
Mqtt = emqx_conf:get_raw([mqtt]),
|
|
Base = base_conf(),
|
|
Conf = Base#{<<"mqtt">> => Mqtt#{<<"keepalive_multiplier">> => 3}},
|
|
ConfBin = hocon_pp:do(Conf, #{}),
|
|
ConfFile = prepare_conf_file(?FUNCTION_NAME, ConfBin, Config),
|
|
application:set_env(emqx, config_files, [ConfFile]),
|
|
ok = emqx_conf_cli:conf(["reload"]),
|
|
?assertEqual(3, emqx:get_config([mqtt, keepalive_multiplier])),
|
|
?assertNotEqual(
|
|
3,
|
|
emqx_utils_maps:deep_get(
|
|
[<<"mqtt">>, <<"keepalive_multiplier">>],
|
|
emqx_config:read_override_conf(#{}),
|
|
undefined
|
|
)
|
|
),
|
|
ok.
|
|
|
|
base_conf() ->
|
|
#{
|
|
<<"cluster">> => emqx_conf:get_raw([cluster]),
|
|
<<"node">> => emqx_conf:get_raw([node])
|
|
}.
|
|
|
|
changed(cluster) ->
|
|
#{<<"name">> => <<"emqx-test">>};
|
|
changed(node) ->
|
|
#{
|
|
<<"name">> => <<"emqx-test@127.0.0.1">>,
|
|
<<"cookie">> => <<"gokdfkdkf1122">>,
|
|
<<"data_dir">> => <<"data">>
|
|
};
|
|
changed(rpc) ->
|
|
#{<<"mode">> => <<"sync">>}.
|