This commit is contained in:
Craig Everett 2018-04-23 07:58:56 +09:00
parent 66b65d3fd4
commit 2e9df29149
5 changed files with 206 additions and 80 deletions

42
TODO
View File

@ -1,24 +1,36 @@
- zomp nodes must report the realms they provide to upstream nodes to which they connect. - Make the create project command fail if the realm is invalid or the project bame already exists.
On redirect a list of hosts of the form [{zx:host(), [zx:realm()]}] must be provided to the downstream client.
This is the only way that downstream clients can determine which redirect hosts are useful to it.
- Change zx_daemon request references to counters. - Add a module name conflict checker to ZX
Count everything.
This will be the only way to sort of track stats other than log analysis.
Log analysis sucks.
- Double-indexing must happen everywhere for anything to be discoverable without traversing the entire state of zx_daemon whenever a client or conn crashes. - Check whether the "repo name doesn't have to be the same as package, package doesn't have to be the same as main interface module" statement is true.
- zx_daemon request() types have been changes to flat, wide tuples as in the main zx module. - Make the "create user" bundle command check whether a user already exists at that realm.
This change is not yet refected in the using code.
- Write a logging process. - Define the user bundle file contents: .zuf
Pick a log rotation scheme.
Eventually make it so that it can shed log loads in the event they get out of hand.
- Make it possible for a site administrator to declare specific versions for programs executed by clients.
Site adminisration via an organizational mirror should not be ricket science.
- zomp nodes must report the realms they provide to upstream nodes to which they connect.
On redirect a list of hosts of the form [{zx:host(), [zx:realm()]}] must be provided to the downstream client.
This is the only way that downstream clients can determine which redirect hosts are useful to it.
- Write a logging process.
Pick a log rotation scheme.
Eventually make it so that it can shed log loads in the event they get out of hand.
- Create zx_daemon primes.
See below
- Create persistent Windows alias for "zx" using the doskey command and adding it to the localuser's registry.
The installation escript will need to be updated to update the windows registry for HKEY_CURRENT_USER\Software\Microsoft\Command Processor
AutoRun will need to be set for %USERPROFILE%\zomp_alias.cmd
zomp_alias.cmd will need to do something like:
`doskey zx="werl.exe -pa %zx_dir%/ebin -run zx start $*"`
https://stackoverflow.com/questions/20530996/aliases-in-windows-command-prompt
Whatever happens, local users must be able to do `zx $@` and get the expected results.
Somewhat more tricky is how to make the creation of shortcuts less taxing on the lay user... ?
- Create zx_daemon primes.
See below
New Feature: ZX Universal lock New Feature: ZX Universal lock
Cross-instance communication Cross-instance communication

Binary file not shown.

View File

@ -2492,6 +2492,7 @@ usage() ->
" zx add package PackageName~n" " zx add package PackageName~n"
" zx add packager PackageName~n" " zx add packager PackageName~n"
" zx add maintainer PackageName~n" " zx add maintainer PackageName~n"
" zx add sysop UserID~n"
" zx review PackageID~n" " zx review PackageID~n"
" zx approve PackageID~n" " zx approve PackageID~n"
" zx reject PackageID~n" " zx reject PackageID~n"

View File

