From 84d7b044e5cc149359c99a8f4d397fcfb4ab4004 Mon Sep 17 00:00:00 2001 From: Luke Hackwell Date: Thu, 22 Aug 2024 17:13:32 +0100 Subject: [PATCH] test(qemu): Provide example QEMU runner --- e2e/qemu/BUILD.bazel | 112 ++++++++++++++++++++++ e2e/qemu/data/install_debian.sh | 63 +++++++++++++ e2e/qemu/data/preseed.cfg | 162 ++++++++++++++++++++++++++++++++ e2e/qemu/manager.py | 18 ++++ e2e/qemu/ssh.py | 107 +++++++++++++++++++++ e2e/qemu/version.txt | 9 ++ e2e/qemu/x86-ubuntu-24.04.yaml | 15 +++ 7 files changed, 486 insertions(+) create mode 100644 e2e/qemu/BUILD.bazel create mode 100755 e2e/qemu/data/install_debian.sh create mode 100755 e2e/qemu/data/preseed.cfg create mode 100644 e2e/qemu/manager.py create mode 100755 e2e/qemu/ssh.py create mode 100644 e2e/qemu/version.txt create mode 100644 e2e/qemu/x86-ubuntu-24.04.yaml diff --git a/e2e/qemu/BUILD.bazel b/e2e/qemu/BUILD.bazel new file mode 100644 index 00000000..c9ab7dc6 --- /dev/null +++ b/e2e/qemu/BUILD.bazel @@ -0,0 +1,112 @@ +load("@rules_python//python:defs.bzl", "py_binary", "py_library") +load("@rules_labgrid//labgrid/genrule:defs.bzl", "labgrid_genrule") +load("@rules_labgrid//labgrid/executor:defs.bzl", "labgrid_executor") +load("@rules_labgrid//labgrid/transition:defs.bzl", "labgrid_transition") +load("@rules_diff//diff/file/test:defs.bzl", "diff_file_test") +load("@toolchain_utils//toolchain/info:defs.bzl", "toolchain_info") + +genrule( + name = "install_debian", + srcs = ["data/preseed.cfg"], + outs = ["debian.qcow2"], + cmd_bash = "./qemu/data/install_debian.sh $@ $(location data/preseed.cfg)", + tools = ["data/install_debian.sh"], +) + +py_binary( + name = "ssh", + srcs = ["ssh.py"], + args = [ + "--lg-env=$(location x86-ubuntu-24.04.yaml)", + "--lg-qemu-hda=$(location debian.qcow2)", + ], + data = [ + "x86-ubuntu-24.04.yaml", + "debian.qcow2", + ], + tags = ["manual"], + deps = [ + "@rules_labgrid//labgrid/python", + "@rules_python//python/runfiles", + ], +) + +# An execution manager that is responsible for setting up the LabGrid environment +py_library( + name = "manager", + srcs = ["manager.py"], + data = ["x86-ubuntu-24.04.yaml"], + deps = ["@rules_labgrid//labgrid/executor:manager"], +) + +# Create the LabGrid executor binary using our manager +labgrid_executor( + name = "executor", + deps = [":manager"], +) + +# Create toolchain information around the executor +toolchain_info( + name = "info", + target = ":executor", +) + +# A constraint to register the toolchain to +constraint_setting(name = "device") + +# This value would usually be shared in a common definitions module +constraint_value( + name = "constraint", + constraint_setting = ":device", +) + +# A platform that describes the Docker "platform" +platform( + name = "platform", + constraint_values = [ + ":constraint", + "@toolchain_utils//toolchain/constraint/os:linux", + ], +) + +# Provide the toolchain with Bazel +toolchain( + name = "toolchain", + target_compatible_with = [ + ":constraint", + "@toolchain_utils//toolchain/constraint/os:linux", + ], + toolchain = ":info", + toolchain_type = "@rules_labgrid//labgrid/toolchain/executor:type", +) + +# Run within the LabGrid environment +# This shows a few things: +# - It resolves the executor registered above to set up the `LG_ENV` enviroment variable +# - `:ssh` target uses `LG_ENV` to start the Docker container +# - `@ape//:cat` is provided to +labgrid_genrule( + name = "echo", + srcs = ["@ape//:cat"], + outs = ["stdout.log"], + cmd = "$(location :ssh) $(location @ape//:cat) /etc/os-release > $@", + tags = ["manual"], + tools = [":ssh"], +) + +# Transition the above `genrule` to the Docker platform +labgrid_transition( + name = "transition", + srcs = [":stdout.log"], + platforms = [":platform"], + tags = ["manual"], +) + +# Check that the output of `/proc/version` from within the container is what we expect +diff_file_test( + name = "test", + size = "small", + a = ":version.txt", + b = ":transition", + tags = ["manual"], +) diff --git a/e2e/qemu/data/install_debian.sh b/e2e/qemu/data/install_debian.sh new file mode 100755 index 00000000..cf485777 --- /dev/null +++ b/e2e/qemu/data/install_debian.sh @@ -0,0 +1,63 @@ +#!/bin/bash -e + +rm -rf +BUILD_DIR=build_dir +rm -rf $BUILD_DIR +mkdir $BUILD_DIR +cp $2 $BUILD_DIR +pushd $BUILD_DIR + +eval `ssh-agent` +ssh-add +AUTHORIZED_KEYS="$(ssh-add -L)" +echo $AUTHORIZED_KEYS +if [ -n "$AUTHORIZED_KEYS" ] +then + echo "Pre-populating authorized_keys for image" + echo "$AUTHORIZED_KEYS" > authorized_keys +fi + +PASS="asdf" + +echo "Running simple webserver on port 4321 for host files..." +PYTHON_PID=$(sh -c 'echo $$ ; exec >/dev/null 2>&1 ; exec python3 -m http.server 4321' &) + +echo "Downloading Debian Buster x86_64 netboot installer..." +curl --location --output netboot.tar.gz https://deb.debian.org/debian/dists/buster/main/installer-amd64/current/images/netboot/netboot.tar.gz +mkdir -p tftpserver +pushd tftpserver +tar xzvf ../netboot.tar.gz + +echo "Customising network boot parameters..." +cat > debian-installer/amd64/pxelinux.cfg/default < Iterator[Run]: + runfiles = Runfiles.Create() + config = runfiles.Rlocation("_main/qemu/x86-ubuntu-24.04.yaml") + env = { + "LG_ENV": config, + } + yield Data(env=env) diff --git a/e2e/qemu/ssh.py b/e2e/qemu/ssh.py new file mode 100755 index 00000000..f57ecc6a --- /dev/null +++ b/e2e/qemu/ssh.py @@ -0,0 +1,107 @@ +#! /usr/bin/env python3 + +from argparse import ArgumentParser +from os import environ, linesep +import os +from pathlib import Path, PurePath +from shlex import join +from sys import argv, stderr, stdout +from typing import Collection, Protocol, Tuple +import logging +import time +import shutil + +from labgrid import Environment + + +class Shell(Protocol): + def run(cmd: str) -> Tuple[Collection[str], Collection[str], int]: ... + + +def arguments(prsr: ArgumentParser) -> None: + prsr.add_argument( + "program", + metavar="PROG", + help="The program to run on the device.", + type=Path, + ) + prsr.add_argument( + "arguments", metavar="ARG", nargs="*", help="Command to run over SSH." + ) + prsr.add_argument( + "--lg-env", + help="The LabGrid environment configuration.", + dest="config", + default=PurePath(environ.get("LG_ENV", "config.yaml")), + type=PurePath, + ) + prsr.add_argument( + "--lg-qemu-hda", + help="The QEMU hard drive file", + dest="qemu_hda", + type=PurePath, + ) + + +def main(exe: Path, *args: str) -> int: + prsr = ArgumentParser( + prog=str(exe), description="TODO" + ) + arguments(prsr) + parsed = prsr.parse_args(args) + + if parsed.qemu_hda: + hda = parsed.qemu_hda + else: + hda = str(PurePath(os.environ["RUNFILES_DIR"]) / PurePath("_main/qemu/debian.qcow2")) + + # Set the hard drive that the QEMU will read + os.environ['LG_QEMU_HDA'] = 'debian.qcow2' + + env = Environment(str(parsed.config)) + target = env.get_target() + + # Copy the hard drive. This makes it read-writable + shutil.copyfile(hda, 'debian.qcow2') + + # Start the QEMU instance + qemu_driver = target.get_driver("QEMUDriver") + qemu_driver.on() + + # Wait until the VM has booted + console = target.get_driver("ConsoleProtocol") + for _ in range(300): + time.sleep(1) + try: + msg = console.read(timeout=0.1).decode('utf-8') + except: + pass + logging.error(msg) + if "login:" in msg: + break + + # Copy the program to run onto VM + program = "/tmp/{}".format(parsed.program.name) + transfer = target.get_driver("FileTransferProtocol") + transfer.put(parsed.program, program) + + # Run the transferred program + shell = target.get_driver("CommandProtocol") + out, err, code = shell.run(join((program, *parsed.arguments))) + for line in out: + logging.error(f"{line}{linesep}") + stdout.write(f"{line}{linesep}") + for line in err: + stderr.write(f"{line}{linesep}") + + qemu_driver.off() + + return code + + +def entry(): + exit(main(Path(argv[0]), *argv[1:])) + + +if __name__ == "__main__": + entry() diff --git a/e2e/qemu/version.txt b/e2e/qemu/version.txt new file mode 100644 index 00000000..9b5419df --- /dev/null +++ b/e2e/qemu/version.txt @@ -0,0 +1,9 @@ +PRETTY_NAME="Debian GNU/Linux 10 (buster)" +NAME="Debian GNU/Linux" +VERSION_ID="10" +VERSION="10 (buster)" +VERSION_CODENAME=buster +ID=debian +HOME_URL="https://www.debian.org/" +SUPPORT_URL="https://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" diff --git a/e2e/qemu/x86-ubuntu-24.04.yaml b/e2e/qemu/x86-ubuntu-24.04.yaml new file mode 100644 index 00000000..2a4286e3 --- /dev/null +++ b/e2e/qemu/x86-ubuntu-24.04.yaml @@ -0,0 +1,15 @@ +targets: + main: + resources: + NetworkService: + address: 'localhost' + port: 2222 + username: 'root' + drivers: + QEMUDriver: + qemu_bin: 'qemu-system-x86_64' + machine: 'pc' + cpu: 'max' + memory: '12G' + extra_args: !template '-hda $LG_QEMU_HDA -net nic -net user,hostfwd=tcp::2222-:22' + SSHDriver: {} -- GitLab