Implement service manager
This commit is contained in:
parent
7b045cf9c3
commit
d14fba7ec1
@ -97,6 +97,7 @@ def install_app(app):
|
|||||||
print_install_status(app)
|
print_install_status(app)
|
||||||
|
|
||||||
def 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:
|
if app.stage == Stage.QUEUED:
|
||||||
print('\x1b[KQueued...', end='\r')
|
print('\x1b[KQueued...', end='\r')
|
||||||
elif app.stage == Stage.DOWNLOAD:
|
elif app.stage == Stage.DOWNLOAD:
|
||||||
|
@ -7,8 +7,6 @@ from cryptography.hazmat.backends import default_backend
|
|||||||
from cryptography.hazmat.primitives import hashes, serialization
|
from cryptography.hazmat.primitives import hashes, serialization
|
||||||
from cryptography.hazmat.primitives.asymmetric import ec
|
from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
|
|
||||||
from .paths import REPO_SIG_FILE
|
|
||||||
|
|
||||||
def verify_signature(public_key_path, input_data, signature_data):
|
def verify_signature(public_key_path, input_data, signature_data):
|
||||||
# Verifies ECDSA HMAC SHA512 signature of a file
|
# Verifies ECDSA HMAC SHA512 signature of a file
|
||||||
with open(public_key_path, 'rb') as f:
|
with open(public_key_path, 'rb') as f:
|
||||||
|
@ -12,7 +12,8 @@ from .templates import LXC_CONTAINER
|
|||||||
def prepare_container(container, layers):
|
def prepare_container(container, layers):
|
||||||
# Remove ephemeral layer data
|
# Remove ephemeral layer data
|
||||||
clean_ephemeral_layer(container)
|
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')
|
rootfs = os.path.join(LXC_ROOT, container, 'rootfs')
|
||||||
# Unmount rootfs in case it remained mounted for whatever reason
|
# Unmount rootfs in case it remained mounted for whatever reason
|
||||||
subprocess.run(['umount', rootfs])
|
subprocess.run(['umount', rootfs])
|
||||||
|
@ -13,3 +13,8 @@ HOSTS_LOCK = '/var/lock/lxcmgr-hosts.lock'
|
|||||||
LXC_LOGS = '/var/log/lxc'
|
LXC_LOGS = '/var/log/lxc'
|
||||||
LXC_ROOT = '/var/lib/lxc'
|
LXC_ROOT = '/var/lib/lxc'
|
||||||
LXC_STORAGE_DIR = '/var/lib/lxcmgr/storage'
|
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'
|
||||||
|
@ -12,6 +12,7 @@ from pkg_resources import parse_version
|
|||||||
from . import crypto
|
from . import crypto
|
||||||
from . import flock
|
from . import flock
|
||||||
from . import lxcmgr
|
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
|
from .paths import LXC_STORAGE_DIR, REPO_CACHE_DIR, REPO_CONF_FILE, REPO_LOCAL_FILE, REPO_LOCK, REPO_SIG_FILE
|
||||||
|
|
||||||
class Stage(Enum):
|
class Stage(Enum):
|
||||||
@ -99,7 +100,7 @@ class PkgMgr:
|
|||||||
images.extend(self.online_packages['images'][image]['layers'])
|
images.extend(self.online_packages['images'][image]['layers'])
|
||||||
images = [image for image in set(images) if image not in self.installed_packages['images']]
|
images = [image for image in set(images) if image not in self.installed_packages['images']]
|
||||||
# Calculate bytes to download
|
# 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
|
# Download layers and setup script files
|
||||||
app.stage = Stage.DOWNLOAD
|
app.stage = Stage.DOWNLOAD
|
||||||
for image in images:
|
for image in images:
|
||||||
@ -214,8 +215,10 @@ class PkgMgr:
|
|||||||
image = self.online_packages['images'][image].copy()
|
image = self.online_packages['images'][image].copy()
|
||||||
if 'mounts' in self.online_packages['apps'][app]['containers'][container]:
|
if 'mounts' in self.online_packages['apps'][app]['containers'][container]:
|
||||||
image['mounts'] = self.online_packages['apps'][app]['containers'][container]['mounts']
|
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)
|
lxcmgr.create_container(container, image)
|
||||||
# TODO: Create services
|
svcmgr.create_service(app, container, image)
|
||||||
|
|
||||||
@flock.flock_ex(REPO_LOCK)
|
@flock.flock_ex(REPO_LOCK)
|
||||||
def uninstall_app(self, app):
|
def uninstall_app(self, app):
|
||||||
@ -225,11 +228,13 @@ class PkgMgr:
|
|||||||
self.run_uninstall_script(app)
|
self.run_uninstall_script(app)
|
||||||
self.destroy_containers(app)
|
self.destroy_containers(app)
|
||||||
self.purge_scripts(app)
|
self.purge_scripts(app)
|
||||||
|
self.unregister_app(app)
|
||||||
self.purge_unused_layers()
|
self.purge_unused_layers()
|
||||||
|
|
||||||
def destroy_containers(self, app):
|
def destroy_containers(self, app):
|
||||||
# Destroy LXC containers
|
# Destroy LXC containers
|
||||||
for container in self.installed_packages['apps'][app]['containers']:
|
for container in self.installed_packages['apps'][app]['containers']:
|
||||||
|
svcmgr.delete_service(container)
|
||||||
lxcmgr.destroy_container(container)
|
lxcmgr.destroy_container(container)
|
||||||
|
|
||||||
def purge_unused_layers(self):
|
def purge_unused_layers(self):
|
||||||
|
48
usr/lib/python3.6/lxcmgr/svcmgr.py
Normal file
48
usr/lib/python3.6/lxcmgr/svcmgr.py
Normal 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])
|
@ -49,3 +49,24 @@ lxc.cap.drop = sys_admin
|
|||||||
lxc.include = /usr/share/lxc/config/common.conf
|
lxc.include = /usr/share/lxc/config/common.conf
|
||||||
lxc.include = /usr/share/lxc/config/userns.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}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
@ -10,17 +10,6 @@ ACME_DIR = '/etc/acme.sh.d'
|
|||||||
CERT_KEY_FILE = '/etc/ssl/services.key'
|
CERT_KEY_FILE = '/etc/ssl/services.key'
|
||||||
CERT_PUB_FILE = '/etc/ssl/services.pem'
|
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
|
# OS
|
||||||
ISSUE_FILE = '/etc/issue'
|
ISSUE_FILE = '/etc/issue'
|
||||||
MOTD_FILE = '/etc/motd'
|
MOTD_FILE = '/etc/motd'
|
||||||
|
Loading…
Reference in New Issue
Block a user