Introduce BuildType for normal, force, scratch and metadata builds
This commit is contained in:
parent
e1b7ba1204
commit
e794ced82a
@ -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):
|
||||
|
@ -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
|
||||
try:
|
||||
builder = ImageBuilder(self)
|
||||
try:
|
||||
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:
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user