mirror of https://github.com/aya-rs/aya
Merge pull request #733 from tamird/kernel-test
integration-test: Implement running on VMspull/736/head
commit
412a0875b4
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "init"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Tamir Duberstein <tamird@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = { workspace = true, features = ["std"] }
|
||||||
|
nix = { workspace = true, features = ["fs", "mount", "reboot"] }
|
@ -0,0 +1,166 @@
|
|||||||
|
//! init is the first process started by the kernel.
|
||||||
|
//!
|
||||||
|
//! This implementation creates the minimal mounts required to run BPF programs, runs all binaries
|
||||||
|
//! in /bin, prints a final message ("init: success|failure"), and powers off the machine.
|
||||||
|
|
||||||
|
use anyhow::Context as _;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Errors(Vec<anyhow::Error>);
|
||||||
|
|
||||||
|
impl std::fmt::Display for Errors {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self(errors) = self;
|
||||||
|
for (i, error) in errors.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
writeln!(f)?;
|
||||||
|
}
|
||||||
|
write!(f, "{:?}", error)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Errors {}
|
||||||
|
|
||||||
|
fn run() -> anyhow::Result<()> {
|
||||||
|
const RXRXRX: nix::sys::stat::Mode = nix::sys::stat::Mode::empty()
|
||||||
|
.union(nix::sys::stat::Mode::S_IRUSR)
|
||||||
|
.union(nix::sys::stat::Mode::S_IXUSR)
|
||||||
|
.union(nix::sys::stat::Mode::S_IRGRP)
|
||||||
|
.union(nix::sys::stat::Mode::S_IXGRP)
|
||||||
|
.union(nix::sys::stat::Mode::S_IROTH)
|
||||||
|
.union(nix::sys::stat::Mode::S_IXOTH);
|
||||||
|
|
||||||
|
struct Mount {
|
||||||
|
source: &'static str,
|
||||||
|
target: &'static str,
|
||||||
|
fstype: &'static str,
|
||||||
|
flags: nix::mount::MsFlags,
|
||||||
|
data: Option<&'static str>,
|
||||||
|
target_mode: Option<nix::sys::stat::Mode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
for Mount {
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
fstype,
|
||||||
|
flags,
|
||||||
|
data,
|
||||||
|
target_mode,
|
||||||
|
} in [
|
||||||
|
Mount {
|
||||||
|
source: "proc",
|
||||||
|
target: "/proc",
|
||||||
|
fstype: "proc",
|
||||||
|
flags: nix::mount::MsFlags::empty(),
|
||||||
|
data: None,
|
||||||
|
target_mode: Some(RXRXRX),
|
||||||
|
},
|
||||||
|
Mount {
|
||||||
|
source: "sysfs",
|
||||||
|
target: "/sys",
|
||||||
|
fstype: "sysfs",
|
||||||
|
flags: nix::mount::MsFlags::empty(),
|
||||||
|
data: None,
|
||||||
|
target_mode: Some(RXRXRX),
|
||||||
|
},
|
||||||
|
Mount {
|
||||||
|
source: "debugfs",
|
||||||
|
target: "/sys/kernel/debug",
|
||||||
|
fstype: "debugfs",
|
||||||
|
flags: nix::mount::MsFlags::empty(),
|
||||||
|
data: None,
|
||||||
|
target_mode: None,
|
||||||
|
},
|
||||||
|
Mount {
|
||||||
|
source: "bpffs",
|
||||||
|
target: "/sys/fs/bpf",
|
||||||
|
fstype: "bpf",
|
||||||
|
flags: nix::mount::MsFlags::empty(),
|
||||||
|
data: None,
|
||||||
|
target_mode: None,
|
||||||
|
},
|
||||||
|
] {
|
||||||
|
match target_mode {
|
||||||
|
None => {
|
||||||
|
// Must exist.
|
||||||
|
let nix::sys::stat::FileStat { st_mode, .. } = nix::sys::stat::stat(target)
|
||||||
|
.with_context(|| format!("stat({target}) failed"))?;
|
||||||
|
let s_flag = nix::sys::stat::SFlag::from_bits_truncate(st_mode);
|
||||||
|
|
||||||
|
if !s_flag.contains(nix::sys::stat::SFlag::S_IFDIR) {
|
||||||
|
anyhow::bail!("{target} is not a directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(target_mode) => {
|
||||||
|
// Must not exist.
|
||||||
|
nix::unistd::mkdir(target, target_mode)
|
||||||
|
.with_context(|| format!("mkdir({target}) failed"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nix::mount::mount(Some(source), target, Some(fstype), flags, data).with_context(|| {
|
||||||
|
format!("mount({source}, {target}, {fstype}, {flags:?}, {data:?}) failed")
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// By contract we run everything in /bin and assume they're rust test binaries.
|
||||||
|
//
|
||||||
|
// If the user requested command line arguments, they're named init.arg={}.
|
||||||
|
|
||||||
|
// Read kernel parameters from /proc/cmdline. They're space separated on a single line.
|
||||||
|
let cmdline = std::fs::read_to_string("/proc/cmdline")
|
||||||
|
.with_context(|| "read_to_string(/proc/cmdline) failed")?;
|
||||||
|
let args = cmdline
|
||||||
|
.split_whitespace()
|
||||||
|
.filter_map(|parameter| {
|
||||||
|
parameter
|
||||||
|
.strip_prefix("init.arg=")
|
||||||
|
.map(std::ffi::OsString::from)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Iterate files in /bin.
|
||||||
|
let read_dir = std::fs::read_dir("/bin").context("read_dir(/bin) failed")?;
|
||||||
|
let errors = read_dir
|
||||||
|
.filter_map(|entry| {
|
||||||
|
match (|| {
|
||||||
|
let entry = entry.context("read_dir(/bin) failed")?;
|
||||||
|
let path = entry.path();
|
||||||
|
let status = std::process::Command::new(&path)
|
||||||
|
.args(&args)
|
||||||
|
.status()
|
||||||
|
.with_context(|| format!("failed to execute {}", path.display()))?;
|
||||||
|
|
||||||
|
if status.code() == Some(0) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!("{} failed: {status:?}", path.display()))
|
||||||
|
}
|
||||||
|
})() {
|
||||||
|
Ok(()) => None,
|
||||||
|
Err(err) => Some(err),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if errors.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Errors(errors).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
match run() {
|
||||||
|
Ok(()) => {
|
||||||
|
println!("init: success");
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("{err:?}");
|
||||||
|
println!("init: failure");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let how = nix::sys::reboot::RebootMode::RB_POWER_OFF;
|
||||||
|
let _: std::convert::Infallible = nix::sys::reboot::reboot(how)
|
||||||
|
.unwrap_or_else(|err| panic!("reboot({how:?}) failed: {err:?}"));
|
||||||
|
}
|
@ -1,264 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
VERBOSITY=0
|
|
||||||
TEMP_D=""
|
|
||||||
DEF_DISK_FORMAT="raw"
|
|
||||||
DEF_FILESYSTEM="iso9660"
|
|
||||||
CR="
|
|
||||||
"
|
|
||||||
|
|
||||||
error() { echo "$@" 1>&2; }
|
|
||||||
fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
|
|
||||||
|
|
||||||
Usage() {
|
|
||||||
cat <<EOF
|
|
||||||
Usage: ${0##*/} [ options ] output user-data [meta-data]
|
|
||||||
|
|
||||||
Create a disk for cloud-init to utilize nocloud
|
|
||||||
|
|
||||||
options:
|
|
||||||
-h | --help show usage
|
|
||||||
-d | --disk-format D disk format to output. default: raw
|
|
||||||
can be anything supported by qemu-img or
|
|
||||||
tar, tar-seed-local, tar-seed-net
|
|
||||||
-H | --hostname H set hostname in metadata to H
|
|
||||||
-f | --filesystem F filesystem format (vfat or iso), default: iso9660
|
|
||||||
|
|
||||||
-i | --interfaces F write network interfaces file into metadata
|
|
||||||
-N | --network-config F write network config file to local datasource
|
|
||||||
-m | --dsmode M add 'dsmode' ('local' or 'net') to the metadata
|
|
||||||
default in cloud-init is 'net', meaning network is
|
|
||||||
required.
|
|
||||||
-V | --vendor-data F vendor-data file
|
|
||||||
-v | --verbose increase verbosity
|
|
||||||
|
|
||||||
Note, --dsmode, --hostname, and --interfaces are incompatible
|
|
||||||
with metadata.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
* cat my-user-data
|
|
||||||
#cloud-config
|
|
||||||
password: passw0rd
|
|
||||||
chpasswd: { expire: False }
|
|
||||||
ssh_pwauth: True
|
|
||||||
* echo "instance-id: \$(uuidgen || echo i-abcdefg)" > my-meta-data
|
|
||||||
* ${0##*/} my-seed.img my-user-data my-meta-data
|
|
||||||
* kvm -net nic -net user,hostfwd=tcp::2222-:22 \\
|
|
||||||
-drive file=disk1.img,if=virtio -drive file=my-seed.img,if=virtio
|
|
||||||
* ssh -p 2222 ubuntu@localhost
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; exit 1; }
|
|
||||||
cleanup() {
|
|
||||||
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
|
|
||||||
}
|
|
||||||
|
|
||||||
debug() {
|
|
||||||
local level=${1}; shift;
|
|
||||||
[ "${level}" -gt "${VERBOSITY}" ] && return
|
|
||||||
error "${@}"
|
|
||||||
}
|
|
||||||
|
|
||||||
has_cmd() {
|
|
||||||
command -v "$1" >/dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
short_opts="hH:i:d:f:m:N:o:V:v"
|
|
||||||
long_opts="disk-format:,dsmode:,filesystem:,help,hostname:,interfaces:,"
|
|
||||||
long_opts="${long_opts}network-config:,output:,vendor-data:,verbose"
|
|
||||||
getopt_out=$(getopt -n "${0##*/}" \
|
|
||||||
-o "${short_opts}" -l "${long_opts}" -- "$@") &&
|
|
||||||
eval set -- "${getopt_out}" ||
|
|
||||||
bad_Usage
|
|
||||||
|
|
||||||
## <<insert default variables here>>
|
|
||||||
output=""
|
|
||||||
userdata=""
|
|
||||||
metadata=""
|
|
||||||
vendordata=""
|
|
||||||
filesystem=""
|
|
||||||
diskformat=$DEF_DISK_FORMAT
|
|
||||||
interfaces=_unset
|
|
||||||
dsmode=""
|
|
||||||
hostname=""
|
|
||||||
ncname="network-config"
|
|
||||||
|
|
||||||
|
|
||||||
while [ $# -ne 0 ]; do
|
|
||||||
cur=${1}; next=${2};
|
|
||||||
case "$cur" in
|
|
||||||
-h|--help) Usage ; exit 0;;
|
|
||||||
-d|--disk-format) diskformat=$next; shift;;
|
|
||||||
-f|--filesystem) filesystem=$next; shift;;
|
|
||||||
-H|--hostname) hostname=$next; shift;;
|
|
||||||
-i|--interfaces) interfaces=$next; shift;;
|
|
||||||
-N|--network-config) netcfg=$next; shift;;
|
|
||||||
-m|--dsmode) dsmode=$next; shift;;
|
|
||||||
-v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
|
|
||||||
-V|--vendor-data) vendordata="$next";;
|
|
||||||
--) shift; break;;
|
|
||||||
esac
|
|
||||||
shift;
|
|
||||||
done
|
|
||||||
|
|
||||||
## check arguments here
|
|
||||||
## how many args do you expect?
|
|
||||||
echo $1
|
|
||||||
echo $2
|
|
||||||
echo $3
|
|
||||||
[ $# -ge 2 ] || bad_Usage "must provide output, userdata"
|
|
||||||
[ $# -le 3 ] || bad_Usage "confused by additional args"
|
|
||||||
|
|
||||||
output=$1
|
|
||||||
userdata=$2
|
|
||||||
metadata=$3
|
|
||||||
|
|
||||||
if [ -n "$metadata" ]; then
|
|
||||||
[ "$interfaces" = "_unset" -a -z "$dsmode" -a -z "$hostname" ] ||
|
|
||||||
fail "metadata is incompatible with:" \
|
|
||||||
"--interfaces, --hostname, --dsmode"
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$diskformat" in
|
|
||||||
tar|tar-seed-local|tar-seed-net)
|
|
||||||
if [ "${filesystem:-tar}" != "tar" ]; then
|
|
||||||
fail "diskformat=tar is incompatible with filesystem"
|
|
||||||
fi
|
|
||||||
filesystem="$diskformat"
|
|
||||||
;;
|
|
||||||
tar*)
|
|
||||||
fail "supported 'tar' formats are tar, tar-seed-local, tar-seed-net"
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -z "$filesystem" ]; then
|
|
||||||
filesystem="$DEF_FILESYSTEM"
|
|
||||||
fi
|
|
||||||
if [ "$filesystem" = "iso" ]; then
|
|
||||||
filesystem="iso9660"
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$filesystem" in
|
|
||||||
tar*)
|
|
||||||
has_cmd tar ||
|
|
||||||
fail "missing 'tar'. Required for --filesystem=$filesystem";;
|
|
||||||
vfat)
|
|
||||||
has_cmd mkfs.vfat ||
|
|
||||||
fail "missing 'mkfs.vfat'. Required for --filesystem=vfat."
|
|
||||||
has_cmd mcopy ||
|
|
||||||
fail "missing 'mcopy'. Required for --filesystem=vfat."
|
|
||||||
;;
|
|
||||||
iso9660)
|
|
||||||
has_cmd mkisofs ||
|
|
||||||
fail "missing 'mkisofs'. Required for --filesystem=iso9660."
|
|
||||||
;;
|
|
||||||
*) fail "unknown filesystem $filesystem";;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case "$diskformat" in
|
|
||||||
tar*|raw) :;;
|
|
||||||
*) has_cmd "qemu-img" ||
|
|
||||||
fail "missing 'qemu-img'. Required for --disk-format=$diskformat."
|
|
||||||
esac
|
|
||||||
|
|
||||||
[ "$interfaces" = "_unset" -o -r "$interfaces" ] ||
|
|
||||||
fail "$interfaces: not a readable file"
|
|
||||||
|
|
||||||
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
|
|
||||||
fail "failed to make tempdir"
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
files=( "${TEMP_D}/user-data" "${TEMP_D}/meta-data" )
|
|
||||||
if [ -n "$metadata" ]; then
|
|
||||||
cp "$metadata" "$TEMP_D/meta-data" || fail "$metadata: failed to copy"
|
|
||||||
else
|
|
||||||
instance_id="iid-local01"
|
|
||||||
iface_data=""
|
|
||||||
[ "$interfaces" != "_unset" ] &&
|
|
||||||
iface_data=$(sed ':a;N;$!ba;s/\n/\\n/g' "$interfaces")
|
|
||||||
|
|
||||||
# write json formatted user-data (json is a subset of yaml)
|
|
||||||
mdata=""
|
|
||||||
for kv in "instance-id:$instance_id" "local-hostname:$hostname" \
|
|
||||||
"interfaces:${iface_data}" "dsmode:$dsmode"; do
|
|
||||||
key=${kv%%:*}
|
|
||||||
val=${kv#*:}
|
|
||||||
[ -n "$val" ] || continue
|
|
||||||
mdata="${mdata:+${mdata},${CR}}\"$key\": \"$val\""
|
|
||||||
done
|
|
||||||
printf "{\n%s\n}\n" "$mdata" > "${TEMP_D}/meta-data"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$netcfg" ]; then
|
|
||||||
cp "$netcfg" "${TEMP_D}/$ncname" ||
|
|
||||||
fail "failed to copy network config"
|
|
||||||
files[${#files[@]}]="$TEMP_D/$ncname"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$vendordata" ]; then
|
|
||||||
cp "$vendordata" "${TEMP_D}/vendor-data" ||
|
|
||||||
fail "failed to copy vendor data"
|
|
||||||
files[${#files[@]}]="$TEMP_D/vendor-data"
|
|
||||||
fi
|
|
||||||
|
|
||||||
files_rel=( )
|
|
||||||
for f in "${files[@]}"; do
|
|
||||||
files_rel[${#files_rel[@]}]="${f#${TEMP_D}/}"
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$userdata" = "-" ]; then
|
|
||||||
cat > "$TEMP_D/user-data" || fail "failed to read from stdin"
|
|
||||||
else
|
|
||||||
cp "$userdata" "$TEMP_D/user-data" || fail "$userdata: failed to copy"
|
|
||||||
fi
|
|
||||||
|
|
||||||
## alternatively, create a vfat filesystem with same files
|
|
||||||
img="$TEMP_D/seed-data"
|
|
||||||
tar_opts=( --owner=root --group=root )
|
|
||||||
|
|
||||||
case "$filesystem" in
|
|
||||||
tar)
|
|
||||||
tar "${tar_opts[@]}" -C "${TEMP_D}" -cf "$img" "${files_rel[@]}" ||
|
|
||||||
fail "failed to create tarball of ${files_rel[*]}"
|
|
||||||
;;
|
|
||||||
tar-seed-local|tar-seed-net)
|
|
||||||
if [ "$filesystem" = "tar-seed-local" ]; then
|
|
||||||
path="var/lib/cloud/seed/nocloud"
|
|
||||||
else
|
|
||||||
path="var/lib/cloud/seed/nocloud-net"
|
|
||||||
fi
|
|
||||||
mkdir -p "${TEMP_D}/${path}" ||
|
|
||||||
fail "failed making path for seed files"
|
|
||||||
mv "${files[@]}" "${TEMP_D}/$path" ||
|
|
||||||
fail "failed moving files"
|
|
||||||
tar "${tar_opts[@]}" -C "${TEMP_D}" -cf "$img" "${path}" ||
|
|
||||||
fail "failed to create tarball with $path"
|
|
||||||
;;
|
|
||||||
iso9660)
|
|
||||||
mkisofs -output "$img" -volid cidata \
|
|
||||||
-joliet -rock "${files[@]}" > "$TEMP_D/err" 2>&1 ||
|
|
||||||
{ cat "$TEMP_D/err" 1>&2; fail "failed to mkisofs"; }
|
|
||||||
;;
|
|
||||||
vfat)
|
|
||||||
truncate -s 128K "$img" || fail "failed truncate image"
|
|
||||||
out=$(mkfs.vfat -n cidata "$img" 2>&1) ||
|
|
||||||
{ error "failed: mkfs.vfat -n cidata $img"; error "$out"; }
|
|
||||||
mcopy -oi "$img" "${files[@]}" :: ||
|
|
||||||
fail "failed to copy user-data, meta-data to img"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
[ "$output" = "-" ] && output="$TEMP_D/final"
|
|
||||||
if [ "${diskformat#tar}" != "$diskformat" -o "$diskformat" = "raw" ]; then
|
|
||||||
cp "$img" "$output" ||
|
|
||||||
fail "failed to copy image to $output"
|
|
||||||
else
|
|
||||||
qemu-img convert -f raw -O "$diskformat" "$img" "$output" ||
|
|
||||||
fail "failed to convert to disk format $diskformat"
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ "$output" != "$TEMP_D/final" ] || { cat "$output" && output="-"; } ||
|
|
||||||
fail "failed to write to -"
|
|
||||||
|
|
||||||
debug 1 "wrote ${output} with filesystem=$filesystem and diskformat=$diskformat"
|
|
||||||
# vi: ts=4 noexpandtab
|
|
@ -1,241 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
if [ "$(uname -s)" = "Darwin" ]; then
|
|
||||||
PATH="$(dirname "$(brew list gnu-getopt | grep "bin/getopt$")"):$PATH"
|
|
||||||
export PATH
|
|
||||||
fi
|
|
||||||
|
|
||||||
AYA_SOURCE_DIR="$(realpath "$(dirname "$0")"/..)"
|
|
||||||
|
|
||||||
# Temporary directory for tests to use.
|
|
||||||
AYA_TMPDIR="${AYA_SOURCE_DIR}/.tmp"
|
|
||||||
|
|
||||||
# Directory for VM images
|
|
||||||
AYA_IMGDIR=${AYA_TMPDIR}
|
|
||||||
|
|
||||||
if [ -z "${AYA_BUILD_TARGET}" ]; then
|
|
||||||
AYA_BUILD_TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
|
||||||
fi
|
|
||||||
|
|
||||||
AYA_HOST_ARCH=$(uname -m)
|
|
||||||
if [ "${AYA_HOST_ARCH}" = "arm64" ]; then
|
|
||||||
AYA_HOST_ARCH="aarch64"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "${AYA_GUEST_ARCH}" ]; then
|
|
||||||
AYA_GUEST_ARCH="${AYA_HOST_ARCH}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${AYA_GUEST_ARCH}" = "aarch64" ]; then
|
|
||||||
if [ -z "${AARCH64_UEFI}" ]; then
|
|
||||||
AARCH64_UEFI="$(brew list qemu -1 -v | grep edk2-aarch64-code.fd)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$AYA_MUSL_TARGET" ]; then
|
|
||||||
AYA_MUSL_TARGET=${AYA_GUEST_ARCH}-unknown-linux-musl
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Test Image
|
|
||||||
if [ -z "${AYA_TEST_IMAGE}" ]; then
|
|
||||||
AYA_TEST_IMAGE="fedora38"
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "${AYA_TEST_IMAGE}" in
|
|
||||||
fedora*) AYA_SSH_USER="fedora";;
|
|
||||||
centos*) AYA_SSH_USER="centos";;
|
|
||||||
esac
|
|
||||||
|
|
||||||
download_images() {
|
|
||||||
mkdir -p "${AYA_IMGDIR}"
|
|
||||||
case $1 in
|
|
||||||
fedora37)
|
|
||||||
if [ ! -f "${AYA_IMGDIR}/fedora37.${AYA_GUEST_ARCH}.qcow2" ]; then
|
|
||||||
IMAGE="Fedora-Cloud-Base-37-1.7.${AYA_GUEST_ARCH}.qcow2"
|
|
||||||
IMAGE_URL="https://download.fedoraproject.org/pub/fedora/linux/releases/37/Cloud/${AYA_GUEST_ARCH}/images"
|
|
||||||
echo "Downloading: ${IMAGE}, this may take a while..."
|
|
||||||
curl -o "${AYA_IMGDIR}/fedora37.${AYA_GUEST_ARCH}.qcow2" -sSL "${IMAGE_URL}/${IMAGE}"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
fedora38)
|
|
||||||
if [ ! -f "${AYA_IMGDIR}/fedora38.${AYA_GUEST_ARCH}.qcow2" ]; then
|
|
||||||
IMAGE="Fedora-Cloud-Base-38_Beta-1.3.${AYA_GUEST_ARCH}.qcow2"
|
|
||||||
IMAGE_URL="https://fr2.rpmfind.net/linux/fedora/linux/releases/test/38_Beta/Cloud/${AYA_GUEST_ARCH}/images"
|
|
||||||
echo "Downloading: ${IMAGE}, this may take a while..."
|
|
||||||
curl -o "${AYA_IMGDIR}/fedora38.${AYA_GUEST_ARCH}.qcow2" -sSL "${IMAGE_URL}/${IMAGE}"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
centos8)
|
|
||||||
if [ ! -f "${AYA_IMGDIR}/centos8.${AYA_GUEST_ARCH}.qcow2" ]; then
|
|
||||||
IMAGE="CentOS-8-GenericCloud-8.4.2105-20210603.0.${AYA_GUEST_ARCH}.qcow2"
|
|
||||||
IMAGE_URL="https://cloud.centos.org/centos/8/${AYA_GUEST_ARCH}/images"
|
|
||||||
echo "Downloading: ${IMAGE}, this may take a while..."
|
|
||||||
curl -o "${AYA_IMGDIR}/centos8.${AYA_GUEST_ARCH}.qcow2" -sSL "${IMAGE_URL}/${IMAGE}"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "$1 is not a recognized image name"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
start_vm() {
|
|
||||||
download_images "${AYA_TEST_IMAGE}"
|
|
||||||
# prepare config
|
|
||||||
cat > "${AYA_TMPDIR}/metadata.yaml" <<EOF
|
|
||||||
instance-id: iid-local01
|
|
||||||
local-hostname: test
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ ! -f "${AYA_TMPDIR}/test_rsa" ]; then
|
|
||||||
ssh-keygen -t rsa -b 4096 -f "${AYA_TMPDIR}/test_rsa" -N "" -C "" -q
|
|
||||||
pub_key=$(cat "${AYA_TMPDIR}/test_rsa.pub")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "${AYA_TMPDIR}/ssh_config" ]; then
|
|
||||||
cat > "${AYA_TMPDIR}/ssh_config" <<EOF
|
|
||||||
StrictHostKeyChecking=no
|
|
||||||
UserKnownHostsFile=/dev/null
|
|
||||||
GlobalKnownHostsFile=/dev/null
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat > "${AYA_TMPDIR}/user-data.yaml" <<EOF
|
|
||||||
#cloud-config
|
|
||||||
ssh_authorized_keys:
|
|
||||||
- ${pub_key}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
"$AYA_SOURCE_DIR"/test/cloud-localds "${AYA_TMPDIR}/seed.img" "${AYA_TMPDIR}/user-data.yaml" "${AYA_TMPDIR}/metadata.yaml"
|
|
||||||
case "${AYA_GUEST_ARCH}" in
|
|
||||||
x86_64)
|
|
||||||
QEMU=qemu-system-x86_64
|
|
||||||
machine="q35"
|
|
||||||
cpu="qemu64"
|
|
||||||
nr_cpus="$(nproc --all)"
|
|
||||||
if [ "${AYA_HOST_ARCH}" = "${AYA_GUEST_ARCH}" ]; then
|
|
||||||
if [ -c /dev/kvm ]; then
|
|
||||||
machine="${machine},accel=kvm"
|
|
||||||
cpu="host"
|
|
||||||
elif [ "$(uname -s)" = "Darwin" ]; then
|
|
||||||
machine="${machine},accel=hvf"
|
|
||||||
cpu="host"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
aarch64)
|
|
||||||
QEMU=qemu-system-aarch64
|
|
||||||
machine="virt"
|
|
||||||
cpu="cortex-a57"
|
|
||||||
uefi=("-drive" "file=${AARCH64_UEFI},if=pflash,format=raw,readonly=on")
|
|
||||||
if [ "${AYA_HOST_ARCH}" = "${AYA_GUEST_ARCH}" ]; then
|
|
||||||
if [ -c /dev/kvm ]; then
|
|
||||||
machine="${machine},accel=kvm"
|
|
||||||
cpu="host"
|
|
||||||
nr_cpus="$(nproc --all)"
|
|
||||||
elif [ "$(uname -s)" = "Darwin" ]; then
|
|
||||||
machine="${machine},accel=hvf,highmem=off"
|
|
||||||
cpu="cortex-a72"
|
|
||||||
# nrpoc --all on apple silicon returns the two extra fancy
|
|
||||||
# cores and then qemu complains that nr_cpus > actual_cores
|
|
||||||
nr_cpus=8
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "${AYA_GUEST_ARCH} is not supported"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ ! -f "${AYA_IMGDIR}/vm.qcow2" ]; then
|
|
||||||
echo "Creating VM image"
|
|
||||||
qemu-img create -F qcow2 -f qcow2 -o backing_file="${AYA_IMGDIR}/${AYA_TEST_IMAGE}.${AYA_GUEST_ARCH}.qcow2" "${AYA_IMGDIR}/vm.qcow2" || return 1
|
|
||||||
else
|
|
||||||
echo "Reusing existing VM image"
|
|
||||||
fi
|
|
||||||
$QEMU \
|
|
||||||
-machine "${machine}" \
|
|
||||||
-cpu "${cpu}" \
|
|
||||||
-m 3G \
|
|
||||||
-smp "${nr_cpus}" \
|
|
||||||
-display none \
|
|
||||||
-monitor none \
|
|
||||||
-daemonize \
|
|
||||||
-pidfile "${AYA_TMPDIR}/vm.pid" \
|
|
||||||
-device virtio-net-pci,netdev=net0 \
|
|
||||||
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
|
|
||||||
"${uefi[@]}" \
|
|
||||||
-drive if=virtio,format=qcow2,file="${AYA_IMGDIR}/vm.qcow2" \
|
|
||||||
-drive if=virtio,format=raw,file="${AYA_TMPDIR}/seed.img" || return 1
|
|
||||||
|
|
||||||
trap cleanup_vm EXIT
|
|
||||||
echo "Waiting for SSH on port 2222..."
|
|
||||||
retry=0
|
|
||||||
max_retries=300
|
|
||||||
while ! ssh -q -F "${AYA_TMPDIR}/ssh_config" -o ConnectTimeout=1 -i "${AYA_TMPDIR}/test_rsa" "${AYA_SSH_USER}"@localhost -p 2222 echo "Hello VM"; do
|
|
||||||
retry=$((retry+1))
|
|
||||||
if [ ${retry} -gt ${max_retries} ]; then
|
|
||||||
echo "Unable to connect to VM"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "VM launched"
|
|
||||||
exec_vm uname -a
|
|
||||||
echo "Enabling testing repositories"
|
|
||||||
exec_vm sudo dnf config-manager --set-enabled updates-testing
|
|
||||||
exec_vm sudo dnf config-manager --set-enabled updates-testing-modular
|
|
||||||
}
|
|
||||||
|
|
||||||
scp_vm() {
|
|
||||||
local=$1
|
|
||||||
remote=$(basename "$1")
|
|
||||||
scp -q -F "${AYA_TMPDIR}/ssh_config" \
|
|
||||||
-i "${AYA_TMPDIR}/test_rsa" \
|
|
||||||
-P 2222 "${local}" \
|
|
||||||
"${AYA_SSH_USER}@localhost:${remote}"
|
|
||||||
}
|
|
||||||
|
|
||||||
rsync_vm() {
|
|
||||||
rsync -a -e "ssh -p 2222 -F ${AYA_TMPDIR}/ssh_config -i ${AYA_TMPDIR}/test_rsa" "$1" "$AYA_SSH_USER"@localhost:
|
|
||||||
}
|
|
||||||
|
|
||||||
exec_vm() {
|
|
||||||
ssh -q -F "${AYA_TMPDIR}/ssh_config" \
|
|
||||||
-i "${AYA_TMPDIR}/test_rsa" \
|
|
||||||
-p 2222 \
|
|
||||||
"${AYA_SSH_USER}"@localhost \
|
|
||||||
"$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_vm() {
|
|
||||||
if [ -f "${AYA_TMPDIR}/vm.pid" ]; then
|
|
||||||
echo "Stopping VM forcefully"
|
|
||||||
kill -9 "$(cat "${AYA_TMPDIR}/vm.pid")"
|
|
||||||
rm "${AYA_TMPDIR}/vm.pid"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup_vm() {
|
|
||||||
if ! stop_vm; then
|
|
||||||
rm -f "${AYA_IMGDIR}/vm.qcow2"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
start_vm
|
|
||||||
trap cleanup_vm EXIT
|
|
||||||
|
|
||||||
# make sure we always use fresh sources (also see comment at the end)
|
|
||||||
rsync_vm "$*"
|
|
||||||
|
|
||||||
exec_vm "find $* -type f -executable -print0 | xargs -0 -I {} sudo {} --test-threads=1"
|
|
||||||
|
|
||||||
# we rm and sync but it doesn't seem to work reliably - I guess we could sleep a
|
|
||||||
# few seconds after but ain't nobody got time for that. Instead we also rm
|
|
||||||
# before rsyncing.
|
|
||||||
exec_vm "rm -rf $*; sync"
|
|
Loading…
Reference in New Issue