diff --git a/.github/workflows/build_slim_packages.yaml b/.github/workflows/build_slim_packages.yaml
index 9ae5ba944..06bcb98a2 100644
--- a/.github/workflows/build_slim_packages.yaml
+++ b/.github/workflows/build_slim_packages.yaml
@@ -194,15 +194,12 @@ jobs:
run: |
CID=$(docker run -d --rm -P $EMQX_IMAGE_TAG)
HTTP_PORT=$(docker inspect --format='{{(index (index .NetworkSettings.Ports "18083/tcp") 0).HostPort}}' $CID)
- export EMQX_SMOKE_TEST_CHECK_HIDDEN_FIELDS='yes'
./scripts/test/emqx-smoke-test.sh localhost $HTTP_PORT
docker stop $CID
- name: test two nodes cluster with proto_dist=inet_tls in docker
run: |
./scripts/test/start-two-nodes-in-docker.sh -P $EMQX_IMAGE_TAG $EMQX_IMAGE_OLD_VERSION_TAG
HTTP_PORT=$(docker inspect --format='{{(index (index .NetworkSettings.Ports "18083/tcp") 0).HostPort}}' haproxy)
- # versions before 5.0.22 have hidden fields included in the API spec
- export EMQX_SMOKE_TEST_CHECK_HIDDEN_FIELDS='no'
./scripts/test/emqx-smoke-test.sh localhost $HTTP_PORT
# cleanup
./scripts/test/start-two-nodes-in-docker.sh -c
diff --git a/apps/emqx_conf/src/emqx_conf.erl b/apps/emqx_conf/src/emqx_conf.erl
index 8632df139..eaa16ab5a 100644
--- a/apps/emqx_conf/src/emqx_conf.erl
+++ b/apps/emqx_conf/src/emqx_conf.erl
@@ -31,8 +31,9 @@
%% TODO: move to emqx_dashboard when we stop building api schema at build time
-export([
- hotconf_schema_json/1,
- bridge_schema_json/1
+ hotconf_schema_json/0,
+ bridge_schema_json/0,
+ hocon_schema_to_spec/2
]).
%% for rpc
@@ -149,7 +150,6 @@ dump_schema(Dir, SchemaModule) ->
lists:foreach(
fun(Lang) ->
ok = gen_config_md(Dir, SchemaModule, Lang),
- ok = gen_api_schema_json(Dir, Lang),
ok = gen_schema_json(Dir, SchemaModule, Lang)
end,
["en", "zh"]
@@ -176,41 +176,15 @@ gen_schema_json(Dir, SchemaModule, Lang) ->
IoData = emqx_utils_json:encode(JsonMap, [pretty, force_utf8]),
ok = file:write_file(SchemaJsonFile, IoData).
-%% TODO: delete this function when we stop generating this JSON at build time.
-gen_api_schema_json(Dir, Lang) ->
- gen_api_schema_json_hotconf(Dir, Lang),
- gen_api_schema_json_bridge(Dir, Lang).
-
-%% TODO: delete this function when we stop generating this JSON at build time.
-gen_api_schema_json_hotconf(Dir, Lang) ->
- File = schema_filename(Dir, "hot-config-schema-", Lang),
- IoData = hotconf_schema_json(Lang),
- ok = write_api_schema_json_file(File, IoData).
-
-%% TODO: delete this function when we stop generating this JSON at build time.
-gen_api_schema_json_bridge(Dir, Lang) ->
- File = schema_filename(Dir, "bridge-api-", Lang),
- IoData = bridge_schema_json(Lang),
- ok = write_api_schema_json_file(File, IoData).
-
-%% TODO: delete this function when we stop generating this JSON at build time.
-write_api_schema_json_file(File, IoData) ->
- io:format(user, "===< Generating: ~s~n", [File]),
- file:write_file(File, IoData).
-
%% TODO: move this function to emqx_dashboard when we stop generating this JSON at build time.
-hotconf_schema_json(Lang) ->
+hotconf_schema_json() ->
SchemaInfo = #{title => <<"EMQX Hot Conf API Schema">>, version => <<"0.1.0">>},
- gen_api_schema_json_iodata(emqx_mgmt_api_configs, SchemaInfo, Lang).
+ gen_api_schema_json_iodata(emqx_mgmt_api_configs, SchemaInfo).
%% TODO: move this function to emqx_dashboard when we stop generating this JSON at build time.
-bridge_schema_json(Lang) ->
+bridge_schema_json() ->
SchemaInfo = #{title => <<"EMQX Data Bridge API Schema">>, version => <<"0.1.0">>},
- gen_api_schema_json_iodata(emqx_bridge_api, SchemaInfo, Lang).
-
-schema_filename(Dir, Prefix, Lang) ->
- Filename = Prefix ++ Lang ++ ".json",
- filename:join([Dir, Filename]).
+ gen_api_schema_json_iodata(emqx_bridge_api, SchemaInfo).
%% TODO: remove it and also remove hocon_md.erl and friends.
%% markdown generation from schema is a failure and we are moving to an interactive
@@ -270,50 +244,11 @@ gen_example(File, SchemaModule) ->
Example = hocon_schema_example:gen(SchemaModule, Opts),
file:write_file(File, Example).
-%% TODO: move this to emqx_dashboard when we stop generating
-%% this JSON at build time.
-gen_api_schema_json_iodata(SchemaMod, SchemaInfo, Lang) ->
- {ApiSpec0, Components0} = emqx_dashboard_swagger:spec(
+gen_api_schema_json_iodata(SchemaMod, SchemaInfo) ->
+ emqx_dashboard_swagger:gen_api_schema_json_iodata(
SchemaMod,
- #{
- schema_converter => fun hocon_schema_to_spec/2,
- i18n_lang => Lang
- }
- ),
- ApiSpec = lists:foldl(
- fun({Path, Spec, _, _}, Acc) ->
- NewSpec = maps:fold(
- fun(Method, #{responses := Responses}, SubAcc) ->
- case Responses of
- #{
- <<"200">> :=
- #{
- <<"content">> := #{
- <<"application/json">> := #{<<"schema">> := Schema}
- }
- }
- } ->
- SubAcc#{Method => Schema};
- _ ->
- SubAcc
- end
- end,
- #{},
- Spec
- ),
- Acc#{list_to_atom(Path) => NewSpec}
- end,
- #{},
- ApiSpec0
- ),
- Components = lists:foldl(fun(M, Acc) -> maps:merge(M, Acc) end, #{}, Components0),
- emqx_utils_json:encode(
- #{
- info => SchemaInfo,
- paths => ApiSpec,
- components => #{schemas => Components}
- },
- [pretty, force_utf8]
+ SchemaInfo,
+ fun ?MODULE:hocon_schema_to_spec/2
).
-define(TO_REF(_N_, _F_), iolist_to_binary([to_bin(_N_), ".", to_bin(_F_)])).
diff --git a/apps/emqx_dashboard/src/emqx_dashboard_schema_api.erl b/apps/emqx_dashboard/src/emqx_dashboard_schema_api.erl
index 898d95b3c..e4f2f0c1a 100644
--- a/apps/emqx_dashboard/src/emqx_dashboard_schema_api.erl
+++ b/apps/emqx_dashboard/src/emqx_dashboard_schema_api.erl
@@ -45,18 +45,11 @@ schema("/schemas/:name") ->
'operationId' => get_schema,
get => #{
parameters => [
- {name, hoconsc:mk(hoconsc:enum([hotconf, bridges]), #{in => path})},
- {lang,
- hoconsc:mk(typerefl:string(), #{
- in => query,
- default => <<"en">>,
- desc => <<"The language of the schema.">>
- })}
+ {name, hoconsc:mk(hoconsc:enum([hotconf, bridges]), #{in => path})}
],
desc => <<
"Get the schema JSON of the specified name. "
- "NOTE: you should never need to make use of this API "
- "unless you are building a multi-lang dashboaard."
+ "NOTE: only intended for EMQX Dashboard."
>>,
tags => ?TAGS,
security => [],
@@ -71,14 +64,13 @@ schema("/schemas/:name") ->
%%--------------------------------------------------------------------
get_schema(get, #{
- bindings := #{name := Name},
- query_string := #{<<"lang">> := Lang}
+ bindings := #{name := Name}
}) ->
- {200, gen_schema(Name, iolist_to_binary(Lang))};
+ {200, gen_schema(Name)};
get_schema(get, _) ->
{400, ?BAD_REQUEST, <<"unknown">>}.
-gen_schema(hotconf, Lang) ->
- emqx_conf:hotconf_schema_json(Lang);
-gen_schema(bridges, Lang) ->
- emqx_conf:bridge_schema_json(Lang).
+gen_schema(hotconf) ->
+ emqx_conf:hotconf_schema_json();
+gen_schema(bridges) ->
+ emqx_conf:bridge_schema_json().
diff --git a/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl b/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl
index e471486e5..fec9717ba 100644
--- a/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl
+++ b/apps/emqx_dashboard/src/emqx_dashboard_swagger.erl
@@ -26,7 +26,11 @@
-export([error_codes/1, error_codes/2]).
-export([file_schema/1]).
--export([filter_check_request/2, filter_check_request_and_translate_body/2]).
+-export([
+ filter_check_request/2,
+ filter_check_request_and_translate_body/2,
+ gen_api_schema_json_iodata/3
+]).
-ifdef(TEST).
-export([
@@ -72,6 +76,8 @@
])
).
+-define(SPECIAL_LANG_MSGID, <<"$msgid">>).
+
-define(MAX_ROW_LIMIT, 1000).
-define(DEFAULT_ROW, 100).
@@ -192,6 +198,50 @@ file_schema(FileName) ->
}
}.
+gen_api_schema_json_iodata(SchemaMod, SchemaInfo, Converter) ->
+ {ApiSpec0, Components0} = emqx_dashboard_swagger:spec(
+ SchemaMod,
+ #{
+ schema_converter => Converter,
+ i18n_lang => ?SPECIAL_LANG_MSGID
+ }
+ ),
+ ApiSpec = lists:foldl(
+ fun({Path, Spec, _, _}, Acc) ->
+ NewSpec = maps:fold(
+ fun(Method, #{responses := Responses}, SubAcc) ->
+ case Responses of
+ #{
+ <<"200">> :=
+ #{
+ <<"content">> := #{
+ <<"application/json">> := #{<<"schema">> := Schema}
+ }
+ }
+ } ->
+ SubAcc#{Method => Schema};
+ _ ->
+ SubAcc
+ end
+ end,
+ #{},
+ Spec
+ ),
+ Acc#{list_to_atom(Path) => NewSpec}
+ end,
+ #{},
+ ApiSpec0
+ ),
+ Components = lists:foldl(fun(M, Acc) -> maps:merge(M, Acc) end, #{}, Components0),
+ emqx_utils_json:encode(
+ #{
+ info => SchemaInfo,
+ paths => ApiSpec,
+ components => #{schemas => Components}
+ },
+ [pretty, force_utf8]
+ ).
+
%%------------------------------------------------------------------------------
%% Private functions
%%------------------------------------------------------------------------------
@@ -482,6 +532,14 @@ maybe_add_summary_from_label(Spec, Hocon, Options) ->
get_i18n(Tag, ?DESC(Namespace, Id), Default, Options) ->
Lang = get_lang(Options),
+ case Lang of
+ ?SPECIAL_LANG_MSGID ->
+ make_msgid(Namespace, Id, Tag);
+ _ ->
+ get_i18n_text(Lang, Namespace, Id, Tag, Default)
+ end.
+
+get_i18n_text(Lang, Namespace, Id, Tag, Default) ->
case emqx_dashboard_desc_cache:lookup(Lang, Namespace, Id, Tag) of
undefined ->
Default;
@@ -489,6 +547,14 @@ get_i18n(Tag, ?DESC(Namespace, Id), Default, Options) ->
Text
end.
+%% Format:$msgid:Namespace.Id.Tag
+%% e.g. $msgid:emqx_schema.key.desc
+%% $msgid:emqx_schema.key.label
+%% if needed, the consumer of this schema JSON can use this msgid to
+%% resolve the text in the i18n database.
+make_msgid(Namespace, Id, Tag) ->
+ iolist_to_binary(["$msgid:", to_bin(Namespace), ".", to_bin(Id), ".", Tag]).
+
%% So far i18n_lang in options is only used at build time.
%% At runtime, it's still the global config which controls the language.
get_lang(#{i18n_lang := Lang}) -> Lang;
diff --git a/apps/emqx_dashboard/test/emqx_dashboard_schema_api_SUITE.erl b/apps/emqx_dashboard/test/emqx_dashboard_schema_api_SUITE.erl
new file mode 100644
index 000000000..e4425aed8
--- /dev/null
+++ b/apps/emqx_dashboard/test/emqx_dashboard_schema_api_SUITE.erl
@@ -0,0 +1,52 @@
+%%--------------------------------------------------------------------
+%% 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_dashboard_schema_api_SUITE).
+
+-compile(nowarn_export_all).
+-compile(export_all).
+
+-include_lib("emqx/include/http_api.hrl").
+
+-include_lib("eunit/include/eunit.hrl").
+
+-define(SERVER, "http://127.0.0.1:18083/api/v5").
+
+-import(emqx_mgmt_api_test_util, [request/2]).
+
+all() ->
+ emqx_common_test_helpers:all(?MODULE).
+
+init_per_suite(Config) ->
+ emqx_mgmt_api_test_util:init_suite([emqx_conf]),
+ Config.
+
+end_per_suite(_Config) ->
+ emqx_mgmt_api_test_util:end_suite([emqx_conf]).
+
+t_hotconf(_) ->
+ Url = ?SERVER ++ "/schemas/hotconf",
+ {ok, 200, Body} = request(get, Url),
+ %% assert it's a valid json
+ _ = emqx_utils_json:decode(Body),
+ ok.
+
+t_bridges(_) ->
+ Url = ?SERVER ++ "/schemas/bridges",
+ {ok, 200, Body} = request(get, Url),
+ %% assert it's a valid json
+ _ = emqx_utils_json:decode(Body),
+ ok.
diff --git a/apps/emqx_management/src/emqx_mgmt_api_status.erl b/apps/emqx_management/src/emqx_mgmt_api_status.erl
index 7d5c18e59..c0ee42e2b 100644
--- a/apps/emqx_management/src/emqx_mgmt_api_status.erl
+++ b/apps/emqx_management/src/emqx_mgmt_api_status.erl
@@ -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 ->
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 f0200c410..e8e0b4ac9 100644
--- a/apps/emqx_management/test/emqx_mgmt_api_status_SUITE.erl
+++ b/apps/emqx_management/test/emqx_mgmt_api_status_SUITE.erl
@@ -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.
diff --git a/build b/build
index 77d4dbfc8..05246a359 100755
--- a/build
+++ b/build
@@ -92,7 +92,7 @@ log() {
}
make_docs() {
- local libs_dir1 libs_dir2 libs_dir3 docdir dashboard_www_static
+ local libs_dir1 libs_dir2 libs_dir3 docdir
libs_dir1="$("$FIND" "_build/$PROFILE/lib/" -maxdepth 2 -name ebin -type d)"
if [ -d "_build/default/lib/" ]; then
libs_dir2="$("$FIND" "_build/default/lib/" -maxdepth 2 -name ebin -type d)"
@@ -113,14 +113,11 @@ make_docs() {
;;
esac
docdir="_build/docgen/$PROFILE"
- dashboard_www_static='apps/emqx_dashboard/priv/www/static/'
- mkdir -p "$docdir" "$dashboard_www_static"
+ mkdir -p "$docdir"
# shellcheck disable=SC2086
erl -noshell -pa $libs_dir1 $libs_dir2 $libs_dir3 -eval \
"ok = emqx_conf:dump_schema('$docdir', $SCHEMA_MODULE), \
halt(0)."
- cp "$docdir"/bridge-api-*.json "$dashboard_www_static"
- cp "$docdir"/hot-config-schema-*.json "$dashboard_www_static"
}
assert_no_compile_time_only_deps() {
diff --git a/rel/i18n/emqx_mgmt_api_status.hocon b/rel/i18n/emqx_mgmt_api_status.hocon
index 28278b747..2034d13bc 100644
--- a/rel/i18n/emqx_mgmt_api_status.hocon
+++ b/rel/i18n/emqx_mgmt_api_status.hocon
@@ -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:
+{
+ "rel_vsn": "v5.0.23",
+ "node_name": "emqx@127.0.0.1",
+ "broker_status": "started",
+ "app_status": "running"
+}
+
+Otherwise it returns free text strings as below:
+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."""
}
diff --git a/rel/i18n/zh/emqx_mgmt_api_status.hocon b/rel/i18n/zh/emqx_mgmt_api_status.hocon
index 3625db967..3938f47c1 100644
--- a/rel/i18n/zh/emqx_mgmt_api_status.hocon
+++ b/rel/i18n/zh/emqx_mgmt_api_status.hocon
@@ -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:
+{
+ "rel_vsn": "v5.0.23",
+ "node_name": "emqx@127.0.0.1",
+ "broker_status": "started",
+ "app_status": "running"
+}
+
+否则返回2行自由格式的文本,第一行描述节点的状态,第二行描述 EMQX 应用运行状态。例如:
+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 格式。"""
}
diff --git a/scripts/test/emqx-smoke-test.sh b/scripts/test/emqx-smoke-test.sh
index ce8116b39..44df5b5bd 100755
--- a/scripts/test/emqx-smoke-test.sh
+++ b/scripts/test/emqx-smoke-test.sh
@@ -2,42 +2,89 @@
set -euo pipefail
-[ $# -ne 2 ] && { echo "Usage: $0 ip port"; exit 1; }
+[ $# -ne 2 ] && { echo "Usage: $0 host port"; exit 1; }
-IP=$1
+HOST=$1
PORT=$2
-URL="http://$IP:$PORT/status"
+BASE_URL="http://$HOST:$PORT"
## Check if EMQX is responding
-ATTEMPTS=10
-while ! curl "$URL" >/dev/null 2>&1; do
- if [ $ATTEMPTS -eq 0 ]; then
- echo "emqx is not responding on $URL"
- exit 1
+wait_for_emqx() {
+ local attempts=10
+ local url="$BASE_URL"/status
+ while ! curl "$url" >/dev/null 2>&1; do
+ if [ $attempts -eq 0 ]; then
+ echo "emqx is not responding on $url"
+ exit 1
+ fi
+ sleep 5
+ attempts=$((attempts-1))
+ done
+}
+
+## Get the JSON format status which is jq friendly and includes a version string
+json_status() {
+ local url="${BASE_URL}/status?format=json"
+ local resp
+ resp="$(curl -s "$url")"
+ if (echo "$resp" | jq . >/dev/null 2>&1); then
+ echo "$resp"
+ else
+ echo 'NOT_JSON'
fi
- sleep 5
- ATTEMPTS=$((ATTEMPTS-1))
-done
+}
## Check if the API docs are available
-API_DOCS_URL="http://$IP:$PORT/api-docs/index.html"
-API_DOCS_STATUS="$(curl -s -o /dev/null -w "%{http_code}" "$API_DOCS_URL")"
-if [ "$API_DOCS_STATUS" != "200" ]; then
- echo "emqx is not responding on $API_DOCS_URL"
- exit 1
-fi
+check_api_docs() {
+ local url="$BASE_URL/api-docs/index.html"
+ local status
+ status="$(curl -s -o /dev/null -w "%{http_code}" "$url")"
+ if [ "$status" != "200" ]; then
+ echo "emqx is not responding on $API_DOCS_URL"
+ exit 1
+ fi
+}
## Check if the swagger.json contains hidden fields
## fail if it does
-SWAGGER_JSON_URL="http://$IP:$PORT/api-docs/swagger.json"
-## assert swagger.json is valid json
-JSON="$(curl -s "$SWAGGER_JSON_URL")"
-echo "$JSON" | jq . >/dev/null
-
-if [ "${EMQX_SMOKE_TEST_CHECK_HIDDEN_FIELDS:-yes}" = 'yes' ]; then
+check_swagger_json() {
+ local url="$BASE_URL/api-docs/swagger.json"
+ ## assert swagger.json is valid json
+ JSON="$(curl -s "$url")"
+ echo "$JSON" | jq . >/dev/null
## assert swagger.json does not contain trie_compaction (which is a hidden field)
if echo "$JSON" | grep -q trie_compaction; then
echo "swagger.json contains hidden fields"
exit 1
fi
-fi
+}
+
+check_schema_json() {
+ local name="$1"
+ local expected_title="$2"
+ local url="$BASE_URL/api/v5/schemas/$name"
+ local json
+ json="$(curl -s "$url" | jq .)"
+ title="$(echo "$json" | jq -r '.info.title')"
+ if [[ "$title" != "$expected_title" ]]; then
+ echo "unexpected value from GET $url"
+ echo "expected: $expected_title"
+ echo "got : $title"
+ exit 1
+ fi
+}
+
+main() {
+ wait_for_emqx
+ local JSON_STATUS
+ JSON_STATUS="$(json_status)"
+ check_api_docs
+ ## The json status feature was added after hotconf and bridges schema API
+ if [ "$JSON_STATUS" != 'NOT_JSON' ]; then
+ check_swagger_json
+ check_schema_json hotconf "EMQX Hot Conf API Schema"
+ check_schema_json bridges "EMQX Data Bridge API Schema"
+ fi
+}
+
+main