m/{t,n}/installer: use bundles and test install
This makes the installer use actual bundles and uses TestOS bundles
to test the installation process end-to-end.
Change-Id: I64fa412032796d7d7633e9944dbae727d90a863e
Reviewed-on: https://review.monogon.dev/c/monogon/+/433
Reviewed-by: Mateusz Zalega <mateusz@monogon.tech>
diff --git a/metropolis/node/installer/main.go b/metropolis/node/installer/main.go
index 356247a..6eb6706 100644
--- a/metropolis/node/installer/main.go
+++ b/metropolis/node/installer/main.go
@@ -20,6 +20,7 @@
package main
import (
+ "archive/zip"
"fmt"
"io"
"log"
@@ -196,6 +197,28 @@
log.Fatalf("while mounting the installer ESP: %s", err.Error())
}
+ nodeParameters, err := os.Open("/installer/EFI/metropolis-installer/nodeparams.pb")
+ if err != nil {
+ log.Fatalf("failed to open node parameters from ESP: %v", err)
+ }
+
+ // TODO(lorenz): Replace with proper bundles
+ bundle, err := zip.OpenReader("/installer/EFI/metropolis-installer/bundle.bin")
+ if err != nil {
+ log.Fatalf("failed to open node bundle from ESP: %v", err)
+ }
+ defer bundle.Close()
+ efiPayload, err := bundle.Open("kernel_efi.efi")
+ if err != nil {
+ log.Fatalf("Cannot open EFI payload in bundle: %v", err)
+ }
+ defer efiPayload.Close()
+ systemImage, err := bundle.Open("rootfs.img")
+ if err != nil {
+ log.Fatalf("Cannot open system image in bundle: %v", err)
+ }
+ defer systemImage.Close()
+
// Build the osimage parameters.
installParams := osimage.Params{
PartitionSize: osimage.PartitionSizeInfo{
@@ -215,9 +238,9 @@
// TODO(mateusz@monogon.tech): Address that bug either by patching go-diskfs
// or rewriting osimage.
SystemImage: nil,
- // TODO(mateusz@monogon.tech): Plug in.
- EFIPayload: strings.NewReader("TODO"),
- NodeParameters: strings.NewReader("TODO"),
+
+ EFIPayload: efiPayload,
+ NodeParameters: nodeParameters,
}
// Calculate the minimum target size based on the installation parameters.
minSize := uint64((installParams.PartitionSize.ESP +
@@ -259,8 +282,7 @@
}
sysBlkdevPath := filepath.Join("/dev", sysBlkdevName)
// Copy the system partition contents.
- contents := strings.NewReader("TODO") // TODO(mz): plug in
- if err := initializeSystemPartition(contents, sysBlkdevPath); err != nil {
+ if err := initializeSystemPartition(systemImage, sysBlkdevPath); err != nil {
log.Fatalf("while initializing the system partition at %q: %s", sysBlkdevPath, err.Error())
}
diff --git a/metropolis/test/installer/BUILD.bazel b/metropolis/test/installer/BUILD.bazel
index 28ede12..c6bc09e 100644
--- a/metropolis/test/installer/BUILD.bazel
+++ b/metropolis/test/installer/BUILD.bazel
@@ -5,6 +5,7 @@
size = "small",
data = [
"//metropolis/node/installer:kernel",
+ "//metropolis/test/installer/testos:testos_bundle",
"//third_party/edk2:firmware",
"@qemu//:qemu-x86_64-softmmu",
],
@@ -20,6 +21,7 @@
deps = [
"//metropolis/cli/metroctl/core:go_default_library",
"//metropolis/node/build/mkimage/osimage:go_default_library",
+ "//metropolis/proto/api:go_default_library",
"@com_github_diskfs_go_diskfs//:go_default_library",
"@com_github_diskfs_go_diskfs//disk:go_default_library",
"@com_github_diskfs_go_diskfs//partition/gpt:go_default_library",
diff --git a/metropolis/test/installer/main.go b/metropolis/test/installer/main.go
index f31290d..ea2c20a 100644
--- a/metropolis/test/installer/main.go
+++ b/metropolis/test/installer/main.go
@@ -33,12 +33,15 @@
diskfs "github.com/diskfs/go-diskfs"
"github.com/diskfs/go-diskfs/disk"
"github.com/diskfs/go-diskfs/partition/gpt"
+ "source.monogon.dev/metropolis/proto/api"
+
mctl "source.monogon.dev/metropolis/cli/metroctl/core"
osimage "source.monogon.dev/metropolis/node/build/mkimage/osimage"
)
const (
InstallerEFIPayload = "metropolis/node/installer/kernel.efi"
+ TestOSBundle = "metropolis/test/installer/testos/testos_bundle.zip"
InstallerImage = "metropolis/test/installer/installer.img"
NodeStorage = "metropolis/test/installer/stor.img"
)
@@ -55,7 +58,6 @@
"-cpu", "host",
"-drive", "if=pflash,format=raw,readonly,file=external/edk2/OVMF_CODE.fd",
"-drive", "if=pflash,format=raw,snapshot=on,file=external/edk2/OVMF_VARS.fd",
- "-drive", "if=virtio,format=raw,snapshot=on,cache=unsafe,file=" + InstallerImage,
"-serial", "stdio",
"-no-reboot",
}
@@ -83,6 +85,14 @@
return result, nil
}
+// runQemuWithInstaller starts a QEMU process and waits for it to finish. args is
+// concatenated to the list of predefined default arguments. It returns true if
+// expectedOutput is found in the serial port output. It may return an error.
+func runQemuWithInstaller(args []string, expectedOutput string) (bool, error) {
+ args = append(args, "-drive", "if=virtio,format=raw,snapshot=on,cache=unsafe,file="+InstallerImage)
+ return runQemu(args, expectedOutput)
+}
+
// getStorage creates a sparse file, given a size expressed in mebibytes, and
// returns a path to that file. It may return an error.
func getStorage(size int64) (string, error) {
@@ -132,10 +142,21 @@
if err != nil {
log.Fatalf("Couldn't stat the installer EFI executable: %s", err.Error())
}
+ bundle, err := os.Open(TestOSBundle)
+ if err != nil {
+ log.Fatalf("failed to open TestOS bundle: %v", err)
+ }
+ bundleStat, err := bundle.Stat()
+ if err != nil {
+ log.Fatalf("failed to stat() TestOS bundle: %v", err)
+ }
iargs := mctl.MakeInstallerImageArgs{
Installer: installer,
InstallerSize: uint64(info.Size()),
TargetPath: InstallerImage,
+ NodeParams: &api.NodeParameters{},
+ Bundle: bundle,
+ BundleSize: uint64(bundleStat.Size()),
}
if err := mctl.MakeInstallerImage(iargs); err != nil {
log.Fatalf("Couldn't create the installer image at %q: %s", InstallerImage, err.Error())
@@ -181,7 +202,7 @@
// the installer to fail at the device probe stage rather than attempting to
// use the medium as the target device.
expectedOutput := "couldn't find a suitable block device"
- result, err := runQemu(nil, expectedOutput)
+ result, err := runQemuWithInstaller(nil, expectedOutput)
if err != nil {
t.Error(err.Error())
}
@@ -201,7 +222,7 @@
// Run QEMU. Expect the installer to fail with a predefined error string.
expectedOutput := "couldn't find a suitable block device"
- result, err := runQemu(qemuDriveParam(imagePath), expectedOutput)
+ result, err := runQemuWithInstaller(qemuDriveParam(imagePath), expectedOutput)
if err != nil {
t.Error(err.Error())
}
@@ -220,7 +241,7 @@
// Run QEMU. Expect the installer to succeed.
expectedOutput := "Installation completed"
- result, err := runQemu(qemuDriveParam(storagePath), expectedOutput)
+ result, err := runQemuWithInstaller(qemuDriveParam(storagePath), expectedOutput)
if err != nil {
t.Error(err.Error())
}
@@ -263,4 +284,13 @@
if err := checkEspContents(storage); err != nil {
t.Error(err.Error())
}
+ // Run QEMU again. Expect TestOS to launch successfully.
+ expectedOutput = "_TESTOS_LAUNCH_SUCCESS_"
+ result, err = runQemu(qemuDriveParam(storagePath), expectedOutput)
+ if err != nil {
+ t.Error(err.Error())
+ }
+ if result != true {
+ t.Errorf("QEMU didn't produce the expected output %q", expectedOutput)
+ }
}