From dd20cf33309de41c16f51265f29080684e7c8343 Mon Sep 17 00:00:00 2001 From: Tobias Lindahl Date: Wed, 6 May 2020 08:25:44 +0200 Subject: [PATCH 01/12] Add REST endpoint /health --- apps/roster/src/rest/rest_cowboy_handler.erl | 1 + .../src/rest/rest_cowboy_health_handler.erl | 105 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 apps/roster/src/rest/rest_cowboy_health_handler.erl diff --git a/apps/roster/src/rest/rest_cowboy_handler.erl b/apps/roster/src/rest/rest_cowboy_handler.erl index 942a84ecf..7668463d1 100644 --- a/apps/roster/src/rest/rest_cowboy_handler.erl +++ b/apps/roster/src/rest/rest_cowboy_handler.erl @@ -55,6 +55,7 @@ routes() -> , {"/cri/bubbles", rest_cowboy_cri_handler, basic_state(cri_bubble)} , {"/test/", rest_cowboy_test_handler, basic_state(test_api)} , {"/link/:link_id/room_id", rest_cowboy_link_handler, basic_state(link_room)} + , {"/health/", rest_cowboy_health_handler, basic_state(health)} , {"/api/v1/groups/:room_id/messages/csv", rest_cowboy_csv_handler, token_state(groups_csv)} , {"/api/v1/chats/:phone_id/messages/csv", diff --git a/apps/roster/src/rest/rest_cowboy_health_handler.erl b/apps/roster/src/rest/rest_cowboy_health_handler.erl new file mode 100644 index 000000000..8c3994ddd --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_health_handler.erl @@ -0,0 +1,105 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /health/ endpoint +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_health_handler). +-include_lib("kernel/include/logger.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_provided/2 + , init/2 + , is_authorized/2 + ]). + +%% Custom callbacks +-export([ health_to_json/2 + ]). + + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, #{endpoint := health} = State) -> + Headers = cowboy_req:headers(Req), + CT = maps:get(<<"content-type">>, Headers, <<"no-content-type">>), + ?LOG_INFO("~s:~s:~s ~p", [Method, Path, CT, QS]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, State) -> + {[<<"GET">>], Req, State}. + +content_types_provided(Req, State) -> + {[{<<"application/json">>, health_to_json} + ], Req, State}. + +health_to_json(Req, State) -> + Apps = [X || X <- application:which_applications(), + X =:= roster orelse X =:= emqttd], + Status = #{ db_state => db_state() + , emq_state => emq_state(Apps) + , roster_state => roster_state(Apps) + }, + Content = jsx:encode(Status#{accept_traffic => accept_traffic(Status)}), + {Content, Req, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +db_state() -> + case mnesia:system_info(is_running) of + yes -> + case mnesia:system_info(local_tables) of + [] -> + not_ready; + Tables -> + case mnesia:wait_for_tables(Tables, 0) of + ok -> ready; + {timeout, _} -> not_ready + end + end; + starting -> + not_ready; + stopping -> + not_ready; + no -> + not_ready + end. + +roster_state(Apps) -> + %%TODO: Should probably be a deeper check. + case lists:member(roster, Apps) of + false -> + not_ready; + true -> + ready + end. + +emq_state(Apps) -> + %%TODO: Should probably be a deeper check. + case lists:member(emqttd, Apps) of + false -> not_ready; + true -> ready + end. + +accept_traffic(#{ db_state := ready + , emq_state := ready + , roster_state := ready + }) -> + case application:get_env(roster, accept_traffic, undefined) of + undefined -> true; + false -> false; + true -> true + end; +accept_traffic(#{ db_state := _ + , emq_state := _ + , roster_state := _ + }) -> + false. + -- GitLab From bfbf7be39f702a67e247ad23ad1ec010091ee51d Mon Sep 17 00:00:00 2001 From: Tobias Lindahl Date: Tue, 12 May 2020 08:59:39 +0200 Subject: [PATCH 02/12] Add spec for health endpoint --- spec/nynja_rest.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/spec/nynja_rest.yaml b/spec/nynja_rest.yaml index 85b079def..59cc246ab 100644 --- a/spec/nynja_rest.yaml +++ b/spec/nynja_rest.yaml @@ -1052,6 +1052,31 @@ paths: content: application/json: + /health: + get: + operationId: 'GetHealth' + description: 'Get health parameters' + parameters: + responses: + '200': + content: + application/json: + schema: + type: object + properties: + db_state: + type: string + description: 'ready | not_ready' + emq_state: + type: string + description: 'ready | not_ready' + roster_state: + type: string + description: 'ready | not_ready' + '401': + description: 'Unauhorized' + content: + text/plain: components: schemas: -- GitLab From f22800ee24c47874f3d70fc0e09d95d65a4fbfb0 Mon Sep 17 00:00:00 2001 From: Tobias Lindahl Date: Tue, 12 May 2020 09:04:33 +0200 Subject: [PATCH 03/12] Add parameter accept_traffic to health endpoint --- spec/nynja_rest.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/nynja_rest.yaml b/spec/nynja_rest.yaml index 59cc246ab..b9b2aa83a 100644 --- a/spec/nynja_rest.yaml +++ b/spec/nynja_rest.yaml @@ -1064,6 +1064,9 @@ paths: schema: type: object properties: + accept_traffic: + type: boolean + description: 'Ready to accept traffic' db_state: type: string description: 'ready | not_ready' -- GitLab From ca7bf4660ae253e8ca469065c9f138e4b1912af2 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Tue, 12 May 2020 09:10:19 +0200 Subject: [PATCH 04/12] update spec --- spec/nynja_rest.yaml | 88 +++++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/spec/nynja_rest.yaml b/spec/nynja_rest.yaml index b9b2aa83a..54206ae16 100644 --- a/spec/nynja_rest.yaml +++ b/spec/nynja_rest.yaml @@ -49,6 +49,8 @@ paths: /sessions: get: operationId: 'GetSessions' + security: + - basicAuth:[] parameters: - in: query name: phone @@ -747,7 +749,8 @@ paths: description: "Not found" content: text/plain: - content: + schema: + type: string application/json: schema: type: object @@ -961,6 +964,8 @@ paths: get: operationId: 'ChatsCSV' description: 'Get csv of all chats to user with phone_id (Authentication gives from is)' + security: + - bearerAuth: [] parameters: - in: path name: phone_id @@ -1057,29 +1062,31 @@ paths: operationId: 'GetHealth' description: 'Get health parameters' parameters: - responses: - '200': - content: - application/json: - schema: - type: object - properties: - accept_traffic: - type: boolean - description: 'Ready to accept traffic' - db_state: - type: string - description: 'ready | not_ready' - emq_state: - type: string - description: 'ready | not_ready' - roster_state: - type: string - description: 'ready | not_ready' - '401': - description: 'Unauhorized' - content: - text/plain: + responses: + '200': + content: + application/json: + schema: + type: object + properties: + accept_traffic: + type: boolean + description: 'Ready to accept traffic' + db_state: + type: string + description: 'ready | not_ready' + emq_state: + type: string + description: 'ready | not_ready' + roster_state: + type: string + description: 'ready | not_ready' + '401': + description: 'Unauhorized' + content: + text/plain: + schema: + type: string components: schemas: @@ -1103,7 +1110,7 @@ components: properties: created: type: string - formst: date-time + format: date-time phone: type: integer @@ -1123,3 +1130,34 @@ components: RoomId: type: object + + responses: + NotFound: + description: "Not found" + content: + text/plain: + schema: + type: string + application/json: + schema: + type: object + properties: + status: + type: string + data: + type: string + Unauthorized: + description: Unauthorized + content: + test/plain: + schema: + type: string + + securitySchemes: + bearerAuth: # arbitrary name for the security scheme + type: http + scheme: bearer + bearerFormat: JWT + basicAuth: # <-- arbitrary name for the security scheme + type: http + scheme: basic -- GitLab From 184a81d2b257db3b7b8e94c6ca65ac11a5d6257e Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Tue, 12 May 2020 09:15:35 +0200 Subject: [PATCH 05/12] New api_nynja --- test/api_nynja.erl | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/test/api_nynja.erl b/test/api_nynja.erl index bf858c121..43d1e1cd3 100644 --- a/test/api_nynja.erl +++ b/test/api_nynja.erl @@ -1,6 +1,6 @@ %% coding: latin-1 %% This code is generated from ../server/spec/nynja_rest.yaml -%% on 2020-05-11 8:39:07 UTC +%% on 2020-05-12 7:14:17 UTC %% Using openapi rebar3 plugin version: #6ad56be %% Do not manually change this code! %% @@ -32,7 +32,7 @@ definitions() -> {"/components/schemas/PhoneEntry", #{<<"properties">> => #{<<"created">> => - #{<<"formst">> => <<"date-time">>,<<"type">> => <<"string">>}, + #{<<"format">> => <<"date-time">>,<<"type">> => <<"string">>}, <<"phone">> => #{<<"type">> => <<"integer">>}}, <<"type">> => <<"object">>}}, {"/components/schemas/Session", @@ -156,7 +156,15 @@ operations() -> <<"status">> => #{<<"type">> => <<"integer">>}}, <<"type">> => <<"object">>}}}], - 400 => [{"text/plain",undefined}], + 400 => + [{"application/json", + #{schema => + #{<<"properties">> => + #{<<"data">> => #{<<"type">> => <<"string">>}, + <<"status">> => + #{<<"type">> => <<"string">>}}, + <<"type">> => <<"object">>}}}, + {"text/plain",#{schema => #{<<"type">> => <<"string">>}}}], 401 => [{"text/plain",undefined}], 403 => [{"text/plain",undefined}]}, tags => []}, @@ -191,6 +199,33 @@ operations() -> #{method => get,parameters => [],path => <<"/fake_numbers">>, responses => #{200 => [{"text/html",undefined}]}, tags => []}, + 'GetHealth' => + #{method => get,parameters => [],path => <<"/health">>, + responses => + #{200 => + [{"application/json", + #{schema => + #{<<"properties">> => + #{<<"accept_traffic">> => + #{<<"description">> => + <<"Ready to accept traffic">>, + <<"type">> => <<"boolean">>}, + <<"db_state">> => + #{<<"description">> => + <<"ready | not_ready">>, + <<"type">> => <<"string">>}, + <<"emq_state">> => + #{<<"description">> => + <<"ready | not_ready">>, + <<"type">> => <<"string">>}, + <<"roster_state">> => + #{<<"description">> => + <<"ready | not_ready">>, + <<"type">> => <<"string">>}}, + <<"type">> => <<"object">>}}}], + 401 => + [{"text/plain",#{schema => #{<<"type">> => <<"string">>}}}]}, + tags => []}, 'GetMembersCRIRoom' => #{method => get, parameters => -- GitLab From 7471556f532dc22be44782800bf1dfcbfe20b997 Mon Sep 17 00:00:00 2001 From: Tobias Lindahl Date: Tue, 12 May 2020 11:23:49 +0200 Subject: [PATCH 06/12] Upgrade to latest emqttd (process registration) --- rebar.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.lock b/rebar.lock index 722d41f1d..d4cf166bb 100644 --- a/rebar.lock +++ b/rebar.lock @@ -43,7 +43,7 @@ 0}, {<<"emqttd">>, {git,"git://github.com/NYNJA-MC/emqttd", - {ref,"44352b2d4ea3fa0d41120c4310b149a0ca66a18a"}}, + {ref,"364bf8d4979884fc5e9e765c0fa92d97b78a8fa9"}}, 0}, {<<"enenra">>, {git,"https://github.com/nlfiedler/enenra", -- GitLab From a4809d23462dfb84669e575733031d20efa22b5b Mon Sep 17 00:00:00 2001 From: Tobias Lindahl Date: Tue, 12 May 2020 11:25:55 +0200 Subject: [PATCH 07/12] Better checks for health endpoint --- .../roster/src/rest/rest_cowboy_health_handler.erl | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/roster/src/rest/rest_cowboy_health_handler.erl b/apps/roster/src/rest/rest_cowboy_health_handler.erl index 8c3994ddd..9808e257c 100644 --- a/apps/roster/src/rest/rest_cowboy_health_handler.erl +++ b/apps/roster/src/rest/rest_cowboy_health_handler.erl @@ -39,11 +39,9 @@ content_types_provided(Req, State) -> ], Req, State}. health_to_json(Req, State) -> - Apps = [X || X <- application:which_applications(), - X =:= roster orelse X =:= emqttd], Status = #{ db_state => db_state() - , emq_state => emq_state(Apps) - , roster_state => roster_state(Apps) + , emq_state => emq_state() + , roster_state => roster_state() }, Content = jsx:encode(Status#{accept_traffic => accept_traffic(Status)}), {Content, Req, State}. @@ -72,18 +70,18 @@ db_state() -> not_ready end. -roster_state(Apps) -> +roster_state() -> %%TODO: Should probably be a deeper check. - case lists:member(roster, Apps) of + case lists:keymember(roster, 1, application:which_applications()) of false -> not_ready; true -> ready end. -emq_state(Apps) -> +emq_state() -> %%TODO: Should probably be a deeper check. - case lists:member(emqttd, Apps) of + case emqttd:is_running(node()) of false -> not_ready; true -> ready end. -- GitLab From 3ae40585bb29982fc0547a6ae6054915f283fea8 Mon Sep 17 00:00:00 2001 From: Tobias Lindahl Date: Tue, 12 May 2020 11:26:41 +0200 Subject: [PATCH 08/12] Add rudimentary test for health endpoint --- test/server_SUITE.erl | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/server_SUITE.erl b/test/server_SUITE.erl index 500de8b59..ac3c7d034 100644 --- a/test/server_SUITE.erl +++ b/test/server_SUITE.erl @@ -31,6 +31,7 @@ , room_chat_message/1 , room_chat_message_errors/1 , call_bubble_message/1 + , health_endpoint_test/1 ]). all() -> @@ -43,7 +44,8 @@ groups() -> p2p_chat_message_get, p2p_chat_message_get_errors, p2p_chat_message_post, p2p_chat_message_post_errors, room_chat_message, room_chat_message_errors, - create_and_join_room_cri, call_bubble_message + create_and_join_room_cri, call_bubble_message, + health_endpoint_test ]} ]. @@ -813,6 +815,24 @@ wait_for_call_bubble(User, StartTime, FeedId, N) -> wait_for_call_bubble(User, StartTime, FeedId, N - 1) end. +health_endpoint_test(Cfg) -> + %% No basic auth + {ok, 401, _} + = request('GetHealth', + #{}, Cfg), + + %% Correct response + {ok, 200, #{ db_state := _ + , emq_state := _ + , roster_state := _ + , accept_traffic := _ + }} + = request('GetHealth', + #{}, [basic_auth()|Cfg]), + + ok. + + %%% ------------- helpers --------------------- register_fresh_user() -> -- GitLab From 54e82588db4c5dd282033182bbcd0a6d37ec7c6e Mon Sep 17 00:00:00 2001 From: Tobias Lindahl Date: Tue, 12 May 2020 19:46:01 +0200 Subject: [PATCH 09/12] Make job log as well as print --- apps/roster/src/processes/job.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/roster/src/processes/job.erl b/apps/roster/src/processes/job.erl index 6418d14de..aa83e085e 100644 --- a/apps/roster/src/processes/job.erl +++ b/apps/roster/src/processes/job.erl @@ -2,6 +2,7 @@ %-include_lib("kvs/include/feed.hrl"). -include("roster.hrl"). -include_lib("kvs/include/user.hrl"). +-include_lib("kernel/include/logger.hrl"). -compile(export_all). -define(QUANT, 500). @@ -11,6 +12,7 @@ def(J) -> job_process:definition(J). action({request,'Init'}, Proc) -> + ?LOG_INFO("Action Init"), io:format("Action Init~n"), {reply,Proc}; @@ -22,10 +24,12 @@ action({request,'Init'}, Proc) -> %% false -> {reply,'Update',Proc} end; action({request,'Stop'}, Proc) -> - io:format("Stop Process~n"), - {reply,'Action',Proc}; + ?LOG_INFO("Stop Process"), + io:format("Stop Process~n"), + {reply,'Action',Proc}; action({request,'Timeout'}, #process{id=_Id}=Proc) -> + ?LOG_INFO("Timeout Process"), io:format("Timeout Process~n"), %#process{options=Opts}=bpe:load(Id), case bpe:doc(#'Schedule'{},Proc) of @@ -41,6 +45,7 @@ action({request,'Timeout'}, #process{id=_Id}=Proc) -> %% {reply,Proc}; action({request,'Action'}, #process{}=Proc) -> + ?LOG_INFO("Action Process"), io:format("Action Process ~n"), %C= proplists:get_value(send_pid,Opts,undefined), CTimeout= case lists:keytake(timeoutEvent,1,bpe:events(Proc)) of -- GitLab From 3001b090fa5da505a367c4e55587f63859f404c3 Mon Sep 17 00:00:00 2001 From: Tobias Lindahl Date: Wed, 13 May 2020 10:16:30 +0200 Subject: [PATCH 10/12] Add release version in health endpoint --- apps/roster/src/rest/rest_cowboy_health_handler.erl | 8 ++++++++ spec/nynja_rest.yaml | 2 ++ test/server_SUITE.erl | 9 +++++---- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/apps/roster/src/rest/rest_cowboy_health_handler.erl b/apps/roster/src/rest/rest_cowboy_health_handler.erl index 9808e257c..4b61734f3 100644 --- a/apps/roster/src/rest/rest_cowboy_health_handler.erl +++ b/apps/roster/src/rest/rest_cowboy_health_handler.erl @@ -42,6 +42,7 @@ health_to_json(Req, State) -> Status = #{ db_state => db_state() , emq_state => emq_state() , roster_state => roster_state() + , release_version => release_version() }, Content = jsx:encode(Status#{accept_traffic => accept_traffic(Status)}), {Content, Req, State}. @@ -101,3 +102,10 @@ accept_traffic(#{ db_state := _ }) -> false. +release_version() -> + case release_handler:which_releases(permanent) of + [{"server", VsnString, _Libs, permanent}] -> + iolist_to_binary(VsnString); + _ -> + <<"unknown">> + end. diff --git a/spec/nynja_rest.yaml b/spec/nynja_rest.yaml index 54206ae16..5f2123dce 100644 --- a/spec/nynja_rest.yaml +++ b/spec/nynja_rest.yaml @@ -1081,6 +1081,8 @@ paths: roster_state: type: string description: 'ready | not_ready' + release_version: + type: string '401': description: 'Unauhorized' content: diff --git a/test/server_SUITE.erl b/test/server_SUITE.erl index ac3c7d034..369584cb7 100644 --- a/test/server_SUITE.erl +++ b/test/server_SUITE.erl @@ -822,10 +822,11 @@ health_endpoint_test(Cfg) -> #{}, Cfg), %% Correct response - {ok, 200, #{ db_state := _ - , emq_state := _ - , roster_state := _ - , accept_traffic := _ + {ok, 200, #{ db_state := _ + , emq_state := _ + , roster_state := _ + , accept_traffic := _ + , release_version := _ }} = request('GetHealth', #{}, [basic_auth()|Cfg]), -- GitLab From dee047e639a57e172fa30749f070ce5e0a7856df Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Wed, 13 May 2020 10:32:07 +0200 Subject: [PATCH 11/12] Update api_nynja.erl --- test/api_nynja.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/api_nynja.erl b/test/api_nynja.erl index 43d1e1cd3..a153e8b3b 100644 --- a/test/api_nynja.erl +++ b/test/api_nynja.erl @@ -1,6 +1,6 @@ %% coding: latin-1 %% This code is generated from ../server/spec/nynja_rest.yaml -%% on 2020-05-12 7:14:17 UTC +%% on 2020-05-13 8:31:46 UTC %% Using openapi rebar3 plugin version: #6ad56be %% Do not manually change this code! %% @@ -218,6 +218,8 @@ operations() -> #{<<"description">> => <<"ready | not_ready">>, <<"type">> => <<"string">>}, + <<"release_version">> => + #{<<"type">> => <<"string">>}, <<"roster_state">> => #{<<"description">> => <<"ready | not_ready">>, -- GitLab From 48cc61676ab4dc775b9e22bced4672eae1105173 Mon Sep 17 00:00:00 2001 From: Tobias Lindahl Date: Wed, 13 May 2020 10:46:49 +0200 Subject: [PATCH 12/12] Set default reporting of health endpoint to accept traffic --- apps/roster/src/rest/rest_cowboy_health_handler.erl | 2 +- sys.config | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/roster/src/rest/rest_cowboy_health_handler.erl b/apps/roster/src/rest/rest_cowboy_health_handler.erl index 4b61734f3..a53335a32 100644 --- a/apps/roster/src/rest/rest_cowboy_health_handler.erl +++ b/apps/roster/src/rest/rest_cowboy_health_handler.erl @@ -91,7 +91,7 @@ accept_traffic(#{ db_state := ready , emq_state := ready , roster_state := ready }) -> - case application:get_env(roster, accept_traffic, undefined) of + case application:get_env(roster, health_endpoint_accept_traffic, undefined) of undefined -> true; false -> false; true -> true diff --git a/sys.config b/sys.config index 08ff1f643..49650bba3 100644 --- a/sys.config +++ b/sys.config @@ -40,6 +40,7 @@ {review,[{host,"ns.synrc.com"}]}, {roster, [ + {health_endpoint_accept_traffic, true}, {freeze_time, 1000}, {upload,"./storage"}, {db_backup, "./backup"}, -- GitLab