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

Multi tenancy init #3061

Merged
merged 18 commits into from
Mar 27, 2021
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ccb6be9
fixing typos
DenysGonchar Mar 17, 2021
e235578
fixing race condition at mongoose_domain_loader:check_for_updates/2
DenysGonchar Mar 22, 2021
5ea6e78
implementing proper init for mongoose_domain_core
DenysGonchar Mar 22, 2021
1be30c4
adding basic unit tests for mongoose_domain_core
DenysGonchar Mar 22, 2021
e93a77b
basic suite for testing 1-2-1 messages fordynamic domains
DenysGonchar Mar 22, 2021
960650f
adding tracking of the source pid for the added dynamic domains
DenysGonchar Mar 23, 2021
6b5ae8d
avoiding mongoose_domain_core crashes in service_domain_db:init/1
DenysGonchar Mar 23, 2021
360ae08
avoid node crashes if service_domain_db goes out of sync
DenysGonchar Mar 23, 2021
cd9ca2a
applying changes after review
DenysGonchar Mar 23, 2021
4e5aad0
moving last event id to persistent_term storage
DenysGonchar Mar 24, 2021
fe627f8
some more changes after code review
DenysGonchar Mar 26, 2021
9743f99
fixing and renaming db_initial_load_crashes_node testcase
DenysGonchar Mar 26, 2021
8110cee
adding db_restarts_properly test case for service_domain_db
DenysGonchar Mar 26, 2021
95a40e9
removing 'test type' host type configuration from default mongooseim.…
DenysGonchar Mar 26, 2021
7170272
setting source for domain names in mongoose_domain_core explicitely
DenysGonchar Mar 26, 2021
b8e04cb
making endless recursion impossible in mongoose_domain_sql
DenysGonchar Mar 26, 2021
86e8fd8
extending mongoose_domain_core_SUITE
DenysGonchar Mar 27, 2021
194e07a
Merge remote-tracking branch 'origin/multi-tenancy-phase-1' into mult…
DenysGonchar Mar 27, 2021
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
29 changes: 29 additions & 0 deletions big_tests/tests/dynamic_domains_pm_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-module(dynamic_domains_pm_SUITE).

%% API
-compile(export_all).
-import(distributed_helper, [mim/0, mim2/0, require_rpc_nodes/1, rpc/4]).

suite() ->
require_rpc_nodes([mim, mim2]).


all() ->
[can_authenticate].

init_per_suite(Config) ->
Domain = <<"example.com">>,
HostType = <<"test type">>,
Source = dummy_source,
rpc(mim(), mongoose_domain_core, insert, [Domain, HostType, Source]),
rpc(mim2(), mongoose_domain_core, insert, [Domain, HostType, Source]),
Config.

end_per_suite(Config) ->
rpc(mim(), mongoose_domain_core, delete, [<<"example.com">>]),
rpc(mim2(), mongoose_domain_core, delete, [<<"example.com">>]),
Config.

can_authenticate(Config) ->
%% TODO: implement later
ok.
161 changes: 82 additions & 79 deletions big_tests/tests/service_domain_db_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ suite() ->

all() ->
[
core_lookup_works,
core_lookup_not_found,
core_static_domain,
core_cannot_insert_static,
core_cannot_disable_static,
core_cannot_enable_static,
core_get_all_static,
core_get_domains_by_host_type,
api_lookup_works,
api_lookup_not_found,
api_cannot_insert_static,
api_cannot_disable_static,
api_cannot_enable_static,
api_get_all_static,
DenysGonchar marked this conversation as resolved.
Show resolved Hide resolved
api_get_domains_by_host_type,
{group, db}
].

