From e794ced82a0d35ac4a20ed2d1ed7c486726a0e1c Mon Sep 17 00:00:00 2001 From: Disassembler Date: Sat, 30 Nov 2019 15:56:29 +0100 Subject: [PATCH] Introduce BuildType for normal, force, scratch and metadata builds --- build/usr/bin/lxcbuild | 8 ++++--- build/usr/lib/python3.6/lxcbuild/image.py | 24 ++++++++++++++----- .../lib/python3.6/lxcbuild/imagebuilder.py | 15 +++++++++--- .../usr/lib/python3.6/lxcbuild/imagepacker.py | 2 +- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/build/usr/bin/lxcbuild b/build/usr/bin/lxcbuild index e15b61b..437d59e 100755 --- a/build/usr/bin/lxcbuild +++ b/build/usr/bin/lxcbuild @@ -5,7 +5,7 @@ import argparse import os import sys from lxcbuild.app import App -from lxcbuild.image import Image +from lxcbuild.image import BuildType, Image parser = argparse.ArgumentParser(description='VM application builder and packager') group = parser.add_mutually_exclusive_group() @@ -22,8 +22,10 @@ args = parser.parse_args() def build_and_pack_image(path, args): image = Image() - image.force_build = args.force or args.scratch - image.scratch_build = args.scratch + if args.scratch: + image.build_type = BuildType.SCRATCH + elif args.force: + image.build_type = BuildType.FORCE image.build_and_pack(path) def pack_app(path): diff --git a/build/usr/lib/python3.6/lxcbuild/image.py b/build/usr/lib/python3.6/lxcbuild/image.py index 838914a..d903e63 100644 --- a/build/usr/lib/python3.6/lxcbuild/image.py +++ b/build/usr/lib/python3.6/lxcbuild/image.py @@ -3,43 +3,55 @@ import os import sys +from enum import Enum from lxcmgr import lxcmgr from .imagebuilder import ImageBuilder, ImageExistsError, ImageNotFoundError from .imagepacker import ImagePacker from .packer import PackageExistsError +class BuildType(Enum): + NORMAL = 1 + FORCE = 2 + SCRATCH = 3 + METADATA = 4 + class Image: def __init__(self): self.name = None self.conf = {} self.lxcfile = None self.build_dir = None - self.force_build = False - self.scratch_build = False + self.build_type = BuildType.NORMAL + self.pack = False def build_and_pack(self, lxcfile): self.lxcfile = lxcfile self.build_dir = os.path.dirname(lxcfile) self.conf['build'] = True + builder = ImageBuilder(self) try: - builder = ImageBuilder(self) builder.build() # Packaging needs to happen in any case after a successful build in order to prevent outdated packages - self.force_build = True + self.pack = True except ImageExistsError as e: + # If container already exists and build hasn't been forced, rerun the build just for metadata which are still needed for packaging print('Image {} already exists, skipping build tasks'.format(e)) + self.build_type = BuildType.METADATA + builder.build() except ImageNotFoundError as e: + # If one of the layers is missing, cleanup and die print('Image {} not found, can\'t build {}'.format(e, self.name)) builder.clean() sys.exit(1) except: - if not self.scratch_build: + # If build fails with another exception, cleanup (unless we were doing scratch build) and re-raise + if not self.build_type == BuildType.SCRATCH: builder.clean() raise del self.conf['build'] # If we're doing a scratch build, regenerate the final LXC container configuration including ephemeral layer - if self.scratch_build: + if self.build_type == BuildType.SCRATCH: lxcmgr.create_container(self.name, self.conf) else: try: diff --git a/build/usr/lib/python3.6/lxcbuild/imagebuilder.py b/build/usr/lib/python3.6/lxcbuild/imagebuilder.py index b2a20e0..ba65188 100644 --- a/build/usr/lib/python3.6/lxcbuild/imagebuilder.py +++ b/build/usr/lib/python3.6/lxcbuild/imagebuilder.py @@ -9,6 +9,8 @@ from lxcmgr import lxcmgr from lxcmgr.paths import LXC_STORAGE_DIR from lxcmgr.pkgmgr import PkgMgr +from .image import BuildType + class ImageExistsError(Exception): pass @@ -65,6 +67,9 @@ class ImageBuilder: def run_script(self, script): # Creates a temporary container, runs a script in its namespace, and stores the modifications as part of the image + if self.image.build_type == BuildType.METADATA: + # Don't run anything if we're building just metadata + return lxcmgr.create_container(self.image.name, self.image.conf) sh = os.path.join(LXC_STORAGE_DIR, self.image.name, 'run.sh') with open(sh, 'w') as f: @@ -73,7 +78,8 @@ class ImageBuilder: os.chown(sh, 100000, 100000) subprocess.run(['lxc-execute', self.image.name, '--', '/bin/sh', '-lc', '/run.sh'], check=True) os.unlink(sh) - if not self.image.scratch_build: + if not self.image.build_type == BuildType.SCRATCH: + # Don't delete the temporary container if we're doing scratch build lxcmgr.destroy_container(self.image.name) def set_name(self, name): @@ -82,7 +88,7 @@ class ImageBuilder: self.image.conf['layers'] = [name] image_path = self.get_layer_path(name) if os.path.exists(image_path): - if self.image.force_build: + if self.image.build_type in (BuildType.FORCE, BuildType.SCRATCH): self.clean() else: raise ImageExistsError(image_path) @@ -93,10 +99,13 @@ class ImageBuilder: # Extend list of layers with the list of layers from parent image # Raies an exception when IMAGE has no name pkgmgr = PkgMgr() - self.image.conf['layers'].extend(pkgmgr.installed_packages[image]['layers']) + self.image.conf['layers'].extend(pkgmgr.installed_packages['images'][image]['layers']) def copy_files(self, src, dst): # Copy files from the host or download them from a http(s) URL + if self.image.build_type == BuildType.METADATA: + # Don't copy anything if we're building just metadata + return dst = os.path.join(LXC_STORAGE_DIR, self.image.name, dst) if src.startswith('http://') or src.startswith('https://'): unpack_http_archive(src, dst) diff --git a/build/usr/lib/python3.6/lxcbuild/imagepacker.py b/build/usr/lib/python3.6/lxcbuild/imagepacker.py index 072c707..4ba2655 100644 --- a/build/usr/lib/python3.6/lxcbuild/imagepacker.py +++ b/build/usr/lib/python3.6/lxcbuild/imagepacker.py @@ -19,7 +19,7 @@ class ImagePacker(Packer): self.xz_path = '{}.xz'.format(self.tar_path) def pack(self): - if self.image.force_build: + if self.image.pack: self.unregister() try: os.unlink(self.xz_path)