Introduce BuildType for normal, force, scratch and metadata builds

This commit is contained in:
Disassembler 2019-11-30 15:56:29 +01:00
parent e1b7ba1204
commit e794ced82a
Signed by: Disassembler
GPG Key ID: 524BD33A0EE29499
4 changed files with 36 additions and 13 deletions

View File

@ -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):

View File

@ -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:

View File

@ -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)

View File

@ -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)