# -*- coding: utf-8 -*- import json import os import subprocess import sys from lxcmgr.paths import LXC_STORAGE_DIR from . import crypto from .paths import APP_DIR, IMAGE_DIR, META_FILE, PRIVATE_KEY, ROOT_DIR, SIGNATURE_FILE class PackageExistsError(Exception): pass class Packer: def __init__(self): self.app = None self.image = None self.tar_path = None self.xz_path = None def load_packages_meta(self): if os.path.exists(PKG_META): with open(PKG_META, 'r') as f: return json.load(f) else: return {'apps': {}, 'images': {}} def save_packages_meta(self, packages): with open(PKG_META, 'w') as f: json.dump(packages, f, sort_keys=True, indent=4) def pack_image(self, image, force): self.image = image # Prepare package file names self.tar_path = os.path.join(IMAGE_DIR, '{}.tar'.format(self.image.name)) self.xz_path = '{}.xz'.format(self.tar_path) if os.path.exists(self.xz_path): if force: self.unregister_image() os.unlink(self.xz_path) else: raise PackageExistsError(self.xz_path) self.create_image_archive() self.register_image() self.sign_packages() def create_image_archive(self): # Create archive print('Archiving', self.image.path) subprocess.run(['tar', '--xattrs', '-cpf', self.tar_path, self.image.name], cwd=LXC_STORAGE_DIR) self.compress_archive() def compress_archive(self): # Compress the tarball with xz (LZMA2) print('Compressing', self.tar_path, '({:.2f} MB)'.format(os.path.getsize(self.tar_path)/1048576)) subprocess.run(['xz', '-9', self.tar_path]) print('Compressed ', self.xz_path, '({:.2f} MB)'.format(os.path.getsize(self.xz_path)/1048576)) def register_image(self): # Register package in global repository metadata file print('Registering package {}'.format(self.image.name)) packages = self.load_packages_meta() packages['images'][self.image.name] = self.image.conf.copy() packages['images'][self.image.name]['size'] = os.path.getsize(self.xz_path) packages['images'][self.image.name]['sha512'] = crypto.hash_file(self.xz_path) self.save_packages_meta(packages) def sign_packages(self): signature = crypto.sign_file(PRIVATE_KEY, META_FILE) with open(SIGNATURE_FILE, 'wb') as f: f.write(signature) def unregister_image(self): # Removes package from global repository metadata file packages = self.load_packages_meta() if self.image.name in packages['images']: del packages['images'][self.image.name] self.save_packages_meta(packages) def pack_app(self, app): self.app = app # Prepare package file names self.tar_path = os.path.join(APP_DIR, '{}.tar'.format(self.image.name)) self.xz_path = '{}.xz'.format(self.tar_path) if os.path.exists(self.xz_path): os.unlink(self.xz_path) self.create_app_archive() self.register_app() self.sign_packages() def create_app_archive(self): # Create archive with application setup scripts print('Archiving setup scripts for', self.app.name) scripts = ('install', 'install.sh', 'upgrade', 'upgrade.sh', 'uninstall', 'uninstall.sh') scripts = [s for s in scripts if os.path.exists(os.path.join(self.app.build_dir, s))] subprocess.run(['tar', '--xattrs', '-cpf', self.tar_path] + scripts, cwd=self.app.build_dir) self.compress_archive() def register_app(self): # Register package in global repository metadata file print('Registering package {}'.format(self.app.name)) packages = self.load_packages_meta() packages['apps'][self.image.name] = self.app.conf.copy() packages['apps'][self.image.name]['size'] = os.path.getsize(self.xz_path) packages['apps'][self.image.name]['sha512'] = crypto.hash_file(self.xz_path) self.save_packages_meta(packages)