From 6454fe6b597595c8b833f408102d4a7f1124954d Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Mon, 17 Oct 2022 00:05:04 +0200 Subject: [PATCH] Implement an async endpoint for domain deletion --- big_tests/tests/domain_rest_helper.erl | 8 ++++++ src/domain/mongoose_domain_api.erl | 19 ++++++++++--- src/domain/mongoose_domain_db_cleaner.erl | 18 +++++++++++- src/domain/mongoose_domain_sql.erl | 2 ++ .../mongoose_admin_api_domain.erl | 28 +++++++++++++++++-- 5 files changed, 68 insertions(+), 7 deletions(-) diff --git a/big_tests/tests/domain_rest_helper.erl b/big_tests/tests/domain_rest_helper.erl index b41d77e8c1c..dc3bddd032b 100644 --- a/big_tests/tests/domain_rest_helper.erl +++ b/big_tests/tests/domain_rest_helper.erl @@ -11,6 +11,7 @@ putt_domain_with_custom_body/2, rest_select_domain/2, rest_delete_domain/3, + request_delete_domain/3, delete_custom/4, patch_custom/4]). @@ -73,6 +74,13 @@ rest_delete_domain(Config, Domain, HostType) -> creds => make_creds(Config), body => Params }). +request_delete_domain(Config, Domain, HostType) -> + Params = #{<<"host_type">> => HostType, <<"request">> => true}, + rest_helper:make_request(#{ role => admin, method => <<"DELETE">>, + path => domain_path(Domain), + creds => make_creds(Config), + body => Params }). + delete_custom(Config, Role, Path, Body) -> rest_helper:make_request(#{ role => Role, method => <<"DELETE">>, path => Path, creds => make_creds(Config), diff --git a/src/domain/mongoose_domain_api.erl b/src/domain/mongoose_domain_api.erl index 15c20769080..ca165592e97 100644 --- a/src/domain/mongoose_domain_api.erl +++ b/src/domain/mongoose_domain_api.erl @@ -11,6 +11,7 @@ %% domain API -export([insert_domain/2, delete_domain/2, + request_delete_domain/2, disable_domain/1, enable_domain/1, get_domain_host_type/1, @@ -30,7 +31,8 @@ get_all_subdomains_for_domain/1]). %% Helper for remove_domain --export([remove_domain_wrapper/3]). +-export([remove_domain_wrapper/3, + do_delete_domain_in_progress/3]). %% For testing -export([get_all_dynamic/0]). @@ -82,13 +84,20 @@ insert_domain(Domain, HostType) -> %% Domain should be nameprepped using `jid:nameprep'. -spec delete_domain(domain(), host_type()) -> delete_domain_return(). delete_domain(Domain, HostType) -> + do_delete_domain(Domain, HostType, sync). + +-spec request_delete_domain(domain(), host_type()) -> delete_domain_return(). +request_delete_domain(Domain, HostType) -> + do_delete_domain(Domain, HostType, async). + +do_delete_domain(Domain, HostType, RequestType) -> case check_domain(Domain, HostType) of ok -> Res0 = check_db(mongoose_domain_sql:set_domain_for_deletion(Domain, HostType)), case Res0 of ok -> delete_domain_password(Domain), - do_delete_domain_in_progress(Domain, HostType); + do_delete_domain_in_progress(Domain, HostType, RequestType); Other -> Other end; @@ -98,8 +107,10 @@ delete_domain(Domain, HostType) -> %% This is ran only in the context of `do_delete_domain', %% so it can already skip some checks --spec do_delete_domain_in_progress(domain(), host_type()) -> delete_domain_return(). -do_delete_domain_in_progress(Domain, HostType) -> +-spec do_delete_domain_in_progress(domain(), host_type(), sync | async) -> delete_domain_return(). +do_delete_domain_in_progress(Domain, HostType, async) -> + mongoose_domain_db_cleaner:request_delete_domain(Domain, HostType); +do_delete_domain_in_progress(Domain, HostType, sync) -> case mongoose_hooks:remove_domain(HostType, Domain) of #{failed := []} -> check_db(mongoose_domain_sql:delete_domain(Domain, HostType)); diff --git a/src/domain/mongoose_domain_db_cleaner.erl b/src/domain/mongoose_domain_db_cleaner.erl index 17fc6c1813e..ff2bc8e3e9a 100644 --- a/src/domain/mongoose_domain_db_cleaner.erl +++ b/src/domain/mongoose_domain_db_cleaner.erl @@ -4,7 +4,7 @@ -include("mongoose_logger.hrl"). -export([start/1, stop/0]). --export([start_link/1]). +-export([start_link/1, request_delete_domain/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -37,6 +37,10 @@ stop() -> start_link(Opts) -> gen_server:start_link({local, ?MODULE}, ?MODULE, Opts, []). +-spec request_delete_domain(jid:lserver(), mongooseim:host_type()) -> ok. +request_delete_domain(Domain, HostType) -> + gen_server:cast(?MODULE, {delete_domain, Domain, HostType}). + %% --------------------------------------------------------------------------- %% Server callbacks @@ -51,6 +55,8 @@ handle_call(Request, From, State) -> ?UNEXPECTED_CALL(Request, From), {reply, ok, State}. +handle_cast({delete_domain, Domain, HostType}, State) -> + {noreply, handle_delete_domain(Domain, HostType, State)}; handle_cast(Msg, State) -> ?UNEXPECTED_CAST(Msg), {noreply, State}. @@ -90,3 +96,13 @@ schedule_removal(State = #{max_age := MaxAge}) -> handle_timeout(_TimerRef, {do_removal, LastEventId}, State) -> mongoose_domain_sql:delete_events_older_than(LastEventId), State. + +handle_delete_domain(Domain, HostType, State) -> + try + mongoose_domain_api:do_delete_domain_in_progress(Domain, HostType, sync) + catch Class:Reason:Stacktrace -> + ?LOG_ERROR(#{what => domain_deletion_failed, + domain => Domain, host_type => HostType, + class => Class, reason => Reason, stacktrace => Stacktrace}) + end, + State. diff --git a/src/domain/mongoose_domain_sql.erl b/src/domain/mongoose_domain_sql.erl index 7844d4a1f11..b474cfff515 100644 --- a/src/domain/mongoose_domain_sql.erl +++ b/src/domain/mongoose_domain_sql.erl @@ -340,6 +340,8 @@ set_status(Domain, Status) -> case mongoose_domain_core:is_host_type_allowed(HostType) of false -> {error, unknown_host_type}; + true when deleting =:= CurrentStatus -> + {error, domain_deleted}; true when Status =:= CurrentStatus -> ok; true -> diff --git a/src/mongoose_admin_api/mongoose_admin_api_domain.erl b/src/mongoose_admin_api/mongoose_admin_api_domain.erl index affede9ca67..2e854a6bdf1 100644 --- a/src/mongoose_admin_api/mongoose_admin_api_domain.erl +++ b/src/mongoose_admin_api/mongoose_admin_api_domain.erl @@ -11,7 +11,8 @@ allowed_methods/2, to_json/2, from_json/2, - delete_resource/2]). + delete_resource/2, + delete_completed/2]). -ignore_xref([to_json/2, from_json/2]). @@ -69,6 +70,12 @@ from_json(Req, State) -> delete_resource(Req, State) -> try_handle_request(Req, State, fun handle_delete/2). +-spec delete_completed(req(), state()) -> {boolean(), req(), state()}. +delete_completed(Req, #{deletion := in_process} = State) -> + {false, Req, State}; +delete_completed(Req, State) -> + {true, Req, State}. + %% Internal functions handle_get(Req, State) -> @@ -128,7 +135,24 @@ handle_delete(Req, State) -> Bindings = cowboy_req:bindings(Req), Domain = get_domain(Bindings), Args = parse_body(Req), - HostType = get_host_type(Args), + handle_delete(Req, State, Domain, Args). + +handle_delete(Req, State, Domain, #{host_type := HostType, request := true}) -> + async_delete(Req, State, Domain, HostType); +handle_delete(Req, State, Domain, #{host_type := HostType}) -> + sync_delete(Req, State, Domain, HostType); +handle_delete(_Req, _State, _Domain, #{}) -> + throw_error(bad_request, <<"'host_type' field is missing">>); +handle_delete(_Req, _State, _Domain, {error, empty}) -> + throw_error(bad_request, <<"body is empty">>); +handle_delete(_Req, _State, _Domain, {error, _}) -> + throw_error(bad_request, <<"failed to parse JSON">>). + +async_delete(Req, State, Domain, HostType) -> + mongoose_domain_api:request_delete_domain(Domain, HostType), + {true, Req, State#{deletion => in_process}}. + +sync_delete(Req, State, Domain, HostType) -> case mongoose_domain_api:delete_domain(Domain, HostType) of ok -> {true, Req, State};