feat: add a json format support for the /status API
This commit is contained in:
parent
9260b5ec6c
commit
ed7a8659d2
|
@ -45,6 +45,17 @@ schema("/status") ->
|
|||
#{
|
||||
'operationId' => get_status,
|
||||
get => #{
|
||||
parameters => [
|
||||
{format,
|
||||
hoconsc:mk(
|
||||
string(),
|
||||
#{
|
||||
in => query,
|
||||
default => <<"text">>,
|
||||
desc => ?DESC(get_status_api_format)
|
||||
}
|
||||
)}
|
||||
],
|
||||
description => ?DESC(get_status_api),
|
||||
tags => ?TAGS,
|
||||
security => [],
|
||||
|
@ -70,7 +81,16 @@ path() ->
|
|||
"/status".
|
||||
|
||||
init(Req0, State) ->
|
||||
{Code, Headers, Body} = running_status(),
|
||||
Format =
|
||||
try
|
||||
QS = cowboy_req:parse_qs(Req0),
|
||||
{_, F} = lists:keyfind(<<"format">>, 1, QS),
|
||||
F
|
||||
catch
|
||||
_:_ ->
|
||||
<<"text">>
|
||||
end,
|
||||
{Code, Headers, Body} = running_status(Format),
|
||||
Req = cowboy_req:reply(Code, Headers, Body, Req0),
|
||||
{ok, Req, State}.
|
||||
|
||||
|
@ -78,29 +98,52 @@ init(Req0, State) ->
|
|||
%% API Handler funcs
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
get_status(get, _Params) ->
|
||||
running_status().
|
||||
get_status(get, Params) ->
|
||||
Format = maps:get(<<"format">>, maps:get(query_string, Params, #{}), <<"text">>),
|
||||
running_status(iolist_to_binary(Format)).
|
||||
|
||||
running_status() ->
|
||||
running_status(Format) ->
|
||||
case emqx_dashboard_listener:is_ready(timer:seconds(20)) of
|
||||
true ->
|
||||
BrokerStatus = broker_status(),
|
||||
AppStatus = application_status(),
|
||||
Body = io_lib:format("Node ~ts is ~ts~nemqx is ~ts", [node(), BrokerStatus, AppStatus]),
|
||||
Body = do_get_status(AppStatus, Format),
|
||||
StatusCode =
|
||||
case AppStatus of
|
||||
running -> 200;
|
||||
not_running -> 503
|
||||
end,
|
||||
ContentType =
|
||||
case Format of
|
||||
<<"json">> -> <<"applicatin/json">>;
|
||||
_ -> <<"text/plain">>
|
||||
end,
|
||||
Headers = #{
|
||||
<<"content-type">> => <<"text/plain">>,
|
||||
<<"content-type">> => ContentType,
|
||||
<<"retry-after">> => <<"15">>
|
||||
},
|
||||
{StatusCode, Headers, list_to_binary(Body)};
|
||||
{StatusCode, Headers, iolist_to_binary(Body)};
|
||||
false ->
|
||||
{503, #{<<"retry-after">> => <<"15">>}, <<>>}
|
||||
end.
|
||||
|
||||
do_get_status(AppStatus, <<"json">>) ->
|
||||
BrokerStatus = broker_status(),
|
||||
emqx_utils_json:encode(#{
|
||||
node_name => atom_to_binary(node(), utf8),
|
||||
rel_vsn => vsn(),
|
||||
broker_status => atom_to_binary(BrokerStatus),
|
||||
app_status => atom_to_binary(AppStatus)
|
||||
});
|
||||
do_get_status(AppStatus, _) ->
|
||||
BrokerStatus = broker_status(),
|
||||
io_lib:format("Node ~ts is ~ts~nemqx is ~ts", [node(), BrokerStatus, AppStatus]).
|
||||
|
||||
vsn() ->
|
||||
iolist_to_binary([
|
||||
emqx_release:edition_vsn_prefix(),
|
||||
emqx_release:version()
|
||||
]).
|
||||
|
||||
broker_status() ->
|
||||
case emqx:is_running() of
|
||||
true ->
|
||||
|
|
|
@ -38,7 +38,10 @@ all() ->
|
|||
get_status_tests() ->
|
||||
[
|
||||
t_status_ok,
|
||||
t_status_not_ok
|
||||
t_status_not_ok,
|
||||
t_status_text_format,
|
||||
t_status_json_format,
|
||||
t_status_bad_format_qs
|
||||
].
|
||||
|
||||
groups() ->
|
||||
|
@ -87,8 +90,10 @@ do_request(Opts) ->
|
|||
headers := Headers,
|
||||
body := Body0
|
||||
} = Opts,
|
||||
QS = maps:get(qs, Opts, ""),
|
||||
URL = ?HOST ++ filename:join(Path0),
|
||||
{ok, #{host := Host, port := Port, path := Path}} = emqx_http_lib:uri_parse(URL),
|
||||
{ok, #{host := Host, port := Port, path := Path1}} = emqx_http_lib:uri_parse(URL),
|
||||
Path = Path1 ++ QS,
|
||||
%% we must not use `httpc' here, because it keeps retrying when it
|
||||
%% receives a 503 with `retry-after' header, and there's no option
|
||||
%% to stop that behavior...
|
||||
|
@ -165,3 +170,73 @@ t_status_not_ok(Config) ->
|
|||
Headers
|
||||
),
|
||||
ok.
|
||||
|
||||
t_status_text_format(Config) ->
|
||||
Path = ?config(get_status_path, Config),
|
||||
#{
|
||||
body := Resp,
|
||||
status_code := StatusCode
|
||||
} = do_request(#{
|
||||
method => get,
|
||||
path => Path,
|
||||
qs => "?format=text",
|
||||
headers => [],
|
||||
body => no_body
|
||||
}),
|
||||
?assertEqual(200, StatusCode),
|
||||
?assertMatch(
|
||||
{match, _},
|
||||
re:run(Resp, <<"emqx is running$">>)
|
||||
),
|
||||
ok.
|
||||
|
||||
t_status_json_format(Config) ->
|
||||
Path = ?config(get_status_path, Config),
|
||||
#{
|
||||
body := Resp,
|
||||
status_code := StatusCode
|
||||
} = do_request(#{
|
||||
method => get,
|
||||
path => Path,
|
||||
qs => "?format=json",
|
||||
headers => [],
|
||||
body => no_body
|
||||
}),
|
||||
?assertEqual(200, StatusCode),
|
||||
?assertMatch(
|
||||
#{<<"app_status">> := <<"running">>},
|
||||
emqx_utils_json:decode(Resp)
|
||||
),
|
||||
ok.
|
||||
|
||||
t_status_bad_format_qs(Config) ->
|
||||
lists:foreach(
|
||||
fun(QS) ->
|
||||
test_status_bad_format_qs(QS, Config)
|
||||
end,
|
||||
[
|
||||
"?a=b",
|
||||
"?format=",
|
||||
"?format=x"
|
||||
]
|
||||
).
|
||||
|
||||
%% when query-sting is invalid, fallback to text format
|
||||
test_status_bad_format_qs(QS, Config) ->
|
||||
Path = ?config(get_status_path, Config),
|
||||
#{
|
||||
body := Resp,
|
||||
status_code := StatusCode
|
||||
} = do_request(#{
|
||||
method => get,
|
||||
path => Path,
|
||||
qs => QS,
|
||||
headers => [],
|
||||
body => no_body
|
||||
}),
|
||||
?assertEqual(200, StatusCode),
|
||||
?assertMatch(
|
||||
{match, _},
|
||||
re:run(Resp, <<"emqx is running$">>)
|
||||
),
|
||||
ok.
|
||||
|
|
|
@ -1,21 +1,42 @@
|
|||
emqx_mgmt_api_status {
|
||||
|
||||
get_status_api.desc:
|
||||
"""Serves as a health check for the node. Returns a plain text response describing the status of the node. This endpoint requires no authentication.
|
||||
"""Serves as a health check for the node.
|
||||
Returns response to describe the status of the node and the application.
|
||||
|
||||
This endpoint requires no authentication.
|
||||
|
||||
Returns status code 200 if the EMQX application is up and running, 503 otherwise.
|
||||
This API was introduced in v5.0.10.
|
||||
The GET `/status` endpoint (without the `/api/...` prefix) is also an alias to this endpoint and works in the same way. This alias has been available since v5.0.0."""
|
||||
The GET `/status` endpoint (without the `/api/...` prefix) is also an alias to this endpoint and works in the same way.
|
||||
This alias has been available since v5.0.0.
|
||||
|
||||
Starting from v5.0.25 or e5.0.4, you can also use 'format' parameter to get JSON format information.
|
||||
"""
|
||||
|
||||
get_status_api.label:
|
||||
"""Service health check"""
|
||||
|
||||
get_status_response200.desc:
|
||||
"""Node emqx@127.0.0.1 is started
|
||||
"""If 'format' parameter is 'json', then it returns a JSON like below:<br/>
|
||||
{
|
||||
"rel_vsn": "v5.0.23",
|
||||
"node_name": "emqx@127.0.0.1",
|
||||
"broker_status": "started",
|
||||
"app_status": "running"
|
||||
}
|
||||
<br/>
|
||||
Otherwise it returns free text strings as below:<br/>
|
||||
Node emqx@127.0.0.1 is started
|
||||
emqx is running"""
|
||||
|
||||
get_status_response503.desc:
|
||||
"""Node emqx@127.0.0.1 is stopped
|
||||
emqx is not_running"""
|
||||
"""When EMQX application is temporary not running or being restarted, it may return 'emqx is not_running'.
|
||||
If the 'format' parameter is provided 'json', the nthe 'app_status' field in the JSON object is 'not_running'.
|
||||
"""
|
||||
|
||||
get_status_api_format.desc:
|
||||
"""Specify the response format, 'text' (default) to return the HTTP body in free text,
|
||||
or 'json' to return the HTTP body with a JSON object."""
|
||||
|
||||
}
|
||||
|
|
|
@ -1,22 +1,34 @@
|
|||
emqx_mgmt_api_status {
|
||||
|
||||
get_status_api.desc:
|
||||
"""作为节点的健康检查。 返回一个纯文本的响应,描述节点的状态。
|
||||
"""节点的健康检查。 返回节点状态的描述信息。
|
||||
|
||||
如果 EMQX 应用程序已经启动并运行,返回状态代码 200,否则返回 503。
|
||||
|
||||
这个API是在v5.0.10中引入的。
|
||||
GET `/status`端点(没有`/api/...`前缀)也是这个端点的一个别名,工作方式相同。 这个别名从v5.0.0开始就有了。"""
|
||||
GET `/status`端点(没有`/api/...`前缀)也是这个端点的一个别名,工作方式相同。 这个别名从v5.0.0开始就有了。
|
||||
自 v5.0.25 和 e5.0.4 开始,可以通过指定 'format' 参数来得到 JSON 格式的信息。"""
|
||||
|
||||
get_status_api.label:
|
||||
"""服务健康检查"""
|
||||
|
||||
get_status_response200.desc:
|
||||
"""Node emqx@127.0.0.1 is started
|
||||
"""如果 'format' 参数为 'json',则返回如下JSON:<br/>
|
||||
{
|
||||
"rel_vsn": "v5.0.23",
|
||||
"node_name": "emqx@127.0.0.1",
|
||||
"broker_status": "started",
|
||||
"app_status": "running"
|
||||
}
|
||||
<br/>
|
||||
否则返回2行自由格式的文本,第一行描述节点的状态,第二行描述 EMQX 应用运行状态。例如:<br/>
|
||||
Node emqx@127.0.0.1 is started
|
||||
emqx is running"""
|
||||
|
||||
get_status_response503.desc:
|
||||
"""Node emqx@127.0.0.1 is stopped
|
||||
emqx is not_running"""
|
||||
"""如果 EMQX 应用暂时没有启动,或正在重启,则可能返回 'emqx is not_running'"""
|
||||
|
||||
get_status_api_format.desc:
|
||||
"""指定返回的内容格式。使用 'text'(默认)则返回自由格式的字符串; 'json' 则返回 JSON 格式。"""
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue