Harp daemon architecture¶
TODO
Operator-facing modules¶
Internal modules¶
Network server and daemonization operations¶
-
class
harpd.daemon.
SSLServer
(host, port, procs, authdb, cert_file, key_file, ca_file=None)¶ Parameters: - host (string or
None
) – address to bind to - port (integer) – port to listen on
- procs (dict(name->callable)) – table with procedures to expose
- cert_file (path) – X.509 certificate
- key_file (path) – private key for
cert_file
- ca_file (path) – file with all X.509 CA certificates
SSL connection server. Uses
RequestHandler
to handle SSL connections.-
close_request
(client_socket)¶ Parameters: client_socket – socket to close Close the client socket, but without tearing down the connection (e.g. SSL shutdown handshake).
In forking SocketServer, this is called in parent (listener) process, and may be called by implementation of
shutdown_request()
.
-
fileno
()¶ Returns: file descriptor Return file descriptor to wait for I/O events (
poll()
).
-
finish_request
(*args, **kwargs)¶ Main work method. Creates a
RequestHandler
instance and does all the processing.This method additionally closes listening socket just before passing the control to
RequestHandler
, since it’s executed in the child process and not in parent.
-
get_request
()¶ Returns: client socket and client address (IP+port) Return type: 2-tuple (socket, address) Accept a new connection.
-
handle_signal
(signum, stack_frame)¶ Signal handler for
signal.signal()
function. Callssys.exit(0)
, in daemon’s main process additionally forwarding signal to all the children.
-
handle_tick
(signum, stack_frame)¶ Signal handler for SIGALRM signal loop. The handler advances clock in
TimeoutQueue
, makes it receive any outstanding delayed kill requests, and kills any children that have their timeouts fired (SIGXCPU is the signal used here).Function sets up another timer to fire in one second.
-
server_activate
()¶ Prepare server for accepting connections.
-
server_bind
()¶ Bind server to its socket.
-
server_close
()¶ Shutdown the server.
-
set_timeout
(timeout)¶ Parameters: timeout – time after which the process will be killed or None
Request a delayed kill and close the communication channel with parent process.
-
shutdown_request
(client_socket)¶ Parameters: client_socket – socket where client connection is to be terminated Properly close the client socket, with notifying the client.
In forking SocketServer, this happens in child (worker) process.
- host (string or
-
class
harpd.daemon.
RequestHandler
(request, client_address, server)¶ HarpRPC call request handler.
Attributes defined by parent class:
-
request
¶ client socket, as returned by
SSLServer.get_request()
in the first field
-
client_address
¶ client address
(IP, port)
, as returned bySSLServer.get_request()
in the second field
-
exception
RequestError
(type, message, data=None)¶ Request processing error. Note that this is a different thing than exception raised in called procedure’s code.
-
struct
()¶ Return the error as a dict suitable for transmitting to client.
-
-
exception
RequestHandler.
Timeout
¶ Signal that execution time expired. This exception is thrown from SIGXCPU handler.
-
RequestHandler.
finish
()¶ Clean up request handler after work.
-
RequestHandler.
handle
()¶ Handle the connection. This means reading client’s request, processing it, and sending results back.
-
RequestHandler.
handle_call
(proc_name, arguments)¶ Parameters: - proc_name (string) – name of the procedure to call
- arguments (list or dict) – arguments (positional or named) to the called procedure
Handle a procedure call request.
-
RequestHandler.
log
(message, **context)¶ Parameters: - message – message to log
- context – additional attributes to attach to the log
Log a message using
logging
module.
-
RequestHandler.
read_request
()¶ Returns: procedure name, its arguments, and authentication data Return type: tuple ( RequestHandler.Call
,RequestHandler.Credentials
)Raise: RequestHandler.RequestError
Read call request from socket.
-
RequestHandler.
send
(data, ignore_network_errors=False)¶ Parameters: data (dict or RequestHandler.RequestError
) – response to sendSend response (stream, returned value, exception, error) to client.
-
RequestHandler.
set_timeout
(timeout)¶ Parameters: timeout – timeout or None
if no timeout applicableSetup a timeout for this process.
-
RequestHandler.
setguid
(uid, gid)¶ Parameters: - uid (string, integer, or
None
) – user/UID to change to - gid (string, integer, or
None
) – group/UID to change to
Set UID/GID (and supplementary groups) to whatever was provided. If UID was specified as a name and GID is
None
, GID defaults to primary group of the user. In any other caseNone
means “don’t change”.- uid (string, integer, or
-
RequestHandler.
setup
()¶ Prepare request handler instance for work.
-
-
class
harpd.daemon.
Daemon
(pidfile=None, detach=False)¶ Daemonization helper. If detach was requested,
os.fork()
is called, the parent process waits for start confirmation (confirm()
) and exits with code of0
(1
if no confirmation was received).Child process changes its working directory to
/
and after confirmation, closes its STDIN, STDOUT, and STDERR (this way any uncaught exception is printed to terminal).Parameters: - pidfile – pidfile path
- detach – whether the daemon should detach from terminal or not
-
class
PidFile
(filename)¶ Handle of a pidfile.
Creating an instance of this class automatically registers
close()
asatexit
handler. Seerelease()
if you want to detach your daemon from terminal.Parameters: filename – pidfile path -
close
()¶ Close and delete pidfile, if a pidfile is open.
-
release
()¶ Close pidfile without deleting it.
To be used in parent process when detaching from terminal.
-
update
()¶ Update PID stored in pidfile to the one of this process.
-
-
Daemon.
confirm
()¶ Confirm to parent that initialization was successful and daemon can continue work from here.
Method to be called in child process when detaching from terminal.
-
class
harpd.daemon.
TimeoutQueue
¶ Combined queue for delayed kill requests, interprocess channel for such requests, and a system-independent clock.
-
close
()¶ Close the sockets.
-
kill_ready
()¶ Returns: list of PIDs Pop from the queue all the processes that requested being killed.
Note that this queue does not keep track of which children terminated and which are alive. Caller needs to check this manually (a process could have spawned with the same PID as some terminated child).
-
receive_kill_requests
()¶ Read all the delayed kill requests sent from child processes and add them to internal queue.
Use
kill_ready()
to check which kill requests are ready to be realized.
-
static
socketpair
()¶ Returns: (read_end, write_end) Create a pair of AF_UNIX sockets. These sockets are unidirectional.
-
tick
()¶ Advance internal system-independent clock by one second.
-
timeout
(after)¶ Parameters: after – number of seconds after which the process should be killed Request a delayed kill.
-
Module loader module¶
Module loader can load Python modules as well as snippets stored in arbitrary files. The latter enables storing configuration as a Python code.
Note that typically it’s a better idea to store configuration in INI, YAML or JSON files than in Python code, because it’s easier to generate and process in other tools and even languages.
-
class
harpd.module.
ModuleLoader
¶ Module loader. It can load module from
sys.path
or a code snippet from outside.ModuleLoader
is a context manager.-
close
()¶ Clean up temporary directory. This function is also called on object destruction, so there’s no need (but no harm, either) to call it separately.
-
load
(name, file=None)¶ Parameters: - name – name of the module to load
- file – module’s file name
Returns: imported module’s handle
Load specified module and return its handle. Module can be loaded from outside of
sys.path
(e.g. from/etc
) by providing its file name. (In such case, no*.pyc
is stored along the original file.)NOTE: Specifying a
name
under non-existent hierarchy may cause a warning to be issued. Better stick to a name that exists except for the last component, e.g.harpd.__config__
.
-
Interface for authentication backends¶
-
class
harpd.auth.
AuthDB
¶ Base class for authentication objects. Instance of this class (or rather, a subclass of this class) should be returned by
create()
.-
authenticate
(user, password)¶ Returns: True
ifuser
+password
pair was correct,False
otherwiseCheck if the
password
was valid for specifieduser
.
-
Communication protocol¶
Protocol is based on a line-wise JSON. Each message is serialized to a single line. The protocol is synchronous, i.e., client waits for a response after each request.
Since HarpRPC is not planned to be a heavy duty service, requests are intended to be carried in separate connections. Cancelling a request boils down to simply closing the connection before receiving result.
Call request¶
Request¶
Call with positional arguments:
{"harp": 1, "procedure": "...", "arguments": [...], "auth": {"user": "...", "password": "..."}}
Call with named arguments:
{"harp": 1, "procedure": "...", "arguments": {...}, "auth": {"user": "...", "password": "..."}}
Call with no arguments:
{"harp": 1, "procedure": "...", "arguments": [], "auth": {"user": "...", "password": "..."}}
Each call request needs to be authenticated. Currently, the only authentication method is username+password pair.
Acknowledgement¶
Call request is immediately acknowledged with a message indicating whether there will be a streamed result or not. The message looks as follows:
{"harp": 1, "stream_result": true | false}
Transport-level and daemon operational errors¶
If the request is ill-formatted, contains invalid data, points to a non-existing function, carries incorrect number of arguments, or there are other server-side problems with execution of the procedure, error is returned instead of or after acknowledgement (possibly in the middle of stream result):
{"harp": 1, "error": {"type": "...", "message": "...", "data": ...}}
"type"
indicates the kind of the error, in similar fashion as errno
in
unix system calls (except for being string). "message"
is more detailed
string that is readable to human. "data"
key is optional and may carry any
value that could help in troubleshooting. Structured data (i.e. JSON object)
is preferred.
Predefined values of "type"
are:
"parse_error"
– request line is not a valid JSON"invalid_protocol"
–"harp"
field from the request is missing or carries wrong value"invalid_request"
– any other error with request structure"auth_error"
– user doesn’t exist or password is invalid"no_such_procedure"
– requested procedure does not exist"procedure_loading_error"
– requested procedure could not be loaded"invalid_argument_list"
– argument list does not match the signature of the requested procedure
Other values of "type"
are allowed.
Response¶
After acknowledgement message, one or more messages with the result is sent.
Single result:
{"result": ...}
Streamed result:
{"stream": ...}
{"stream": ...}
...
{"result": ...}
Exception raised in the procedure is reported by sending following message
instead of {"result":...}
:
{"exception": {"type": "...", "message": "...", "data": ...}}
Meaning and format of these fields is similar to protocol-level error
message. "data"
is an optional field here as well.
Streamed result always ends with either {"result":...}
,
{"exception":...}
, or {"error":..., "harp": 1}
message.