#!/usr/bin/python3

import os
import shutil
import subprocess
import sys

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.254/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
'''

image = None
layers = []
mounts = []
uid = 0
gid = 0
cmd = '/bin/true'
script = []
in_script = False

if os.path.isfile(sys.argv[1]):
    lxcfile = os.path.realpath(sys.argv[1])
    build_context = os.path.dirname(lxcfile)
else:
    build_context = os.path.realpath(sys.argv[1])
    lxcfile = os.path.join(build_context, 'lxcfile')

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', 0o755, True))
        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('#!/bin/sh\nset -ev\n\n{}\n'.format('\n'.join(script)))
    os.chmod(script_filename, 0o700)
    subprocess.run(['lxc-execute', '-n', image, '--', '/bin/sh', '-lc', '/run.sh'], check=True)
    os.unlink(script_filename)

def copy_tree(src, dst):
    if not os.path.isdir(src):
        shutil.copy2(src, dst)
    else:
        os.makedirs(dst, exist_ok=True)
        for name in os.listdir(src):
            copy_tree(os.path.join(src, name), os.path.join(dst, name))
    shutil.copystat(src, dst)

def copy_files(src, dst):
    src = os.path.join(build_context, src)
    dst = os.path.join(LXC_ROOT, layers[-1], dst)
    copy_tree(src, dst)

with open(lxcfile, 'r') as fd:
    recipe = fd.readlines()

for line in recipe:
    line = line.strip()
    if line == 'RUN':
        in_script = False
        run_script()
    elif in_script:
        script.append(line)
    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()
    elif line == 'FIXWORLD':
        fix_world()
    elif line.startswith('COPY'):
        srcdst = line.split()
        copy_files(srcdst[1], srcdst[2] if len(srcdst) == 3 else '')
    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))
