Blame internal/boot/context-managers.go

Packit Service 509fd4
// +build integration
Packit Service 509fd4
Packit Service 509fd4
package boot
Packit Service 509fd4
Packit Service 509fd4
import (
Packit Service 509fd4
	"fmt"
Packit Service 509fd4
	"io"
Packit Service 509fd4
	"io/ioutil"
Packit Service 509fd4
	"log"
Packit Service 509fd4
	"os"
Packit Service 509fd4
	"os/exec"
Packit Service 509fd4
	"runtime"
Packit Service 509fd4
	"strconv"
Packit Service 509fd4
	"strings"
Packit Service 509fd4
	"time"
Packit Service 509fd4
Packit Service 509fd4
	"github.com/osbuild/osbuild-composer/cmd/osbuild-image-tests/constants"
Packit Service 509fd4
	"github.com/osbuild/osbuild-composer/internal/common"
Packit Service 509fd4
	"github.com/osbuild/osbuild-composer/internal/distro"
Packit Service 509fd4
)
Packit Service 509fd4
Packit Service 509fd4
// WithNetworkNamespace provides the function f with a new network namespace
Packit Service 509fd4
// which is deleted immediately after f returns
Packit Service 509fd4
func WithNetworkNamespace(f func(ns NetNS) error) error {
Packit Service 509fd4
	ns, err := newNetworkNamespace()
Packit Service 509fd4
	if err != nil {
Packit Service 509fd4
		return err
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	defer func() {
Packit Service 509fd4
		err := ns.Delete()
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			log.Printf("cannot delete network namespace: %v", err)
Packit Service 509fd4
		}
Packit Service 509fd4
	}()
Packit Service 509fd4
Packit Service 509fd4
	return f(ns)
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// withTempFile provides the function f with a new temporary file
Packit Service 509fd4
// dir and pattern parameters have the same semantics as in ioutil.TempFile
Packit Service 509fd4
func withTempFile(dir, pattern string, f func(file *os.File) error) error {
Packit Service 509fd4
	tempFile, err := ioutil.TempFile(dir, pattern)
Packit Service 509fd4
	if err != nil {
Packit Service 509fd4
		return fmt.Errorf("cannot create the temporary file: %v", err)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	defer func() {
Packit Service 509fd4
		err := os.Remove(tempFile.Name())
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			log.Printf("cannot remove the temporary file: %v", err)
Packit Service 509fd4
		}
Packit Service 509fd4
	}()
Packit Service 509fd4
Packit Service 509fd4
	return f(tempFile)
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func withTempDir(dir, pattern string, f func(dir string) error) error {
Packit Service 509fd4
	tempDir, err := ioutil.TempDir(dir, pattern)
Packit Service 509fd4
	if err != nil {
Packit Service 509fd4
		return fmt.Errorf("cannot create the temporary directory %v", err)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	defer func() {
Packit Service 509fd4
		err := os.RemoveAll(tempDir)
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			log.Printf("cannot remove the temporary directory: %v", err)
Packit Service 509fd4
		}
Packit Service 509fd4
	}()
Packit Service 509fd4
Packit Service 509fd4
	return f(tempDir)
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// writeCloudInitSO creates cloud-init iso from specified userData and
Packit Service 509fd4
// metaData and writes it to the writer
Packit Service 509fd4
func writeCloudInitISO(writer io.Writer, userData, metaData string) error {
Packit Service 509fd4
	isoCmd := exec.Command(
Packit Service 509fd4
		"genisoimage",
Packit Service 509fd4
		"-quiet",
Packit Service 509fd4
		"-input-charset", "utf-8",
Packit Service 509fd4
		"-volid", "cidata",
Packit Service 509fd4
		"-joliet",
Packit Service 509fd4
		"-rock",
Packit Service 509fd4
		userData,
Packit Service 509fd4
		metaData,
Packit Service 509fd4
	)
Packit Service 509fd4
	isoCmd.Stdout = writer
Packit Service 509fd4
	isoCmd.Stderr = os.Stderr
Packit Service 509fd4
Packit Service 509fd4
	err := isoCmd.Run()
Packit Service 509fd4
	if err != nil {
Packit Service 509fd4
		return fmt.Errorf("cannot create cloud-init iso: %v", err)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	return nil
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// WithBootedQemuImage boots the specified image in the specified namespace
Packit Service 509fd4
// using qemu. The VM is killed immediately after function returns.
Packit Service 509fd4
func WithBootedQemuImage(image string, ns NetNS, f func() error) error {
Packit Service 509fd4
	return withTempFile("", "osbuild-image-tests-cloudinit", func(cloudInitFile *os.File) error {
Packit Service 509fd4
		err := writeCloudInitISO(
Packit Service 509fd4
			cloudInitFile,
Packit Service 509fd4
			constants.TestPaths.UserData,
Packit Service 509fd4
			constants.TestPaths.MetaData,
Packit Service 509fd4
		)
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			return err
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		err = cloudInitFile.Close()
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			return fmt.Errorf("cannot close temporary cloudinit file: %v", err)
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		var qemuCmd *exec.Cmd
Packit Service 509fd4
		if common.CurrentArch() == "x86_64" {
Packit Service 509fd4
			hostDistroName, _, err := distro.GetHostDistroName()
Packit Service 509fd4
			if err != nil {
Packit Service 509fd4
				return fmt.Errorf("cannot determing the current distro: %v", err)
Packit Service 509fd4
			}
Packit Service 509fd4
Packit Service 509fd4
			var qemuPath string
Packit Service 509fd4
			if strings.HasPrefix(hostDistroName, "rhel") {
Packit Service 509fd4
				qemuPath = "/usr/libexec/qemu-kvm"
Packit Service 509fd4
			} else {
Packit Service 509fd4
				qemuPath = "qemu-system-x86_64"
Packit Service 509fd4
			}
Packit Service 509fd4
Packit Service 509fd4
			qemuCmd = ns.NamespacedCommand(
Packit Service 509fd4
				qemuPath,
Packit Service 509fd4
				"-cpu", "host",
Packit Service 509fd4
				"-smp", strconv.Itoa(runtime.NumCPU()),
Packit Service 509fd4
				"-m", "1024",
Packit Service 509fd4
				"-snapshot",
Packit Service 509fd4
				"-M", "accel=kvm",
Packit Service 509fd4
				"-cdrom", cloudInitFile.Name(),
Packit Service 509fd4
				"-net", "nic,model=rtl8139", "-net", "user,hostfwd=tcp::22-:22",
Packit Service 509fd4
				"-nographic",
Packit Service 509fd4
				image,
Packit Service 509fd4
			)
Packit Service 509fd4
		} else if common.CurrentArch() == "aarch64" {
Packit Service 509fd4
			// This command does not use KVM as I was unable to make it work in Beaker,
Packit Service 509fd4
			// once we have machines that can use KVM, enable it to make it faster
Packit Service 509fd4
			qemuCmd = ns.NamespacedCommand(
Packit Service 509fd4
				"qemu-system-aarch64",
Packit Service 509fd4
				"-cpu", "host",
Packit Service 509fd4
				"-M", "virt",
Packit Service 509fd4
				"-m", "2048",
Packit Service 509fd4
				// As opposed to x86_64, aarch64 uses UEFI, this one comes from edk2-aarch64 package on Fedora
Packit Service 509fd4
				"-bios", "/usr/share/edk2/aarch64/QEMU_EFI.fd",
Packit Service 509fd4
				"-boot", "efi",
Packit Service 509fd4
				"-M", "accel=kvm",
Packit Service 509fd4
				"-snapshot",
Packit Service 509fd4
				"-cdrom", cloudInitFile.Name(),
Packit Service 509fd4
				"-net", "nic,model=rtl8139", "-net", "user,hostfwd=tcp::22-:22",
Packit Service 509fd4
				"-nographic",
Packit Service 509fd4
				image,
Packit Service 509fd4
			)
Packit Service 509fd4
		} else {
Packit Service 509fd4
			panic("Running on unknown architecture.")
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		err = qemuCmd.Start()
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			return fmt.Errorf("cannot start the qemu process: %v", err)
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		defer func() {
Packit Service 509fd4
			err := killProcessCleanly(qemuCmd.Process, time.Second)
Packit Service 509fd4
			if err != nil {
Packit Service 509fd4
				log.Printf("cannot kill the qemu process: %v", err)
Packit Service 509fd4
			}
Packit Service 509fd4
		}()
Packit Service 509fd4
Packit Service 509fd4
		return f()
Packit Service 509fd4
	})
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// WithBootedNspawnImage boots the specified image in the specified namespace
Packit Service 509fd4
// using nspawn. The VM is killed immediately after function returns.
Packit Service 509fd4
func WithBootedNspawnImage(image string, ns NetNS, f func() error) error {
Packit Service 509fd4
	cmd := exec.Command(
Packit Service 509fd4
		"systemd-nspawn",
Packit Service 509fd4
		"--boot", "--register=no",
Packit Service 509fd4
		"--image", image,
Packit Service 509fd4
		"--network-namespace-path", ns.Path(),
Packit Service 509fd4
	)
Packit Service 509fd4
Packit Service 509fd4
	err := cmd.Start()
Packit Service 509fd4
	if err != nil {
Packit Service 509fd4
		return fmt.Errorf("cannot start the systemd-nspawn process: %v", err)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	defer func() {
Packit Service 509fd4
		err := killProcessCleanly(cmd.Process, time.Second)
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			log.Printf("cannot kill the systemd-nspawn process: %v", err)
Packit Service 509fd4
		}
Packit Service 509fd4
	}()
Packit Service 509fd4
Packit Service 509fd4
	return f()
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// WithBootedNspawnImage boots the specified directory in the specified namespace
Packit Service 509fd4
// using nspawn. The VM is killed immediately after function returns.
Packit Service 509fd4
func WithBootedNspawnDirectory(dir string, ns NetNS, f func() error) error {
Packit Service 509fd4
	cmd := exec.Command(
Packit Service 509fd4
		"systemd-nspawn",
Packit Service 509fd4
		"--boot", "--register=no",
Packit Service 509fd4
		"--directory", dir,
Packit Service 509fd4
		"--network-namespace-path", ns.Path(),
Packit Service 509fd4
	)
Packit Service 509fd4
Packit Service 509fd4
	err := cmd.Start()
Packit Service 509fd4
	if err != nil {
Packit Service 509fd4
		return fmt.Errorf("cannot start the systemd-nspawn process: %v", err)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	defer func() {
Packit Service 509fd4
		err := killProcessCleanly(cmd.Process, time.Second)
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			log.Printf("cannot kill the systemd-nspawn process: %v", err)
Packit Service 509fd4
		}
Packit Service 509fd4
	}()
Packit Service 509fd4
Packit Service 509fd4
	return f()
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// WithExtractedTarArchive extracts the provided archive and passes
Packit Service 509fd4
// a path to the result to the function f. The result is deleted
Packit Service 509fd4
// immediately after the function returns.
Packit Service 509fd4
func WithExtractedTarArchive(archive string, f func(dir string) error) error {
Packit Service 509fd4
	return withTempDir("", "tar-archive", func(dir string) error {
Packit Service 509fd4
		cmd := exec.Command(
Packit Service 509fd4
			"tar",
Packit Service 509fd4
			"xf", archive,
Packit Service 509fd4
			"-C", dir,
Packit Service 509fd4
		)
Packit Service 509fd4
		cmd.Stderr = os.Stderr
Packit Service 509fd4
		cmd.Stdout = os.Stdout
Packit Service 509fd4
Packit Service 509fd4
		err := cmd.Run()
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			return fmt.Errorf("cannot untar the archive: %v", err)
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		return f(dir)
Packit Service 509fd4
	})
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// WithSSHKeyPair runs the function f with a newly generated
Packit Service 509fd4
// ssh key-pair, they key-pair is deleted immediately after
Packit Service 509fd4
// the function f returns
Packit Service 509fd4
func WithSSHKeyPair(f func(privateKey, publicKey string) error) error {
Packit Service 509fd4
	return withTempDir("", "keys", func(dir string) error {
Packit Service 509fd4
		privateKey := dir + "/id_rsa"
Packit Service 509fd4
		publicKey := dir + "/id_rsa.pub"
Packit Service 509fd4
		cmd := exec.Command("ssh-keygen",
Packit Service 509fd4
			"-N", "",
Packit Service 509fd4
			"-f", privateKey,
Packit Service 509fd4
		)
Packit Service 509fd4
Packit Service 509fd4
		err := cmd.Run()
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			return fmt.Errorf("ssh-keygen failed: %v", err)
Packit Service 509fd4
		}
Packit Service 509fd4
Packit Service 509fd4
		return f(privateKey, publicKey)
Packit Service 509fd4
	})
Packit Service 509fd4
}