diff --git a/mantle/kola/tests/ignition/qemufailure.go b/mantle/kola/tests/ignition/qemufailure.go index 681f2295a2..b010428b61 100644 --- a/mantle/kola/tests/ignition/qemufailure.go +++ b/mantle/kola/tests/ignition/qemufailure.go @@ -17,6 +17,9 @@ package ignition import ( "context" "fmt" + "os" + "os/exec" + "regexp" "time" "github.com/pkg/errors" @@ -37,6 +40,13 @@ func init() { Platforms: []string{"qemu"}, Tags: []string{"ignition"}, }) + register.RegisterTest(®ister.Test{ + Name: "coreos.unique.boot.failure", + ClusterSize: 0, + Description: "Verify boot fails if there are pre-existing boot filesystems.", + Platforms: []string{"qemu"}, + Run: runBootfsFailure, + }) } func runIgnitionFailure(c cluster.TestCluster) { @@ -45,6 +55,12 @@ func runIgnitionFailure(c cluster.TestCluster) { } } +func runBootfsFailure(c cluster.TestCluster) { + if err := dualBootfsFailure(c); err != nil { + c.Fatal(err.Error()) + } +} + func ignitionFailure(c cluster.TestCluster) error { // We can't create files in / due to the immutable bit OSTree creates, so // this is a convenient way to test Ignition failure. @@ -101,3 +117,99 @@ func ignitionFailure(c cluster.TestCluster) error { return nil } } + +// Verify that there is only one boot filesystem attached to the device +func dualBootfsFailure(c cluster.TestCluster) error { + builder := platform.NewQemuBuilder() + + consoleFile, err := builder.TempFile("console.log") + if err != nil { + return err + } + builder.ConsoleFile = consoleFile.Name() + + // get current path and create tmp dir + fakeBootFile, err := builder.TempFile("fakeBoot") + if err != nil { + return err + } + + // Truncate the file to 1 gigabyte + const oneGB = 1 << 30 + err = fakeBootFile.Truncate(oneGB) + if err != nil { + return err + } + + cmd := exec.Command("mkfs.ext4", "-L", "boot", fakeBootFile.Name()) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + c.Fatal(err) + } + + err = builder.AddBootDisk(&platform.Disk{ + BackingFile: kola.QEMUOptions.DiskImage, + }) + if err != nil { + return err + } + err = builder.AddDisk(&platform.Disk{ + BackingFile: fakeBootFile.Name(), + BackingFormat: "raw", + }) + if err != nil { + return err + } + builder.MemoryMiB = 1024 + builder.Firmware = kola.QEMUOptions.Firmware + inst, err := builder.Exec() + if err != nil { + return err + } + defer inst.Destroy() + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + errchan := make(chan error) + go func() { + resultingError := inst.WaitAll(ctx) + + if resultingError == nil { + resultingError = fmt.Errorf("bootfs unexpectedly succeeded") + } else if resultingError == platform.ErrInitramfsEmergency { + // Expectred initramfs failure, checking the console file to insure + // that coreos-unique-boot.service failed + b, err := os.ReadFile(builder.ConsoleFile) + if err != nil { + panic(err) + } + isExist, err := regexp.Match("Error: System has 2 devices with a filesystem labeled 'boot'", b) + if err != nil { + panic(err) + } + if isExist { + // The expected case + resultingError = nil + } else { + resultingError = errors.Wrapf(err, "expected coreos-unique-boot.service to fail") + } + } else { + resultingError = errors.Wrapf(err, "expected initramfs emergency.target error") + } + errchan <- resultingError + }() + + select { + case <-ctx.Done(): + if err := inst.Kill(); err != nil { + return errors.Wrapf(err, "failed to kill the vm instance") + } + return errors.Wrapf(ctx.Err(), "timed out waiting for initramfs error") + case err := <-errchan: + if err != nil { + return err + } + return nil + } +}