diff --git a/coreos-assembler b/coreos-assembler index a5b77df3b4..7313cfd2ac 100755 --- a/coreos-assembler +++ b/coreos-assembler @@ -37,7 +37,7 @@ cmd=${1:-} # commands we'd expect to use in the local dev path build_commands="init fetch build run prune clean" # commands more likely to be used in a prod pipeline only -advanced_build_commands="buildprep buildextend-ec2 buildextend-openstack oscontainer" +advanced_build_commands="buildprep buildextend-ec2 buildextend-openstack buildextend-squashfs oscontainer" utility_commands="tag compress bump-timestamp" other_commands="shell" if [ -z "${cmd}" ]; then diff --git a/src/cmd-buildextend-squashfs b/src/cmd-buildextend-squashfs new file mode 100755 index 0000000000..d586942a77 --- /dev/null +++ b/src/cmd-buildextend-squashfs @@ -0,0 +1,66 @@ +#!/usr/bin/python3 -u +# An operation that mutates a build by generating a squashfs rootfs. + +import os +import sys +import json +import yaml +import shutil +import argparse + +sys.path.insert(0, '/usr/lib/coreos-assembler') +from cmdlib import run_supermin, run_verbose, write_json, sha256sum_file + +# Parse args and dispatch +parser = argparse.ArgumentParser() +parser.add_argument("--build", help="Build ID") +args = parser.parse_args() + +# default to latest build if not specified +if not args.build: + with open('builds/builds.json') as f: + j = json.load(f) + args.build = j['builds'][0] + +print(f"Targeting build: {args.build}") + +with open('src/config/manifest.yaml') as f: + manifest = yaml.safe_load(f) + +base_name = manifest['rojig']['name'] +img_prefix = f'{base_name}-{args.build}' +artifact_name = f'{img_prefix}.rootfs.squashfs' + +builddir = f'builds/{args.build}' +buildmeta_path = f'{builddir}/meta.json' +with open(buildmeta_path) as f: + buildmeta = json.load(f) + +tmpdir = 'tmp/buildpost-squashfs' +if os.path.isdir(tmpdir): + shutil.rmtree(tmpdir) +os.mkdir(tmpdir) + + +def generate_squashfs(workdir): + ref = buildmeta['ref'] + tmp_img = f'{tmpdir}/{artifact_name}' + script = f'''#!/usr/bin/env bash +set -xeuo pipefail +ostree checkout --repo {workdir}/repo/ {ref} /tmp/rootfs +/sbin/mksquashfs /tmp/rootfs {workdir}/{tmp_img} + ''' + + run_supermin(workdir, script) + checksum = sha256sum_file(tmp_img) + buildmeta['images']['squashfs'] = { + 'path': artifact_name, + 'sha256': checksum + } + os.rename(tmp_img, f"{builddir}/{artifact_name}") + write_json(buildmeta_path, buildmeta) + print(f"Updated: {buildmeta_path}") + + +# Do it! +generate_squashfs(os.getcwd()) diff --git a/src/cmdlib.py b/src/cmdlib.py index cdb266e555..c61006f940 100755 --- a/src/cmdlib.py +++ b/src/cmdlib.py @@ -2,12 +2,15 @@ import hashlib import json +import multiprocessing import os +import shutil import subprocess import sys import tempfile from datetime import datetime +libdir = '/usr/lib/coreos-assembler/' def run_verbose(args, **kwargs): print("+ {}".format(subprocess.list2cmdline(args))) @@ -50,3 +53,82 @@ def rm_allow_noent(path): os.unlink(path) except FileNotFoundError: pass + + +def run_supermin(workdir, script=''): + vmpreparedir = f"{workdir}/tmp/supermin.prepare" + if os.path.isdir(vmpreparedir): + shutil.rmtree(vmpreparedir) + os.makedirs(vmpreparedir, exist_ok=True) + + vmbuilddir = f"{workdir}/tmp/supermin.build" + if os.path.isdir(vmbuilddir): + shutil.rmtree(vmbuilddir) + os.makedirs(vmbuilddir, exist_ok=True) + + rpms = [] + with open(f"{libdir}/vmdeps.txt") as vmdeps: + for line in vmdeps: + if not line or line.startswith('#'): + continue + for pkg in line.split(): + rpms.append(pkg) + + run_verbose(['supermin', + '--prepare', + '--use-installed', + '-o', f"{vmpreparedir}"] + rpms) + + with open(f"{libdir}/supermin-init-prelude.sh") as prelude: + sm_init = prelude.read() + + with open(f"{workdir}/tmp/cmd.sh", "w+") as scriptfile: + scriptfile.write(script) + initscript = f'''#!/usr/bin/env bash +set -xeuo pipefail +workdir={workdir} +{sm_init} +RC=0 +sh {workdir}/tmp/cmd.sh || RC=$? +echo $RC > {workdir}/tmp/rc +/sbin/fstrim -v {workdir}/cache +/sbin/poweroff -f + ''' + with open(f"{vmpreparedir}/init", "w+") as vminit: + vminit.write(initscript) + os.chmod(f"{vmpreparedir}/init", 0o755) + run_verbose(['tar', + '-C', f"{vmpreparedir}", + '-czf', f"{vmpreparedir}/init.tar.gz", + '--remove-files', + 'init']) + + run_verbose(['supermin', + '--build', f"{vmpreparedir}", + '--size', '5G', + '-f', 'ext2', + '-o', f"{vmbuilddir}"]) + + nproc = multiprocessing.cpu_count() + run_verbose(['qemu-kvm', '-nodefaults', '-nographic', '-no-reboot', + '-smp', f"{nproc}", + '-m', '2048', + '-kernel', f"{vmbuilddir}/kernel", + '-initrd', f"{vmbuilddir}/initrd", + '-netdev', 'user,id=eth0,hostname=supermin', + '-device', 'virtio-net-pci,netdev=eth0', + '-device', 'virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0x3', + '-drive', f"if=none,id=drive-scsi0-0-0-0,snapshot=on,file={vmbuilddir}/root", + '-device', 'scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,bootindex=1', + '-drive', f"if=none,id=drive-scsi0-0-0-1,discard=unmap,file={workdir}/cache/cache.qcow2", + '-device', 'scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=1,drive=drive-scsi0-0-0-1,id=scsi0-0-0-1', + '-virtfs', f"local,id=workdir,path={workdir},security_model=none,mount_tag=workdir", + '-serial', 'stdio', '-append', 'root=/dev/sda console=ttyS0 selinux=1 enforcing=0 autorelabel=1']) + + rc = 99 + with open(f"{workdir}/tmp/rc") as rcfile: + rc = int(rcfile.readline()) + + if rc != 0: + raise Exception(f"failed to run supermin (rc: {rc})") + return diff --git a/src/deps.txt b/src/deps.txt index a96dc4f0f8..12b7ee46fb 100644 --- a/src/deps.txt +++ b/src/deps.txt @@ -28,7 +28,7 @@ libvirt libguestfs-tools qemu-kvm /usr/bin/qemu-img /usr/bin/virsh /usr/bin/virt rsync python2-gobject-base python3-gobject-base # To support recursive containerization and manipulating images -podman buildah skopeo +podman buildah skopeo squashfs-tools # Miscellaneous tools jq awscli diff --git a/src/vmdeps.txt b/src/vmdeps.txt index ed77186200..2234cbe262 100644 --- a/src/vmdeps.txt +++ b/src/vmdeps.txt @@ -6,6 +6,9 @@ bash vim-minimal coreutils util-linux procps-ng kmod kernel-modules # for composes rpm-ostree distribution-gpg-keys jq +# for artifacts +squashfs-tools + # for clean reboot systemd