From a158106e21c91b5af9e540ba97cd1e0a16b44cb4 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 12 Nov 2023 09:18:39 -0500 Subject: [PATCH] Misc container usage improvements --- .github/workflows/build-image.yml | 2 + Containerfile | 1 + README.md | 18 +++++++++ cmd/osbuildbootc.go | 16 +++++++- cmd/qemuexec/qemuexec.go | 2 + internal/pkg/qemu/qemu.go | 6 ++- src/cmdlib.sh | 63 ++----------------------------- 7 files changed, 44 insertions(+), 64 deletions(-) diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml index f0b14d4..a6895f5 100644 --- a/.github/workflows/build-image.yml +++ b/.github/workflows/build-image.yml @@ -1,6 +1,8 @@ name: Build containers on: + push: + branches: [main] pull_request: branches: [main] paths-ignore: diff --git a/Containerfile b/Containerfile index c813374..934ed3d 100644 --- a/Containerfile +++ b/Containerfile @@ -6,3 +6,4 @@ RUN cd /src && make && make install DESTDIR=/instroot FROM quay.io/fedora/fedora:39 COPY --from=builder /instroot / RUN /usr/lib/osbuildbootc/installdeps.sh +ENTRYPOINT ["osbuildbootc"] diff --git a/README.md b/README.md index 46e10f8..fa59275 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,24 @@ ## Usage +This tool can be invoked as a pre-built container image, and it can also be installed +as a standalone tool inside another environment. The implementation uses qemu+KVM. + +Example invocation for the container image: + +```bash +podman run --rm -ti --security-opt label=disable --device /dev/kvm -v $(pwd):/srv -w /srv ghcr.io/cgwalters/osbuildbootc:latest build-qcow2 -I quay.io/cgwalters/ostest example.qcow2 +``` + +Explanation of podman arguments: + +- `--security-opt label=disable`: This is necessary to bind mount in host paths at all +- `--device /dev/kvm`: Pass the KVM device into the container image +- `-v $(pwd):/srv -w /srv`: Pass the current directory as `/srv` into the container + +Note that by default KVM is required. You can set the `OSBUILD_NO_KVM` environment variable +to use full qemu emulation if necessary. + ### Take a container image from remote registry, output a qcow2 ```bash diff --git a/cmd/osbuildbootc.go b/cmd/osbuildbootc.go index ab1542b..d4133be 100644 --- a/cmd/osbuildbootc.go +++ b/cmd/osbuildbootc.go @@ -30,6 +30,10 @@ var ( source := args[0] dest := args[1] + if err := KVMPreflightCheck(); err != nil { + return err + } + if err := os.MkdirAll("tmp", 0755); err != nil { return err } @@ -78,7 +82,7 @@ var ( }, } - cmdVMSHell = &cobra.Command{ + cmdVMShell = &cobra.Command{ Use: "vmshell", Short: "Run a shell in the build VM", Args: cobra.MatchAll(cobra.ExactArgs(0), cobra.OnlyValidArgs), @@ -95,6 +99,14 @@ var ( } ) +func KVMPreflightCheck() error { + _, err := os.Stat("/dev/kvm") + if err != nil { + return fmt.Errorf("failed to access /dev/kvm; you can set OSBUILD_NO_KVM to bypass this at the cost of performance: %w", err) + } + return nil +} + func init() { rootCmd.AddCommand(cmdBuildQcow2) cmdBuildQcow2.Flags().StringVar(&sourceTransport, "transport", "docker://", "Source image stransport") @@ -102,7 +114,7 @@ func init() { cmdBuildQcow2.Flags().StringVarP(&targetImage, "target", "t", "", "Target image (e.g. quay.io/exampleuser/someimg:latest)") cmdBuildQcow2.Flags().BoolVarP(&targetInsecure, "target-no-signature-verification", "I", false, "Disable signature verification for target") cmdBuildQcow2.Flags().BoolVarP(&skipFetchCheck, "skip-fetch-check", "S", false, "Skip verification of target image") - rootCmd.AddCommand(cmdVMSHell) + rootCmd.AddCommand(cmdVMShell) rootCmd.AddCommand(qemuexec.CmdQemuExec) rootCmd.AddCommand(builddiskimpl.CmdBuildDiskImpl) } diff --git a/cmd/qemuexec/qemuexec.go b/cmd/qemuexec/qemuexec.go index a2033bc..0143268 100644 --- a/cmd/qemuexec/qemuexec.go +++ b/cmd/qemuexec/qemuexec.go @@ -17,6 +17,7 @@ var ( usernet bool cpuCountHost bool nvme bool + disableKVM bool architecture string @@ -47,6 +48,7 @@ func init() { CmdQemuExec.Flags().StringVar(&firmware, "firmware", "", "Boot firmware: bios,uefi,uefi-secure (default bios)") CmdQemuExec.Flags().StringVar(&diskimage, "image", "", "path to primary disk image") CmdQemuExec.Flags().BoolVarP(&usernet, "usernet", "U", false, "Enable usermode networking") + CmdQemuExec.Flags().BoolVar(&disableKVM, "disable-kvm", false, "Do not use KVM hardware acceleration") CmdQemuExec.Flags().StringVarP(&hostname, "hostname", "", "", "Set hostname via DHCP") CmdQemuExec.Flags().IntVarP(&memory, "memory", "m", 0, "Memory in MB") CmdQemuExec.Flags().StringVar(&architecture, "arch", "", "Use full emulation for target architecture (e.g. aarch64, x86_64, s390x, ppc64le)") diff --git a/internal/pkg/qemu/qemu.go b/internal/pkg/qemu/qemu.go index f85cdd4..9a5d743 100644 --- a/internal/pkg/qemu/qemu.go +++ b/internal/pkg/qemu/qemu.go @@ -353,6 +353,8 @@ type QemuBuilder struct { Swtpm bool Pdeathsig bool Argv []string + // Whether or not KVM is enabled + KVM bool // AppendKernelArgs are appended to the bootloader config AppendKernelArgs string @@ -407,6 +409,7 @@ func NewQemuBuilder() *QemuBuilder { Firmware: defaultFirmware, Swtpm: true, Pdeathsig: true, + KVM: true, Argv: []string{}, architecture: coreosarch.CurrentRpmArch(), } @@ -860,7 +863,7 @@ func baseQemuArgs(arch string, memoryMiB int) ([]string, error) { // The machine argument needs to reference our memory device; see below machineArg := "memory-backend=" + memoryDevice accel := "accel=kvm" - if _, ok := os.LookupEnv("COSA_NO_KVM"); ok || hostArch != arch { + if _, ok := os.LookupEnv("OSBUILD_NO_KVM"); ok || hostArch != arch { accel = "accel=tcg" kvm = false } @@ -965,7 +968,6 @@ func (builder *QemuBuilder) setupUefi(secureBoot bool) error { return nil } - // VirtioChannelRead allocates a virtio-serial channel that will appear in // the guest as /dev/virtio-ports/. The guest can write to it, and // the host can read. diff --git a/src/cmdlib.sh b/src/cmdlib.sh index af38438..1d3f855 100755 --- a/src/cmdlib.sh +++ b/src/cmdlib.sh @@ -47,55 +47,6 @@ case $arch in esac export DEFAULT_TERMINAL -COSA_PRIVILEGED= -has_privileges() { - if [ -z "${COSA_PRIVILEGED:-}" ]; then - if [ -n "${FORCE_UNPRIVILEGED:-}" ]; then - info "Detected FORCE_UNPRIVILEGED; using virt" - COSA_PRIVILEGED=0 - elif ! capsh --print | grep -q 'Bounding.*cap_sys_admin'; then - info "Missing CAP_SYS_ADMIN; using virt" - COSA_PRIVILEGED=0 - elif [ "$(id -u)" != "0" ] && ! sudo true; then - info "Missing sudo privs; using virt" - COSA_PRIVILEGED=0 - else - COSA_PRIVILEGED=1 - fi - export COSA_PRIVILEGED - fi - [ ${COSA_PRIVILEGED} == 1 ] -} - -preflight() { - depcheck - - # See https://pagure.io/centos-infra/issue/48 - if test "$(umask)" = 0000; then - fatal "Your umask is unset, please use umask 0022 or so" - fi -} - -preflight_kvm() { - # permissions on /dev/kvm vary by (host) distro. If it's - # not writable, recreate it. - - if test -z "${COSA_NO_KVM:-}"; then - if ! test -c /dev/kvm; then - fatal "Missing /dev/kvm; you can set COSA_NO_KVM=1 to bypass this at the cost of performance." - fi - if ! [ -w /dev/kvm ]; then - 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 - fi -} - # runvm generates and runs a minimal VM which we use to "bootstrap" our build # process. It mounts the workdir via virtiofs. If you need to add new packages into # the vm, update `vmdeps.txt`. @@ -203,6 +154,9 @@ EOF qemuexec_args=(osbuildbootc qemuexec -m ${memory_default} --auto-cpus -U \ --console-to-file "${runvm_console}" --bind-rw "${workdir},workdir") + if [ -n "${OSBUILD_NO_KVM:-}" ]; then + qemuexec_args+=("--disable-kvm") + fi base_qemu_args=(-drive 'if=none,id=root,format=raw,snapshot=on,file='"${vmbuilddir}"'/root,index=1' \ -device 'virtio-blk,drive=root' \ @@ -237,14 +191,3 @@ EOF return "${rc}" } - -jq_git() { - # jq_git extracts JSON elements generated using prepare_git_artifacts. - # ARG1 is the element name, and ARG2 is the location of the - # json document. - jq -rM ".git.$1" "${2}" -} - -sha256sum_str() { - sha256sum | cut -f 1 -d ' ' -}