#!/usr/bin/python3 import hashlib import json import os import subprocess import sys 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_private_key PKG_ROOT = '/srv/build/lxc' PRIVATE_KEY = '/srv/build/packages.key' LXC_ROOT = '/var/lib/lxc' def pack(path): # Determine correct metadata file and package name path = os.path.realpath(path) if os.path.isdir(path): meta_dir = path meta_file = os.path.join(meta_dir, 'meta') else: meta_dir = os.path.dirname(path) meta_file = path pkg_name = os.path.basename(meta_dir) # Load metadata with open(meta_file) as f: meta = json.load(f) # Prepare package file names os.makedirs(PKG_ROOT, 0o755, True) tar_path = os.path.join(PKG_ROOT, '{}_{}-{}.tar'.format(pkg_name, meta['version'], meta['release'])) xz_path = '{}.xz'.format(tar_path) # Remove old package if os.path.exists(tar_path): os.unlink(tar_path) if os.path.exists(xz_path): os.unlink(xz_path) # Create archive print('Archiving', meta['lxcpath']) subprocess.run(['tar', '--xattrs', '-cpf', tar_path, os.path.join(LXC_ROOT, meta['lxcpath'])], cwd='/') # Add install/upgrade/uninstall scripts scripts = ('install', 'install.sh', 'upgrade', 'upgrade.sh', 'uninstall', 'uninstall.sh') scripts = [s for s in scripts if os.path.exists(os.path.join(meta_dir, s))] subprocess.run(['tar', '--transform', 's|^|srv/{}/|'.format(pkg_name), '-rpf', tar_path] + scripts, cwd=meta_dir) # Compress the tarball with xz (LZMA2) print('Compressing', tar_path, '({:.2f} MB)'.format(os.path.getsize(tar_path)/1048576)) subprocess.run(['xz', '-9', tar_path]) print('Compressed ', xz_path, '({:.2f} MB)'.format(os.path.getsize(xz_path)/1048576)) # Register package print('Registering package') packages = {} packages_file = os.path.join(PKG_ROOT, 'packages') if os.path.exists(packages_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) # Sign packages file print('Signing packages') with open(PRIVATE_KEY, 'rb') as f: priv_key = load_pem_private_key(f.read(), None, default_backend()) with open(os.path.join(PKG_ROOT, 'packages'), 'rb') as f: data = f.read() with open(os.path.join(PKG_ROOT, 'packages.sig'), 'wb') as f: f.write(priv_key.sign(data, ec.ECDSA(hashes.SHA512()))) 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() if __name__ == '__main__': if len(sys.argv) != 2 or sys.argv[1] in ('-h', '--help'): print('Usage: lxc-pack \n where the buildpath can be either specific meta file or a directory containing one') else: pack(sys.argv[1])