Spotter-VM/zz-extra/lxc-build

184 lines
5.7 KiB
Plaintext
Raw Normal View History

2018-09-05 17:41:38 +02:00
#!/usr/bin/python3
import os
import shutil
import subprocess
import sys
LXC_ROOT = '/var/lib/lxc'
CONFIG_TEMPLATE = '''# Image name
2018-09-06 09:32:20 +02:00
lxc.uts.name = {name}
2018-09-05 17:41:38 +02:00
# Network
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
# Volumes
lxc.rootfs.path = {rootfs}
# Mounts
2018-09-07 18:46:30 +02:00
lxc.mount.entry = shm dev/shm tmpfs rw,nodev,noexec,nosuid,relatime,mode=1777,create=dir 0 0
2018-09-05 17:41:38 +02:00
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}
2018-09-06 09:32:20 +02:00
2018-09-05 17:41:38 +02:00
# Init
lxc.init.cmd = {cmd}
lxc.init.uid = {uid}
lxc.init.gid = {gid}
2018-09-12 16:08:10 +02:00
lxc.init.cwd = {cwd}
2018-09-05 17:41:38 +02:00
2018-09-11 19:20:18 +02:00
# Environment
2018-09-13 19:17:29 +02:00
lxc.environment = PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
2018-09-11 19:20:18 +02:00
{env}
2018-09-05 17:41:38 +02:00
# Halt
lxc.signal.halt = SIGTERM
2018-09-07 14:37:38 +02:00
# Log
lxc.console.size = 1MB
lxc.console.logfile = /var/log/lxc/{name}.log
2018-09-05 17:41:38 +02:00
# Other
lxc.arch = x86_64
lxc.cap.drop = sys_admin
2018-09-12 10:36:48 +02:00
lxc.hook.start-host = /usr/bin/vmmgr register-container
lxc.hook.post-stop = /usr/bin/vmmgr unregister-container
2018-09-07 14:37:38 +02:00
lxc.include = /usr/share/lxc/config/common.conf
2018-09-05 17:41:38 +02:00
'''
2018-09-06 09:32:20 +02:00
class LXCImage:
def __init__(self, build_path):
self.name = None
self.layers = []
self.mounts = []
2018-09-11 19:20:18 +02:00
self.env = []
2018-09-06 09:32:20 +02:00
self.uid = 0
self.gid = 0
self.cmd = '/bin/true'
2018-09-12 16:08:10 +02:00
self.cwd = '/'
2018-09-06 09:32:20 +02:00
if os.path.isfile(build_path):
self.lxcfile = os.path.realpath(build_path)
2018-09-06 09:41:40 +02:00
self.build_dir = os.path.dirname(self.lxcfile)
2018-09-06 09:32:20 +02:00
else:
2018-09-06 09:41:40 +02:00
self.build_dir = os.path.realpath(build_path)
self.lxcfile = os.path.join(self.build_dir, 'lxcfile')
2018-09-06 09:32:20 +02:00
def build(self):
with open(self.lxcfile, 'r') as fd:
lxcfile = [l.strip() for l in fd.readlines()]
script = []
2018-09-12 16:08:10 +02:00
script_eof = None
2018-09-06 09:32:20 +02:00
for line in lxcfile:
2018-09-12 16:12:23 +02:00
if script_eof:
if line == script_eof:
script_eof = None
self.run_script(script)
2018-09-12 16:16:38 +02:00
else:
2018-09-12 16:12:23 +02:00
script.append(line)
2018-09-12 16:08:10 +02:00
elif line.startswith('RUN'):
2018-09-06 09:32:20 +02:00
script = []
2018-09-12 16:08:10 +02:00
script_eof = line.split()[1]
2018-09-06 09:32:20 +02:00
elif line.startswith('IMAGE'):
self.set_name(line.split()[1])
elif line.startswith('LAYER'):
self.add_layer(line.split()[1])
2018-09-12 19:04:55 +02:00
elif line.startswith('FIXLAYER'):
self.fix_layer(line.split()[1])
2018-09-06 09:32:20 +02:00
elif line.startswith('COPY'):
srcdst = line.split()
self.copy_files(srcdst[1], srcdst[2] if len(srcdst) == 3 else '')
elif line.startswith('MOUNT'):
2018-09-12 19:04:55 +02:00
mount = line.split()
self.add_mount(mount[1], mount[2])
2018-09-11 19:20:18 +02:00
elif line.startswith('ENV'):
env = line.split()
self.add_env(env[1], env[2])
2018-09-06 09:32:20 +02:00
elif line.startswith('USER'):
uidgid = line.split()
self.set_user(uidgid[1], uidgid[2])
elif line.startswith('CMD'):
2018-09-06 14:20:30 +02:00
self.set_cmd(' '.join(line.split()[1:]))
2018-09-12 16:08:10 +02:00
elif line.startswith('WORKDIR'):
self.set_cwd(line.split()[1])
# Add the final layer which can be treated as ephemeral
2018-09-06 09:32:20 +02:00
self.add_layer('{}/delta0'.format(self.name))
def rebuild_config(self):
2018-09-06 09:41:40 +02:00
if len(self.layers) == 1:
2018-09-12 19:04:55 +02:00
rootfs = self.layers[0]
2018-09-06 09:32:20 +02:00
else:
# Multiple lower overlayfs layers are ordered from right to left (lower2:lower1:rootfs:upper)
2018-09-12 19:04:55 +02:00
rootfs = 'overlay:{}:{}'.format(':'.join(self.layers[:-1][::-1]), self.layers[-1])
mounts = '\n'.join(self.mounts)
env = '\n'.join(self.env)
2018-09-06 09:32:20 +02:00
with open(os.path.join(LXC_ROOT, self.name, 'config'), 'w') as fd:
2018-09-12 19:04:55 +02:00
fd.write(CONFIG_TEMPLATE.format(name=self.name, rootfs=rootfs, mounts=mounts, env=env, uid=self.uid, gid=self.gid, cmd=self.cmd, cwd=self.cwd))
2018-09-06 09:32:20 +02:00
def run_script(self, script):
sh = os.path.join(self.layers[-1], 'run.sh')
with open(sh, 'w') as fd:
fd.write('#!/bin/sh\nset -ev\n\n{}\n'.format('\n'.join(script)))
os.chmod(sh, 0o700)
subprocess.run(['lxc-execute', '-n', self.name, '--', '/bin/sh', '-lc', '/run.sh'], check=True)
os.unlink(sh)
def set_name(self, name):
self.name = name
os.makedirs(os.path.join(LXC_ROOT, self.name), 0o755, True)
def add_layer(self, layer):
layer = os.path.join(LXC_ROOT, layer)
self.layers.append(layer)
2018-09-05 17:41:38 +02:00
os.makedirs(layer, 0o755, True)
2018-09-06 09:32:20 +02:00
self.rebuild_config()
2018-09-12 19:04:55 +02:00
def fix_layer(self, cmd):
2018-09-12 16:08:10 +02:00
subprocess.run([cmd]+self.layers, check=True)
2018-09-06 09:32:20 +02:00
def copy_files(self, src, dst):
2018-09-06 10:02:10 +02:00
src = os.path.join(self.build_dir, src)
2018-09-06 09:45:13 +02:00
dst = os.path.join(self.layers[-1], dst)
2018-09-06 09:32:20 +02:00
copy_tree(src, dst)
def add_mount(self, src, dst):
2018-09-12 19:04:55 +02:00
self.mounts.append('lxc.mount.entry = {} {} none bind 0 0'.format(src, dst))
2018-09-06 09:32:20 +02:00
self.rebuild_config()
2018-09-11 19:20:18 +02:00
def add_env(self, key, value):
2018-09-12 19:04:55 +02:00
self.env.append('lxc.environment = {}={}'.format(key, value))
2018-09-11 19:20:18 +02:00
self.rebuild_config()
2018-09-06 09:32:20 +02:00
def set_user(self, uid, gid):
self.uid = uid
self.gid = gid
self.rebuild_config()
def set_cmd(self, cmd):
self.cmd = cmd
self.rebuild_config()
2018-09-05 17:41:38 +02:00
2018-09-12 16:08:10 +02:00
def set_cwd(self, cwd):
self.cwd = cwd
self.rebuild_config()
2018-09-05 21:21:07 +02:00
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)
2018-09-06 09:32:20 +02:00
if __name__ == '__main__':
if len(sys.argv) != 2:
print('Usage: lxc-build <buildpath>\n where the buildpath can be either specific lxcfile or a directory containing one')
else:
i = LXCImage(sys.argv[1])
i.build()