-
Notifications
You must be signed in to change notification settings - Fork 190
Use supermin in unprivileged environments #190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d0f9aa0
d909410
c444585
8c380d0
f302c82
91d8f2a
18a0752
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,32 @@ | ||
| # Shared shell script library | ||
|
|
||
| DIR=$(dirname $0) | ||
|
|
||
| info() { | ||
| echo "info: $@" 1>&2 | ||
| } | ||
|
|
||
| fatal() { | ||
| echo "error: $@" 1>&2; exit 1 | ||
| info "$@"; exit 1 | ||
| } | ||
|
|
||
| _privileged= | ||
|
jlebon marked this conversation as resolved.
|
||
| has_privileges() { | ||
| if [ -z "${_privileged:-}" ]; then | ||
| if [ -n "${FORCE_UNPRIVILEGED:-}" ]; then | ||
| info "Detected FORCE_UNPRIVILEGED; using virt" | ||
| _privileged=0 | ||
| elif ! capsh --print | grep -q 'Current.*cap_sys_admin'; then | ||
| info "Missing CAP_SYS_ADMIN; using virt" | ||
| _privileged=0 | ||
| elif ! sudo true; then | ||
| info "Missing sudo privs; using virt" | ||
| _privileged=0 | ||
| else | ||
| _privileged=1 | ||
| fi | ||
| fi | ||
| [ ${_privileged} == 1 ] | ||
| } | ||
|
|
||
| preflight() { | ||
|
|
@@ -28,27 +53,25 @@ 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 ! 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. | ||
| if ! [ -w /dev/kvm ]; then | ||
| sudo rm -f /dev/kvm | ||
| sudo mknod /dev/kvm c 10 232 | ||
| sudo setfacl -m u:$USER:rw /dev/kvm | ||
| if ! has_privileges; then | ||
| fatal "running unprivileged, and /dev/kvm not writable" | ||
| else | ||
| sudo rm -f /dev/kvm | ||
| sudo mknod /dev/kvm c 10 232 | ||
| sudo setfacl -m u:$USER:rw /dev/kvm | ||
| fi | ||
| fi | ||
| } | ||
|
|
||
| prepare_build() { | ||
| preflight | ||
| if ! [ -d repo ]; then | ||
| fatal "No $(pwd)/repo found; did you run coreos-assembler init?" | ||
| elif ! has_privileges && [ ! -f cache/cache.qcow2 ]; then | ||
| fatal "No cache.qcow2 found; did you run coreos-assembler init?" | ||
| fi | ||
|
|
||
| export workdir=$(pwd) | ||
|
|
@@ -88,17 +111,12 @@ prepare_build() { | |
| cd ${tmp_builddir} | ||
| # *This* tmp directory is truly temporary to this build, and | ||
| # contains artifacts we definitely don't want to outlive it, unlike | ||
| # other things in ${workdir}/tmp. We also export it as an environment | ||
| # variable for child processes like gf-oemid. | ||
| mkdir tmp | ||
| export TMPDIR=$(pwd)/tmp | ||
| # other things in ${workdir}/tmp. But we don't export it since e.g. if it's | ||
| # over an NFS mount (like a PVC in OCP), some apps might error out. | ||
| mkdir tmp && TMPDIR=$(pwd)/tmp | ||
| } | ||
|
|
||
| runcompose() { | ||
| local treecompose_args="" | ||
| if ! grep -q '^# disable-unified-core' "${manifest}"; then | ||
| treecompose_args="${treecompose_args} --unified-core" | ||
| fi | ||
| # Implement support for automatic local overrides: | ||
| # https://github.com/coreos/coreos-assembler/issues/118 | ||
| local overridesdir=${workdir}/overrides/ | ||
|
|
@@ -126,10 +144,82 @@ EOF | |
| fi | ||
|
|
||
| rm -f ${changed_stamp} | ||
| set -x | ||
| sudo rpm-ostree compose tree --repo=${workdir}/repo-build --cachedir=${workdir}/cache \ | ||
| --touch-if-changed "${changed_stamp}" \ | ||
| ${treecompose_args} \ | ||
| ${TREECOMPOSE_FLAGS:-} ${manifest} "$@" | ||
| set +x | ||
|
|
||
| set - rpm-ostree compose tree --repo=${workdir}/repo \ | ||
| --cachedir=${workdir}/cache --touch-if-changed "${changed_stamp}" \ | ||
| --unified-core ${manifest} "$@" | ||
|
|
||
| echo "Running: $@" | ||
|
|
||
| # this is the heart of the privs vs no privs dual path | ||
| if has_privileges; then | ||
| sudo "$@" | ||
| else | ||
| runvm "$@" | ||
| fi | ||
| } | ||
|
|
||
| runvm() { | ||
| local vmpreparedir=${workdir}/tmp/supermin.prepare | ||
| local vmbuilddir=${workdir}/tmp/supermin.build | ||
|
|
||
| # use REBUILDVM=1 if e.g. hacking on rpm-ostree/ostree and wanting to get | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the problem with not building the vm every time is that it can get out of date (i.e. new deps needed, etc). This is probably fine for your local dev case, but not ideal for having to go do something in your CI. I converted my master branch to just rebuild it every time. I found it didn't really add that much extra time to the compose.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. then again, if we are doing it for fetch and build then it starts to get a bit more heavyweight
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that's the thing. We're calling this now both on
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could make fetch unprivileged without too much effort BTW.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
right, but if we are in "unpriv mode" we still need to run it in the VM so it can access the cache I think. |
||
| # the new bits in the VM | ||
| if [ ! -f ${vmbuilddir}/.done ] || [ -n "${REBUILDVM:-}" ]; then | ||
| rm -rf ${vmpreparedir} ${vmbuilddir} | ||
| mkdir -p ${vmpreparedir} ${vmbuilddir} | ||
|
|
||
| local rpms= | ||
| # then add all the base deps | ||
| for dep in $(grep -v '^#' ${DIR}/vmdeps.txt); do | ||
| rpms+="$dep " | ||
| done | ||
|
|
||
| supermin --prepare --use-installed $rpms -o "${vmpreparedir}" | ||
|
|
||
| # the reason we do a heredoc here is so that the var substition takes | ||
| # place immediately instead of having to proxy them through to the VM | ||
| cat > "${vmpreparedir}/init" <<EOF | ||
| #!/bin/bash | ||
| set -xeuo pipefail | ||
| workdir=${workdir} | ||
| $(cat ${DIR}/supermin-init-prelude.sh) | ||
| rc=0 | ||
| sh ${TMPDIR}/cmd.sh || rc=\$? | ||
| echo \$rc > ${workdir}/tmp/rc | ||
| /sbin/fstrim -v ${workdir}/cache | ||
| /sbin/reboot -f | ||
| EOF | ||
| chmod a+x ${vmpreparedir}/init | ||
| (cd ${vmpreparedir} && tar -czf init.tar.gz --remove-files init) | ||
| supermin --build "${vmpreparedir}" --size 5G -f ext2 -o "${vmbuilddir}" | ||
| touch "${vmbuilddir}/.done" | ||
| fi | ||
|
|
||
| echo "$@" > ${TMPDIR}/cmd.sh | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in this case this works because I've actually seen issues running in openshift where having $TMPDIR be on the nfs mounted volume caused real issues for me so I changed it to not be exported: dustymabe@7ecba34 |
||
|
|
||
| # support local dev cases where src/config is a symlink | ||
| srcvirtfs= | ||
| if [ -L "${workdir}/src/config" ]; then | ||
| # qemu follows symlinks | ||
| srcvirtfs="-virtfs local,id=source,path=${workdir}/src/config,security_model=none,mount_tag=source" | ||
| fi | ||
|
|
||
| qemu-kvm -nodefaults -nographic -m 2048 -no-reboot \ | ||
| -kernel "${vmbuilddir}/kernel" \ | ||
| -initrd "${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 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 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 local,id=workdir,path="${workdir}",security_model=none,mount_tag=workdir \ | ||
| ${srcvirtfs} -serial stdio -append "root=/dev/sda console=ttyS0 selinux=1 enforcing=0 autorelabel=1" | ||
|
|
||
| if [ ! -f ${workdir}/tmp/rc ]; then | ||
| fatal "Couldn't find rc file, something went terribly wrong!" | ||
| fi | ||
| return $(cat ${workdir}/tmp/rc) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| # For privileged ops | ||
| supermin | ||
|
|
||
| # We default to builder user, but sudo where necessary | ||
| sudo | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| 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 | ||
|
jlebon marked this conversation as resolved.
|
||
|
|
||
| # set up workdir | ||
| mkdir -p ${workdir} | ||
| mount -t 9p -o rw,trans=virtio,version=9p2000.L workdir ${workdir} | ||
| if [ -L ${workdir}/src/config ]; then | ||
| mkdir -p $(readlink ${workdir}/src/config) | ||
| mount -t 9p -o rw,trans=virtio,version=9p2000.L source ${workdir}/src/config | ||
| fi | ||
| mkdir -p ${workdir}/cache | ||
| mount /dev/sdb1 ${workdir}/cache | ||
|
|
||
| cd ${workdir} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # Base deps for a viable VM environment. | ||
|
|
||
| # bare essentials | ||
| bash vim-minimal coreutils util-linux procps-ng kmod kernel-modules | ||
|
|
||
| # for composes | ||
| rpm-ostree distribution-gpg-keys jq | ||
|
|
||
| # for clean reboot | ||
| systemd | ||
|
|
||
| # networking | ||
| dhcp-client bind-export-libs iproute | ||
|
|
||
| # SELinux | ||
| selinux-policy selinux-policy-targeted policycoreutils |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ./src/vmdeps.txt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dustymabe I slightly modified your patch here to put it in
/var/tmpinstead of/tmpsince the former is less likely to be on tmpfs.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good