emqx_config_handler_SUITE.erl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2019-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
  3. %%
  4. %% Licensed under the Apache License, Version 2.0 (the "License");
  5. %% you may not use this file except in compliance with the License.
  6. %% You may obtain a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing, software
  11. %% distributed under the License is distributed on an "AS IS" BASIS,
  12. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. %% See the License for the specific language governing permissions and
  14. %% limitations under the License.
  15. %%--------------------------------------------------------------------
  16. -module(emqx_config_handler_SUITE).
  17. -compile(export_all).
  18. -compile(nowarn_export_all).
  19. -define(MOD, '$mod').
  20. -define(WKEY, '?').
  21. -define(CLUSTER_CONF, "/tmp/cluster.conf").
  22. -include_lib("eunit/include/eunit.hrl").
  23. -include_lib("common_test/include/ct.hrl").
  24. all() ->
  25. emqx_common_test_helpers:all(?MODULE).
  26. init_per_suite(Config) ->
  27. Apps = emqx_cth_suite:start([emqx], #{work_dir => emqx_cth_suite:work_dir(Config)}),
  28. [{apps, Apps} | Config].
  29. end_per_suite(Config) ->
  30. emqx_cth_suite:stop(?config(apps, Config)).
  31. init_per_testcase(_Case, Config) ->
  32. _ = file:delete(?CLUSTER_CONF),
  33. Config.
  34. end_per_testcase(_Case, _Config) ->
  35. ok.
  36. t_handler(_Config) ->
  37. BadCallBackMod = emqx,
  38. RootKey = sysmon,
  39. %% bad
  40. ?assertError(
  41. #{msg := "bad_emqx_config_handler_callback", module := BadCallBackMod},
  42. emqx_config_handler:add_handler([RootKey], BadCallBackMod)
  43. ),
  44. %% simple
  45. ok = emqx_config_handler:add_handler([RootKey], ?MODULE),
  46. #{handlers := Handlers0} = emqx_config_handler:info(),
  47. ?assertMatch(#{RootKey := #{?MOD := ?MODULE}}, Handlers0),
  48. ok = emqx_config_handler:remove_handler([RootKey]),
  49. #{handlers := Handlers1} = emqx_config_handler:info(),
  50. ct:pal("Key:~p simple: ~p~n", [RootKey, Handlers1]),
  51. ?assertEqual(false, maps:is_key(RootKey, Handlers1)),
  52. %% wildcard 1
  53. Wildcard1 = [RootKey, '?', cpu_check_interval],
  54. ok = emqx_config_handler:add_handler(Wildcard1, ?MODULE),
  55. #{handlers := Handlers2} = emqx_config_handler:info(),
  56. ?assertMatch(#{RootKey := #{?WKEY := #{cpu_check_interval := #{?MOD := ?MODULE}}}}, Handlers2),
  57. ok = emqx_config_handler:remove_handler(Wildcard1),
  58. #{handlers := Handlers3} = emqx_config_handler:info(),
  59. ct:pal("Key:~p wildcard1: ~p~n", [Wildcard1, Handlers3]),
  60. ?assertEqual(false, maps:is_key(RootKey, Handlers3)),
  61. %% can_override_a_wildcard_path
  62. ok = emqx_config_handler:add_handler(Wildcard1, ?MODULE),
  63. ?assertEqual(ok, emqx_config_handler:add_handler([RootKey, os, cpu_check_interval], ?MODULE)),
  64. ok = emqx_config_handler:remove_handler(Wildcard1),
  65. ok = emqx_config_handler:remove_handler([RootKey, os, cpu_check_interval]),
  66. ok = emqx_config_handler:add_handler([RootKey, os, cpu_check_interval], ?MODULE),
  67. ok = emqx_config_handler:add_handler(Wildcard1, ?MODULE),
  68. ok = emqx_config_handler:remove_handler([RootKey, os, cpu_check_interval]),
  69. ok = emqx_config_handler:remove_handler(Wildcard1),
  70. ok.
  71. t_conflict_handler(_Config) ->
  72. ok = emqx_config_handler:add_handler([sysmon, '?', '?'], ?MODULE),
  73. ?assertMatch(
  74. {error, {conflict, _}},
  75. emqx_config_handler:add_handler([sysmon, '?', cpu_check_interval], ?MODULE)
  76. ),
  77. ok = emqx_config_handler:remove_handler([sysmon, '?', '?']),
  78. ok = emqx_config_handler:add_handler([sysmon, '?', cpu_check_interval], ?MODULE),
  79. ?assertMatch(
  80. {error, {conflict, _}},
  81. emqx_config_handler:add_handler([sysmon, '?', '?'], ?MODULE)
  82. ),
  83. ok = emqx_config_handler:remove_handler([sysmon, '?', cpu_check_interval]),
  84. %% override
  85. ok = emqx_config_handler:add_handler([sysmon], emqx_config_logger),
  86. ?assertMatch(
  87. #{handlers := #{sysmon := #{?MOD := emqx_config_logger}}},
  88. emqx_config_handler:info()
  89. ),
  90. ok.
  91. t_root_key_update(_Config) ->
  92. PathKey = [sysmon],
  93. Opts = #{rawconf_with_defaults => true},
  94. ok = emqx_config_handler:add_handler(PathKey, ?MODULE),
  95. %% update
  96. Old = #{<<"os">> := OS} = emqx:get_raw_config(PathKey),
  97. {ok, Res} = emqx:update_config(
  98. PathKey,
  99. Old#{<<"os">> => OS#{<<"cpu_check_interval">> => <<"12s">>}},
  100. Opts
  101. ),
  102. ?assertMatch(
  103. #{
  104. config := #{os := #{cpu_check_interval := 12000}},
  105. post_config_update := #{?MODULE := ok},
  106. raw_config := #{<<"os">> := #{<<"cpu_check_interval">> := <<"12s">>}}
  107. },
  108. Res
  109. ),
  110. ?assertMatch(#{os := #{cpu_check_interval := 12000}}, emqx:get_config(PathKey)),
  111. %% update sub key
  112. SubKey = PathKey ++ [os, cpu_high_watermark],
  113. ?assertEqual(
  114. {ok, #{
  115. config => 0.81,
  116. post_config_update => #{},
  117. raw_config => <<"81%">>
  118. }},
  119. emqx:update_config(SubKey, "81%", Opts)
  120. ),
  121. ?assertEqual(0.81, emqx:get_config(SubKey)),
  122. ?assertEqual("81%", emqx:get_raw_config(SubKey)),
  123. %% remove
  124. ?assertEqual({error, "remove_root_is_forbidden"}, emqx:remove_config(PathKey)),
  125. ?assertMatch(true, is_map(emqx:get_raw_config(PathKey))),
  126. ok = emqx_config_handler:remove_handler(PathKey),
  127. ok.
  128. t_sub_key_update_remove(_Config) ->
  129. KeyPath = [sysmon, os, cpu_check_interval],
  130. Opts = #{},
  131. ok = emqx_config_handler:add_handler(KeyPath, ?MODULE),
  132. {ok, Res} = emqx:update_config(KeyPath, <<"60s">>, Opts),
  133. ?assertMatch(
  134. #{
  135. config := 60000,
  136. post_config_update := #{?MODULE := ok},
  137. raw_config := <<"60s">>
  138. },
  139. Res
  140. ),
  141. ?assertMatch(60000, emqx:get_config(KeyPath)),
  142. KeyPath2 = [sysmon, os, cpu_low_watermark],
  143. ok = emqx_config_handler:add_handler(KeyPath2, ?MODULE),
  144. {ok, Res1} = emqx:update_config(KeyPath2, <<"40%">>, Opts),
  145. ?assertMatch(
  146. #{
  147. config := 0.4,
  148. post_config_update := #{},
  149. raw_config := <<"40%">>
  150. },
  151. Res1
  152. ),
  153. ?assertMatch(0.4, emqx:get_config(KeyPath2)),
  154. %% remove
  155. ?assertEqual(
  156. {ok, #{post_config_update => #{?MODULE => ok}}},
  157. emqx:remove_config(KeyPath)
  158. ),
  159. ?assertError(
  160. {config_not_found, [<<"sysmon">>, os, cpu_check_interval]}, emqx:get_raw_config(KeyPath)
  161. ),
  162. OSKey = maps:keys(emqx:get_raw_config([sysmon, os])),
  163. ?assertEqual(false, lists:member(<<"cpu_check_interval">>, OSKey)),
  164. ?assert(length(OSKey) > 0),
  165. ok = emqx_config_handler:remove_handler(KeyPath),
  166. ok = emqx_config_handler:remove_handler(KeyPath2),
  167. ok.
  168. t_check_failed(_Config) ->
  169. KeyPath = [sysmon, os, cpu_check_interval],
  170. Opts = #{rawconf_with_defaults => true},
  171. Origin = emqx:get_raw_config(KeyPath),
  172. ok = emqx_config_handler:add_handler(KeyPath, ?MODULE),
  173. %% It should be a duration("1h"), but we set it as a percent.
  174. ?assertMatch({error, _Res}, emqx:update_config(KeyPath, <<"80%">>, Opts)),
  175. New = emqx:get_raw_config(KeyPath),
  176. ?assertEqual(Origin, New),
  177. ok = emqx_config_handler:remove_handler(KeyPath),
  178. ok.
  179. t_stop(_Config) ->
  180. OldPid = erlang:whereis(emqx_config_handler),
  181. OldInfo = emqx_config_handler:info(),
  182. emqx_config_handler:stop(),
  183. NewPid = wait_for_new_pid(),
  184. NewInfo = emqx_config_handler:info(),
  185. ?assertNotEqual(OldPid, NewPid),
  186. ?assertEqual(OldInfo, NewInfo),
  187. ok.
  188. t_callback_crash(_Config) ->
  189. CrashPath = [sysmon, os, procmem_high_watermark],
  190. Opts = #{rawconf_with_defaults => true},
  191. ok = emqx_config_handler:add_handler(CrashPath, ?MODULE),
  192. Old = emqx:get_raw_config(CrashPath),
  193. ?assertMatch(
  194. {error, {config_update_crashed, _}}, emqx:update_config(CrashPath, <<"89%">>, Opts)
  195. ),
  196. New = emqx:get_raw_config(CrashPath),
  197. ?assertEqual(Old, New),
  198. ok = emqx_config_handler:remove_handler(CrashPath),
  199. ok.
  200. t_pre_assert_update_result(_Config) ->
  201. assert_update_result(
  202. [sysmon, os, mem_check_interval],
  203. <<"100s">>,
  204. {error, {pre_config_update, ?MODULE, pre_config_update_error}}
  205. ),
  206. ok.
  207. t_post_update_error(_Config) ->
  208. assert_update_result(
  209. [sysmon, os, sysmem_high_watermark],
  210. <<"60%">>,
  211. {error, {post_config_update, ?MODULE, post_config_update_error}}
  212. ),
  213. ok.
  214. t_post_update_propagate_error_wkey(_Config) ->
  215. Conf0 = emqx_config:get_raw([sysmon]),
  216. Conf1 = emqx_utils_maps:deep_put([<<"os">>, <<"sysmem_high_watermark">>], Conf0, <<"60%">>),
  217. assert_update_result(
  218. [
  219. [sysmon, '?', sysmem_high_watermark],
  220. [sysmon]
  221. ],
  222. [sysmon],
  223. Conf1,
  224. {error, {post_config_update, ?MODULE, post_config_update_error}}
  225. ),
  226. ok.
  227. t_post_update_propagate_error_key(_Config) ->
  228. Conf0 = emqx_config:get_raw([sysmon]),
  229. Conf1 = emqx_utils_maps:deep_put([<<"os">>, <<"sysmem_high_watermark">>], Conf0, <<"60%">>),
  230. assert_update_result(
  231. [
  232. [sysmon, os, sysmem_high_watermark],
  233. [sysmon]
  234. ],
  235. [sysmon],
  236. Conf1,
  237. {error, {post_config_update, ?MODULE, post_config_update_error}}
  238. ),
  239. ok.
  240. t_pre_update_propagate_error_wkey(_Config) ->
  241. Conf0 = emqx_config:get_raw([sysmon]),
  242. Conf1 = emqx_utils_maps:deep_put([<<"os">>, <<"mem_check_interval">>], Conf0, <<"70s">>),
  243. assert_update_result(
  244. [
  245. [sysmon, '?', mem_check_interval],
  246. [sysmon]
  247. ],
  248. [sysmon],
  249. Conf1,
  250. {error, {pre_config_update, ?MODULE, pre_config_update_error}}
  251. ),
  252. ok.
  253. t_pre_update_propagate_error_key(_Config) ->
  254. Conf0 = emqx_config:get_raw([sysmon]),
  255. Conf1 = emqx_utils_maps:deep_put([<<"os">>, <<"mem_check_interval">>], Conf0, <<"70s">>),
  256. assert_update_result(
  257. [
  258. [sysmon, os, mem_check_interval],
  259. [sysmon]
  260. ],
  261. [sysmon],
  262. Conf1,
  263. {error, {pre_config_update, ?MODULE, pre_config_update_error}}
  264. ),
  265. ok.
  266. t_pre_update_propagate_key_rewrite(_Config) ->
  267. Conf0 = emqx_config:get_raw([sysmon]),
  268. Conf1 = emqx_utils_maps:deep_put([<<"os">>, <<"cpu_check_interval">>], Conf0, <<"333s">>),
  269. with_update_result(
  270. [
  271. [sysmon, '?', cpu_check_interval],
  272. [sysmon]
  273. ],
  274. [sysmon],
  275. Conf1,
  276. fun(_, Result) ->
  277. ?assertMatch(
  278. {ok, #{config := #{os := #{cpu_check_interval := 444000}}}},
  279. Result
  280. )
  281. end
  282. ),
  283. ok.
  284. t_handler_root() ->
  285. %% Don't rely on default emqx_config_handler's merge behaviour.
  286. RootKey = [],
  287. Opts = #{rawconf_with_defaults => true},
  288. ok = emqx_config_handler:add_handler(RootKey, ?MODULE),
  289. %% update
  290. Old = #{<<"sysmon">> := #{<<"os">> := OS}} = emqx:get_raw_config(RootKey),
  291. {ok, Res} = emqx:update_config(
  292. RootKey,
  293. Old#{<<"sysmon">> => #{<<"os">> => OS#{<<"cpu_check_interval">> => <<"12s">>}}},
  294. Opts
  295. ),
  296. ?assertMatch(
  297. #{
  298. config := #{os := #{cpu_check_interval := 12000}},
  299. post_config_update := #{?MODULE := ok},
  300. raw_config := #{<<"os">> := #{<<"cpu_check_interval">> := <<"12s">>}}
  301. },
  302. Res
  303. ),
  304. ?assertMatch(#{sysmon := #{os := #{cpu_check_interval := 12000}}}, emqx:get_config(RootKey)),
  305. ok = emqx_config_handler:remove_handler(RootKey),
  306. ok.
  307. t_get_raw_cluster_override_conf(_Config) ->
  308. Raw0 = emqx_config:read_override_conf(#{override_to => cluster}),
  309. Raw1 = emqx_config_handler:get_raw_cluster_override_conf(),
  310. ?assertEqual(Raw0, Raw1),
  311. OldPid = erlang:whereis(emqx_config_handler),
  312. OldInfo = emqx_config_handler:info(),
  313. ?assertEqual(ok, gen_server:call(emqx_config_handler, bad_call_msg)),
  314. gen_server:cast(emqx_config_handler, bad_cast_msg),
  315. erlang:send(emqx_config_handler, bad_info_msg),
  316. NewPid = erlang:whereis(emqx_config_handler),
  317. NewInfo = emqx_config_handler:info(),
  318. ?assertEqual(OldPid, NewPid),
  319. ?assertEqual(OldInfo, NewInfo),
  320. ok.
  321. pre_config_update([sysmon], UpdateReq, _RawConf) ->
  322. {ok, UpdateReq};
  323. pre_config_update([sysmon, os], UpdateReq, _RawConf) ->
  324. {ok, UpdateReq};
  325. pre_config_update([sysmon, os, cpu_check_interval], UpdateReq, _RawConf) ->
  326. {ok, UpdateReq};
  327. pre_config_update([sysmon, os, cpu_low_watermark], UpdateReq, _RawConf) ->
  328. {ok, UpdateReq};
  329. pre_config_update([sysmon, os, cpu_high_watermark], UpdateReq, _RawConf) ->
  330. {ok, UpdateReq};
  331. pre_config_update([sysmon, os, sysmem_high_watermark], UpdateReq, _RawConf) ->
  332. {ok, UpdateReq};
  333. pre_config_update([sysmon, os, mem_check_interval], _UpdateReq, _RawConf) ->
  334. {error, pre_config_update_error}.
  335. propagated_pre_config_update(
  336. [<<"sysmon">>, <<"os">>, <<"cpu_check_interval">>], <<"333s">>, _RawConf
  337. ) ->
  338. {ok, <<"444s">>};
  339. propagated_pre_config_update(
  340. [<<"sysmon">>, <<"os">>, <<"mem_check_interval">>], _UpdateReq, _RawConf
  341. ) ->
  342. {error, pre_config_update_error};
  343. propagated_pre_config_update(_ConfKeyPath, _UpdateReq, _RawConf) ->
  344. ok.
  345. post_config_update([sysmon], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
  346. {ok, ok};
  347. post_config_update([sysmon, os], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
  348. {ok, ok};
  349. post_config_update([sysmon, os, cpu_check_interval], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
  350. {ok, ok};
  351. post_config_update([sysmon, os, cpu_low_watermark], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
  352. ok;
  353. post_config_update([sysmon, os, cpu_high_watermark], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
  354. ok;
  355. post_config_update([sysmon, os, sysmem_high_watermark], _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
  356. {error, post_config_update_error}.
  357. propagated_post_config_update(
  358. [sysmon, os, sysmem_high_watermark], _UpdateReq, _NewConf, _OldConf, _AppEnvs
  359. ) ->
  360. {error, post_config_update_error};
  361. propagated_post_config_update(_ConfKeyPath, _UpdateReq, _NewConf, _OldConf, _AppEnvs) ->
  362. ok.
  363. wait_for_new_pid() ->
  364. case erlang:whereis(emqx_config_handler) of
  365. undefined ->
  366. ct:sleep(10),
  367. wait_for_new_pid();
  368. Pid ->
  369. Pid
  370. end.
  371. assert_update_result(FailedPath, Update, Expect) ->
  372. assert_update_result([FailedPath], FailedPath, Update, Expect).
  373. assert_update_result(Paths, UpdatePath, Update, Expect) ->
  374. with_update_result(Paths, UpdatePath, Update, fun(Old, Result) ->
  375. case Expect of
  376. {error, {post_config_update, ?MODULE, post_config_update_error}} ->
  377. ?assertMatch(
  378. {error, {post_config_update, ?MODULE, {post_config_update_error, _}}}, Result
  379. );
  380. _ ->
  381. ?assertEqual(Expect, Result)
  382. end,
  383. New = emqx:get_raw_config(UpdatePath, undefined),
  384. ?assertEqual(Old, New)
  385. end).
  386. with_update_result(Paths, UpdatePath, Update, Fun) ->
  387. ok = lists:foreach(
  388. fun(Path) -> emqx_config_handler:add_handler(Path, ?MODULE) end,
  389. Paths
  390. ),
  391. Opts = #{rawconf_with_defaults => true},
  392. Old = emqx:get_raw_config(UpdatePath, undefined),
  393. Result = emqx:update_config(UpdatePath, Update, Opts),
  394. _ = Fun(Old, Result),
  395. ok = lists:foreach(
  396. fun(Path) -> emqx_config_handler:remove_handler(Path) end,
  397. Paths
  398. ),
  399. ok.