Spotter-VM/basic/srv/vm/mgr/pkgmgr.py

106 lines
3.8 KiB
Python
Raw Normal View History

2018-10-02 22:13:39 +02:00
# -*- coding: utf-8 -*-
2018-10-15 15:40:40 +02:00
import hashlib
2018-10-02 22:13:39 +02:00
import json
2018-10-15 14:58:24 +02:00
import os
2018-10-02 22:13:39 +02:00
import requests
import shutil
2018-10-15 14:58:24 +02:00
import subprocess
2018-10-02 22:13:39 +02:00
import tempfile
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import load_pem_public_key
2018-10-21 10:09:02 +02:00
from . import config
PUB_FILE = '/srv/vm/packages.pub'
2018-10-02 22:13:39 +02:00
LXC_ROOT = '/var/lib/lxc'
class PackageManager:
def __init__(self):
# Load JSON configuration
2018-10-21 10:09:02 +02:00
self.conf = config.Config()
2018-10-15 14:58:24 +02:00
self.repo_url = self.conf['host']['repo']
2018-10-02 22:13:39 +02:00
self.online_packages = {}
2018-10-15 15:40:40 +02:00
def fetch_online_packages(self):
2018-10-02 22:13:39 +02:00
# Fetches and verifies online packages. Can raise InvalidSignature
2018-10-15 14:58:24 +02:00
packages = requests.get('{}/packages'.format(self.repo_url)).content
packages_sig = requests.get('{}/packages.sig'.format(self.repo_url)).content
with open(PUB_FILE, 'rb') as f:
2018-10-02 22:13:39 +02:00
pub_key = load_pem_public_key(f.read(), default_backend())
pub_key.verify(packages_sig, packages, ec.ECDSA(hashes.SHA512()))
2018-10-15 15:40:40 +02:00
self.online_packages = json.loads(packages)
2018-10-02 22:13:39 +02:00
def install_package(self, name):
2018-10-21 10:09:02 +02:00
# Main installation function. Wrapper for download, registration and setup
2018-10-15 15:40:40 +02:00
self.fetch_online_packages()
2018-10-02 22:13:39 +02:00
for dep in self.get_deps(name):
if dep not in self.conf['packages']:
2018-10-15 15:40:40 +02:00
self.download_package(dep)
self.register_package(dep)
2018-10-02 22:13:39 +02:00
self.setup_package()
def download_package(self, name):
# Downloads, verifies, unpacks and sets up a package
2018-10-15 14:58:24 +02:00
tmp_archive = tempfile.mkstemp('.tar.xz')[1]
r = requests.get('{}/{}.tar.xz'.format(self.repo_url, name), auth=('test', 'txUqqZLaM.Z;3E2E'), stream=True) # TODO: Remove the testing password
2018-10-15 14:58:24 +02:00
with open(tmp_archive, 'wb') as f:
for chunk in r.iter_content(chunk_size=65536):
2018-10-02 22:13:39 +02:00
if chunk:
f.write(chunk)
# Verify hash
2018-10-15 14:58:24 +02:00
if self.online_packages[name]['sha512'] != hash_file(tmp_archive):
2018-10-02 22:13:39 +02:00
raise InvalidSignature(name)
# Unpack
2018-10-15 14:58:24 +02:00
subprocess.run(['tar', 'xJf', tmp_archive], cwd=LXC_ROOT)
os.unlink(tmp_archive)
2018-10-02 22:13:39 +02:00
2018-10-15 14:58:24 +02:00
def register_package(self, name):
2018-10-21 10:09:02 +02:00
# Registers a package in local configuration
2018-10-15 14:58:24 +02:00
metadata = self.online_packages[name]
self.conf['packages'][name] = {
2018-10-02 22:13:39 +02:00
'version': metadata['version'],
}
2018-10-21 10:09:02 +02:00
# If host definition is present, register the package as application
2018-10-15 15:40:40 +02:00
if 'host' in metadata:
self.conf['apps'][name] = {
'title': metadata['title'],
'host': metadata['host'],
'login': 'N/A',
'password': 'N/A',
'visible': False
}
2018-10-21 10:09:02 +02:00
self.conf.save()
2018-10-02 22:13:39 +02:00
def setup_package(self):
2018-10-21 10:09:02 +02:00
# Runs setup.sh for a package, if the script is present
2018-10-02 22:13:39 +02:00
setup_dir = os.path.join(LXC_ROOT, 'setup')
setup_script = os.path.join(LXC_ROOT, 'setup.sh')
if os.path.exists(setup_script):
subprocess.run(setup_script)
os.unlink(setup_script)
if os.path.exists(setup_dir):
shutil.rmtree(setup_dir)
def get_deps(self, name):
2018-10-21 10:09:02 +02:00
# Flatten dependency tree for a package
2018-10-15 14:58:24 +02:00
deps = self.online_packages[name]['deps'].copy()
2018-10-02 22:13:39 +02:00
for dep in deps:
deps[:0] = [d for d in self.get_deps(dep) if d not in deps]
2018-10-15 14:58:24 +02:00
deps.append(name)
2018-10-02 22:13:39 +02:00
return deps
def hash_file(file_path):
sha512 = hashlib.sha512()
with open(file_path, 'rb') as f:
while True:
data = f.read(65536)
if not data:
break
sha512.update(data)
return sha512.hexdigest()