|
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 |
}
|