Switch the API to use a Unix Domain Socket

This drops support for the TCP port and switches to using a socket at
/var/run/weldr/api.socket

Also add the start of some docs for lorax-composer.

--host and --port argument have been removed.

--group sets the group name to use for access to the socket and its
parent directory. Defaults to 'weldr'

--socket sets the full path to the socket to create. Defaults to
'/var/run/weldr/api.socket'
This commit is contained in:
Brian C. Lane 2017-12-20 14:31:17 -08:00
parent 3c18a63f76
commit 33c5d0ce4a
3 changed files with 77 additions and 6 deletions

View File

@ -14,6 +14,7 @@ Contents:
intro intro
lorax lorax
livemedia-creator livemedia-creator
lorax-composer
product-images product-images
modules modules

37
docs/lorax-composer.rst Normal file
View File

@ -0,0 +1,37 @@
lorax-composer
==============
:Authors:
Brian C. Lane <bcl@redhat.com>
lorax-composer is an API server that is compatible with the Weldr project's
bdcs-api REST protocol. More information on Weldr can be found [on the Weldr
blog](https://www.weldr.io).
The server runs as root, and communication with it is via a unix domain socket
(``/run/weldr/api.socket`` by default). The directory and socket are owned by
root:weldr so that any user in the weldr group can use the API to control
lorax-composer.
When starting the server it will check for the correct permissions and
ownership of pre-existing directory, or it will create a new one if none exist.
The socket path and group owner's name can be changed from the cmdline by
passing it the ``--socket`` and ``--group`` arguments.
Logs
----
Logs are stored under ``/var/log/lorax-composer/`` and includes all console
messages as well as extra debugging info and API requests.
Quickstart
----------
1. Create a ``weldr`` group by running ``groupadd weldr``
2. Remove any pre-existing socket directory with ``rm -rf /run/weldr/``
A new directory with correct permissions will be created the first time the server runs.
3. Either start it via systemd with ``systemctl start lorax-composer`` or
run it directly with ``lorax-composer /path/to/recipes/``
The ``/path/to/recipes/`` is where the recipe's git repo will be created, and all
the recipes created with the ``/api/v0/recipes/new`` route will be stored.

View File

@ -25,9 +25,11 @@ server_log = logging.getLogger("server")
yum_log = logging.getLogger("yum") yum_log = logging.getLogger("yum")
import argparse import argparse
import grp
import os import os
import sys import sys
from threading import Lock from threading import Lock
from gevent import socket
from gevent.wsgi import WSGIServer from gevent.wsgi import WSGIServer
from pylorax import vernum from pylorax import vernum
@ -44,10 +46,10 @@ def get_parser():
parser = argparse.ArgumentParser(description="Lorax Composer API Server", parser = argparse.ArgumentParser(description="Lorax Composer API Server",
fromfile_prefix_chars="@") fromfile_prefix_chars="@")
parser.add_argument("--host", default="127.0.0.1", metavar="HOST", parser.add_argument("--socket", default="/run/weldr/api.socket", metavar="SOCKET",
help="Host or IP to bind to (127.0.0.1)") help="Path to the socket file to listen on")
parser.add_argument("--port", default=4000, metavar="PORT", parser.add_argument("--group", default="weldr", metavar="GROUP",
help="Port to bind to (4000)") help="Group to set ownership of the socket to")
parser.add_argument("--log", dest="logfile", default="/var/log/lorax-composer/composer.log", metavar="LOG", parser.add_argument("--log", dest="logfile", default="/var/log/lorax-composer/composer.log", metavar="LOG",
help="Path to logfile (/var/log/lorax-composer/composer.log)") help="Path to logfile (/var/log/lorax-composer/composer.log)")
parser.add_argument("--mockfiles", default="/var/tmp/bdcs-mockfiles/", metavar="MOCKFILES", parser.add_argument("--mockfiles", default="/var/tmp/bdcs-mockfiles/", metavar="MOCKFILES",
@ -129,6 +131,29 @@ if __name__ == '__main__':
setup_logging(opts.logfile) setup_logging(opts.logfile)
log.debug("opts=%s", opts) log.debug("opts=%s", opts)
# Check to make sure the group exists and get its gid
try:
gid = grp.getgrnam(opts.group).gr_gid
except KeyError:
log.error("Missing group '%s'", opts.group)
sys.exit(1)
# Check the socket path to make sure it exists, and that ownership and permissions are correct.
socket_dir = os.path.dirname(opts.socket)
if not os.path.exists(socket_dir):
# Create the directory and set permissions and ownership
os.makedirs(socket_dir, 0o750)
os.chown(socket_dir, 0, gid)
sockdir_stat = os.stat(socket_dir)
if sockdir_stat.st_mode & 0o007 != 0:
log.error("Incorrect permissions on %s, no 'other' permissions are allowed.")
sys.exit(1)
if sockdir_stat.st_gid != gid or sockdir_stat.st_uid != 0:
log.error("%s should be owned by root:%s", socket_dir, opts.group)
sys.exit(1)
if not os.path.isdir(opts.RECIPES): if not os.path.isdir(opts.RECIPES):
log.warn("Creating empty recipe directory at %s", opts.RECIPES) log.warn("Creating empty recipe directory at %s", opts.RECIPES)
os.makedirs(opts.RECIPES) os.makedirs(opts.RECIPES)
@ -149,8 +174,16 @@ if __name__ == '__main__':
# Import example recipes # Import example recipes
commit_recipe_directory(server.config["GITLOCK"].repo, "master", opts.RECIPES) commit_recipe_directory(server.config["GITLOCK"].repo, "master", opts.RECIPES)
log.info("Starting %s on %s:%d with recipes from %s", VERSION, opts.host, opts.port, opts.RECIPES) # Setup the Unix Domain Socket, remove old one, set ownership and permissions
if os.path.exists(opts.socket):
os.unlink(opts.socket)
listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
listener.bind(opts.socket)
os.chmod(opts.socket, 0o660)
os.chown(opts.socket, 0, gid)
listener.listen(1)
http_server = WSGIServer((opts.host, opts.port), server, log=LogWrapper(server_log)) log.info("Starting %s on %s with recipes from %s", VERSION, opts.socket, opts.RECIPES)
http_server = WSGIServer(listener, server, log=LogWrapper(server_log))
# The server writes directly to a file object, so point to our log directory # The server writes directly to a file object, so point to our log directory
http_server.serve_forever() http_server.serve_forever()