|
Packit Service |
3a6627 |
"""entrypoint - Containerized OSBuild Composer
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
This provides the entrypoint for a containerized osbuild-composer image. It
|
|
Packit Service |
3a6627 |
spawns `osbuild-composer` on start and manages it until it exits. The main
|
|
Packit Service |
3a6627 |
purpose of this entrypoint is to prepare everything to be usable from within
|
|
Packit Service |
3a6627 |
a container.
|
|
Packit Service |
3a6627 |
"""
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
import argparse
|
|
Packit Service |
3a6627 |
import contextlib
|
|
Packit Service |
3a6627 |
import os
|
|
Packit Service |
3a6627 |
import socket
|
|
Packit Service |
3a6627 |
import subprocess
|
|
Packit Service |
3a6627 |
import sys
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
class Cli(contextlib.AbstractContextManager):
|
|
Packit Service |
3a6627 |
"""Command Line Interface"""
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
def __init__(self, argv):
|
|
Packit Service |
3a6627 |
self.args = None
|
|
Packit Service |
3a6627 |
self._argv = argv
|
|
Packit Service |
3a6627 |
self._exitstack = None
|
|
Packit Service |
3a6627 |
self._parser = None
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
def _parse_args(self):
|
|
Packit Service |
3a6627 |
self._parser = argparse.ArgumentParser(
|
|
Packit Service |
3a6627 |
add_help=True,
|
|
Packit Service |
3a6627 |
allow_abbrev=False,
|
|
Packit Service |
3a6627 |
argument_default=None,
|
|
Packit Service |
3a6627 |
description="Containerized OSBuild Composer",
|
|
Packit Service |
3a6627 |
prog="container/osbuild-composer",
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
# --[no-]composer-api
|
|
Packit Service |
3a6627 |
self._parser.add_argument(
|
|
Packit Service |
3a6627 |
"--composer-api",
|
|
Packit Service |
3a6627 |
action="store_true",
|
|
Packit Service |
3a6627 |
dest="composer_api",
|
|
Packit Service |
3a6627 |
help="Enable the composer-API",
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
self._parser.add_argument(
|
|
Packit Service |
3a6627 |
"--no-composer-api",
|
|
Packit Service |
3a6627 |
action="store_false",
|
|
Packit Service |
3a6627 |
dest="composer_api",
|
|
Packit Service |
3a6627 |
help="Disable the composer-API",
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
self._parser.add_argument(
|
|
Packit Service |
3a6627 |
"--composer-api-port",
|
|
Packit Service |
3a6627 |
type=int,
|
|
Packit Service |
3a6627 |
default=443,
|
|
Packit Service |
3a6627 |
dest="composer_api_port",
|
|
Packit Service |
3a6627 |
help="Port which the composer-API listens on",
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
# --[no-]local-worker-api
|
|
Packit Service |
3a6627 |
self._parser.add_argument(
|
|
Packit Service |
3a6627 |
"--local-worker-api",
|
|
Packit Service |
3a6627 |
action="store_true",
|
|
Packit Service |
3a6627 |
dest="local_worker_api",
|
|
Packit Service |
3a6627 |
help="Enable the local-worker-API",
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
self._parser.add_argument(
|
|
Packit Service |
3a6627 |
"--no-local-worker-api",
|
|
Packit Service |
3a6627 |
action="store_false",
|
|
Packit Service |
3a6627 |
dest="local_worker_api",
|
|
Packit Service |
3a6627 |
help="Disable the local-worker-API",
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
# --[no-]remote-worker-api
|
|
Packit Service |
3a6627 |
self._parser.add_argument(
|
|
Packit Service |
3a6627 |
"--remote-worker-api",
|
|
Packit Service |
3a6627 |
action="store_true",
|
|
Packit Service |
3a6627 |
dest="remote_worker_api",
|
|
Packit Service |
3a6627 |
help="Enable the remote-worker-API",
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
self._parser.add_argument(
|
|
Packit Service |
3a6627 |
"--no-remote-worker-api",
|
|
Packit Service |
3a6627 |
action="store_false",
|
|
Packit Service |
3a6627 |
dest="remote_worker_api",
|
|
Packit Service |
3a6627 |
help="Disable the remote-worker-API",
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
# --[no-]weldr-api
|
|
Packit Service |
3a6627 |
self._parser.add_argument(
|
|
Packit Service |
3a6627 |
"--weldr-api",
|
|
Packit Service |
3a6627 |
action="store_true",
|
|
Packit Service |
3a6627 |
dest="weldr_api",
|
|
Packit Service |
3a6627 |
help="Enable the weldr-API",
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
self._parser.add_argument(
|
|
Packit Service |
3a6627 |
"--no-weldr-api",
|
|
Packit Service |
3a6627 |
action="store_false",
|
|
Packit Service |
3a6627 |
dest="weldr_api",
|
|
Packit Service |
3a6627 |
help="Disable the weldr-API",
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
self._parser.set_defaults(
|
|
Packit Service |
3a6627 |
builtin_worker=False,
|
|
Packit Service |
3a6627 |
composer_api=False,
|
|
Packit Service |
3a6627 |
local_worker_api=False,
|
|
Packit Service |
3a6627 |
remote_worker_api=False,
|
|
Packit Service |
3a6627 |
weldr_api=False,
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
return self._parser.parse_args(self._argv[1:])
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
def __enter__(self):
|
|
Packit Service |
3a6627 |
self._exitstack = contextlib.ExitStack()
|
|
Packit Service |
3a6627 |
self.args = self._parse_args()
|
|
Packit Service |
3a6627 |
return self
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
def __exit__(self, exc_type, exc_value, exc_tb):
|
|
Packit Service |
3a6627 |
self._exitstack.close()
|
|
Packit Service |
3a6627 |
self._exitstack = None
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
def _prepare_sockets(self):
|
|
Packit Service |
3a6627 |
# Prepare all the API sockets that osbuild-composer expectes, and make
|
|
Packit Service |
3a6627 |
# sure to pass them according to the systemd socket-activation API.
|
|
Packit Service |
3a6627 |
#
|
|
Packit Service |
3a6627 |
# Note that we rely on this being called early, so we get the correct
|
|
Packit Service |
3a6627 |
# FD numbers assigned. We need FD-#3 onwards for compatibility with
|
|
Packit Service |
3a6627 |
# socket activation (because python `subprocess.Popen` does not support
|
|
Packit Service |
3a6627 |
# renumbering the sockets we pass down).
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
index = 3
|
|
Packit Service |
3a6627 |
sockets = []
|
|
Packit Service |
3a6627 |
names = []
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
# osbuild-composer.socket
|
|
Packit Service |
3a6627 |
if self.args.weldr_api:
|
|
Packit Service |
3a6627 |
print("Create weldr-api socket", file=sys.stderr)
|
|
Packit Service |
3a6627 |
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
Packit Service |
3a6627 |
self._exitstack.enter_context(contextlib.closing(sock))
|
|
Packit Service |
3a6627 |
sock.bind("/run/weldr/api.socket")
|
|
Packit Service |
3a6627 |
sock.listen()
|
|
Packit Service |
3a6627 |
sockets.append(sock)
|
|
Packit Service |
3a6627 |
names.append("osbuild-composer.socket")
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
assert(sock.fileno() == index)
|
|
Packit Service |
3a6627 |
index += 1
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
# osbuild-composer-api.socket
|
|
Packit Service |
3a6627 |
if self.args.composer_api:
|
|
Packit Service |
3a6627 |
print("Create composer-api socket on port {}".format(self.args.composer_api_port) , file=sys.stderr)
|
|
Packit Service |
3a6627 |
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
|
Packit Service |
3a6627 |
self._exitstack.enter_context(contextlib.closing(sock))
|
|
Packit Service |
3a6627 |
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
Packit Service |
3a6627 |
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
|
Packit Service |
3a6627 |
sock.bind(("::", self.args.composer_api_port))
|
|
Packit Service |
3a6627 |
sock.listen()
|
|
Packit Service |
3a6627 |
sockets.append(sock)
|
|
Packit Service |
3a6627 |
names.append("osbuild-composer-api.socket")
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
assert(sock.fileno() == index)
|
|
Packit Service |
3a6627 |
index += 1
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
# osbuild-local-worker.socket
|
|
Packit Service |
3a6627 |
if self.args.local_worker_api:
|
|
Packit Service |
3a6627 |
print("Create local-worker-api socket", file=sys.stderr)
|
|
Packit Service |
3a6627 |
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
Packit Service |
3a6627 |
self._exitstack.enter_context(contextlib.closing(sock))
|
|
Packit Service |
3a6627 |
sock.bind("/run/osbuild-composer/job.socket")
|
|
Packit Service |
3a6627 |
sock.listen()
|
|
Packit Service |
3a6627 |
sockets.append(sock)
|
|
Packit Service |
3a6627 |
names.append("osbuild-local-worker.socket")
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
assert(sock.fileno() == index)
|
|
Packit Service |
3a6627 |
index += 1
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
# osbuild-remote-worker.socket
|
|
Packit Service |
3a6627 |
if self.args.remote_worker_api:
|
|
Packit Service |
3a6627 |
print("Create remote-worker-api socket", file=sys.stderr)
|
|
Packit Service |
3a6627 |
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
|
Packit Service |
3a6627 |
self._exitstack.enter_context(contextlib.closing(sock))
|
|
Packit Service |
3a6627 |
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
Packit Service |
3a6627 |
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
|
Packit Service |
3a6627 |
sock.bind(("::", 8700))
|
|
Packit Service |
3a6627 |
sock.listen(256)
|
|
Packit Service |
3a6627 |
sockets.append(sock)
|
|
Packit Service |
3a6627 |
names.append("osbuild-remote-worker.socket")
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
assert(sock.fileno() == index)
|
|
Packit Service |
3a6627 |
index += 1
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
# Prepare FD environment for the child process.
|
|
Packit Service |
3a6627 |
os.environ["LISTEN_FDS"] = str(len(sockets))
|
|
Packit Service |
3a6627 |
os.environ["LISTEN_FDNAMES"] = ":".join(names)
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
return sockets
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
@staticmethod
|
|
Packit Service |
3a6627 |
def _spawn_worker():
|
|
Packit Service |
3a6627 |
cmd = [
|
|
Packit Service |
3a6627 |
"/usr/libexec/osbuild-composer/osbuild-worker",
|
|
Packit Service |
3a6627 |
"-unix",
|
|
Packit Service |
3a6627 |
"/run/osbuild-composer/job.socket",
|
|
Packit Service |
3a6627 |
]
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
env = os.environ.copy()
|
|
Packit Service |
3a6627 |
env["CACHE_DIRECTORY"] = "/var/cache/osbuild-worker"
|
|
Packit Service |
3a6627 |
env["STATE_DIRECTORY"] = "/var/lib/osbuild-worker"
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
return subprocess.Popen(
|
|
Packit Service |
3a6627 |
cmd,
|
|
Packit Service |
3a6627 |
cwd="/",
|
|
Packit Service |
3a6627 |
env=env,
|
|
Packit Service |
3a6627 |
stdin=subprocess.DEVNULL,
|
|
Packit Service |
3a6627 |
stderr=subprocess.STDOUT,
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
@staticmethod
|
|
Packit Service |
3a6627 |
def _spawn_composer(sockets):
|
|
Packit Service |
3a6627 |
cmd = [
|
|
Packit Service |
3a6627 |
"/usr/libexec/osbuild-composer/osbuild-composer",
|
|
Packit Service |
3a6627 |
"-v",
|
|
Packit Service |
3a6627 |
]
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
# Prepare the environment for osbuild-composer. Note that we cannot use
|
|
Packit Service |
3a6627 |
# the `env` parameter of `subprocess.Popen()`, because it conflicts
|
|
Packit Service |
3a6627 |
# with the `preexec_fn=` parameter. Therefore, we have to modify the
|
|
Packit Service |
3a6627 |
# caller's environment.
|
|
Packit Service |
3a6627 |
os.environ["CACHE_DIRECTORY"] = "/var/cache/osbuild-composer"
|
|
Packit Service |
3a6627 |
os.environ["STATE_DIRECTORY"] = "/var/lib/osbuild-composer"
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
# We need to set `LISTEN_PID=` to the target PID. The only way python
|
|
Packit Service |
3a6627 |
# allows us to do this is to hook into `preexec_fn=`, which is executed
|
|
Packit Service |
3a6627 |
# by `subprocess.Popen()` after forking, but before executing the new
|
|
Packit Service |
3a6627 |
# executable.
|
|
Packit Service |
3a6627 |
preexec_setenv = lambda: os.putenv("LISTEN_PID", str(os.getpid()))
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
return subprocess.Popen(
|
|
Packit Service |
3a6627 |
cmd,
|
|
Packit Service |
3a6627 |
cwd="/usr/libexec/osbuild-composer",
|
|
Packit Service |
3a6627 |
stdin=subprocess.DEVNULL,
|
|
Packit Service |
3a6627 |
stderr=subprocess.STDOUT,
|
|
Packit Service |
3a6627 |
pass_fds=[sock.fileno() for sock in sockets],
|
|
Packit Service |
3a6627 |
preexec_fn=preexec_setenv,
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
def run(self):
|
|
Packit Service |
3a6627 |
"""Program Runtime"""
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
proc_composer = None
|
|
Packit Service |
3a6627 |
proc_worker = None
|
|
Packit Service |
3a6627 |
res = 0
|
|
Packit Service |
3a6627 |
sockets = self._prepare_sockets()
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
try:
|
|
Packit Service |
3a6627 |
if self.args.builtin_worker:
|
|
Packit Service |
3a6627 |
proc_worker = self._spawn_worker()
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
proc_composer = self._spawn_composer(sockets)
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
res = proc_composer.wait()
|
|
Packit Service |
3a6627 |
if proc_worker:
|
|
Packit Service |
3a6627 |
proc_worker.terminate()
|
|
Packit Service |
3a6627 |
proc_worker.wait()
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
return res
|
|
Packit Service |
3a6627 |
except KeyboardInterrupt:
|
|
Packit Service |
3a6627 |
if proc_worker:
|
|
Packit Service |
3a6627 |
proc_worker.terminate()
|
|
Packit Service |
3a6627 |
proc_worker.wait()
|
|
Packit Service |
3a6627 |
if proc_composer:
|
|
Packit Service |
3a6627 |
proc_composer.terminate()
|
|
Packit Service |
3a6627 |
res = proc_composer.wait()
|
|
Packit Service |
3a6627 |
except:
|
|
Packit Service |
3a6627 |
if proc_worker:
|
|
Packit Service |
3a6627 |
proc_worker.kill()
|
|
Packit Service |
3a6627 |
if proc_composer:
|
|
Packit Service |
3a6627 |
proc_composer.kill()
|
|
Packit Service |
3a6627 |
raise
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
return res
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
if __name__ == "__main__":
|
|
Packit Service |
3a6627 |
with Cli(sys.argv) as global_main:
|
|
Packit Service |
3a6627 |
sys.exit(global_main.run())
|