Store full image definitons

This commit is contained in:
Disassembler 2020-02-07 15:12:27 +01:00
parent a131948826
commit ac596a6dc1
No known key found for this signature in database
GPG Key ID: 524BD33A0EE29499
4 changed files with 36 additions and 33 deletions

View File

@ -66,7 +66,8 @@ def modify_container(container, depends, mounts, envs, uid, gid, cmd, cwd, ready
setattr(container, member, value)
def create(container_name, image_name, depends, mounts, env, uid, gid, cmd, cwd, ready, halt, autostart):
container = Image(image_name).get_container(container_name)
container = Container(container_name, False)
container.set_definition(Image(image_name).get_definition())
modify_container(container, depends, mounts, env, uid, gid, cmd, cwd, ready, halt, autostart)
container.create()

View File

@ -6,6 +6,7 @@ import argparse
from spoc import repo_local
from spoc import repo_publish
from spoc.image import Image
from spoc.imagebuilder import ImageBuilder
from spoc.utils import readable_size
ACTION_LIST = 1
@ -44,11 +45,11 @@ def delete(image_name):
def build(filename, force, do_publish):
# Check if a build is needed and attempt to build the image from image file
image_name = get_image_name(filename)
image = Image(image_name, False)
if force or image.name not in repo_local.get_images():
if force or image_name not in repo_local.get_images():
image = Image(image_name, False)
image.delete()
print(f'Building image {image_name} from file {filename}')
image.build(filename)
ImageBuilder().build(image, filename)
print(f'Image {image_name} built successfully')
# If publishing was requested, force publish after successful build
force = True
@ -59,8 +60,8 @@ def build(filename, force, do_publish):
def publish(image_name, force):
# Check if publishing is needed and attempt to publish the image
image = Image(image_name)
if force or image.name not in repo_publish.get_images():
if force or image_name not in repo_publish.get_images():
image = Image(image_name, False)
image.unpublish()
print(f'Publishing image {image_name}')
image.publish()

View File

@ -8,18 +8,16 @@ from . import crypto
from . import repo_local
from . import repo_publish
from . import utils
from .container import Container
from .imagebuilder import ImageBuilder
from .paths import LAYERS_DIR, PUB_LAYERS_DIR
DEFINITION_MEMBERS = {'parent', 'env', 'uid', 'gid', 'cmd', 'cwd', 'ready', 'halt', 'size', 'dlsize', 'hash'}
DEFINITION_MEMBERS = {'layers', 'env', 'uid', 'gid', 'cmd', 'cwd', 'ready', 'halt', 'size', 'dlsize', 'hash'}
class Image:
def __init__(self, name, load_from_repo=True):
self.name = name
self.layer_path = os.path.join(LAYERS_DIR, name)
self.archive_path = os.path.join(PUB_LAYERS_DIR, f'{name}.tar.xz')
self.parent = None
self.layers = [name]
self.env = {}
self.uid = None
self.gid = None
@ -45,22 +43,12 @@ class Image:
definition[key] = value
return definition
def get_container(self, container_name):
container = Image(self.parent).get_container(container_name) if self.parent else Container(container_name, False)
container.layers.append(self.name)
container.env.update(self.env)
for member in ('uid', 'gid', 'cmd', 'cwd', 'ready', 'halt'):
value = getattr(self, member)
if value:
setattr(container, member, value)
return container
def build(self, filename):
def create(self, imagebuilder):
# Build the container from image file and save to local repository
# Chown is possible only when the process is running as root, for user namespaces, see https://linuxcontainers.org/lxc/manpages/man1/lxc-usernsexec.1.html
os.makedirs(self.layer_path, 0o755, True)
os.chown(self.layer_path, 100000, 100000)
ImageBuilder().build(self, filename)
imagebuilder.process_file()
repo_local.register_image(self.name, self.get_definition())
def delete(self):

View File

@ -7,6 +7,7 @@ import subprocess
import tempfile
from .container import Container
from .image import Image
from .paths import VOLUME_DIR
class ImageBuilder:
@ -16,7 +17,10 @@ class ImageBuilder:
self.filename = filename
self.script_eof = None
self.script_lines = []
with open(filename, 'r') as f:
self.image.create(self)
def process_file(self):
with open(self.filename, 'r') as f:
for line in f:
self.process_line(line.strip())
@ -37,17 +41,18 @@ class ImageBuilder:
self.script_lines = []
self.script_eof = args
elif 'FROM' == directive:
# Set the name of image from which this one inherits
self.image.parent = args
# Set the values of image from which this one inherits
self.image.set_definition(Image(args).get_definition())
self.image.layers.append(self.image.name)
elif 'COPY' == directive:
srcdst = args.split()
self.copy_files(srcdst[0], srcdst[1] if len(srcdst) > 1 else '')
elif 'ENV' == directive:
# Sets environment records
self.image.env.append(args.split(None, 1))
# Sets/unsets environment variable
self.set_env(*args.split(None, 1))
elif 'USER' == directive:
# Sets init UID / GID
self.image.uid,self.image.gid = args.split() #TODO: Get UID/GID by name + get GIT automatically as primary UID group
self.image.uid,self.image.gid = args.split() #TODO: Get UID/GID by name + get GID automatically as primary UID group if only UID is given
elif 'CMD' == directive:
# Sets init command
self.image.cmd = args
@ -65,23 +70,31 @@ class ImageBuilder:
# Creates a temporary container, runs a script in its namespace, and stores the files modified by it as part of the layer
# TODO: Run the script as the correct user if UID/GID has been already set - doesn't this the LXC init do automatically?
os.makedirs(VOLUME_DIR, 0o755, True)
script_fd, script_path = tempfile.mkstemp(suffix='.sh', dir=VOLUME_DIR, text=True)
script_fd, script_path = tempfile.mkstemp(suffix='.sh', dir=self.image.layer_path, text=True)
script_name = os.path.basename(script_path)
script_lines = '\n'.join(script_lines)
with os.fdopen(script_fd, 'w') as script:
script.write(f'#!/bin/sh\nset -ev\n\n{script_lines}\n')
os.chmod(script_path, 0o700)
os.chown(script_path, 100000, 100000)
# Create a temporary container from the current image definition
container = self.image.get_container(self.image.name)
# Create a temporary container from the current image definition and execute the script within the container
container = Container(self.image.name, False)
container.set_definition(self.image.get_definition())
container.build = True
# Add the script file as a mount and execute it withing the container
container.mounts[script_name] = (os.path.basename(script_path), False)
container.create()
container.execute(['/bin/sh', '-lc', os.path.join('/', script_name)], True)
container.destroy()
os.unlink(script_path)
def set_env(self, key, value=None):
if value:
self.image.env[key] = value
else:
try:
del self.image.env[key]
except KeyError:
pass
def copy_files(self, src, dst):
# Copy files from the host or download them from a http(s) URL
dst = os.path.join(self.image.layer_path, dst.lstrip('/'))