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
25 changes: 12 additions & 13 deletions src/cmd-build
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,18 @@ EOF
composejson=$(pwd)/tmp/compose.json
changed_stamp=$(pwd)/tmp/treecompose.changed
# --cache-only is here since `fetch` is a separate verb.
runcompose --cache-only --add-metadata-from-json ${commitmeta_input_json} \
--touch-if-changed "${changed_stamp}" \
--write-composejson-to ${composejson}
commit=$(ostree --repo=${workdir}/repo-build rev-parse "${ref}")
version=$(ostree --repo=${workdir}/repo-build show --print-metadata-key=version ${commit} | sed -e "s,',,g")
args=" --cache-only --add-metadata-from-json ${commitmeta_input_json}"
args+=" --touch-if-changed ${changed_stamp}"
args+=" --write-composejson-to ${composejson}"

runcompose_in_vm ${previous_commit:-null} $ref $args

# update summary file
ostree --repo=${workdir}/repo summary -u

commit=$(ostree --repo=${workdir}/repo rev-parse "${ref}")
version=$(ostree --repo=${workdir}/repo show --print-metadata-key=version ${commit} | sed -e "s,',,g")

# Very special handling for --write-composejson-to as rpm-ostree doesn't
# write it if the commit didn't change.
if [ -f "${changed_stamp}" ]; then
Expand All @@ -45,14 +52,6 @@ else
# Grab the previous JSON
cp -a --reflink=auto ${workdir}/tmp/compose-${commit}.json ${composejson}
fi
# https://github.com/ostreedev/ostree/issues/1562#issuecomment-385393872
# The passwd files (among others) don't have world readability. This won't
# actually corrupt the repository as the *canonical* permissions are stored
# as xattrs. Probably what we should do is have an ostree option to specify
# a permission mask for objects.
sudo chmod -R a+rX ${workdir}/repo-build/objects
ostree --repo=${workdir}/repo pull-local ${workdir}/repo-build "${ref}"
ostree --repo=${workdir}/repo summary -u

sha256sum_str() {
sha256sum | cut -f 1 -d ' '
Expand Down
5 changes: 3 additions & 2 deletions src/cmd-init
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ mkdir -p installer
fi
)

mkdir -p cache
mkdir -p builds
mkdir -p tmp
ostree --repo=repo init --mode=archive
ostree --repo=repo-build init --mode=bare-user

# prepare the unprivileged vm to run the compose in
prepare_vm
107 changes: 98 additions & 9 deletions src/cmdlib.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Shared shell script library

# Global variables
export workdir=$(pwd)
export configdir=${workdir}/src/config
export manifest=${configdir}/manifest.yaml
export superminpreparedir="${workdir}/tmp/supermin-prepare.d"
export superminbuilddir="${workdir}/tmp/supermin-build.d"
export cachesimg="${workdir}/caches.qcow2"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some of this could be split into a prep commit.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yep. i want to do that and also was thinking about making global variables all caps.


