Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error handling in mnesia API #3896

Merged
merged 2 commits into from
Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 56 additions & 4 deletions big_tests/tests/graphql_mnesia_SUITE.erl
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
-module(graphql_mnesia_SUITE).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").

-compile([export_all, nowarn_export_all]).

-import(distributed_helper, [require_rpc_nodes/1, mim/0, mim2/0, rpc/4]).
-import(domain_helper, [host_type/1]).
-import(mongooseimctl_helper, [rpc_call/3]).
-import(graphql_helper, [execute_command/4, execute_user_command/5, user_to_bin/1,
get_ok_value/2, get_err_code/1, get_err_value/2, get_unauthorized/1]).
get_ok_value/2, get_err_code/1, get_err_value/2, get_unauthorized/1,
get_coercion_err_msg/1]).

-record(mnesia_table_test, {key :: integer(), name :: binary()}).
-record(vcard, {us, vcard}).
Expand All @@ -31,19 +31,27 @@ admin_mnesia_tests() ->
dump_mnesia_file_error_test,
backup_and_restore_test,
backup_wrong_filename_test,
backup_wrong_path_test,
restore_no_file_test,
restore_wrong_file_format_test,
restore_bad_file_test,
restore_bad_path_test,
load_mnesia_test,
load_mnesia_no_file_test,
load_mnesia_bad_file_test,
load_mnesia_bad_file2_test,
change_nodename_test,
change_nodename_bad_name,
change_nodename_empty_name,
change_nodename_no_file_error_test,
change_nodename_bad_file_error_test,
get_info_test,
get_all_info_test,
install_fallback_error_test,
set_master_test].
set_master_test,
set_master_self_test,
set_master_bad_name_test,
set_master_empty_name_test].

