diff --git a/basic/srv/vm/mgr/__init__.py b/basic/srv/vm/mgr/__init__.py index 52e2f4b..7384532 100644 --- a/basic/srv/vm/mgr/__init__.py +++ b/basic/srv/vm/mgr/__init__.py @@ -1,16 +1,15 @@ # -*- coding: utf-8 -*- -import json import os import shutil import subprocess +from . import config from . import tools from . import validator VERSION = '0.0.1' -CONF_FILE = '/srv/vm/config.json' ISSUE_FILE = '/etc/issue' NGINX_DIR = '/etc/nginx/conf.d' ACME_CRON = '/etc/periodic/daily/acme-sh' @@ -124,16 +123,10 @@ subjectAltName=DNS:{domain},DNS:*.{domain}" class VMMgr: def __init__(self): # Load JSON configuration - with open(CONF_FILE, 'r') as f: - self.conf = json.load(f) + self.conf = config.Config() self.domain = self.conf['host']['domain'] self.port = self.conf['host']['port'] - def save_conf(self): - # Save a sorted JSON configuration object with indentation - with open(CONF_FILE, 'w') as f: - json.dump(self.conf, f, sort_keys=True, indent=4) - def update_login(self, app, login, password): # Update login and password for an app in the configuration if not validator.is_valid_app(app, self.conf): @@ -142,21 +135,21 @@ class VMMgr: self.conf['apps'][app]['login'] = login if password is not None: self.conf['apps'][app]['password'] = password - self.save_conf() + self.conf.save() def show_tiles(self, app): # Update visibility for the app in the configuration if not validator.is_valid_app(app, self.conf): raise validator.InvalidValueException('app', app) self.conf['apps'][app]['visible'] = True - self.save_conf() + self.conf.save() def hide_tiles(self, app): # Update visibility for the app in the configuration if not validator.is_valid_app(app, self.conf): raise validator.InvalidValueException('app', app) self.conf['apps'][app]['visible'] = False - self.save_conf() + self.conf.save() def start_app(self, app): # Start the actual app service @@ -268,7 +261,7 @@ class VMMgr: raise validator.InvalidValueException('port', port) self.domain = self.conf['host']['domain'] = domain self.port = self.conf['host']['port'] = port - self.save_conf() + self.conf.save() # Restart all apps to trigger configuration refresh for app in self.conf['apps']: if tools.is_service_started(app): @@ -309,7 +302,7 @@ class VMMgr: # Update Google Maps API key self.conf['common']['gmaps-api-key'] = gmaps_api_key # Save config to file - self.save_conf() + self.conf.save() for app in self.conf['apps']: # Restart currently running apps in order to update their config if tools.is_service_started(app): @@ -322,7 +315,7 @@ class VMMgr: # Update bcrypt-hashed password in config self.conf['host']['adminpwd'] = tools.adminpwd_hash(newpassword) # Save config to file - self.save_conf() + self.conf.save() def create_selfsigned(self): # Create selfsigned certificate with wildcard alternative subject name diff --git a/basic/srv/vm/mgr/config.py b/basic/srv/vm/mgr/config.py new file mode 100644 index 0000000..294f794 --- /dev/null +++ b/basic/srv/vm/mgr/config.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +import json + +CONF_FILE = '/srv/vm/config.json' + +class Config: + def __init__(self): + with open(CONF_FILE, 'r') as f: + self.data = json.load(f) + + def save(self): + with open(CONF_FILE, 'w') as f: + json.dump(self.data, f, sort_keys=True, indent=4) + + def __getitem__(self, attr): + return self.data[attr] diff --git a/basic/srv/vm/mgr/pkgmgr.py b/basic/srv/vm/mgr/pkgmgr.py index 48fc80b..fd6d0b1 100644 --- a/basic/srv/vm/mgr/pkgmgr.py +++ b/basic/srv/vm/mgr/pkgmgr.py @@ -14,23 +14,18 @@ from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.serialization import load_pem_public_key -CONF_FILE = '/srv/vm/config.json' +from . import config + PUB_FILE = '/srv/vm/packages.pub' LXC_ROOT = '/var/lib/lxc' class PackageManager: def __init__(self): # Load JSON configuration - with open(CONF_FILE, 'r') as f: - self.conf = json.load(f) + self.conf = config.Config() self.repo_url = self.conf['host']['repo'] self.online_packages = {} - def save_conf(self): - # Save a sorted JSON configuration object with indentation - with open(CONF_FILE, 'w') as f: - json.dump(self.conf, f, sort_keys=True, indent=4) - def fetch_online_packages(self): # Fetches and verifies online packages. Can raise InvalidSignature packages = requests.get('{}/packages'.format(self.repo_url)).content @@ -41,6 +36,7 @@ class PackageManager: self.online_packages = json.loads(packages) def install_package(self, name): + # Main installation function. Wrapper for download, registration and setup self.fetch_online_packages() for dep in self.get_deps(name): if dep not in self.conf['packages']: @@ -64,10 +60,12 @@ class PackageManager: os.unlink(tmp_archive) def register_package(self, name): + # Registers a package in local configuration metadata = self.online_packages[name] self.conf['packages'][name] = { 'version': metadata['version'], } + # If host definition is present, register the package as application if 'host' in metadata: self.conf['apps'][name] = { 'title': metadata['title'], @@ -76,9 +74,10 @@ class PackageManager: 'password': 'N/A', 'visible': False } - self.save_conf() + self.conf.save() def setup_package(self): + # Runs setup.sh for a package, if the script is present setup_dir = os.path.join(LXC_ROOT, 'setup') setup_script = os.path.join(LXC_ROOT, 'setup.sh') if os.path.exists(setup_script): @@ -88,6 +87,7 @@ class PackageManager: shutil.rmtree(setup_dir) def get_deps(self, name): + # Flatten dependency tree for a package deps = self.online_packages[name]['deps'].copy() for dep in deps: deps[:0] = [d for d in self.get_deps(dep) if d not in deps] diff --git a/zz-extra/lxc-pack b/zz-extra/lxc-pack index 2bb2f5d..84824fe 100755 --- a/zz-extra/lxc-pack +++ b/zz-extra/lxc-pack @@ -54,6 +54,7 @@ def pack(pkg_file): with open(packages_file, 'r') as f: packages = json.load(f) packages[pkg_name] = meta + packages[pkg_name]['size'] = os.path.getsize(xz_path) packages[pkg_name]['sha512'] = hash_file(xz_path) with open(packages_file, 'w') as f: json.dump(packages, f, sort_keys=True, indent=4)