diff --git a/apps/emqx_management/i18n/emqx_mgmt_api_status_i18n.conf b/apps/emqx_management/i18n/emqx_mgmt_api_status_i18n.conf new file mode 100644 index 000000000..9c09806cd --- /dev/null +++ b/apps/emqx_management/i18n/emqx_mgmt_api_status_i18n.conf @@ -0,0 +1,38 @@ +emqx_mgmt_api_status { + get_status_api { + desc { + en: "Serves as a health check for the node. Returns a plain text response" + " describing the status of the node. This endpoint requires no" + " authentication.\n" + "\n" + "Returns status code 200 if the EMQX application is up and running, " + "503 otherwise." + "\n" + "The GET `/status` endpoint (without the `/api/...` prefix) is also an alias" + " to this endpoint and works in the same way." + zh: "作为节点的健康检查。 返回一个纯文本的响应,描述节点的状态。\n" + "\n" + "如果 EMQX 应用程序已经启动并运行,返回状态代码 200,否则返回 503。\n" + "\n" + "GET `/status`端点(没有`/api/...`前缀)也是这个端点的一个别名,工作方式相同。" + } + } + + get_status_response200 { + desc { + en: "Node emqx@127.0.0.1 is started\n" + "emqx is running" + zh: "Node emqx@127.0.0.1 is started\n" + "emqx is running" + } + } + + get_status_response503 { + desc { + en: "Node emqx@127.0.0.1 is stopped\n" + "emqx is not_running" + zh: "Node emqx@127.0.0.1 is stopped\n" + "emqx is not_running" + } + } +} diff --git a/apps/emqx_management/src/emqx_mgmt_api_status.erl b/apps/emqx_management/src/emqx_mgmt_api_status.erl index b313f4bcc..ea91f1c03 100644 --- a/apps/emqx_management/src/emqx_mgmt_api_status.erl +++ b/apps/emqx_management/src/emqx_mgmt_api_status.erl @@ -15,11 +15,50 @@ %%-------------------------------------------------------------------- -module(emqx_mgmt_api_status). +-behaviour(minirest_api). + +-include_lib("hocon/include/hoconsc.hrl"). + +%% minirest API +-export([api_spec/0, paths/0, schema/1]). + +-export([get_status/2]). + -export([ init/2, path/0 ]). +-define(TAGS, [<<"Status">>]). + +%%-------------------------------------------------------------------- +%% minirest API and schema +%%-------------------------------------------------------------------- + +api_spec() -> + emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}). + +paths() -> + ["/status"]. + +schema("/status") -> + #{ + 'operationId' => get_status, + get => #{ + description => ?DESC(get_status_api), + tags => ?TAGS, + security => [], + responses => #{ + 200 => ?DESC(get_status_response200), + 503 => ?DESC(get_status_response503) + } + } + }. + +%%-------------------------------------------------------------------- +%% non-minirest (cowboy) API +%%-------------------------------------------------------------------- + %% Note: Because swagger now requires an HTTP prefix (e.g. /api/v5), %% but the `/status` does not require this fixed prefix. %% @@ -39,6 +78,9 @@ init(Req0, State) -> %% API Handler funcs %%-------------------------------------------------------------------- +get_status(get, _Params) -> + running_status(). + running_status() -> case emqx_dashboard_listener:is_ready(timer:seconds(20)) of true -> diff --git a/apps/emqx_management/test/emqx_mgmt_api_status_SUITE.erl b/apps/emqx_management/test/emqx_mgmt_api_status_SUITE.erl index 42d833bda..a768d2bfe 100644 --- a/apps/emqx_management/test/emqx_mgmt_api_status_SUITE.erl +++ b/apps/emqx_management/test/emqx_mgmt_api_status_SUITE.erl @@ -19,6 +19,7 @@ -compile(nowarn_export_all). -include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(HOST, "http://127.0.0.1:18083/"). @@ -27,7 +28,24 @@ %%--------------------------------------------------------------------------------------- all() -> - emqx_common_test_helpers:all(?MODULE). + OtherTCs = emqx_common_test_helpers:all(?MODULE) -- get_status_tests(), + [ + {group, api_status_endpoint}, + {group, non_api_status_endpoint} + | OtherTCs + ]. + +get_status_tests() -> + [ + t_status_ok, + t_status_not_ok + ]. + +groups() -> + [ + {api_status_endpoint, [], get_status_tests()}, + {non_api_status_endpoint, [], get_status_tests()} + ]. init_per_suite(Config) -> emqx_mgmt_api_test_util:init_suite(), @@ -36,6 +54,16 @@ init_per_suite(Config) -> end_per_suite(_) -> emqx_mgmt_api_test_util:end_suite(). +init_per_group(api_status_endpoint, Config) -> + [{get_status_path, ["api", "v5", "status"]} | Config]; +init_per_group(non_api_status_endpoint, Config) -> + [{get_status_path, ["status"]} | Config]; +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + init_per_testcase(t_status_not_ok, Config) -> ok = application:stop(emqx), Config; @@ -97,13 +125,14 @@ data_loop(Gun, Ref, Acc) -> %% Test cases %%--------------------------------------------------------------------------------------- -t_status_ok(_Config) -> +t_status_ok(Config) -> + Path = ?config(get_status_path, Config), #{ body := Resp, status_code := StatusCode } = do_request(#{ method => get, - path => ["status"], + path => Path, headers => [], body => no_body }), @@ -114,14 +143,15 @@ t_status_ok(_Config) -> ), ok. -t_status_not_ok(_Config) -> +t_status_not_ok(Config) -> + Path = ?config(get_status_path, Config), #{ body := Resp, headers := Headers, status_code := StatusCode } = do_request(#{ method => get, - path => ["status"], + path => Path, headers => [], body => no_body }),