domain_admin_tests() ->
[domain_admin_dump_mnesia_table_test,
Expand Down Expand Up @@ -132,10 +140,22 @@ backup_wrong_filename_test(Config) ->
Res = backup_mnesia(<<>>, Config),
?assertEqual(<<"wrong_filename">>, get_err_code(Res)).

backup_wrong_path_test(Config) ->
Res = backup_mnesia(<<"/etc/">>, Config),
?assertEqual(<<"cannot_backup">>, get_err_code(Res)).

restore_no_file_test(Config) ->
Res = restore_mnesia(<<>>, Config),
?assertEqual(<<"file_not_found">>, get_err_code(Res)).

restore_bad_file_test(Config) ->
Res = restore_mnesia(<<"NON_EXISTING">>, Config),
?assertEqual(<<"file_not_found">>, get_err_code(Res)).

restore_bad_path_test(Config) ->
Res = restore_mnesia(<<"/etc/">>, Config),
?assertEqual(<<"cannot_restore">>, get_err_code(Res)).

restore_wrong_file_format_test(Config) ->
Filename = <<"restore_error">>,
FileFullPath = create_full_filename(Filename),
Expand Down Expand Up @@ -185,6 +205,22 @@ change_nodename_test(Config) ->
check_if_response_contains(Value,
<<"Name of the node in the backup was successfully changed">>).

change_nodename_bad_name(Config) ->
Filename1 = <<"change_incorrect_nodename_mnesia_test">>,
Filename2 = <<"change_incorrect_nodename2_mnesia_test">>,
ChangeFrom = <<"mongooseim@localhost">>,
ChangeTo = <<"incorrect_format">>,
Value = change_nodename(ChangeFrom, ChangeTo, Filename1, Filename2, Config),
get_coercion_err_msg(Value).

change_nodename_empty_name(Config) ->
Filename1 = <<"change_incorrect_nodename_mnesia_test">>,
Filename2 = <<"change_incorrect_nodename2_mnesia_test">>,
ChangeFrom = <<"mongooseim@localhost">>,
ChangeTo = <<>>,
Value = change_nodename(ChangeFrom, ChangeTo, Filename1, Filename2, Config),
get_coercion_err_msg(Value).

change_nodename_no_file_error_test(Config) ->
Filename1 = <<"non_existing">>,
Filename2 = <<"change_nodename2_mnesia_test">>,
Expand Down Expand Up @@ -232,6 +268,18 @@ set_master_test(Config) ->
ParsedRes = get_ok_value([data, mnesia, setMaster], set_master(mim(), Config)),
?assertEqual(<<"Master node set">>, ParsedRes).

set_master_self_test(Config) ->
ParsedRes = get_ok_value([data, mnesia, setMaster], set_master(#{node => self}, Config)),
?assertEqual(<<"Master node set">>, ParsedRes).

set_master_bad_name_test(Config) ->
Res = set_master(#{node => incorrect_name}, Config),
get_coercion_err_msg(Res).

set_master_empty_name_test(Config) ->
Res = set_master(#{node => ''}, Config),
get_coercion_err_msg(Res).

% Domain admin tests

domain_admin_dump_mnesia_table_test(Config) ->
Expand All @@ -250,7 +298,11 @@ domain_admin_load_mnesia_test(Config) ->
get_unauthorized(load_mnesia(<<"Path">>, Config)).

domain_admin_change_nodename_test(Config) ->
get_unauthorized(change_nodename(<<"From">>, <<"To">>, <<"file1">>, <<"file2">>, Config)).
Filename1 = <<"change_nodename_mnesia_test">>,
Filename2 = <<"change_nodename2_mnesia_test">>,
ChangeFrom = <<"mongooseim@localhost">>,
ChangeTo = <<"change_nodename_test@localhost">>,
get_unauthorized(change_nodename(ChangeFrom, ChangeTo, Filename1, Filename2, Config)).

domain_admin_install_fallback_test(Config) ->
get_unauthorized(install_fallback(<<"Path">>, Config)).
Expand Down
4 changes: 2 additions & 2 deletions priv/graphql/schemas/admin/mnesia.gql
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ Allow admin to backup, dump, load, restore and modify mnesia database
"""
type MnesiaAdminMutation @protected{
"Set mnesia's master node"
setMaster(node: String!): String
setMaster(node: NodeName!): String
@protected(type: GLOBAL)
"Change nodename from 'fromString' to 'toString' in 'source' backup file and create new 'target' backup file"
changeNodename(fromString: String!, toString: String!,
changeNodename(fromString: NodeName!, toString: NodeName!,
source: String!, target: String!): String
@protected(type: GLOBAL)
"Save mnesia backup to file 'path'"
Expand Down
2 changes: 2 additions & 0 deletions priv/graphql/schemas/global/scalar_types.gql
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ scalar RoomName @spectaql(options: [{ key: "example", value: "my-chat-room" }])
scalar DomainName @spectaql(options: [{ key: "example", value: "localhost" }])
"XMPP resource name (resource part of a JID)"
scalar ResourceName @spectaql(options: [{ key: "example", value: "res1" }])
"Name of the Erlang node. The value **self** is used to refer to the current node"
scalar NodeName @spectaql(options: [{key: "example", value: "mynode@localhost"}])
"String that contains at least one character"
scalar NonEmptyString @spectaql(options: [{ key: "example", value: "xyz789" }])
"Integer that has a value above zero"
Expand Down
4 changes: 2 additions & 2 deletions src/ejabberd_admin.erl
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ commands() ->
longdesc = "If you provide as nodename \"self\", this "
"node will be set as its own master.",
module = mnesia_api, function = set_master,
args = [{nodename, string}], result = {res, restuple}},
args = [{nodename, atom}], result = {res, restuple}},
#ejabberd_commands{name = mnesia_change_nodename, tags = [mnesia],
desc = "Change the erlang node name in a backup file",
module = mnesia_api, function = mnesia_change_nodename,
args = [{oldnodename, string}, {newnodename, string},
args = [{oldnodename, atom}, {newnodename, atom},
{oldbackup, string}, {newbackup, string}],
result = {res, restuple}},
#ejabberd_commands{name = backup, tags = [mnesia],
Expand Down
2 changes: 1 addition & 1 deletion src/ejabberd_commands.erl
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@
-include("mongoose.hrl").

%% Allowed types for arguments are integer, string, tuple and list.
-type atype() :: integer | string | binary | {tuple, [aterm()]} | {list, aterm()}.
-type atype() :: integer | string | atom | binary | {tuple, [aterm()]} | {list, aterm()}.

%% A rtype is either an atom or a tuple with two elements.
-type rtype() :: integer | string | atom | binary | {tuple, [rterm()]}
Expand Down
11 changes: 7 additions & 4 deletions src/ejabberd_ctl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@
-include("ejabberd_commands.hrl").
-include("mongoose_logger.hrl").

-type format() :: integer | string | binary | {list, format()}.
-type format_type() :: binary() | string() | char().
-type format() :: integer | string | atom | binary | {list, format()}.
-type format_type() :: binary() | string() | char() | node().
-type cmd() :: {CallString :: string(), Args :: [string()], Desc :: string()}.

-define(ASCII_SPACE_CHARACTER, $\s).
Expand Down Expand Up @@ -345,8 +345,11 @@ format_arg(Arg, string) ->
format_arg(Arg, binary) ->
list_to_binary(format_arg(Arg, string));
format_arg(Arg, {list, Type}) ->
[format_arg(Token, Type) || Token <- string:tokens(Arg, ";")].

[format_arg(Token, Type) || Token <- string:tokens(Arg, ";")];
format_arg("self", atom) ->
node();
format_arg(Arg, atom) ->
list_to_atom(Arg).

%% @private
-spec format_arg2(Arg :: string(),
Expand Down
24 changes: 11 additions & 13 deletions src/graphql/admin/mongoose_graphql_mnesia_admin_mutation.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,49 @@

-export([execute/4]).

-import(mongoose_graphql_helper, [make_error/2]).
-import(mongoose_graphql_helper, [make_error/2, make_error/3]).

-ignore_xref([execute/4]).

-include("../mongoose_graphql_types.hrl").
-include("mongoose.hrl").
-include("jlib.hrl").

execute(_Ctx, mnesia, <<"setMaster">>, #{<<"node">> := Node}) ->
case mnesia_api:set_master(binary_to_list(Node)) of
case mnesia_api:set_master(Node) of
{ok, _} -> {ok, "Master node set"};
{error, Reason} -> make_error({error, Reason}, #{node => Node})
Error -> make_error(Error, #{node => Node})
end;
execute(_Ctx, mnesia, <<"backup">>, #{<<"path">> := Path}) ->
case mnesia_api:backup_mnesia(binary_to_list(Path)) of
{ok, _} -> {ok, "Mnesia backup was successfully created"};
{error, Error} -> make_error(Error, #{path => Path})
Error -> make_error(Error, #{path => Path})
end;
execute(_Ctx, mnesia, <<"changeNodename">>, #{<<"fromString">> := FromString,
<<"toString">> := ToString, <<"source">> := Source, <<"target">> := Target}) ->
case mnesia_api:mnesia_change_nodename(binary_to_list(FromString), binary_to_list(ToString),
case mnesia_api:mnesia_change_nodename(FromString, ToString,
binary_to_list(Source), binary_to_list(Target)) of
{ok, _} -> {ok, "Name of the node in the backup was successfully changed"};
{error, Error} -> make_error(Error, #{fromString => FromString, toString => ToString,
source => Source, target => Target})
Error -> make_error(Error, #{fromString => FromString, toString => ToString,
source => Source, target => Target})
end;
execute(_Ctx, mnesia, <<"restore">>, #{<<"path">> := Path}) ->
case mnesia_api:restore_mnesia(binary_to_list(Path)) of
{ok, _} -> {ok, "Mnesia was successfully restored"};
{error, Error} -> make_error(Error, #{path => Path})
Error -> make_error(Error, #{path => Path})
end;
execute(_Ctx, mnesia, <<"dump">>, #{<<"path">> := Path}) ->
case mnesia_api:dump_mnesia(binary_to_list(Path)) of
{ok, _} -> {ok, "Mnesia successfully dumped"};
{error, Error} -> make_error(Error, #{path => Path})
Error -> make_error(Error, #{path => Path})
end;
execute(_Ctx, mnesia, <<"dumpTable">>, #{<<"path">> := Path, <<"table">> := Table}) ->
case mnesia_api:dump_table(binary_to_list(Path), binary_to_list(Table)) of
{ok, _} -> {ok, "Mnesia table successfully dumped"};
{error, Error} -> make_error(Error, #{path => Path, table => Table})
Error -> make_error(Error, #{path => Path, table => Table})
end;
execute(_Ctx, mnesia, <<"load">>, #{<<"path">> := Path}) ->
case mnesia_api:load_mnesia(binary_to_list(Path)) of
{ok, _} -> {ok, "Mnesia was successfully loaded"};
{error, Error} -> make_error(Error, #{path => Path})
Error -> make_error(Error, #{path => Path})
end;
execute(_Ctx, mnesia, <<"installFallback">>, #{<<"path">> := Path}) ->
case mnesia_api:install_fallback_mnesia(binary_to_list(Path)) of
Expand Down
4 changes: 1 addition & 3 deletions src/graphql/admin/mongoose_graphql_mnesia_admin_query.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
-ignore_xref([execute/4]).

-include("../mongoose_graphql_types.hrl").
-include("mongoose.hrl").
-include("jlib.hrl").

execute(_Ctx, mnesia, <<"systemInfo">>, #{<<"keys">> := Keys}) ->
ResultList = mnesia_api:mnesia_info(Keys),
{ok, ResultList} = mnesia_api:mnesia_info(Keys),
{ok, lists:map(fun process_result/1, ResultList)}.

process_result({ok, _} = Result) -> Result;
Expand Down
13 changes: 13 additions & 0 deletions src/graphql/mongoose_graphql_scalar.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ input(<<"UserName">>, User) -> user_from_binary(User);
input(<<"RoomName">>, Room) -> room_from_binary(Room);
input(<<"DomainName">>, Domain) -> domain_from_binary(Domain);
input(<<"ResourceName">>, Res) -> resource_from_binary(Res);
input(<<"NodeName">>, Node) -> node_from_binary(Node);
input(<<"NonEmptyString">>, Value) -> non_empty_string_to_binary(Value);
input(<<"PosInt">>, Value) -> validate_pos_integer(Value);
input(<<"NonNegInt">>, Value) -> validate_non_neg_integer(Value);
Expand Down Expand Up @@ -119,6 +120,18 @@ resource_from_binary(Value) ->
{ok, Res}
end.

node_from_binary(<<>>) ->
{error, empty_node_name};
node_from_binary(NodeName) ->
case string:lexemes(binary_to_list(NodeName), "@") of
[_Name, _Host] ->
{ok, binary_to_atom(NodeName)};
["self"] ->
{ok, node()};
_ ->
{error, incorrect_node_name}
end.

binary_to_microseconds(DT) ->
case mod_mam_utils:maybe_microseconds(DT) of
undefined ->
Expand Down
Loading