Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion coreos-assembler
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
66 changes: 66 additions & 0 deletions src/cmd-buildextend-squashfs
Original file line number Diff line number Diff line change
@@ -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}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a stub right? The commit description says it's bootable this won't do that AFAICS.

This whole concept kind of depends on us reworking to use not-anaconda right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and no.

This a basic ostree->squashfs conversion, and the resulting artifact can already be run by systemd-nspawn --volatile --boot (I wouldn't say it's bootable, as it doesn't contain any bootloader). No very interesting per se, but (possibly) an intermediate step towards re-using this rootfs for embeddeding in live PXE/ISO artifacts.

In that regard, this flow is already bypassing anaconda. It consumes an ostree ref and runs this build-extending script in a supermin VM.

'''

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())
82 changes: 82 additions & 0 deletions src/cmdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand Down Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An ensure_empty_dir() in cmdlib.py would make sense.


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('#'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd write if line == "" instead of if not line since I have been bitten in the past trying to remember how different languages coerce to booleans.

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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully a next step after this would be to rewrite runcompose()/runvm() to use this instead.


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
2 changes: 1 addition & 1 deletion src/deps.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/vmdeps.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down