diff --git a/src/ekka_autocluster.erl b/src/ekka_autocluster.erl index de9f8d2..45ffef7 100644 --- a/src/ekka_autocluster.erl +++ b/src/ekka_autocluster.erl @@ -57,6 +57,7 @@ enabled() -> configured() -> case ekka:env(cluster_discovery) of {ok, {manual, _}} -> false; + {ok, {singleton, _}} -> false; {ok, _Strategy} -> true; undefined -> false end. @@ -96,6 +97,8 @@ core_node_discovery_callback() -> case ekka:env(cluster_discovery) of {ok, {manual, _}} -> []; + {ok, {singleton, _}} -> + []; {ok, {Strategy, Options}} -> Mod = strategy_module(Strategy), try ekka_cluster_strategy:discover(Mod, Options) of @@ -197,6 +200,8 @@ with_strategy(Fun) -> case ekka:env(cluster_discovery) of {ok, {manual, _}} -> ignore; + {ok, {singleton, _}} -> + ignore; {ok, {Strategy, Options}} -> Fun(strategy_module(Strategy), Options); undefined -> diff --git a/src/ekka_cluster.erl b/src/ekka_cluster.erl index b7e6795..2a0761a 100644 --- a/src/ekka_cluster.erl +++ b/src/ekka_cluster.erl @@ -24,6 +24,8 @@ , leave/0 , force_leave/1 , status/1 + , is_singleton/0 + , is_singleton/1 ]). -type(info_key() :: running_nodes | stopped_nodes). @@ -50,7 +52,12 @@ status(Node) -> %% @doc Join the cluster -spec(join(node()) -> ok | ignore | {error, term()}). join(Node) -> - mria:join(Node). + case is_singleton() orelse is_singleton(Node) of + true -> + {error, singleton}; + false -> + mria:join(Node) + end. %% @doc Leave from the cluster. -spec(leave() -> ok | {error, any()}). @@ -61,3 +68,23 @@ leave() -> -spec(force_leave(node()) -> ok | ignore | {error, term()}). force_leave(Node) -> mria:force_leave(Node). + +-spec is_singleton() -> boolean(). +is_singleton() -> + case ekka:env(cluster_discovery) of + {ok, {singleton, _}} -> + true; + _ -> + false + end. + +-spec is_singleton(node()) -> boolean(). +is_singleton(Node) when Node =:= node() -> + is_singleton(); +is_singleton(Node) -> + try + erpc:call(Node, ?MODULE, is_singleton, []) + catch + _:_ -> + false + end. diff --git a/test/ekka_autocluster_SUITE.erl b/test/ekka_autocluster_SUITE.erl index 2cd95f5..9eb1540 100644 --- a/test/ekka_autocluster_SUITE.erl +++ b/test/ekka_autocluster_SUITE.erl @@ -41,6 +41,8 @@ {suffix, ""} ]). +-define(ON(NODE, BODY), erpc:call(NODE, fun() -> BODY end)). + all() -> ekka_ct:all(?MODULE). %%-------------------------------------------------------------------- @@ -330,6 +332,31 @@ t_core_node_discovery_callback(Config) -> ), ok. +%%-------------------------------------------------------------------- +%% "Autocluster" via 'singleton' strategy + +t_singleton(_Config) -> + N1 = ekka_ct:start_slave(ekka, n1), + N2 = ekka_ct:start_slave(ekka, n2), + try + ok = ekka_ct:wait_running(N1), + ok = ekka_ct:wait_running(N2), + ok = set_app_env(N1, {singleton, []}), + ok = set_app_env(N2, {static, [{seeds, []}]}), + ?ON(N1, ?assertMatch({error, singleton}, ekka:join(N2))), + %% Other nodes must not join the singleton. + ?ON(N2, ?assertMatch({error, singleton}, ekka:join(N1))), + ?ON(N1, ?assertNot(ekka:autocluster_enabled())), + ?ON(N1, ?assertNot(ekka_autocluster:configured())), + ?ON(N1, ?assertNot(ekka_autocluster:enabled())), + ?ON(N1, ?assertEqual(ignore, ekka_autocluster:run(myapp))), + ?ON(N1, ?assertEqual(ignore, ekka_autocluster:unregister_node())), + ?ON(N1, ?assertEqual([], ekka_autocluster:core_node_discovery_callback())), + ok + after + ok = ekka_ct:stop_slave(N1) + end. + %%-------------------------------------------------------------------- %% Misc tests %%--------------------------------------------------------------------