From 8d1d3032a35e40284800d807dc69e883ed752561 Mon Sep 17 00:00:00 2001 From: Janusz Jakubiec Date: Wed, 5 Oct 2022 08:22:27 +0200 Subject: [PATCH 1/4] Adding support for http calling of the graphql_server commands --- big_tests/tests/graphql_server_SUITE.erl | 91 +++++++++++++++++-- src/ejabberd_ctl.erl | 2 +- ...mongoose_graphql_server_admin_mutation.erl | 28 +++++- src/graphql/mongoose_graphql.erl | 13 ++- src/graphql/mongoose_graphql_commands.erl | 2 +- .../mongoose_graphql_cowboy_handler.erl | 2 +- 6 files changed, 125 insertions(+), 13 deletions(-) diff --git a/big_tests/tests/graphql_server_SUITE.erl b/big_tests/tests/graphql_server_SUITE.erl index 6745b79c76a..9d35afd9fa1 100644 --- a/big_tests/tests/graphql_server_SUITE.erl +++ b/big_tests/tests/graphql_server_SUITE.erl @@ -16,19 +16,24 @@ suite() -> require_rpc_nodes([mim]) ++ escalus:suite(). all() -> - [%{group, admin_http}, % http is not supported for the server category + [{group, admin_http}, {group, admin_cli}]. groups() -> - [{admin_http, [], admin_groups()}, - {admin_cli, [], admin_groups()}, + [{admin_http, [], admin_http_groups()}, + {admin_cli, [], admin_cli_groups()}, {server_tests, [], admin_tests()}, - {clustering_tests, [], clustering_tests()}]. + {clustering_tests, [], clustering_tests()}, + {clustering_http_tests, [], clustering_http_tests()}]. -admin_groups() -> +admin_cli_groups() -> [{group, server_tests}, {group, clustering_tests}]. +admin_http_groups() -> + [{group, server_tests}, + {group, clustering_http_tests}]. + admin_tests() -> [get_cookie_test, set_and_get_loglevel_test, @@ -45,6 +50,15 @@ clustering_tests() -> remove_node_test, stop_node_test]. +clustering_http_tests() -> + [join_successful_http, + leave_successful_http, + join_unsuccessful_http, + remove_dead_from_cluster_http, + remove_alive_from_cluster_http, + remove_node_test, + stop_node_test]. + init_per_suite(Config) -> Config1 = dynamic_modules:save_modules(host_type(), Config), Config2 = lists:foldl(fun(#{node := Node} = RPCNode, ConfigAcc) -> @@ -111,7 +125,9 @@ set_and_get_loglevel_test(Config) -> Value1 = get_ok_value([data, server, getLoglevel], get_loglevel(Config)), ?assertEqual(LogLevel, Value1) end, LogLevels), - ?assertEqual(<<"unknown_enum">>, get_err_code(set_loglevel(<<"AAAA">>, Config))). + {_, Res} = set_loglevel(<<"AAAA">>, Config), + [Res1] = maps:get(<<"errors">>, Res), + ?assertEqual(<<"unknown_enum">>, graphql_helper:get_value([extensions, code], Res1)). get_status_test(Config) -> Result = get_ok_value([data, server, status], get_status(Config)), @@ -198,6 +214,69 @@ stop_node_test(Config) -> mongoose_helper:wait_until(F, {badrpc, nodedown}, #{sleep_time => 1000, name => stop_node}), distributed_helper:start_node(Node3Nodename, Config). +join_successful_http(Config) -> + #{node := Node2} = RPCSpec2 = mim2(), + leave_cluster(Config), + timer:sleep(4500), + get_ok_value([], join_cluster(atom_to_binary(Node2), Config)), + timer:sleep(4500), + distributed_helper:verify_result(RPCSpec2, add). + +leave_successful_http(Config) -> + #{node := Node2} = RPCSpec2 = mim2(), + join_cluster(atom_to_binary(Node2), Config), + timer:sleep(4500), + get_ok_value([], leave_cluster(Config)), + timer:sleep(4500), + distributed_helper:verify_result(RPCSpec2, remove). + +join_unsuccessful_http(Config) -> + Node2 = mim2(), + get_ok_value([], join_cluster(<<>>, Config)), + timer:sleep(4500), + distributed_helper:verify_result(Node2, remove). + +remove_dead_from_cluster_http(Config) -> + % given + Timeout = timer:seconds(60), + #{node := Node1Nodename} = Node1 = mim(), + #{node := _Node2Nodename} = Node2 = mim2(), + #{node := Node3Nodename} = Node3 = mim3(), + ok = rpc(Node2#{timeout => Timeout}, mongoose_cluster, join, [Node1Nodename]), + ok = rpc(Node3#{timeout => Timeout}, mongoose_cluster, join, [Node1Nodename]), + %% when + distributed_helper:stop_node(Node3Nodename, Config), + get_ok_value([data, server, removeFromCluster], + remove_from_cluster(atom_to_binary(Node3Nodename), Config)), + timer:sleep(4500), + %% then + % node is down hence its not in mnesia cluster + have_node_in_mnesia(Node1, Node2, true), + have_node_in_mnesia(Node1, Node3, false), + have_node_in_mnesia(Node2, Node3, false), + % after node awakening nodes are clustered again + distributed_helper:start_node(Node3Nodename, Config), + timer:sleep(1000), + have_node_in_mnesia(Node1, Node3, true), + have_node_in_mnesia(Node2, Node3, true). + +remove_alive_from_cluster_http(Config) -> + % given + Timeout = timer:seconds(60), + #{node := Node1Name} = Node1 = mim(), + #{node := Node2Name} = Node2 = mim2(), + Node3 = mim3(), + ok = rpc(Node2#{timeout => Timeout}, mongoose_cluster, join, [Node1Name]), + ok = rpc(Node3#{timeout => Timeout}, mongoose_cluster, join, [Node1Name]), + %% when + %% Node2 is still running + %% then + get_ok_value([], remove_from_cluster(atom_to_binary(Node2Name), Config)), + timer:sleep(4500), + have_node_in_mnesia(Node1, Node3, true), + have_node_in_mnesia(Node1, Node2, false), + have_node_in_mnesia(Node3, Node2, false). + %----------------------------------------------------------------------- % Helpers %----------------------------------------------------------------------- diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 67427231f8c..34ef90ab40c 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -172,7 +172,7 @@ process(["mnesia", "info"]) -> process(["graphql", Arg]) when is_list(Arg) -> Doc = list_to_binary(Arg), Ep = mongoose_graphql:get_endpoint(admin), - Result = mongoose_graphql:execute(Ep, undefined, Doc), + Result = mongoose_graphql:execute_cli(Ep, undefined, Doc), handle_graphql_result(Result); process(["graphql" | _]) -> ?PRINT("This command requires one string type argument!\n", []), diff --git a/src/graphql/admin/mongoose_graphql_server_admin_mutation.erl b/src/graphql/admin/mongoose_graphql_server_admin_mutation.erl index e679c31a043..82c996df118 100644 --- a/src/graphql/admin/mongoose_graphql_server_admin_mutation.erl +++ b/src/graphql/admin/mongoose_graphql_server_admin_mutation.erl @@ -2,13 +2,14 @@ -behaviour(mongoose_graphql). -export([execute/4]). +-export([await_execution/4]). -import(mongoose_graphql_helper, [make_error/2]). -ignore_xref([execute/4]). -include("../mongoose_graphql_types.hrl"). -execute(_Ctx, server, <<"joinCluster">>, #{<<"node">> := Node}) -> +execute(#{method := cli}, server, <<"joinCluster">>, #{<<"node">> := Node}) -> case mongoose_server_api:join_cluster(binary_to_list(Node)) of {mnesia_error, _} = Error -> make_error(Error, #{cluster => Node}); @@ -20,14 +21,24 @@ execute(_Ctx, server, <<"joinCluster">>, #{<<"node">> := Node}) -> {_, String} -> {ok, String} end; -execute(_Ctx, server, <<"removeFromCluster">>, #{<<"node">> := Node}) -> +execute(#{method := http}, server, <<"joinCluster">>, #{<<"node">> := Node}) -> + spawn(?MODULE, await_execution, + [1000, mongoose_server_api, join_cluster, [binary_to_list(Node)]]), + {ok, "JoinCluster scheduled"}; + +execute(#{method := cli}, server, <<"removeFromCluster">>, #{<<"node">> := Node}) -> case mongoose_server_api:remove_from_cluster(binary_to_list(Node)) of {ok, _} = Result -> Result; Error -> make_error(Error, #{node => Node}) end; -execute(_Ctx, server, <<"leaveCluster">>, #{}) -> +execute(#{method := http}, server, <<"removeFromCluster">>, #{<<"node">> := Node}) -> + spawn(?MODULE, await_execution, + [1000, mongoose_server_api, remove_from_cluster, [binary_to_list(Node)]]), + {ok, "RemoveFromCluster scheduled"}; + +execute(#{method := cli}, server, <<"leaveCluster">>, #{}) -> case mongoose_server_api:leave_cluster() of {error, Message} -> make_error({internal_server_error, io_lib:format("~p", [Message])}, #{}); @@ -36,8 +47,13 @@ execute(_Ctx, server, <<"leaveCluster">>, #{}) -> {_, String} -> {ok, String} end; +execute(#{method := http}, server, <<"leaveCluster">>, #{}) -> + spawn(?MODULE, await_execution, [1000, mongoose_server_api, leave_cluster, []]), + {ok, "LeaveCluster scheduled"}; + execute(_Ctx, server, <<"removeNode">>, #{<<"node">> := Node}) -> mongoose_server_api:remove_node(binary_to_list(Node)); + execute(_Ctx, server, <<"setLoglevel">>, #{<<"level">> := LogLevel}) -> mongoose_server_api:set_loglevel(LogLevel); execute(_Ctx, server, <<"stop">>, #{}) -> @@ -46,3 +62,9 @@ execute(_Ctx, server, <<"stop">>, #{}) -> execute(_Ctx, server, <<"restart">>, #{}) -> spawn(mongoose_server_api, restart, []), {ok, "Restart scheduled"}. + +%% Helpers + +await_execution(Timeout, Module, Fun, Args) -> + timer:sleep(Timeout), + apply(Module, Fun, Args). diff --git a/src/graphql/mongoose_graphql.erl b/src/graphql/mongoose_graphql.erl index 8d8f2fe9d8f..57f42ae6f34 100644 --- a/src/graphql/mongoose_graphql.erl +++ b/src/graphql/mongoose_graphql.erl @@ -10,7 +10,8 @@ get_endpoint/1, create_endpoint/3, execute/2, - execute/3]). + execute/3, + execute_cli/3]). -ignore_xref([create_endpoint/3]). @@ -103,6 +104,16 @@ execute(Ep, OpName, Doc) -> ctx => #{}}, execute(Ep, Req). +-spec execute_cli(graphql:endpoint_context(), undefined | binary(), binary()) -> + {ok, map()} | {error, term()}. +execute_cli(Ep, OpName, Doc) -> + Req = #{document => Doc, + operation_name => OpName, + vars => #{}, + authorized => true, + ctx => #{method => cli}}, + execute(Ep, Req). + % Internal -spec schema_global_patterns(file:name_all()) -> [file:filename_all()]. diff --git a/src/graphql/mongoose_graphql_commands.erl b/src/graphql/mongoose_graphql_commands.erl index 3b1dac5394e..dce16741c2b 100644 --- a/src/graphql/mongoose_graphql_commands.erl +++ b/src/graphql/mongoose_graphql_commands.erl @@ -344,7 +344,7 @@ execute(Ep, Doc, Vars) -> operation_name => undefined, vars => Vars, authorized => true, - ctx => #{}}). + ctx => #{method => cli}}). field_type_query() -> nested_type_query("name kind possibleTypes {name kind}"). diff --git a/src/graphql/mongoose_graphql_cowboy_handler.erl b/src/graphql/mongoose_graphql_cowboy_handler.erl index 4682ff3edbe..7d830eb4049 100644 --- a/src/graphql/mongoose_graphql_cowboy_handler.erl +++ b/src/graphql/mongoose_graphql_cowboy_handler.erl @@ -175,7 +175,7 @@ run_request(#{} = ReqCtx, Req, #{schema_endpoint := EpName, authorized := AuthStatus} = State) -> Ep = mongoose_graphql:get_endpoint(EpName), Ctx = maps:get(schema_ctx, State, #{}), - ReqCtx2 = ReqCtx#{authorized => AuthStatus, ctx => Ctx}, + ReqCtx2 = ReqCtx#{authorized => AuthStatus, ctx => Ctx#{method => http}}, case mongoose_graphql:execute(Ep, ReqCtx2) of {ok, Response} -> ResponseBody = mongoose_graphql_response:term_to_json(Response), From 297677f200a12c4d8f3df86dfb7f787b498aa90c Mon Sep 17 00:00:00 2001 From: Janusz Jakubiec Date: Fri, 7 Oct 2022 08:56:19 +0200 Subject: [PATCH 2/4] Removint sleeps --- big_tests/tests/graphql_server_SUITE.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/big_tests/tests/graphql_server_SUITE.erl b/big_tests/tests/graphql_server_SUITE.erl index 9d35afd9fa1..fc78021207d 100644 --- a/big_tests/tests/graphql_server_SUITE.erl +++ b/big_tests/tests/graphql_server_SUITE.erl @@ -217,23 +217,20 @@ stop_node_test(Config) -> join_successful_http(Config) -> #{node := Node2} = RPCSpec2 = mim2(), leave_cluster(Config), - timer:sleep(4500), + distributed_helper:verify_result(RPCSpec2, remove), get_ok_value([], join_cluster(atom_to_binary(Node2), Config)), - timer:sleep(4500), distributed_helper:verify_result(RPCSpec2, add). leave_successful_http(Config) -> #{node := Node2} = RPCSpec2 = mim2(), join_cluster(atom_to_binary(Node2), Config), - timer:sleep(4500), + distributed_helper:verify_result(RPCSpec2, add), get_ok_value([], leave_cluster(Config)), - timer:sleep(4500), distributed_helper:verify_result(RPCSpec2, remove). join_unsuccessful_http(Config) -> Node2 = mim2(), get_ok_value([], join_cluster(<<>>, Config)), - timer:sleep(4500), distributed_helper:verify_result(Node2, remove). remove_dead_from_cluster_http(Config) -> From 0707398200023a4713ba927db8a7e4cb6510ae76 Mon Sep 17 00:00:00 2001 From: Janusz Jakubiec Date: Fri, 7 Oct 2022 09:44:41 +0200 Subject: [PATCH 3/4] Adding wait_until to remove_from_cluster tests --- big_tests/tests/distributed_helper.erl | 4 +-- big_tests/tests/graphql_server_SUITE.erl | 34 +++++++++++++++--------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/big_tests/tests/distributed_helper.erl b/big_tests/tests/distributed_helper.erl index 73783392fc2..57afeb7965d 100644 --- a/big_tests/tests/distributed_helper.erl +++ b/big_tests/tests/distributed_helper.erl @@ -53,8 +53,8 @@ verify_result(Node, Op) -> mongoose_helper:wait_until(fun() -> catch do_verify_result(Node, Op) end, [], #{ - time_left => timer:seconds(20), - sleep_time => 1000, + time_left => timer:seconds(30), + sleep_time => 3000, name => verify_result }). diff --git a/big_tests/tests/graphql_server_SUITE.erl b/big_tests/tests/graphql_server_SUITE.erl index fc78021207d..9e11bcde000 100644 --- a/big_tests/tests/graphql_server_SUITE.erl +++ b/big_tests/tests/graphql_server_SUITE.erl @@ -245,17 +245,13 @@ remove_dead_from_cluster_http(Config) -> distributed_helper:stop_node(Node3Nodename, Config), get_ok_value([data, server, removeFromCluster], remove_from_cluster(atom_to_binary(Node3Nodename), Config)), - timer:sleep(4500), - %% then - % node is down hence its not in mnesia cluster - have_node_in_mnesia(Node1, Node2, true), - have_node_in_mnesia(Node1, Node3, false), - have_node_in_mnesia(Node2, Node3, false), + have_node_in_mnesia_wait(Node1, Node2, true), + have_node_in_mnesia_wait(Node1, Node3, false), + have_node_in_mnesia_wait(Node2, Node3, false), % after node awakening nodes are clustered again distributed_helper:start_node(Node3Nodename, Config), - timer:sleep(1000), - have_node_in_mnesia(Node1, Node3, true), - have_node_in_mnesia(Node2, Node3, true). + have_node_in_mnesia_wait(Node1, Node3, true), + have_node_in_mnesia_wait(Node2, Node3, true). remove_alive_from_cluster_http(Config) -> % given @@ -269,15 +265,27 @@ remove_alive_from_cluster_http(Config) -> %% Node2 is still running %% then get_ok_value([], remove_from_cluster(atom_to_binary(Node2Name), Config)), - timer:sleep(4500), - have_node_in_mnesia(Node1, Node3, true), - have_node_in_mnesia(Node1, Node2, false), - have_node_in_mnesia(Node3, Node2, false). + have_node_in_mnesia_wait(Node1, Node3, true), + have_node_in_mnesia_wait(Node1, Node2, false), + have_node_in_mnesia_wait(Node3, Node2, false). %----------------------------------------------------------------------- % Helpers %----------------------------------------------------------------------- +have_node_in_mnesia_wait(Node1, #{node := Node2}, Value) -> + mongoose_helper:wait_until(fun() -> + DbNodes1 = distributed_helper:rpc(Node1, mnesia, + system_info, [db_nodes]), + lists:member(Node2, DbNodes1) + end, + Value, + #{ + time_left => timer:seconds(5), + sleep_time => 1000, + name => have_node_in_mnesia + }). + all_log_levels() -> [<<"NONE">>, <<"EMERGENCY">>, From f0ee4f0c47ed13c8c0c311553ed50d2ab5d43e3a Mon Sep 17 00:00:00 2001 From: Janusz Jakubiec Date: Tue, 11 Oct 2022 12:10:34 +0200 Subject: [PATCH 4/4] Changing distributed_helper:verify_result to wait for mongooseim to start --- big_tests/tests/distributed_helper.erl | 18 +++++++++++------- big_tests/tests/graphql_server_SUITE.erl | 6 +++++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/big_tests/tests/distributed_helper.erl b/big_tests/tests/distributed_helper.erl index 57afeb7965d..b16d97d742a 100644 --- a/big_tests/tests/distributed_helper.erl +++ b/big_tests/tests/distributed_helper.erl @@ -50,13 +50,17 @@ script_path(Node, Config, Script) -> filename:join([get_cwd(Node, Config), "bin", Script]). verify_result(Node, Op) -> - mongoose_helper:wait_until(fun() -> catch do_verify_result(Node, Op) end, - [], - #{ - time_left => timer:seconds(30), - sleep_time => 3000, - name => verify_result - }). + mongoose_helper:wait_until(fun() -> catch do_verify_result(Node, Op) end, [], + #{time_left => timer:seconds(20), + sleep_time => 1000, + name => verify_result}), + mongoose_helper:wait_until(fun() -> check_mongooseim_on_node_started(mim()) end, true, + #{time_left => timer:seconds(20), + sleep_time => 1000, + name => verify_mongooseim_started}). + +check_mongooseim_on_node_started(Node) -> + lists:keymember(mongooseim, 1, rpc(Node, application, which_applications, [])). do_verify_result(Node, Op) -> VerifyNode = mim(), diff --git a/big_tests/tests/graphql_server_SUITE.erl b/big_tests/tests/graphql_server_SUITE.erl index 9e11bcde000..94d90d391d0 100644 --- a/big_tests/tests/graphql_server_SUITE.erl +++ b/big_tests/tests/graphql_server_SUITE.erl @@ -102,11 +102,15 @@ init_per_testcase(CaseName, Config) -> end_per_testcase(CaseName, Config) when CaseName == join_successful orelse CaseName == leave_unsuccessful + orelse CaseName == join_successful_http + orelse CaseName == leave_unsuccessful_http orelse CaseName == join_twice -> remove_node_from_cluster(mim2(), Config), escalus:end_per_testcase(CaseName, Config); end_per_testcase(CaseName, Config) when CaseName == remove_alive_from_cluster - orelse CaseName == remove_dead_from_cluster -> + orelse CaseName == remove_dead_from_cluster + orelse CaseName == remove_alive_from_cluster_http + orelse CaseName == remove_dead_from_cluster_http -> remove_node_from_cluster(mim3(), Config), remove_node_from_cluster(mim2(), Config), escalus:end_per_testcase(CaseName, Config);