Files
iconograph/server/build_image.py

292 lines
6.8 KiB
Python
Raw Normal View History

2016-03-30 13:43:20 -07:00
#!/usr/bin/python3
import argparse
import os
import shutil
import subprocess
2016-03-30 15:39:59 -07:00
import sys
2016-03-30 13:43:20 -07:00
import tempfile
2016-03-31 22:50:49 -07:00
import time
2016-03-30 13:43:20 -07:00
parser = argparse.ArgumentParser(description='iconograph build_image')
parser.add_argument(
'--arch',
dest='arch',
action='store',
default='amd64')
parser.add_argument(
'--archive',
dest='archive',
action='store',
default='http://archive.ubuntu.com/ubuntu')
parser.add_argument(
2016-03-31 22:50:49 -07:00
'--image-dir',
dest='image_dir',
2016-03-30 13:43:20 -07:00
action='store',
required=True)
2016-03-31 16:25:41 -07:00
parser.add_argument(
'--module',
dest='modules',
2016-03-31 18:00:39 -07:00
action='append')
2016-03-30 13:43:20 -07:00
parser.add_argument(
'--release',
dest='release',
action='store',
required=True)
2016-03-30 14:26:53 -07:00
parser.add_argument(
'--shell',
dest='shell',
2016-03-30 15:39:59 -07:00
action='store_true',
2016-03-30 14:26:53 -07:00
default=False)
2016-03-30 13:43:20 -07:00
parser.add_argument(
'--source-iso',
dest='source_iso',
action='store',
required=True)
FLAGS = parser.parse_args()
class ImageBuilder(object):
2016-03-30 14:26:53 -07:00
_BASE_PACKAGES = [
'devscripts',
'nano',
'iputils-ping',
2016-03-31 12:14:28 -07:00
'linux-firmware',
2016-03-31 14:30:49 -07:00
'linux-firmware-nonfree',
2016-03-30 15:39:59 -07:00
'openssh-server',
2016-03-31 12:14:28 -07:00
'ubuntu-minimal',
'ubuntu-standard',
2016-03-30 14:26:53 -07:00
'user-setup',
]
2016-03-30 16:16:22 -07:00
_SUITES = [
'%(release)s',
'%(release)s-updates',
]
_SECTIONS = [
'main',
'restricted',
'universe',
2016-03-31 14:30:49 -07:00
'multiverse',
2016-03-30 16:16:22 -07:00
]
_DIVERSIONS = {
'/sbin/initctl': '/bin/true',
'/etc/init.d/systemd-logind': '/bin/true',
}
2016-03-31 11:50:37 -07:00
_ISO_COPIES = {
'grub.cfg': 'boot/grub/grub.cfg',
2016-03-31 11:50:37 -07:00
'loopback.cfg': 'boot/grub/loopback.cfg',
}
2016-03-31 22:50:49 -07:00
def __init__(self, source_iso, image_dir, archive, arch, release, modules):
2016-03-30 13:43:20 -07:00
self._source_iso = source_iso
2016-03-31 22:50:49 -07:00
self._image_dir = image_dir
2016-03-30 13:43:20 -07:00
self._archive = archive
self._arch = arch
self._release = release
2016-03-31 22:50:49 -07:00
self._modules = modules or []
2016-03-30 15:39:59 -07:00
self._ico_server_path = os.path.dirname(sys.argv[0])
def _Exec(self, *args, **kwargs):
2016-03-30 13:43:20 -07:00
print('+', args)
subprocess.check_call(args, **kwargs)
2016-03-30 13:43:20 -07:00
def _ExecChroot(self, chroot_path, *args, **kwargs):
self._Exec('chroot', chroot_path, *args, **kwargs)
2016-03-30 14:26:53 -07:00
2016-03-30 13:43:20 -07:00
def _Debootstrap(self, root):
path = os.path.join(root, 'chroot')
os.mkdir(path)
self._Exec(
'debootstrap',
'--variant=buildd',
'--arch', self._arch,
self._release,
path,
self._archive)
return path
def _CreateUnion(self, root):
iso_path = os.path.join(root, 'iso')
os.mkdir(iso_path)
self._Exec(
'mount',
'--options', 'loop,ro',
self._source_iso,
iso_path)
self._umount.append(iso_path)
2016-03-31 10:02:05 -07:00
upper_path = os.path.join(root, 'upper')
2016-03-30 13:43:20 -07:00
os.mkdir(upper_path)
2016-03-31 10:02:05 -07:00
work_path = os.path.join(root, 'work')
2016-03-30 13:43:20 -07:00
os.mkdir(work_path)
union_path = os.path.join(root, 'union')
os.mkdir(union_path)
self._Exec(
'mount',
'--types', 'overlayfs',
'--options', 'lowerdir=%s,upperdir=%s,workdir=%s' % (iso_path, upper_path, work_path),
'none',
union_path)
self._umount.append(union_path)
return union_path
2016-03-30 16:16:22 -07:00
def _FixSourcesList(self, chroot_path):
path = os.path.join(chroot_path, 'etc', 'apt', 'sources.list')
with open(path, 'w') as fh:
for suite in self._SUITES:
fh.write('deb %(archive)s %(suite)s %(sections)s\n' % {
'archive': self._archive,
'suite': suite % {
'release': self._release,
},
'sections': ' '.join(self._SECTIONS),
})
def _AddDiversions(self, chroot_path):
for source, dest in self._DIVERSIONS.items():
self._ExecChroot(
chroot_path,
'dpkg-divert',
'--local',
'--rename',
'--add',
source)
self._ExecChroot(
chroot_path,
'ln',
'--symbolic',
'--force',
dest,
source)
with open(os.path.join(chroot_path, 'usr', 'sbin', 'policy-rc.d'), 'w') as fh:
fh.write('#!/bin/sh\n')
fh.write('exit 101\n')
2016-04-01 10:04:24 -07:00
os.fchmod(fh.fileno(), 0o744)
2016-03-30 14:26:53 -07:00
def _InstallPackages(self, chroot_path):
2016-03-31 12:14:28 -07:00
os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
2016-03-30 16:16:22 -07:00
self._ExecChroot(
chroot_path,
'apt-get',
'update')
2016-03-30 14:26:53 -07:00
self._ExecChroot(
chroot_path,
'apt-get',
'install',
'--assume-yes',
*self._BASE_PACKAGES)
2016-03-31 16:25:41 -07:00
def _RunModules(self, chroot_path):
for module in self._modules:
self._Exec(
'%(module)s --chroot-path=%(chroot_path)s' % {
'module': module,
'chroot_path': chroot_path,
},
shell=True)
2016-03-31 18:11:24 -07:00
def _CleanPackages(self, chroot_path):
self._ExecChroot(
chroot_path,
'apt-get',
'clean')
def _RemoveDiversions(self, chroot_path):
for source in self._DIVERSIONS:
self._ExecChroot(
chroot_path,
'rm',
source)
self._ExecChroot(
chroot_path,
'dpkg-divert',
'--rename',
'--remove',
source)
os.unlink(os.path.join(chroot_path, 'usr', 'sbin', 'policy-rc.d'))
2016-03-30 13:43:20 -07:00
def _Squash(self, chroot_path, union_path):
self._Exec(
'mksquashfs',
chroot_path,
os.path.join(union_path, 'casper', 'filesystem.squashfs'),
'-noappend')
2016-03-31 11:50:37 -07:00
def _CopyISOFiles(self, union_path):
for source, dest in self._ISO_COPIES.items():
shutil.copyfile(
os.path.join(self._ico_server_path, 'iso_files', source),
os.path.join(union_path, dest))
2016-03-30 15:39:59 -07:00
2016-03-31 22:50:49 -07:00
def _CreateISO(self, union_path, timestamp):
dest_iso = os.path.join(self._image_dir, '%d.iso' % timestamp)
2016-03-30 13:43:20 -07:00
self._Exec(
'grub-mkrescue',
2016-03-31 22:50:49 -07:00
'--output=%s' % dest_iso,
2016-03-30 13:43:20 -07:00
union_path)
def _BuildImage(self):
root = tempfile.mkdtemp()
self._rmtree.append(root)
2016-03-31 22:50:49 -07:00
timestamp = int(time.time())
2016-03-30 13:43:20 -07:00
print('Building image in:', root)
2016-03-31 10:02:05 -07:00
self._Exec(
'mount',
'--types', 'tmpfs',
'none',
root)
self._umount.append(root)
2016-03-30 13:43:20 -07:00
chroot_path = self._Debootstrap(root)
union_path = self._CreateUnion(root)
2016-03-30 16:16:22 -07:00
self._FixSourcesList(chroot_path)
self._AddDiversions(chroot_path)
2016-03-30 14:26:53 -07:00
self._InstallPackages(chroot_path)
2016-03-31 16:25:41 -07:00
self._RunModules(chroot_path)
2016-03-31 18:11:24 -07:00
self._CleanPackages(chroot_path)
self._RemoveDiversions(chroot_path)
2016-03-30 14:26:53 -07:00
if FLAGS.shell:
self._Exec('bash', cwd=root)
2016-03-30 13:43:20 -07:00
self._Squash(chroot_path, union_path)
2016-03-31 11:50:37 -07:00
self._CopyISOFiles(union_path)
2016-03-31 22:50:49 -07:00
self._CreateISO(union_path, timestamp)
2016-03-30 13:43:20 -07:00
def BuildImage(self):
2016-03-31 14:28:45 -07:00
self._umount = []
self._rmtree = []
2016-03-30 13:43:20 -07:00
try:
self._BuildImage()
finally:
2016-03-31 10:02:05 -07:00
for path in reversed(self._umount):
2016-03-30 13:43:20 -07:00
self._Exec('umount', path)
for path in self._rmtree:
shutil.rmtree(path)
def main():
builder = ImageBuilder(
FLAGS.source_iso,
2016-03-31 22:50:49 -07:00
FLAGS.image_dir,
2016-03-30 13:43:20 -07:00
FLAGS.archive,
FLAGS.arch,
2016-03-30 16:54:17 -07:00
FLAGS.release,
2016-03-31 16:25:41 -07:00
FLAGS.modules)
2016-03-30 13:43:20 -07:00
builder.BuildImage()
if __name__ == '__main__':
main()