Skip to content

Commit 0ea1734

Browse files
committed
Added server events support
1 parent ac71129 commit 0ea1734

File tree

2 files changed

+269
-1
lines changed

2 files changed

+269
-1
lines changed

src/nkdocker_events.erl

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
%% -------------------------------------------------------------------
2+
%%
3+
%% Copyright (c) 2016 Carlos Gonzalez Florido. All Rights Reserved.
4+
%%
5+
%% This file is provided to you under the Apache License,
6+
%% Version 2.0 (the "License"); you may not use this file
7+
%% except in compliance with the License. You may obtain
8+
%% a copy of the License at
9+
%%
10+
%% http://www.apache.org/licenses/LICENSE-2.0
11+
%%
12+
%% Unless required by applicable law or agreed to in writing,
13+
%% software distributed under the License is distributed on an
14+
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
%% KIND, either express or implied. See the License for the
16+
%% specific language governing permissions and limitations
17+
%% under the License.
18+
%%
19+
%% -------------------------------------------------------------------
20+
21+
-module(nkdocker_events).
22+
-author('Carlos Gonzalez <[email protected]>').
23+
-behaviour(gen_server).
24+
25+
-export([register/2, unregister/2, start_link/2, find/1]).
26+
-export([init/1, terminate/2, code_change/3, handle_call/3,
27+
handle_cast/2, handle_info/2]).
28+
-export_type([id/0, event/0]).
29+
30+
-define(LLOG(Type, Txt, Args), lager:Type("NkDOCKER Events "++Txt, Args)).
31+
32+
-callback nkdocker_event(id(), event()) ->
33+
ok.
34+
35+
%% ===================================================================
36+
%% Types
37+
%% ===================================================================
38+
39+
-type id() :: {inet:ip_address(), inet:port()}.
40+
41+
-type event() :: {Status::atom(), Id::binary(), From::binary(), Time::integer()}.
42+
43+
44+
45+
%% ===================================================================
46+
%% Public
47+
%% ===================================================================
48+
49+
50+
%% @doc Registers a server to listen to docker events
51+
%% For each new event, Callback:nkdocker_event(event()) will be called
52+
%% The first caller for a specific ip and port daemon will start the server
53+
-spec register(nkdocker:conn_opts(), module()) ->
54+
{ok, pid()} | {error, term()}.
55+
56+
register(Opts, Callback) ->
57+
case find(Opts) of
58+
{ok, Id, not_found} ->
59+
case nkdocker_sup:start_events(Id, Opts) of
60+
{ok, Pid} ->
61+
gen_server:call(Pid, {register, Callback});
62+
{error, Error} ->
63+
{error, Error}
64+
end;
65+
{ok, _Id, Pid} ->
66+
gen_server:call(Pid, {register, Callback});
67+
{error, Error} ->
68+
{error, Error}
69+
end.
70+
71+
72+
%% @doc Unregisters a callback
73+
%% After the last callback is unregistered, the server stops
74+
unregister(Opts, Callback) ->
75+
case find(Opts) of
76+
{ok, _Id, not_found} ->
77+
ok;
78+
{ok, _Id, Pid} ->
79+
gen_server:cast(Pid, {unregister, Callback});
80+
{error, Error} ->
81+
{error, Error}
82+
end.
83+
84+
85+
86+
%% @private
87+
start_link(Id, Opts) ->
88+
gen_server:start_link(?MODULE, [Id, Opts], []).
89+
90+
91+
%% @private
92+
find(Opts) ->
93+
case nkdocker_server:get_conn(Opts) of
94+
{ok, {Ip, #{port:=Port}}} ->
95+
Id = {Ip, Port},
96+
case nklib_proc:values({?MODULE, Id}) of
97+
[] ->
98+
{ok, Id, not_found};
99+
[{_, Pid}] ->
100+
{ok, Id, Pid}
101+
end;
102+
{error, Error} ->
103+
{error, Error}
104+
end.
105+
106+
107+
% ===================================================================
108+
%% gen_server behaviour
109+
%% ===================================================================
110+
111+
-record(state, {
112+
id :: id(),
113+
server :: pid(),
114+
ref :: reference(),
115+
regs = [] :: [module()]
116+
}).
117+
118+
119+
%% @private
120+
-spec init(term()) ->
121+
{ok, tuple()} | {ok, tuple(), timeout()|hibernate} |
122+
{stop, term()} | ignore.
123+
124+
init([Id, Opts]) ->
125+
true = nklib_proc:reg({?MODULE, Id}),
126+
case nkdocker_server:start_link(Opts) of
127+
{ok, Pid} ->
128+
case nkdocker:events(Pid) of
129+
{async, Ref} ->
130+
Regs = nkdocker_app:get(events_callbacks, []),
131+
case Regs of
132+
[] -> ok;
133+
_ -> ?LLOG(warning, "recovered callbacks: ~p", [Regs])
134+
end,
135+
{ok, #state{id=Id, server=Pid, ref=Ref, regs=Regs}};
136+
{error, Error} ->
137+
{stop, Error}
138+
end;
139+
{error, Error} ->
140+
{stop, Error}
141+
end.
142+
143+
144+
%% @private
145+
-spec handle_call(term(), {pid(), term()}, #state{}) ->
146+
{noreply, #state{}} | {reply, term(), #state{}} |
147+
{stop, Reason::term(), #state{}} | {stop, Reason::term(), Reply::term(), #state{}}.
148+
149+
handle_call({register, Module}, _From, #state{regs=Regs}=State) ->
150+
Regs2 = nklib_util:store_value(Module, Regs),
151+
nkdocker_app:put(events_callbacks, Regs2),
152+
{reply, {ok, self()}, State#state{regs=Regs2}};
153+
154+
handle_call(state, _From, State) ->
155+
{reply, State, State};
156+
157+
handle_call(Msg, _From, State) ->
158+
lager:error("Module ~p received unexpected call ~p", [?MODULE, Msg]),
159+
{noreply, State}.
160+
161+
162+
%% @private
163+
-spec handle_cast(term(), #state{}) ->
164+
{noreply, #state{}} | {stop, term(), #state{}}.
165+
166+
handle_cast({unregister, Module}, #state{regs=Regs}=State) ->
167+
case Regs -- [Module] of
168+
[] ->
169+
{stop, normal, State};
170+
Regs2 ->
171+
nkdocker_app:put(events_callbacks, Regs2),
172+
{noreply, State#state{regs=Regs2}}
173+
end;
174+
175+
handle_cast(Msg, State) ->
176+
lager:error("Module ~p received unexpected cast ~p", [?MODULE, Msg]),
177+
{noreply, State}.
178+
179+
180+
%% @private
181+
-spec handle_info(term(), #state{}) ->
182+
{noreply, #state{}} | {stop, term(), #state{}}.
183+
184+
handle_info({nkdocker, Ref, {data, Map}}, #state{ref=Ref}=State) ->
185+
{noreply, event(Map, State)};
186+
187+
handle_info(Info, State) ->
188+
lager:warning("Module ~p received unexpected info: ~p (~p)", [?MODULE, Info, State]),
189+
{noreply, State}.
190+
191+
192+
%% @private
193+
-spec code_change(term(), #state{}, term()) ->
194+
{ok, #state{}}.
195+
196+
code_change(_OldVsn, State, _Extra) ->
197+
{ok, State}.
198+
199+
200+
%% @private
201+
-spec terminate(term(), #state{}) ->
202+
ok.
203+
204+
terminate(Reason, _State) ->
205+
case Reason of
206+
normal ->
207+
?LLOG(notice, "server stop normal", []),
208+
nkdocker_app:put(events_callbacks, []);
209+
_ ->
210+
?LLOG(notice, "server stop anormal: ~p", [Reason]),
211+
ok
212+
end,
213+
ok.
214+
215+
216+
217+
% ===================================================================
218+
%% Internal
219+
%% ===================================================================
220+
221+
222+
event(#{<<"status">>:=Status, <<"id">>:=Id, <<"time">>:=Time}=Msg, State) ->
223+
From = maps:get(<<"from">>, Msg, <<>>),
224+
Event = {binary_to_atom(Status, latin1), Id, From, Time},
225+
#state{id=ServerId, regs=Regs} = State,
226+
lists:foreach(
227+
fun(Module) ->
228+
case catch Module:nkdocker_event(ServerId, Event) of
229+
ok ->
230+
ok;
231+
Error ->
232+
?LLOG(warning, "error calling ~p: ~p", [Module, Error])
233+
end
234+
end,
235+
Regs),
236+
State;
237+
238+
event(Event, State) ->
239+
lager:notice("Unrecognized event: ~p", [Event]),
240+
State.
241+
242+
243+
244+
245+

src/nkdocker_sup.erl

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,30 @@
2323
-author('Carlos Gonzalez <[email protected]>').
2424
-behaviour(supervisor).
2525

26-
-export([init/1, start_link/0]).
26+
-export([start_events/2, init/1, start_link/0]).
27+
28+
% -include("nkmedia.hrl").
29+
30+
start_events(Id, Opts) ->
31+
Spec = {
32+
{events, Id},
33+
{nkdocker_events, start_link, [Id, Opts]},
34+
permanent,
35+
5000,
36+
worker,
37+
[nkdocker_events]
38+
},
39+
case supervisor:start_child(?MODULE, Spec) of
40+
{ok, Pid} ->
41+
{ok, Pid};
42+
{error, already_present} ->
43+
ok = supervisor:delete_child(?MODULE, {events, Id}),
44+
start_events(Id, Opts);
45+
{error, {already_started, Pid}} ->
46+
{ok, Pid};
47+
{error, Error} ->
48+
{error, Error}
49+
end.
2750

2851

2952
%% @private

0 commit comments

Comments
 (0)