|
@@ -26,6 +26,7 @@
|
|
|
-export([ lookup/1
|
|
-export([ lookup/1
|
|
|
, get_metrics/1
|
|
, get_metrics/1
|
|
|
, list_all/0
|
|
, list_all/0
|
|
|
|
|
+ , list_group/1
|
|
|
]).
|
|
]).
|
|
|
|
|
|
|
|
-export([ hash_call/2
|
|
-export([ hash_call/2
|
|
@@ -61,12 +62,12 @@ hash_call(InstId, Request) ->
|
|
|
hash_call(InstId, Request, Timeout) ->
|
|
hash_call(InstId, Request, Timeout) ->
|
|
|
gen_server:call(pick(InstId), Request, Timeout).
|
|
gen_server:call(pick(InstId), Request, Timeout).
|
|
|
|
|
|
|
|
--spec lookup(instance_id()) -> {ok, resource_data()} | {error, not_found}.
|
|
|
|
|
|
|
+-spec lookup(instance_id()) -> {ok, resource_group(), resource_data()} | {error, not_found}.
|
|
|
lookup(InstId) ->
|
|
lookup(InstId) ->
|
|
|
case ets:lookup(emqx_resource_instance, InstId) of
|
|
case ets:lookup(emqx_resource_instance, InstId) of
|
|
|
[] -> {error, not_found};
|
|
[] -> {error, not_found};
|
|
|
- [{_, Data}] ->
|
|
|
|
|
- {ok, Data#{id => InstId, metrics => get_metrics(InstId)}}
|
|
|
|
|
|
|
+ [{_, Group, Data}] ->
|
|
|
|
|
+ {ok, Group, Data#{id => InstId, metrics => get_metrics(InstId)}}
|
|
|
end.
|
|
end.
|
|
|
|
|
|
|
|
make_test_id() ->
|
|
make_test_id() ->
|
|
@@ -77,17 +78,22 @@ get_metrics(InstId) ->
|
|
|
emqx_plugin_libs_metrics:get_metrics(resource_metrics, InstId).
|
|
emqx_plugin_libs_metrics:get_metrics(resource_metrics, InstId).
|
|
|
|
|
|
|
|
force_lookup(InstId) ->
|
|
force_lookup(InstId) ->
|
|
|
- {ok, Data} = lookup(InstId),
|
|
|
|
|
|
|
+ {ok, _Group, Data} = lookup(InstId),
|
|
|
Data.
|
|
Data.
|
|
|
|
|
|
|
|
-spec list_all() -> [resource_data()].
|
|
-spec list_all() -> [resource_data()].
|
|
|
list_all() ->
|
|
list_all() ->
|
|
|
try
|
|
try
|
|
|
- [Data#{id => Id} || {Id, Data} <- ets:tab2list(emqx_resource_instance)]
|
|
|
|
|
|
|
+ [Data#{id => Id} || {Id, _Group, Data} <- ets:tab2list(emqx_resource_instance)]
|
|
|
catch
|
|
catch
|
|
|
error:badarg -> []
|
|
error:badarg -> []
|
|
|
end.
|
|
end.
|
|
|
|
|
|
|
|
|
|
+-spec list_group(resource_group()) -> [instance_id()].
|
|
|
|
|
+list_group(Group) ->
|
|
|
|
|
+ List = ets:match(emqx_resource_instance, {'$1', Group, '_'}),
|
|
|
|
|
+ lists:map(fun([A|_]) -> A end, List).
|
|
|
|
|
+
|
|
|
%%------------------------------------------------------------------------------
|
|
%%------------------------------------------------------------------------------
|
|
|
%% gen_server callbacks
|
|
%% gen_server callbacks
|
|
|
%%------------------------------------------------------------------------------
|
|
%%------------------------------------------------------------------------------
|
|
@@ -99,8 +105,8 @@ init({Pool, Id}) ->
|
|
|
true = gproc_pool:connect_worker(Pool, {Pool, Id}),
|
|
true = gproc_pool:connect_worker(Pool, {Pool, Id}),
|
|
|
{ok, #state{worker_pool = Pool, worker_id = Id}}.
|
|
{ok, #state{worker_pool = Pool, worker_id = Id}}.
|
|
|
|
|
|
|
|
-handle_call({create, InstId, ResourceType, Config, Opts}, _From, State) ->
|
|
|
|
|
- {reply, do_create(InstId, ResourceType, Config, Opts), State};
|
|
|
|
|
|
|
+handle_call({create, InstId, Group, ResourceType, Config, Opts}, _From, State) ->
|
|
|
|
|
+ {reply, do_create(InstId, Group, ResourceType, Config, Opts), State};
|
|
|
|
|
|
|
|
handle_call({create_dry_run, ResourceType, Config}, _From, State) ->
|
|
handle_call({create_dry_run, ResourceType, Config}, _From, State) ->
|
|
|
{reply, do_create_dry_run(ResourceType, Config), State};
|
|
{reply, do_create_dry_run(ResourceType, Config), State};
|
|
@@ -143,41 +149,41 @@ code_change(_OldVsn, State, _Extra) ->
|
|
|
|
|
|
|
|
%% suppress the race condition check, as these functions are protected in gproc workers
|
|
%% suppress the race condition check, as these functions are protected in gproc workers
|
|
|
-dialyzer({nowarn_function, [ do_recreate/4
|
|
-dialyzer({nowarn_function, [ do_recreate/4
|
|
|
- , do_create/4
|
|
|
|
|
|
|
+ , do_create/5
|
|
|
, do_restart/2
|
|
, do_restart/2
|
|
|
- , do_start/4
|
|
|
|
|
|
|
+ , do_start/5
|
|
|
, do_stop/1
|
|
, do_stop/1
|
|
|
, do_health_check/1
|
|
, do_health_check/1
|
|
|
- , start_and_check/5
|
|
|
|
|
|
|
+ , start_and_check/6
|
|
|
]}).
|
|
]}).
|
|
|
|
|
|
|
|
do_recreate(InstId, ResourceType, NewConfig, Opts) ->
|
|
do_recreate(InstId, ResourceType, NewConfig, Opts) ->
|
|
|
case lookup(InstId) of
|
|
case lookup(InstId) of
|
|
|
- {ok, #{mod := ResourceType, status := started} = Data} ->
|
|
|
|
|
|
|
+ {ok, Group, #{mod := ResourceType, status := started} = Data} ->
|
|
|
%% If this resource is in use (status='started'), we should make sure
|
|
%% If this resource is in use (status='started'), we should make sure
|
|
|
%% the new config is OK before removing the old one.
|
|
%% the new config is OK before removing the old one.
|
|
|
case do_create_dry_run(ResourceType, NewConfig) of
|
|
case do_create_dry_run(ResourceType, NewConfig) of
|
|
|
ok ->
|
|
ok ->
|
|
|
- do_remove(Data, false),
|
|
|
|
|
- do_create(InstId, ResourceType, NewConfig, Opts);
|
|
|
|
|
|
|
+ do_remove(Group, Data, false),
|
|
|
|
|
+ do_create(InstId, Group, ResourceType, NewConfig, Opts);
|
|
|
Error ->
|
|
Error ->
|
|
|
Error
|
|
Error
|
|
|
end;
|
|
end;
|
|
|
- {ok, #{mod := ResourceType, status := _} = Data} ->
|
|
|
|
|
- do_remove(Data, false),
|
|
|
|
|
- do_create(InstId, ResourceType, NewConfig, Opts);
|
|
|
|
|
- {ok, #{mod := Mod}} when Mod =/= ResourceType ->
|
|
|
|
|
|
|
+ {ok, Group, #{mod := ResourceType, status := _} = Data} ->
|
|
|
|
|
+ do_remove(Group, Data, false),
|
|
|
|
|
+ do_create(InstId, Group, ResourceType, NewConfig, Opts);
|
|
|
|
|
+ {ok, _, #{mod := Mod}} when Mod =/= ResourceType ->
|
|
|
{error, updating_to_incorrect_resource_type};
|
|
{error, updating_to_incorrect_resource_type};
|
|
|
{error, not_found} ->
|
|
{error, not_found} ->
|
|
|
{error, not_found}
|
|
{error, not_found}
|
|
|
end.
|
|
end.
|
|
|
|
|
|
|
|
-do_create(InstId, ResourceType, Config, Opts) ->
|
|
|
|
|
|
|
+do_create(InstId, Group, ResourceType, Config, Opts) ->
|
|
|
case lookup(InstId) of
|
|
case lookup(InstId) of
|
|
|
- {ok, _} ->
|
|
|
|
|
|
|
+ {ok,_, _} ->
|
|
|
{ok, already_created};
|
|
{ok, already_created};
|
|
|
{error, not_found} ->
|
|
{error, not_found} ->
|
|
|
- case do_start(InstId, ResourceType, Config, Opts) of
|
|
|
|
|
|
|
+ case do_start(InstId, Group, ResourceType, Config, Opts) of
|
|
|
ok ->
|
|
ok ->
|
|
|
ok = emqx_plugin_libs_metrics:create_metrics(resource_metrics, InstId,
|
|
ok = emqx_plugin_libs_metrics:create_metrics(resource_metrics, InstId,
|
|
|
[matched, success, failed, exception], [matched]),
|
|
[matched, success, failed, exception], [matched]),
|
|
@@ -207,9 +213,10 @@ do_remove(Instance) ->
|
|
|
do_remove(Instance, true).
|
|
do_remove(Instance, true).
|
|
|
|
|
|
|
|
do_remove(InstId, ClearMetrics) when is_binary(InstId) ->
|
|
do_remove(InstId, ClearMetrics) when is_binary(InstId) ->
|
|
|
- do_with_instance_data(InstId, fun do_remove/2, [ClearMetrics]);
|
|
|
|
|
-do_remove(#{id := InstId} = Data, ClearMetrics) ->
|
|
|
|
|
- _ = do_stop(Data),
|
|
|
|
|
|
|
+ do_with_group_and_instance_data(InstId, fun do_remove/3, [ClearMetrics]).
|
|
|
|
|
+
|
|
|
|
|
+do_remove(Group, #{id := InstId} = Data, ClearMetrics) ->
|
|
|
|
|
+ _ = do_stop(Group, Data),
|
|
|
ets:delete(emqx_resource_instance, InstId),
|
|
ets:delete(emqx_resource_instance, InstId),
|
|
|
case ClearMetrics of
|
|
case ClearMetrics of
|
|
|
true -> ok = emqx_plugin_libs_metrics:clear_metrics(resource_metrics, InstId);
|
|
true -> ok = emqx_plugin_libs_metrics:clear_metrics(resource_metrics, InstId);
|
|
@@ -219,42 +226,42 @@ do_remove(#{id := InstId} = Data, ClearMetrics) ->
|
|
|
|
|
|
|
|
do_restart(InstId, Opts) ->
|
|
do_restart(InstId, Opts) ->
|
|
|
case lookup(InstId) of
|
|
case lookup(InstId) of
|
|
|
- {ok, #{mod := ResourceType, config := Config} = Data} ->
|
|
|
|
|
- ok = do_stop(Data),
|
|
|
|
|
- do_start(InstId, ResourceType, Config, Opts);
|
|
|
|
|
|
|
+ {ok, Group, #{mod := ResourceType, config := Config} = Data} ->
|
|
|
|
|
+ ok = do_stop(Group, Data),
|
|
|
|
|
+ do_start(InstId, Group, ResourceType, Config, Opts);
|
|
|
Error ->
|
|
Error ->
|
|
|
Error
|
|
Error
|
|
|
end.
|
|
end.
|
|
|
|
|
|
|
|
-do_start(InstId, ResourceType, Config, Opts) when is_binary(InstId) ->
|
|
|
|
|
|
|
+do_start(InstId, Group, ResourceType, Config, Opts) when is_binary(InstId) ->
|
|
|
InitData = #{id => InstId, mod => ResourceType, config => Config,
|
|
InitData = #{id => InstId, mod => ResourceType, config => Config,
|
|
|
status => starting, state => undefined},
|
|
status => starting, state => undefined},
|
|
|
%% The `emqx_resource:call_start/3` need the instance exist beforehand
|
|
%% The `emqx_resource:call_start/3` need the instance exist beforehand
|
|
|
- ets:insert(emqx_resource_instance, {InstId, InitData}),
|
|
|
|
|
|
|
+ ets:insert(emqx_resource_instance, {InstId, Group, InitData}),
|
|
|
case maps:get(async_create, Opts, false) of
|
|
case maps:get(async_create, Opts, false) of
|
|
|
false ->
|
|
false ->
|
|
|
- start_and_check(InstId, ResourceType, Config, Opts, InitData);
|
|
|
|
|
|
|
+ start_and_check(InstId, Group, ResourceType, Config, Opts, InitData);
|
|
|
true ->
|
|
true ->
|
|
|
spawn(fun() ->
|
|
spawn(fun() ->
|
|
|
- start_and_check(InstId, ResourceType, Config, Opts, InitData)
|
|
|
|
|
|
|
+ start_and_check(InstId, Group, ResourceType, Config, Opts, InitData)
|
|
|
end),
|
|
end),
|
|
|
ok
|
|
ok
|
|
|
end.
|
|
end.
|
|
|
|
|
|
|
|
-start_and_check(InstId, ResourceType, Config, Opts, Data) ->
|
|
|
|
|
|
|
+start_and_check(InstId, Group, ResourceType, Config, Opts, Data) ->
|
|
|
case emqx_resource:call_start(InstId, ResourceType, Config) of
|
|
case emqx_resource:call_start(InstId, ResourceType, Config) of
|
|
|
{ok, ResourceState} ->
|
|
{ok, ResourceState} ->
|
|
|
Data2 = Data#{state => ResourceState},
|
|
Data2 = Data#{state => ResourceState},
|
|
|
- ets:insert(emqx_resource_instance, {InstId, Data2}),
|
|
|
|
|
|
|
+ ets:insert(emqx_resource_instance, {InstId, Group, Data2}),
|
|
|
case maps:get(async_create, Opts, false) of
|
|
case maps:get(async_create, Opts, false) of
|
|
|
- false -> case do_health_check(Data2) of
|
|
|
|
|
|
|
+ false -> case do_health_check(Group, Data2) of
|
|
|
ok -> create_default_checker(InstId, Opts);
|
|
ok -> create_default_checker(InstId, Opts);
|
|
|
{error, Reason} -> {error, Reason}
|
|
{error, Reason} -> {error, Reason}
|
|
|
end;
|
|
end;
|
|
|
true -> create_default_checker(InstId, Opts)
|
|
true -> create_default_checker(InstId, Opts)
|
|
|
end;
|
|
end;
|
|
|
{error, Reason} ->
|
|
{error, Reason} ->
|
|
|
- ets:insert(emqx_resource_instance, {InstId, Data#{status => stopped}}),
|
|
|
|
|
|
|
+ ets:insert(emqx_resource_instance, {InstId, Group, Data#{status => stopped}}),
|
|
|
{error, Reason}
|
|
{error, Reason}
|
|
|
end.
|
|
end.
|
|
|
|
|
|
|
@@ -264,37 +271,39 @@ create_default_checker(InstId, Opts) ->
|
|
|
maps:get(health_check_timeout, Opts, 10000)).
|
|
maps:get(health_check_timeout, Opts, 10000)).
|
|
|
|
|
|
|
|
do_stop(InstId) when is_binary(InstId) ->
|
|
do_stop(InstId) when is_binary(InstId) ->
|
|
|
- do_with_instance_data(InstId, fun do_stop/1, []);
|
|
|
|
|
-do_stop(#{state := undefined}) ->
|
|
|
|
|
|
|
+ do_with_group_and_instance_data(InstId, fun do_stop/2, []).
|
|
|
|
|
+
|
|
|
|
|
+do_stop(_Group, #{state := undefined}) ->
|
|
|
ok;
|
|
ok;
|
|
|
-do_stop(#{id := InstId, mod := Mod, state := ResourceState} = Data) ->
|
|
|
|
|
|
|
+do_stop(Group, #{id := InstId, mod := Mod, state := ResourceState} = Data) ->
|
|
|
_ = emqx_resource:call_stop(InstId, Mod, ResourceState),
|
|
_ = emqx_resource:call_stop(InstId, Mod, ResourceState),
|
|
|
_ = emqx_resource_health_check:delete_checker(InstId),
|
|
_ = emqx_resource_health_check:delete_checker(InstId),
|
|
|
- ets:insert(emqx_resource_instance, {InstId, Data#{status => stopped}}),
|
|
|
|
|
|
|
+ ets:insert(emqx_resource_instance, {InstId, Group, Data#{status => stopped}}),
|
|
|
ok.
|
|
ok.
|
|
|
|
|
|
|
|
do_health_check(InstId) when is_binary(InstId) ->
|
|
do_health_check(InstId) when is_binary(InstId) ->
|
|
|
- do_with_instance_data(InstId, fun do_health_check/1, []);
|
|
|
|
|
-do_health_check(#{state := undefined}) ->
|
|
|
|
|
|
|
+ do_with_group_and_instance_data(InstId, fun do_health_check/2, []).
|
|
|
|
|
+
|
|
|
|
|
+do_health_check(_Group, #{state := undefined}) ->
|
|
|
{error, resource_not_initialized};
|
|
{error, resource_not_initialized};
|
|
|
-do_health_check(#{id := InstId, mod := Mod, state := ResourceState0} = Data) ->
|
|
|
|
|
|
|
+do_health_check(Group, #{id := InstId, mod := Mod, state := ResourceState0} = Data) ->
|
|
|
case emqx_resource:call_health_check(InstId, Mod, ResourceState0) of
|
|
case emqx_resource:call_health_check(InstId, Mod, ResourceState0) of
|
|
|
{ok, ResourceState1} ->
|
|
{ok, ResourceState1} ->
|
|
|
ets:insert(emqx_resource_instance,
|
|
ets:insert(emqx_resource_instance,
|
|
|
- {InstId, Data#{status => started, state => ResourceState1}}),
|
|
|
|
|
|
|
+ {InstId, Group, Data#{status => started, state => ResourceState1}}),
|
|
|
ok;
|
|
ok;
|
|
|
{error, Reason, ResourceState1} ->
|
|
{error, Reason, ResourceState1} ->
|
|
|
logger:error("health check for ~p failed: ~p", [InstId, Reason]),
|
|
logger:error("health check for ~p failed: ~p", [InstId, Reason]),
|
|
|
ets:insert(emqx_resource_instance,
|
|
ets:insert(emqx_resource_instance,
|
|
|
- {InstId, Data#{status => stopped, state => ResourceState1}}),
|
|
|
|
|
|
|
+ {InstId, Group, Data#{status => stopped, state => ResourceState1}}),
|
|
|
{error, Reason}
|
|
{error, Reason}
|
|
|
end.
|
|
end.
|
|
|
|
|
|
|
|
do_set_resource_status_stoped(InstId) ->
|
|
do_set_resource_status_stoped(InstId) ->
|
|
|
case emqx_resource_instance:lookup(InstId) of
|
|
case emqx_resource_instance:lookup(InstId) of
|
|
|
- {ok, #{id := InstId} = Data} ->
|
|
|
|
|
|
|
+ {ok, Group, #{id := InstId} = Data} ->
|
|
|
logger:error("health check for ~p failed: timeout", [InstId]),
|
|
logger:error("health check for ~p failed: timeout", [InstId]),
|
|
|
- ets:insert(emqx_resource_instance, {InstId, Data#{status => stopped}});
|
|
|
|
|
|
|
+ ets:insert(emqx_resource_instance, {InstId, Group, Data#{status => stopped}});
|
|
|
Error -> {error, Error}
|
|
Error -> {error, Error}
|
|
|
end.
|
|
end.
|
|
|
|
|
|
|
@@ -302,9 +311,9 @@ do_set_resource_status_stoped(InstId) ->
|
|
|
%% internal functions
|
|
%% internal functions
|
|
|
%%------------------------------------------------------------------------------
|
|
%%------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-do_with_instance_data(InstId, Do, Args) ->
|
|
|
|
|
|
|
+do_with_group_and_instance_data(InstId, Do, Args) ->
|
|
|
case lookup(InstId) of
|
|
case lookup(InstId) of
|
|
|
- {ok, Data} -> erlang:apply(Do, [Data | Args]);
|
|
|
|
|
|
|
+ {ok, Group, Data} -> erlang:apply(Do, [Group, Data | Args]);
|
|
|
Error -> Error
|
|
Error -> Error
|
|
|
end.
|
|
end.
|
|
|
|
|
|