@ -1,4 +1,4 @@
%%% @doc %% @doc
%%% ZX Connector %%% ZX Connector
%%% %%%
%%% This module represents a connection to a Zomp server. %%% This module represents a connection to a Zomp server.
@ -11,12 +11,59 @@
-copyright("Craig Everett <zxq9@zxq9.com>"). -copyright("Craig Everett <zxq9@zxq9.com>").
-license("GPL-3.0"). -license("GPL-3.0").
-export([subscribe/2, unsubscribe/2, fetch/3, query/3]).
-export([start/1, stop/1]). -export([start/1, stop/1]).
-export([start_link/1]). -export([start_link/1, init/2]).
-include("zx_logger.erl"). -include("zx_logger.hrl").
%%% Types
-type incoming() :: ping
| {sub, Channel :: term(), Message :: term()}
| term().
%%% Interface
-spec subscribe(Conn, Package) -> ok
when Conn :: pid(),
Package :: zx:package().
subscribe(Conn, Realm) ->
Conn ! {subscribe, Realm},
ok.
-spec unsubscribe(Conn, Package) -> ok
when Conn :: pid(),
Package :: zx:package().
unsubscribe(Conn, Package) ->
Conn ! {unsubscribe, Package},
ok.
-spec fetch(Conn, ID, Object) -> ok
when Conn :: pid(),
ID :: zx_daemon:id(),
Object :: zx_daemon:object().
fetch(Conn, ID, Object) ->
Conn ! {fetch, ID, Object},
ok.
-spec query(Conn, ID, Action) -> ok
when Conn :: pid(),
ID :: zx_daemon:id(),
Action :: zx_daemon:action().
query(Conn, ID, Action) ->
Conn ! {query, ID, Action},
ok.
%%% Startup %%% Startup
@ -42,25 +89,7 @@ stop(Conn) ->
ok. ok.
-spec subscribe(Conn, Package) -> ok -spec start_link(Target) -> Result
when Conn :: pid(),
Package :: zx:package().
subscribe(Conn, Realm) ->
Conn ! {subscribe, Realm},
ok.
-spec unsubscribe(Conn, Package) -> ok
when Conn :: pid(),
Package :: zx:package().
unsubscribe(Conn, Package) ->
Conn ! {unsubscribe, Package},
ok.
-spec start_link(Target) ->
when Target :: zx:host(), when Target :: zx:host(),
Result :: {ok, pid()} Result :: {ok, pid()}
| {error, Reason}, | {error, Reason},
@ -94,13 +123,13 @@ init(Parent, Target) ->
Target :: zx:host(). Target :: zx:host().
connect(Parent, Debug, {Host, Port}) -> connect(Parent, Debug, {Host, Port}) ->
Options = [{packet, 4}, {mode, binary}, {active, true}], Options = [{packet, 4}, {mode, binary}, {nodelay, true}, {active, true}],
case gen_tcp:connect(Host, Port, Options, 5000) of case gen_tcp:connect(Host, Port, Options, 5000) of
{ok, Socket} -> {ok, Socket} ->
confirm_service(Parent, Debug, Socket); confirm_service(Parent, Debug, Socket);
{error, Error} -> {error, Error} ->
ok = log(warning, "Connection problem with ~tp: ~tp", [Node, Error]), ok = log(warning, "Connection problem with ~tp: ~tp", [Host, Error]),
ok = zx_daemon:report(disconnected) ok = zx_daemon:report(failed),
terminate() terminate()
end. end.
@ -170,12 +199,23 @@ query_realms(Parent, Debug, Socket) ->
loop(Parent, Debug, Socket) -> loop(Parent, Debug, Socket) ->
receive receive
{tcp, Socket, Bin} -> {tcp, Socket, Bin} ->
ok = handle(Bin, Socket), ok = handle_message(Socket, Bin),
ok = inet:setopts(Socket, [{active, once}]), ok = inet:setopts(Socket, [{active, once}]),
loop(Parent, Debug, Socket); loop(Parent, Debug, Socket);
{subscribe, Package} -> {subscribe, Package} ->
ok = zx_net:send(Socket, {subscribe, Package}),
loop(Parent, Debug, Socket);
{unsubscribe, Package} -> {unsubscribe, Package} ->
ok = zx_net:send(Socket, {unsubscribe, Package}),
loop(Parent, Debug, Socket);
{fetch, ID, Object} ->
{ok, Outcome} = handle_fetch(Socket, Object),
ok = zx_daemon:result(ID, Outcome),
loop(Parent, Debug, Socket);
{query, ID, Action} ->
{ok, Outcome} = handle_query(Socket, Action),
ok = zx_daemon:result(ID, Outcome),
loop(Parent, Debug, Socket);
stop -> stop ->
ok = zx_net:disconnect(Socket), ok = zx_net:disconnect(Socket),
terminate(); terminate();
@ -185,41 +225,106 @@ loop(Parent, Debug, Socket) ->
end. end.
-spec handle(Bin, Socket) -> ok | no_return()
when Bin :: binary(), %%% Idle Incoming Upstream Messages
Socket :: gen_tcp:socket().
-spec handle_message(Socket, Bin) -> ok | no_return()
when Socket :: gen_tcp:socket(),
Bin :: binary().
%% @private %% @private
%% Single point to convert a binary message to a safe internal message. Actual handling %% Single point to convert a binary message to a safe internal message. Actual handling
%% of the converted message occurs in dispatch/2. %% of the converted message occurs in dispatch/2.
handle(Bin, Socket) -> handle_message(Socket, Bin) ->
Message = binary_to_term(Bin, [safe]), Message = binary_to_term(Bin, [safe]),
ok = log(info, "Received network message: ~tp", [Message]), ok = log(info, "Received network message: ~tp", [Message]),
dispatch(Message, Socket). case binary_to_term(Bin, [safe]) of
ping ->
zx_net:send(Socket, pong);
{sub, Channel, Message} ->
log("Sub: ~tp - ~tp", [Channel, Message]);
{update, Message} ->
log("Update: ~tp", [Message]);
{redirect, Nodes} ->
log("Redirected to ~tp", [Nodes]);
Invalid ->
{ok, {Addr, Port}} = zomp:peername(Socket),
Host = inet:ntoa(Addr),
ok = log(warning, "Invalid message from ~tp:~p: ", [Invalid]),
ok = zx_net:disconnect(Socket),
terminate()
end.
-spec dispatch(Message, Socket) -> ok | no_return()
when Message :: incoming(),
Socket :: gen_tcp:socket().
%% @private
%% Dispatch a procedure based on the received message.
%% Tranfers and other procedures that involve a sequence of messages occur in discrete
%% states defined in other functions -- this only dispatches based on a valid initial
%% message received in the default waiting-loop state.
dispatch(ping, Socket) -> %%% Incoming Request Actions
zx_net:send(Socket, pong);
dispatch(Invalid, Socket) -> -spec handle_request(Socket, Action) -> Result
{ok, {Addr, Port}} = zomp:peername(Socket), when Socket :: gen_tcp:socket(),
Host = inet:ntoa(Addr), Action :: term(),
ok = log(warning, "Invalid message from ~tp:~p: ", [Invalid]), Result :: {ok, Outcome :: term()}.
ok = zx_net:disconnect(Socket),
terminate(). handle_request(Socket, Action) ->
ok = zx_net:send(Socket, Action),
case element(1, Action) of
list ->
do_list(Action, Socket);
latest ->
do_latest(Action, Socket);
fetch ->
do_fetch(Action, Socket);
key ->
do_key(Action, Socket);
pending ->
do_pending(Action, Socket);
packagers ->
do_packagers(Action, Socket);
maintainers ->
do_maintainers(Action, Socket);
sysops ->
do_sysops(Action, Socket)
end,
handle_response(Socket, Response).
handle_response(Socket, Command) ->
receive
{tcp, Socket, Bin} ->
Outcome = binary_to_term(Bin, [safe]),
interpret_response(Socket, Outcome, Command);
{tcp_closed, Socket} ->
handle_unexpected_close()
after 5000 ->
handle_timeout(Socket)
end.
interpret_response(Socket, ping, Command) ->
ok = zx_net:send(Socket, pong),
handle_response(Socket, Command);
interpret_response(Socket, {sub, Channel, Message}, Command) ->
ok = zx_daemon:notify(Channel, Message),
handle_response(Socket, Command);
interpret_response(Socket, {update, Message}, Command) ->
interpret_response(Socket, Response, list) ->
interpret_response(Socket, Response, latest) ->
interpret_response(Socket, Response, fetch) ->
interpret_response(Socket, Response, key) ->
interpret_response(Socket, Response, pending) ->
interpret_response(Socket, Response, packagers) ->
interpret_response(Socket, Response, maintainers) ->
interpret_response(Socket, Response, sysops) ->
case element(1, Action) of
end,
-spec fetch(Socket, PackageID) -> Result -spec fetch(Socket, PackageID) -> Result
when Socket :: gen_tcp:socket(), when Socket :: gen_tcp:socket(),
PackageID :: package_id(), PackageID :: zx:package_id(),
Result :: ok. Result :: ok.
%% @private %% @private
%% Download a package to the local cache. %% Download a package to the local cache.
@ -227,13 +332,14 @@ dispatch(Invalid, Socket) ->
fetch(Socket, PackageID) -> fetch(Socket, PackageID) ->
{ok, LatestID} = request_zrp(Socket, PackageID), {ok, LatestID} = request_zrp(Socket, PackageID),
ok = receive_zrp(Socket, LatestID), ok = receive_zrp(Socket, LatestID),
log(info, "Fetched ~ts", [package_string(LatestID)]). Latest = zx_lib:package_string(LatestID),
log(info, "Fetched ~ts", [Latest]).
-spec request_zrp(Socket, PackageID) -> Result -spec request_zrp(Socket, PackageID) -> Result
when Socket :: gen_tcp:socket(), when Socket :: gen_tcp:socket(),
PackageID :: package_id(), PackageID :: zx:package_id(),
Result :: {ok, Latest :: package_id()} Result :: {ok, Latest :: zx:package_id()}
| {error, Reason :: timeout | term()}. | {error, Reason :: timeout | term()}.
request_zrp(Socket, PackageID) -> request_zrp(Socket, PackageID) ->
@ -244,7 +350,7 @@ request_zrp(Socket, PackageID) ->
{sending, LatestID} -> {sending, LatestID} ->
{ok, LatestID}; {ok, LatestID};
Error = {error, Reason} -> Error = {error, Reason} ->
PackageString = package_string(PackageID), PackageString = zx_lib:package_string(PackageID),
Message = "Error receiving package ~ts: ~tp", Message = "Error receiving package ~ts: ~tp",
ok = log(info, Message, [PackageString, Reason]), ok = log(info, Message, [PackageString, Reason]),
Error Error
@ -258,7 +364,7 @@ request_zrp(Socket, PackageID) ->
-spec receive_zrp(Socket, PackageID) -> Result -spec receive_zrp(Socket, PackageID) -> Result
when Socket :: gen_tcp:socket(), when Socket :: gen_tcp:socket(),
PackageID :: package_id(), PackageID :: zx:package_id(),
Result :: ok | {error, timeout}. Result :: ok | {error, timeout}.
receive_zrp(Socket, PackageID) -> receive_zrp(Socket, PackageID) ->
@ -286,11 +392,11 @@ handle_unexpected_close() ->
terminate(). terminate().
-spec handle_timeout(gen_tcp:socket()) -> no_return() -spec handle_timeout(gen_tcp:socket()) -> no_return().
handle_timeout(Socket) -> handle_timeout(Socket) ->
ok = zx_daemon:report(timeout), ok = zx_daemon:report(timeout),
ok = disconnect(Socket), ok = zx_net:disconnect(Socket),
terminate(). terminate().