Expand All @@ -33,8 +32,8 @@ db_cases() -> [
db_deleted_domain_from_core,
db_disabled_domain_is_in_db,
db_disabled_domain_not_in_core,
db_reanabled_domain_is_in_db,
db_reanabled_domain_is_in_core,
db_reenabled_domain_is_in_db,
db_reenabled_domain_is_in_core,
db_can_insert_domain_twice_with_the_same_host_type,
db_cannot_insert_domain_twice_with_the_another_host_type,
db_cannot_insert_domain_with_unknown_host_type,
Expand All @@ -43,16 +42,17 @@ db_cases() -> [
db_cannot_disable_domain_with_unknown_host_type,
db_domains_with_unknown_host_type_are_ignored_by_core,
sql_select_from_works,
db_records_are_restored_when_restarted,
db_records_are_restored_on_mim_restart,
db_record_is_ignored_if_domain_static,
db_events_table_gets_truncated,
db_get_all_static,
db_could_sync_between_nodes,
db_deleted_from_one_node_while_service_disabled_on_another,
db_inserted_from_one_node_while_service_disabled_on_another,
db_reinserted_from_one_node_while_service_disabled_on_another,
db_out_of_sync_crashes_node,
db_initial_load_crashes_node
db_out_of_sync_restarts_service,
db_crash_on_initial_load_restarts_service,
db_restarts_properly
].

-define(APPS, [inets, crypto, ssl, ranch, cowlib, cowboy]).
Expand All @@ -68,8 +68,8 @@ init_per_suite(Config) ->
ensure_nodes_know_each_other(),
service_disabled(mim()),
service_disabled(mim2()),
prepare_erase(mim()),
prepare_erase(mim2()),
prepare_test_queries(mim()),
prepare_test_queries(mim2()),
erase_database(mim()),
escalus:init_per_suite([{mim_conf1, Conf1}, {mim_conf2, Conf2}|Config]).

Expand Down Expand Up @@ -100,17 +100,12 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.

init_per_testcase(db_initial_load_crashes_node, Config) ->
setup_meck(db_initial_load_crashes_node),
init_per_testcase(db_crash_on_initial_load_restarts_service, Config) ->
maybe_setup_meck(db_crash_on_initial_load_restarts_service),
init_with(mim(), [], []),
Config;
init_per_testcase(TestcaseName, Config) ->
case TestcaseName of
db_out_of_sync_crashes_node ->
setup_meck(db_out_of_sync_crashes_node);
_ ->
ok
end,
maybe_setup_meck(TestcaseName),
ServiceEnabled = proplists:get_value(service, Config, false),
Pairs1 = [{<<"example.cfg">>, <<"type1">>},
{<<"erlang-solutions.com">>, <<"type2">>},
Expand All @@ -133,14 +128,13 @@ service_opts(db_events_table_gets_truncated) ->
service_opts(_) ->
[].

end_per_testcase(db_initial_load_crashes_node, Config) ->
teardown_meck(),
end_per_testcase(generic, Config);
end_per_testcase(db_out_of_sync_crashes_node, Config) ->
rpc(mim(), sys, resume, [service_domain_db]),
teardown_meck(),
end_per_testcase(generic, Config);
end_per_testcase(_TestcaseName, Config) ->
end_per_testcase(TestcaseName, Config) ->
case TestcaseName of
db_out_of_sync_restarts_service ->
rpc(mim(), sys, resume, [service_domain_db]);
_ -> ok
end,
maybe_teardown_meck(TestcaseName),
service_disabled(mim()),
service_disabled(mim2()),
erase_database(mim()),
Expand All @@ -150,39 +144,36 @@ end_per_testcase(_TestcaseName, Config) ->
%% Tests
%%--------------------------------------------------------------------

core_lookup_works(_) ->
api_lookup_works(_) ->
{ok, <<"type1">>} = get_host_type(mim(), <<"example.cfg">>).

core_lookup_not_found(_) ->
api_lookup_not_found(_) ->
{error, not_found} = get_host_type(mim(), <<"example.missing">>).

core_static_domain(_) ->
true = is_static(<<"example.cfg">>).

core_cannot_insert_static(_) ->
api_cannot_insert_static(_) ->
{error, static} = insert_domain(mim(), <<"example.cfg">>, <<"type1">>).

core_cannot_disable_static(_) ->
api_cannot_disable_static(_) ->
{error, static} = disable_domain(mim(), <<"example.cfg">>).

core_cannot_enable_static(_) ->
api_cannot_enable_static(_) ->
{error, static} = enable_domain(mim(), <<"example.cfg">>).

%% See also db_get_all_static
core_get_all_static(_) ->
api_get_all_static(_) ->
%% Could be in any order
[{<<"erlang-solutions.com">>, <<"type2">>},
{<<"erlang-solutions.local">>, <<"type2">>},
{<<"example.cfg">>, <<"type1">>}] =
lists:sort(get_all_static(mim())).

core_get_domains_by_host_type(_) ->
api_get_domains_by_host_type(_) ->
[<<"erlang-solutions.com">>, <<"erlang-solutions.local">>] =
lists:sort(get_domains_by_host_type(mim(), <<"type2">>)),
[<<"example.cfg">>] = get_domains_by_host_type(mim(), <<"type1">>),
[] = get_domains_by_host_type(mim(), <<"type6">>).

%% Similar to as core_get_all_static, just with DB service enabled
%% Similar to as api_get_all_static, just with DB service enabled
db_get_all_static(_) ->
ok = insert_domain(mim(), <<"example.db">>, <<"type1">>),
sync(),
Expand Down Expand Up @@ -233,14 +224,14 @@ db_disabled_domain_not_in_core(_) ->
sync(),
{error, not_found} = get_host_type(mim(), <<"example.db">>).

db_reanabled_domain_is_in_db(_) ->
db_reenabled_domain_is_in_db(_) ->
ok = insert_domain(mim(), <<"example.db">>, <<"type1">>),
ok = disable_domain(mim(), <<"example.db">>),
ok = enable_domain(mim(), <<"example.db">>),
{ok, #{host_type := <<"type1">>, enabled := true}} =
select_domain(mim(), <<"example.db">>).

db_reanabled_domain_is_in_core(_) ->
db_reenabled_domain_is_in_core(_) ->
ok = insert_domain(mim(), <<"example.db">>, <<"type1">>),
ok = disable_domain(mim(), <<"example.db">>),
ok = enable_domain(mim(), <<"example.db">>),
Expand Down Expand Up @@ -286,7 +277,7 @@ sql_select_from_works(_) ->
[{_, <<"example.db">>, <<"type1">>}] =
rpc(mim(), mongoose_domain_sql, select_from, [0, 100]).

db_records_are_restored_when_restarted(_) ->
db_records_are_restored_on_mim_restart(_) ->
ok = insert_domain(mim(), <<"example.com">>, <<"type1">>),
%% Simulate MIM restart
service_disabled(mim()),
Expand Down Expand Up @@ -322,7 +313,7 @@ db_events_table_gets_truncated(_) ->
ok = insert_domain(mim(), <<"example.net">>, <<"dbgroup">>),
ok = insert_domain(mim(), <<"example.org">>, <<"dbgroup">>),
ok = insert_domain(mim(), <<"example.beta">>, <<"dbgroup">>),
Max = get_max_event_id(mim()),
Max = get_max_event_id_or_set_dummy(mim()),
true = is_integer(Max),
true = Max > 0,
%% The events table is not empty and the size of 1, eventually.
Expand Down Expand Up @@ -360,7 +351,6 @@ db_inserted_from_one_node_while_service_disabled_on_another(_) ->
db_reinserted_from_one_node_while_service_disabled_on_another(_) ->
%% This test shows the behaviour when someone
%% reinserts a domain with a different host type.
%% TLDR: just keep the host_type constant or don't reuse domains.
ok = insert_domain(mim(), <<"example.com">>, <<"dbgroup">>),
sync(),
{ok, <<"dbgroup">>} = get_host_type(mim2(), <<"example.com">>),
Expand All @@ -374,22 +364,23 @@ db_reinserted_from_one_node_while_service_disabled_on_another(_) ->
%% Sync is working again
service_enabled(mim2()),
sync(),
%% A corner case: mim2 sees the change, but core ignores it
%% A corner case: domain name is reinserted with different host type
%% while service was down on mim2. check that mim2 is updated
{ok, <<"dbgroup2">>} = get_host_type(mim(), <<"example.com">>),
{ok, <<"dbgroup">>} = get_host_type(mim2(), <<"example.com">>),
%% But if we delete it, it would be deleted everywhere
ok = delete_domain(mim(), <<"example.com">>, <<"dbgroup2">>),
{ok, <<"dbgroup2">>} = get_host_type(mim2(), <<"example.com">>),
%% check deleting
ok = delete_domain(mim2(), <<"example.com">>, <<"dbgroup2">>),
sync(),
{error, not_found} = get_host_type(mim(), <<"example.com">>),
{error, not_found} = get_host_type(mim2(), <<"example.com">>).

db_initial_load_crashes_node(_) ->
db_crash_on_initial_load_restarts_service(_) ->
service_enabled(mim()),
%% Called halt node function, but it's mocked
true = rpc(mim(), meck, num_calls, [mongoose_domain_utils, halt_node, 1]) > 0,
%% service is restarted
true = rpc(mim(), meck, wait, [service_domain_db, restart, 0, timer:seconds(1)]) > 0,
ok.

db_out_of_sync_crashes_node(_) ->
db_out_of_sync_restarts_service(_) ->
ok = insert_domain(mim2(), <<"example1.com">>, <<"type1">>),
ok = insert_domain(mim2(), <<"example2.com">>, <<"type1">>),
sync(),
Expand All @@ -399,25 +390,34 @@ db_out_of_sync_crashes_node(_) ->
ok = insert_domain(mim2(), <<"example4.com">>, <<"type1">>),
sync_local(mim2()),
%% Truncate events table, keep only one event
MaxId = get_max_event_id(mim2()),
MaxId = get_max_event_id_or_set_dummy(mim2()),
{updated, _} = delete_events_older_than(mim2(), MaxId),
{error, not_found} = get_host_type(mim(), <<"example3.com">>),
%% Resume processing events on one node
ok = rpc(mim(), sys, resume, [service_domain_db]),
%% The size of the table is 1
MaxId = get_min_event_id(mim2()),
%% Resume processing events on one node
ok = rpc(mim(), sys, resume, [service_domain_db]),
sync(),
%% Out of sync detected.
%% Called halt node function, but it's mocked
true = rpc(mim(), meck, num_calls, [mongoose_domain_utils, halt_node, 1]) > 0,
%% Out of sync detected and service is restarted
true = rpc(mim(), meck, num_calls, [service_domain_db, restart, 0]) > 0,
ok.

db_restarts_properly(_) ->
PID = rpc(mim(), erlang, whereis, [service_domain_db]),
ok = rpc(mim(), service_domain_db, restart, []),
F = fun() ->
PID2 = rpc(mim(), erlang, whereis, [service_domain_db]),
PID2 =/= PID
end,
mongoose_helper:wait_until(F, true, #{time_left => timer:seconds(15)}).

%%--------------------------------------------------------------------
%% Helpers
%%--------------------------------------------------------------------

service_enabled(Node) ->
service_enabled(Node, []).
service_enabled(Node, []),
sync_local(Node).

service_enabled(Node, Opts) ->
rpc(Node, mongoose_service, start_service, [service_domain_db, Opts]),
Expand All @@ -429,7 +429,9 @@ service_disabled(Node) ->

init_with(Node, Pairs, AllowedHostTypes) ->
rpc(Node, mongoose_domain_core, stop, []),
rpc(Node, mongoose_domain_api, init, [Pairs, AllowedHostTypes]).
rpc(Node, mongoose_domain_core, start, [Pairs, AllowedHostTypes]),
%% call restart to reset last event id
rpc(Node, service_domain_db, reset_last_event_id, []).

insert_domain(Node, Domain, HostType) ->
rpc(Node, mongoose_domain_api, insert_domain, [Domain, HostType]).
Expand All @@ -446,17 +448,17 @@ erase_database(Node) ->
false -> ok
end.

prepare_erase(Node) ->
prepare_test_queries(Node) ->
case mongoose_helper:is_rdbms_enabled(domain()) of
true -> rpc(Node, mongoose_domain_sql, prepare_erase, []);
true -> rpc(Node, mongoose_domain_sql, prepare_test_queries, []);
false -> ok
end.

get_min_event_id(Node) ->
rpc(Node, mongoose_domain_sql, get_min_event_id, []).

get_max_event_id(Node) ->
rpc(Node, mongoose_domain_sql, get_max_event_id, []).
get_max_event_id_or_set_dummy(Node) ->
rpc(Node, mongoose_domain_sql, get_max_event_id_or_set_dummy, []).

delete_events_older_than(Node, Id) ->
rpc(Node, mongoose_domain_sql, delete_events_older_than, [Id]).
Expand All @@ -476,9 +478,6 @@ disable_domain(Node, Domain) ->
enable_domain(Node, Domain) ->
rpc(Node, mongoose_domain_api, enable_domain, [Domain]).

is_static(Domain) ->
rpc(mim(), mongoose_domain_core, is_static, [Domain]).

%% Call sync before get_host_type, if there are some async changes expected
sync() ->
rpc(mim(), service_domain_db, sync, []).
Expand All @@ -502,14 +501,18 @@ restore_conf(Node, #{loaded := Loaded, service_opts := ServiceOpts, core_opts :=
ensure_nodes_know_each_other() ->
pong = rpc(mim2(), net_adm, ping, [maps:get(node, mim())]).

setup_meck(db_initial_load_crashes_node) ->
maybe_setup_meck(db_crash_on_initial_load_restarts_service) ->
ok = rpc(mim(), meck, new, [mongoose_domain_sql, [passthrough, no_link]]),
ok = rpc(mim(), meck, expect, [mongoose_domain_sql, select_from, 2, something_strange]),
ok = rpc(mim(), meck, new, [mongoose_domain_utils, [passthrough, no_link]]),
ok = rpc(mim(), meck, expect, [mongoose_domain_utils, halt_node, 1, ok]);
setup_meck(db_out_of_sync_crashes_node) ->
ok = rpc(mim(), meck, new, [mongoose_domain_utils, [passthrough, no_link]]),
ok = rpc(mim(), meck, expect, [mongoose_domain_utils, halt_node, 1, ok]).

teardown_meck() ->
rpc(mim(), meck, unload, []).
ok = rpc(mim(), meck, new, [service_domain_db, [passthrough, no_link]]),
ok = rpc(mim(), meck, expect, [service_domain_db, restart, 0, ok]);
maybe_setup_meck(db_out_of_sync_restarts_service) ->
ok = rpc(mim(), meck, new, [service_domain_db, [passthrough, no_link]]),
ok = rpc(mim(), meck, expect, [service_domain_db, restart, 0, ok]);
maybe_setup_meck(_TestCase) ->
ok.

maybe_teardown_meck(TC) when TC =:= db_crash_on_initial_load_restarts_service;
TC =:= db_out_of_sync_restarts_service ->
rpc(mim(), meck, unload, []);
maybe_teardown_meck(_TestCase) -> ok.
2 changes: 1 addition & 1 deletion doc/developers-guide/domain_management.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ It must provide the following interfaces:
Any of these lists can be empty, initial list of domain/host\_type pairs can
have some unique host\_types not mentioned in the host\_types list.
The component must be initialised by the main MIM supervisor.
Implemented in `mongoose_domain_api:init(Pairs)`.
Implemented in `mongoose_domain_api:init()`.
- Insert - adding new domain/host\_type pair.
This function must be **idempotent**, it must return success on attempt to
insert the existing data, but it must fail if ETS already has the domain
Expand Down
1 change: 1 addition & 0 deletions rel/files/mongooseim.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[general]
loglevel = "warning"
hosts = [{{{hosts}}}]
host_types = [{{{host_types}}}]
registration_timeout = "infinity"
language = "en"
all_metrics_are_global = {{{all_metrics_are_global}}}
Expand Down
Loading