Implement service manager

This commit is contained in:
Disassembler 2019-09-24 09:59:45 +02:00
parent 7b045cf9c3
commit d14fba7ec1
No known key found for this signature in database
GPG Key ID: 524BD33A0EE29499
8 changed files with 84 additions and 16 deletions

View File

@ -97,6 +97,7 @@ def install_app(app):
print_install_status(app)
def print_install_status(app):
# Prints current status of the installation. Uses ANSI "erase line" and "carriage return" to rewrite the status on single line.
if app.stage == Stage.QUEUED:
print('\x1b[KQueued...', end='\r')
elif app.stage == Stage.DOWNLOAD:

View File

@ -7,8 +7,6 @@ from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
from .paths import REPO_SIG_FILE
def verify_signature(public_key_path, input_data, signature_data):
# Verifies ECDSA HMAC SHA512 signature of a file
with open(public_key_path, 'rb') as f:

View File

@ -12,7 +12,8 @@ from .templates import LXC_CONTAINER
def prepare_container(container, layers):
# Remove ephemeral layer data
clean_ephemeral_layer(container)
# Prepare and mount overlayfs
# Prepare and mount overlayfs. This needs to be done before handing over control to LXC as we use unprivileged containers
# which don't have the capability to mount overlays - https://www.spinics.net/lists/linux-fsdevel/msg105877.html
rootfs = os.path.join(LXC_ROOT, container, 'rootfs')
# Unmount rootfs in case it remained mounted for whatever reason
subprocess.run(['umount', rootfs])

View File

@ -13,3 +13,8 @@ HOSTS_LOCK = '/var/lock/lxcmgr-hosts.lock'
LXC_LOGS = '/var/log/lxc'
LXC_ROOT = '/var/lib/lxc'
LXC_STORAGE_DIR = '/var/lib/lxcmgr/storage'
# Services
AUTOSTART_SVC_DIR = '/etc/runlevels/default'
SERVICE_DIR = '/etc/init.d'
STARTED_SVC_DIR = '/run/openrc/started'

View File

@ -12,6 +12,7 @@ from pkg_resources import parse_version
from . import crypto
from . import flock
from . import lxcmgr
from . import svcmgr
from .paths import LXC_STORAGE_DIR, REPO_CACHE_DIR, REPO_CONF_FILE, REPO_LOCAL_FILE, REPO_LOCK, REPO_SIG_FILE
class Stage(Enum):
@ -99,7 +100,7 @@ class PkgMgr:
images.extend(self.online_packages['images'][image]['layers'])
images = [image for image in set(images) if image not in self.installed_packages['images']]
# Calculate bytes to download
app.bytes_total = sum(self.online_packages['images'][image]['size'] for image in images) + self.online_packages['apps'][app.name]['size']
app.bytes_total = sum(self.online_packages['images'][image]['pkgsize'] for image in images) + self.online_packages['apps'][app.name]['pkgsize']
# Download layers and setup script files
app.stage = Stage.DOWNLOAD
for image in images:
@ -214,8 +215,10 @@ class PkgMgr:
image = self.online_packages['images'][image].copy()
if 'mounts' in self.online_packages['apps'][app]['containers'][container]:
image['mounts'] = self.online_packages['apps'][app]['containers'][container]['mounts']
if 'depends' in self.online_packages['apps'][app]['containers'][container]
image['depends'] = self.online_packages['apps'][app]['containers'][container]['depends']
lxcmgr.create_container(container, image)
# TODO: Create services
svcmgr.create_service(app, container, image)
@flock.flock_ex(REPO_LOCK)
def uninstall_app(self, app):
@ -225,11 +228,13 @@ class PkgMgr:
self.run_uninstall_script(app)
self.destroy_containers(app)
self.purge_scripts(app)
self.unregister_app(app)
self.purge_unused_layers()
def destroy_containers(self, app):
# Destroy LXC containers
for container in self.installed_packages['apps'][app]['containers']:
svcmgr.delete_service(container)
lxcmgr.destroy_container(container)
def purge_unused_layers(self):

View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
import os
import subprocess
from .paths import AUTOSTART_SVC_DIR, SERVICE_DIR, STARTED_SVC_DIR
from .templates import SERVICE
def create_service(app, container, image):
depends = ' '.join(image['depends']) if 'depends' in image else ''
check = 'lxc-execute {} -- sh -c \'until $({}); do sleep 0.1; done\''.format(container, image['check']) if 'check' in image else ''
with open(os.path.join(SERVICE_DIR, container), 'w') as f:
f.write(SERVICE.format(app=app, container=container, depends=depends, check=check))
update_services()
def delete_service(service):
if is_service_started(service):
stop_service(service)
if is_service_autostarted(service):
update_service_autostart(service, False)
try:
os.unlink(os.path.join(SERVICE_DIR, service))
except FileNotFoundError:
pass
update_services()
def update_services():
subprocess.run(['/sbin/rc-update', '-u'], check=True)
def start_service(service):
if not is_service_started(service):
subprocess.run(['/sbin/service', service, 'stop'], check=True)
def stop_service(service):
if is_service_started(service):
subprocess.run(['/sbin/service', service, 'stop'], check=True)
def is_service_started(self, app):
# Check OpenRC service status without calling any binary
return os.path.exists(os.path.join(STARTED_SVC_DIR, app))
def is_service_autostarted(self, app):
# Check OpenRC service enablement
return os.path.exists(os.path.join(AUTOSTART_SVC_DIR, app))
def update_service_autostart(self, service, enabled):
# Add/remove the app to OpenRC default runlevel
subprocess.run(['/sbin/rc-update', 'add' if enabled else 'del', service])

View File

@ -49,3 +49,24 @@ lxc.cap.drop = sys_admin
lxc.include = /usr/share/lxc/config/common.conf
lxc.include = /usr/share/lxc/config/userns.conf
'''
SERVICE = """#!/sbin/openrc-run
description="{app} {container} LXC container"
depend() {{
need cgroups {depends}
}}
start() {{
lxc-start {container}
}}
start_post() {{
{check}
}}
stop() {{
lxc-stop {container}
}}
"""

View File

@ -10,17 +10,6 @@ ACME_DIR = '/etc/acme.sh.d'
CERT_KEY_FILE = '/etc/ssl/services.key'
CERT_PUB_FILE = '/etc/ssl/services.pem'
# Package manager
REPO_LOCAL_FILE = '/var/lib/lxc-pkg/packages'
REPO_SIG_FILE = '/etc/vmmgr/packages.pub'
REPO_CACHE_DIR = '/var/lib/lxc-pkg/cache'
# LXC
HOSTS_FILE = '/etc/hosts'
HOSTS_LOCK = '/var/lock/vmmgr-hosts.lock'
LXC_ROOT = '/var/lib/lxc'
LXC_STORAGE_DIR = '/var/lib/lxc-pkg/storage'
# OS
ISSUE_FILE = '/etc/issue'
MOTD_FILE = '/etc/motd'