View File

@ -218,7 +218,9 @@
%% Conn Communication %% Conn Communication
-type conn_report() :: {connected, Realms :: [{zx:realm(), zx:serial()}]} -type conn_report() :: {connected, Realms :: [{zx:realm(), zx:serial()}]}
| {redirect, Hosts :: [zx:host()]} | {redirect, Hosts :: [zx:host()]}
| disconnected. | failed
| disconnected
| timeout.
%% Subscriber / Requestor Communication %% Subscriber / Requestor Communication
% Incoming Request messages % Incoming Request messages
@ -229,8 +231,9 @@
| {list, zx:realm(), zx:name(), zx:version()} | {list, zx:realm(), zx:name(), zx:version()}
| {latest, zx:realm(), zx:name(), zx:version()} | {latest, zx:realm(), zx:name(), zx:version()}
| {fetch, zx:realm(), zx:name(), zx:version()} | {fetch, zx:realm(), zx:name(), zx:version()}
| {key, zx:realm(), zx:key_name()} | {fetchkey, zx:realm(), zx:key_name()}
| {pending, zx:realm(), zx:name()} | {pending, zx:realm(), zx:name()}
| {resigns, zx:realm()}
| {packagers, zx:realm(), zx:name()} | {packagers, zx:realm(), zx:name()}
| {maintainers, zx:realm(), zx:name()} | {maintainers, zx:realm(), zx:name()}
| {sysops, zx:realm()}. | {sysops, zx:realm()}.
@ -447,14 +450,14 @@ latest({Realm, Name, Version}) ->
%% Response messages are of the type `result()' where the third element is of the %% Response messages are of the type `result()' where the third element is of the
%% type `fetch_result()'. %% type `fetch_result()'.
fetch({Realm, Name, Version}) -> fetch_zsp(PackageID = {Realm, Name, Version}) ->
true = zx_lib:valid_lower0_9(Realm), true = zx_lib:valid_lower0_9(Realm),
true = zx_lib:valid_lower0_9(Name), true = zx_lib:valid_lower0_9(Name),
true = zx_lib:valid_version(Version), true = zx_lib:valid_version(Version),
request({fetch, Realm, Name, Version}). request({fetch, zsp, PackageID}).
-spec key(KeyID) -> {ok, RequestID} -spec fetch_key(KeyID) -> {ok, RequestID}
when KeyID :: zx:key_id(), when KeyID :: zx:key_id(),
RequestID :: id(). RequestID :: id().
%% @doc %% @doc
@ -464,10 +467,10 @@ fetch({Realm, Name, Version}) ->
%% Response messages are of the type `result()' where the third element is of the %% Response messages are of the type `result()' where the third element is of the
%% type `key_result()'. %% type `key_result()'.
key({Realm, KeyName}) -> fetch_key(KeyID = {Realm, KeyName}) ->
true = zx_lib:valid_lower0_9(Realm), true = zx_lib:valid_lower0_9(Realm),
true = zx_lib:valid_lower0_9(KeyName), true = zx_lib:valid_lower0_9(KeyName),
request({key, Realm, KeyName}). request({fetch, key, KeyID}).
-spec pending(Package) -> {ok, RequestID} -spec pending(Package) -> {ok, RequestID}
@ -561,7 +564,7 @@ report(Message) ->
gen_server:cast(?MODULE, {report, self(), Message}). gen_server:cast(?MODULE, {report, self(), Message}).
-spec result(reference(), result()) -> ok. -spec result(id(), result()) -> ok.
%% @private %% @private
%% Return a tagged result back to the daemon to be forwarded to the original requestor. %% Return a tagged result back to the daemon to be forwarded to the original requestor.
@ -839,6 +842,10 @@ do_report(Conn, failed, State = #s{mx = MX}) ->
NewMX = mx_del_monitor(Conn, attempt, MX), NewMX = mx_del_monitor(Conn, attempt, MX),
failed(Conn, State#s{mx = NewMX}); failed(Conn, State#s{mx = NewMX});
do_report(Conn, disconnected, State = #s{mx = MX}) -> do_report(Conn, disconnected, State = #s{mx = MX}) ->
NewMX = mx_del_monitor(Conn, conn, MX),
disconnected(Conn, State#s{mx = NewMX});
do_report(Conn, timeout, State = #s{mx = MX}) ->
ok = log(warning, "Connection ~tp timed out.", [Conn]),
NewMX = mx_del_monitor(Conn, conn, MX), NewMX = mx_del_monitor(Conn, conn, MX),
disconnected(Conn, State#s{mx = NewMX}). disconnected(Conn, State#s{mx = NewMX}).
@ -1109,7 +1116,7 @@ dispatch_request(Action, ID, CX) ->
Realm = element(2, Action), Realm = element(2, Action),
case cx_pre_send(Realm, ID, CX) of case cx_pre_send(Realm, ID, CX) of
{ok, Conn, NewCX} -> {ok, Conn, NewCX} ->
ok = zx_conn:make_request(Conn, ID, Action), ok = zx_conn:request(Conn, ID, Action),
{dispatched, NewCX}; {dispatched, NewCX};
unassigned -> unassigned ->
wait; wait;