130 lines
3.7 KiB
Plaintext
130 lines
3.7 KiB
Plaintext
|
#!/usr/bin/python3
|
||
|
|
||
|
import os
|
||
|
import shutil
|
||
|
import subprocess
|
||
|
import sys
|
||
|
|
||
|
image = 'build'
|
||
|
layers = []
|
||
|
mounts = []
|
||
|
uid = 0
|
||
|
gid = 0
|
||
|
cmd = '/bin/sh'
|
||
|
script = []
|
||
|
in_script = False
|
||
|
|
||
|
LXC_ROOT = '/var/lib/lxc'
|
||
|
CONFIG_TEMPLATE = '''# Image name
|
||
|
lxc.uts.name = {image}
|
||
|
|
||
|
# Network
|
||
|
lxc.net.0.type = veth
|
||
|
lxc.net.0.link = lxcbr0
|
||
|
lxc.net.0.flags = up
|
||
|
lxc.net.0.ipv4.address = 172.17.0.2/16
|
||
|
lxc.net.0.ipv4.gateway = auto
|
||
|
|
||
|
# Volumes
|
||
|
lxc.rootfs.path = {rootfs}
|
||
|
|
||
|
# Mounts
|
||
|
lxc.mount.entry = /etc/hosts etc/hosts none bind 0 0
|
||
|
lxc.mount.entry = /etc/resolv.conf etc/resolv.conf none bind 0 0
|
||
|
{mounts}
|
||
|
# Init
|
||
|
lxc.init.cmd = {cmd}
|
||
|
lxc.init.uid = {uid}
|
||
|
lxc.init.gid = {gid}
|
||
|
|
||
|
# Halt
|
||
|
lxc.signal.halt = SIGTERM
|
||
|
|
||
|
# Other
|
||
|
lxc.arch = x86_64
|
||
|
lxc.cap.drop = sys_admin
|
||
|
lxc.include = /usr/share/lxc/config/alpine.common.conf
|
||
|
'''
|
||
|
|
||
|
if os.path.isfile(sys.argv[1]):
|
||
|
lxcfile = os.path.realpath(sys.argv[1])
|
||
|
build_context = os.path.basepath(lxcfile)
|
||
|
else:
|
||
|
build_context = os.path.realpath(sys.argv[1])
|
||
|
lxcfile = os.path.join(build_context, 'lxcfile')
|
||
|
|
||
|
def main():
|
||
|
with open(lxcfile, 'r') as fd:
|
||
|
recipe = fd.readlines()
|
||
|
|
||
|
for line in recipe:
|
||
|
if line == 'RUN':
|
||
|
in_script = False
|
||
|
run_script()
|
||
|
elif in_script and not line and not line.startswith('#'):
|
||
|
script.append()
|
||
|
elif line == 'SCRIPT':
|
||
|
script = []
|
||
|
in_script = True
|
||
|
elif line.startswith('IMAGE'):
|
||
|
image = line.split()[1]
|
||
|
os.makedirs(os.path.join(LXC_ROOT, image), 0o755, True)
|
||
|
elif line.startswith('LAYER'):
|
||
|
layers.append(line.split()[1])
|
||
|
rebuild_config()
|
||
|
fix_world()
|
||
|
elif line.startswith('COPY'):
|
||
|
copy_files(*line.split()[1:2])
|
||
|
elif line.startswith('MOUNT'):
|
||
|
mounts.append(line.split()[1])
|
||
|
rebuild_config()
|
||
|
elif line.startswith('USER'):
|
||
|
uid = line.split()[1]
|
||
|
gid = line.split()[2]
|
||
|
rebuild_config()
|
||
|
elif line.startswith('CMD'):
|
||
|
cmd = line.split()[1]
|
||
|
rebuild_config()
|
||
|
layers.append('{}/delta0'.format(image))
|
||
|
|
||
|
def rebuild_config():
|
||
|
rootfs_layers = [os.path.join(LXC_ROOT, l) for l in layers]
|
||
|
for layer in rootfs_layers:
|
||
|
os.makedirs(layer, 0o755, True)
|
||
|
if len(rootfs_layers) == 1:
|
||
|
rootfs_path = rootfs_layers[0]
|
||
|
else:
|
||
|
rootfs_path = 'overlay:{}'.format(':'.join(rootfs_layers))
|
||
|
mount_entries = '\n'.join(['lxc.mount.entry = {} none bind 0 0'.format(m) for m in mounts])
|
||
|
with open(os.path.join(LXC_ROOT, image, 'config'), 'w') as fd:
|
||
|
fd.write(CONFIG_TEMPLATE.format(image=image, rootfs=rootfs_path, mounts=mount_entries, uid=uid, gid=gid, cmd=cmd))
|
||
|
|
||
|
def fix_world():
|
||
|
world_items = []
|
||
|
last_world = []
|
||
|
for layer in layers[:-1]:
|
||
|
with open(os.path.join(LXC_ROOT, layer, 'etc/apk/world'), 'r') as fd:
|
||
|
last_world = fd.read().splitlines()
|
||
|
world_items.extend(last_world)
|
||
|
world_items = sorted(set(world_items))
|
||
|
if world_items != sorted(last_world):
|
||
|
os.makedirs(os.path.join(LXC_ROOT, layers[-1], 'etc/apk'))
|
||
|
with open(os.path.join(LXC_ROOT, layers[-1], 'etc/apk/world'), 'w') as fd:
|
||
|
fd.writelines(world_items)
|
||
|
|
||
|
def run_script():
|
||
|
script_filename = os.path.join(LXC_ROOT, layers[-1], 'run.sh')
|
||
|
with open(script_filename, 'w') as fd:
|
||
|
fd.write(' && '.join([s for s in script]))
|
||
|
os.chmod(script_filename, 0o700)
|
||
|
subprocess.run(['lxc-execute', '-n', image, '--', '/bin/sh', '-lvc', '/run.sh'], check=True)
|
||
|
os.unlink(script_filename)
|
||
|
|
||
|
def copy_files(src, dst):
|
||
|
src = os.path.join(build_context, src)
|
||
|
dst = os.path.join(LXC_ROOT, layers[-1], dst)
|
||
|
shutil.copytree(src, dst)
|
||
|
|
||
|
if __name__ == '__init__':
|
||
|
main()
|