fatal() {
echo "error: $@" 1>&2; exit 1
}
Expand Down Expand Up @@ -28,13 +36,13 @@ preflight() {
fatal "Unable to find /dev/kvm"
fi

if ! capsh --print | grep -q 'Current.*cap_sys_admin'; then
fatal "This container must currently be run with --privileged"
fi
# if ! capsh --print | grep -q 'Current.*cap_sys_admin'; then
# fatal "This container must currently be run with --privileged"
# fi

if ! sudo true; then
fatal "The user must currently have sudo privileges"
fi
# if ! sudo true; then
# fatal "The user must currently have sudo privileges"
# fi

# permissions on /dev/kvm vary by (host) distro. If it's
# not writable, recreate it.
Expand All @@ -51,9 +59,12 @@ prepare_build() {
fatal "No $(pwd)/repo found; did you run coreos-assembler init?"
fi

export workdir=$(pwd)
export configdir=${workdir}/src/config
export manifest=${configdir}/manifest.yaml
# export workdir=$(pwd)
# export configdir=${workdir}/src/config
# export manifest=${configdir}/manifest.yaml
# export superminpreparedir="${workdir}/tmp/supermin-prepare.d"
# export superminbuilddir="${workdir}/tmp/supermin-build.d"
# export cachesimg="${workdir}/caches.qcow2"

if ! [ -f "${manifest}" ]; then
fatal "Failed to find ${manifest}"
Expand Down Expand Up @@ -102,3 +113,81 @@ runcompose() {
${TREECOMPOSE_FLAGS:-} ${manifest} "$@"
set +x
}

prepare_vm() {
# rpms to create appliance VM out of
rpms=' bash vim-minimal coreutils util-linux procps-ng kmod kernel-modules'
rpms+=' cifs-utils' # for samba
rpms+=' systemd' # for clean reboot
rpms+=' dhcp-client bind-export-libs iproute' # networking
rpms+=' rpm-ostree distribution-gpg-keys' # to run the compose
rpms+=' selinux-policy selinux-policy-targeted policycoreutils' #selinux
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe nicer to maintain this as a supermin.txt file or so in our source?

One thing to think about too is that supermin also supports --build explicitly which we could do as part of our container build. (The whole libguestfs/supermin was designed around pulling content dynamically from the host, but in our case the container is static)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Maybe nicer to maintain this as a supermin.txt file or so in our source?

i.e. just the rpm requirements in a separate file?

One thing to think about too is that supermin also supports --build explicitly which we could do as part of our container build. (The whole libguestfs/supermin was designed around pulling content dynamically from the host, but in our case the container is static)

yeah I do a --build below, right? I'd rather not do the --build as part of the container build because it will just add a rootfs filesystem to our container and will make it larger to download with not real benefit. that's why I put it as part of the coreos-assembler init

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Well, the benefit is it's faster to run each time.

Note that coreos-assembler init is intended to be run only once. If the VM image is cached there, then when you pull a new container, it will be out of date.

We have cache/...but really every time the container changes the VM image is going to need to change anyways. And with this, the container doesn't do much without the VM image so...we're trading off download/disk space versus CPU usage every update.


# prepare appliance VM
supermin -v --prepare --use-installed $rpms -o "${superminpreparedir}"
supermin -v --build "${superminpreparedir}" \
--include-packagelist --size 4G -f ext2 -o "${superminbuilddir}"

# Create a disk image for our caches (pkgcache and bare-user build repo)
[ ! -d "${workdir}/tmp/emptydir" ] && mkdir "${workdir}/tmp/emptydir"
if [ ! -f "${cachesimg}" ]; then
virt-make-fs --format=qcow2 --type=xfs \
--size=10G "${workdir}/tmp/emptydir" "${cachesimg}"
fi
}

run_vm() {
init=$1
set -x

# Copy in the init script with the code we want to run
chmod +x "${init}"
virt-copy-in -a "${superminbuilddir}/root" "${init}" /

# Execute unprivileged VM to run compose. Some notes:
# - smb="${workdir}" - we'll serve our workdir over samba
# - sda - rootfs from supermin build
# - sdb - caches.qcow2 - we store the bare-user repo and pkgcache here
# - discard=unmap - using virtio-scsi disks and discard=unmap so we can fstrim
# and recover disk space from the VM
qemu-kvm -nodefaults -nographic -m 2048 -no-reboot \
-kernel "${superminbuilddir}/kernel" \
-initrd "${superminbuilddir}/initrd" \
-netdev user,id=eth0,hostname=supermin,smb="${workdir}",hostfwd=tcp:127.0.0.1:8000-:8000 \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since today we run with --net=host, suddenly we'll need to worry about port conflicts. It is tempting to go back to the default bridged networking. I forget if there was a reason I added --net=host to the default arguments...oh oh right, it's because I use dns=dnsmasq on my host and podman today can't handle that. Urgh. I can deal with working around that here.

But in general...for sharing content between the container and VM I lean a bit towards using say rsync-over-ssh-over-virtio or something like that - there's no good reason to use TCP here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

actually I can remove the hostfwd I don't actually need it. I was experimenting to see if pulling via HTTP was any faster than doing the ostree pull-local over smb. it wasn't, so I'll just remove this.

Since today we run with --net=host

I've actually been running without --net=host in my tests, so my plan was to actually remove that. Are you saying that would be problematic?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's OK to remove if that turns out to be useful, I'll figure out how to make my current setup work with default bridged.

-device virtio-net-pci,netdev=eth0 \
-device virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0x3 \
-drive if=none,id=drive-scsi0-0-0-0,snapshot=on,file="${superminbuilddir}/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 if=none,id=drive-scsi0-0-0-1,discard=unmap,file="${cachesimg}" \
-device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=1,drive=drive-scsi0-0-0-1,id=scsi0-0-0-1 \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Where did you get this list of arguments?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

handcrafted. some of it I copied from other files in this repo. the scsi lines are there so I can fstrim inside the VM and have it provide free space back to the host. I copied those by setting up a libvirt VM with virtio-scsi and discard=unmap and then inspecting the qemu arguments..

see: https://dustymabe.com/2013/06/11/recover-space-from-vm-disk-images-by-using-discardfstrim/

-serial stdio -append "root=/dev/sda console=ttyS0 selinux=1 enforcing=0 autorelabel=1"

if [ -f "${workdir}/tmp/supermin-failure" ]; then
rm -f "${workdir}/tmp/supermin-failure"
fatal "Detected failure from compose VM run"
fi
}

runcompose_in_vm() {
previous_commit=$1
ref=$2
shift; shift;

local treecompose_args=""
if ! grep -q '^# disable-unified-core' "${manifest}"; then
treecompose_args="${treecompose_args} --unified-core"
fi

cmd="rpm-ostree compose tree --repo=${workdir}/repo-build"
cmd+=" --cachedir=${workdir}/cache ${treecompose_args}"
cmd+=" ${TREECOMPOSE_FLAGS:-} ${manifest} $@"

# populate variables in init script
template=$(dirname $0)/supermin-init
sed -e "s|^workdir=|workdir=\"${workdir}\"|" \
-e "s|^previous_commit=|previous_commit=\"${previous_commit}\"|" \
-e "s|^ref=|ref=\"${ref}\"|" \
-e "s|^cmd=|cmd=\"${cmd}\"|" $template > "${superminbuilddir}/init"

run_vm "${superminbuilddir}/init"
}
3 changes: 3 additions & 0 deletions src/deps.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ jq awscli

# For ignition file validation in cmd-run
ignition

# For running supermin VM via qemu and sharing via samba
samba cifs-utils
119 changes: 119 additions & 0 deletions src/supermin-init
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/bin/sh
set -ux

trap fatal SIGHUP SIGINT SIGQUIT SIGABRT

export PATH=/usr/sbin/:$PATH

mount -t proc /proc /proc
mount -t sysfs /sys /sys
mount -t devtmpfs devtmpfs /dev

# Load selinux policy
LANG=C /sbin/load_policy -i

# Load kernel module for 9pnet_virtio for 9pfs mount
#/sbin/modprobe 9pnet_virtio

# Need fuse module for rofiles-fuse/bwrap during post scripts run
/sbin/modprobe fuse

# set up networking
/usr/sbin/dhclient eth0 &
sleep 2 # wait for dhcp
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ewww 😄

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

haha, yeah it's possible I don't need that at all, I just figured I'd need to wait some amount of time :)

Copy link
Copy Markdown

@Promaethius Promaethius Oct 4, 2018

Choose a reason for hiding this comment

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

Are either of you having issues with dhclient not being able to find libirs-export.so.160?
Consider adding /sbin/ldconfig to the init file.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

this is my list of rpms for "networking" that I had listed: rpms+=' dhcp-client bind-export-libs iproute' # networking

Copy link
Copy Markdown

@Promaethius Promaethius Oct 4, 2018

Choose a reason for hiding this comment

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

I have those rpms installed into the supermin appliance. However, dhclient only worked after I relinked the libraries. If you aren't having this issue, great! I'm trying to follow your script through step by step to understand.



# these varaibles will be populated in this script before running
workdir=
ref=
cmd=
previous_commit=

# set a variable for our mountpoint for hostworkdir
hostworkdir='/hostworkdir'

[ -d "${workdir}" ] || mkdir -p "${workdir}"
[ -d "${hostworkdir}" ] || mkdir -p "${hostworkdir}"

# mount cache disk
mount /dev/sdb "${workdir}"

#bash -i

# mount host "${workdir}" via samba
#ount -t 9p -o rw,trans=virtio,version=9p2000.L srv /hostsrv/
# cat /proc/fs/cifs/DebugData
mount -t cifs -o 'username=root,password=,cache=loose' '\\10.0.2.4\qemu' "${hostworkdir}"

# symlink some things from $workdir to $hostworkdir
[ -e "${workdir}/tmp" ] || ln -s "${hostworkdir}/tmp" "${workdir}/tmp"
[ -e "${workdir}/src" ] || ln -s "${hostworkdir}/src" "${workdir}/src"
[ -e "${workdir}/localrepo" ] || ln -s "${hostworkdir}/localrepo" "${workdir}/localrepo"

# create a few directories if they don't exist
[ -d "${workdir}/cache" ] || mkdir "${workdir}/cache"
[ -d "${workdir}/repo-build" ] || mkdir "${workdir}/repo-build"
[ -d "${workdir}/repo" ] || mkdir "${workdir}/repo"

# initialize the repos if they don't exist
[ -f "${workdir}/repo-build/config" ] || ostree init --mode=bare-user --repo="${workdir}/repo-build/"
[ -f "${workdir}/repo/config" ] || ostree init --mode=archive --repo="${workdir}/repo/"


#bash -i


compose() {
# make sure previous_commit exists in the build repo
if [ "${previous_commit}" != "null" ]; then
pc=$(ostree --repo="${workdir}/repo-build" rev-parse "${ref}" || true)
if [ "${previous_commit}" != "${pc}" ]; then
# is it possible to use --commit-metadata-only here ?
ostree pull-local --repo="${workdir}/repo-build" "${hostworkdir}/repo/" "$ref"
fi
fi

# run the command from given to us
$cmd || fatal

# https://github.com/ostreedev/ostree/issues/1562#issuecomment-385393872
# The passwd files (among others) don't have world readability. This won't
# actually corrupt the repository as the *canonical* permissions are stored
# as xattrs. Probably what we should do is have an ostree option to specify
# a permission mask for objects.
chmod -R a+rX "${workdir}/repo-build/objects"

#bash -i

# Sync over to repo in $hostworkdir
ostree pull-local --repo="${hostworkdir}/repo/" "${workdir}/repo-build/" $ref || fatal
# Sync over to repo in $hostworkdir

# prune - we'll leave just one commit in repo
ostree prune --repo="${workdir}/repo-build/" --only-branch "${ref}" --depth=0 || fatal

#bash -i
}

fatal() {
echo "Failure from inside supermin VM"
touch "${workdir}/tmp/supermin-failure"
cleanup
}

cleanup() {
set +eu
# release disk space back to the host and unmount
fstrim -v "${workdir}"
sync
umount "${hostworkdir}"
umount "${workdir}"

# shutdown
reboot -f
}

# run the compose
compose
# run the cleanup
cleanup