90 lines
3.5 KiB
Python
90 lines
3.5 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
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_STORAGE = '/var/lib/lxc/storage'
|
||
|
|
||
|
class LXCPacker:
|
||
|
def __init__(self, image):
|
||
|
self.image = image
|
||
|
self.tar_path = None
|
||
|
self.xz_path = None
|
||
|
|
||
|
def pack(self):
|
||
|
# Prepare package file names
|
||
|
self.tar_path = os.path.join(PKG_ROOT, '{}.tar'.format(self.image.upper_layer))
|
||
|
self.xz_path = '{}.xz'.format(self.tar_path)
|
||
|
if os.path.exists(self.xz_path):
|
||
|
print('Package {} already exists, skipping packaging tasks'.format(self.xz_path))
|
||
|
return
|
||
|
os.makedirs(PKG_ROOT, 0o755, True)
|
||
|
self.create_archive()
|
||
|
self.register_package()
|
||
|
self.sign_packages()
|
||
|
|
||
|
def create_archive(self):
|
||
|
# Create archive
|
||
|
print('Archiving', self.image.upper_layer)
|
||
|
subprocess.run(['tar', '--xattrs', '-cpf', self.tar_path, os.path.join(LXC_STORAGE, self.image.upper_layer)], 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(self.image.build_dir, s))]
|
||
|
subprocess.run(['tar', '--transform', 's|^|srv/{}/|'.format(self.image.upper_layer), '-rpf', self.tar_path] + scripts, cwd=self.image.build_dir)
|
||
|
# 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_package(self):
|
||
|
# Prepare metadata
|
||
|
meta = self.image.meta.copy()
|
||
|
meta['lxc'] = {}
|
||
|
for key in ('layers', 'mounts', 'env', 'cmd', 'cwd', 'uid', 'gid', 'halt'):
|
||
|
value = getattr(self.image, key)
|
||
|
if value:
|
||
|
meta['lxc'][key] = value
|
||
|
|
||
|
# 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[self.image.name] = meta
|
||
|
packages[self.image.name]['size'] = os.path.getsize(self.xz_path)
|
||
|
packages[self.image.name]['sha512'] = hash_file(self.xz_path)
|
||
|
with open(packages_file, 'w') as f:
|
||
|
json.dump(packages, f, sort_keys=True, indent=4)
|
||
|
|
||
|
def sign_packages(self):
|
||
|
# 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()
|