diff --git a/mantle/kola/tests/ignition/qemufailure.go b/mantle/kola/tests/ignition/qemufailure.go index ba5e4798a1..2c3abcaa85 100644 --- a/mantle/kola/tests/ignition/qemufailure.go +++ b/mantle/kola/tests/ignition/qemufailure.go @@ -29,12 +29,10 @@ import ( "github.com/coreos/coreos-assembler/mantle/kola/register" "github.com/coreos/coreos-assembler/mantle/platform" "github.com/coreos/coreos-assembler/mantle/platform/conf" - "github.com/coreos/ignition/v2/config/v3_2/types" "github.com/coreos/coreos-assembler/mantle/util" + "github.com/coreos/ignition/v2/config/v3_2/types" ) -var console bool - func init() { register.RegisterTest(®ister.Test{ Name: "coreos.ignition.failure", @@ -78,42 +76,60 @@ func runDualBootfsIgnitionFailure(c cluster.TestCluster) { } } -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. - failConfig, err := conf.EmptyIgnition().Render(conf.FailWarnings) +// Read file and verify if it contains a pattern +// 1. Read file, make sure it exists +// 2. regex for pattern +func fileHasPattern(tempLogFilePath string, searchPattern string) (bool, error) { + file, err := os.ReadFile(tempLogFilePath) if err != nil { - return errors.Wrapf(err, "creating empty config") + return false, err } - failConfig.AddFile("/notwritable.txt", "Hello world", 0644) - - builder := platform.NewQemuBuilder() // platform.Manhole() - if err != nil { - return err + // File exists but it's empty + if len(file) == 0 { + return false, nil } - builder.MemoryMiB = 1024 - builder.Firmware = kola.QEMUOptions.Firmware + // File has content, but the pattern is not present + match := regexp.MustCompile(searchPattern).Match(file) + if match { + // Pattern found + return true, nil + } + // Pattern not found + return false, nil +} + +// Start the VM, take string and grep for it in the temporary console logs +func verifyError(builder *platform.QemuBuilder, searchPattern string) error { 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() { - err := inst.WaitAll(ctx) - if err == nil { - err = fmt.Errorf("Ignition unexpectedly succeeded") - } else if err == platform.ErrInitramfsEmergency { - // The expected case - err = nil + resultingError := inst.WaitAll(ctx) + if resultingError == nil { + resultingError = fmt.Errorf("ignition unexpectedly succeeded") + } else if resultingError == platform.ErrInitramfsEmergency { + // Expectred initramfs failure, checking the console file to insure + // that coreos.ignition.failure failed + found, err := fileHasPattern(builder.ConsoleFile, searchPattern) + if err != nil { + resultingError = errors.Wrapf(err, "failed to find pattern '%s' in file '%s'", searchPattern, builder.ConsoleFile) + } else if !found { + resultingError = fmt.Errorf("regex match NOT found") + } else { + // The expected case + resultingError = nil + } } else { - err = errors.Wrapf(err, "expected initramfs emergency.target error") + resultingError = errors.Wrapf(resultingError, "expected initramfs emergency.target error") } - errchan <- err + errchan <- resultingError }() select { @@ -130,16 +146,55 @@ func ignitionFailure(c cluster.TestCluster) error { } } -// Verify that there is only one boot filesystem attached to the device -func dualBootfsFailure(c cluster.TestCluster) 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. + failConfig, err := conf.EmptyIgnition().Render(conf.FailWarnings) + if err != nil { + return errors.Wrapf(err, "creating empty config") + } + failConfig.AddFile("/notwritable.txt", "Hello world", 0644) + builder := platform.NewQemuBuilder() - consoleFile, err := builder.TempFile("console.log") + // Create a temporary log file + ConsoleFile, err := builder.TempFile("console.log") + if err != nil { + return err + } + tempLogFilePath := ConsoleFile.Name() + // Instruct builder to use it + builder.ConsoleFile = tempLogFilePath + defer builder.Close() + builder.SetConfig(failConfig) + err = builder.AddBootDisk(&platform.Disk{ + BackingFile: kola.QEMUOptions.DiskImage, + }) if err != nil { return err } - builder.ConsoleFile = consoleFile.Name() + builder.MemoryMiB = 1024 + builder.Firmware = kola.QEMUOptions.Firmware + + searchPattern := "error creating /sysroot/notwritable.txt" + if err := verifyError(builder, searchPattern); err != nil { + return err + } + return nil +} + +// Verify that there is only one boot filesystem attached to the device +func dualBootfsFailure(c cluster.TestCluster) error { + builder := platform.NewQemuBuilder() + // Create a temporary log file + ConsoleFile, err := builder.TempFile("console.log") + if err != nil { + return err + } + tempLogFilePath := ConsoleFile.Name() + // Instruct builder to use it + builder.ConsoleFile = tempLogFilePath // get current path and create tmp dir fakeBootFile, err := builder.TempFile("fakeBoot") if err != nil { @@ -174,85 +229,40 @@ func dualBootfsFailure(c cluster.TestCluster) error { } builder.MemoryMiB = 1024 builder.Firmware = kola.QEMUOptions.Firmware - inst, err := builder.Exec() - if err != nil { - return err - } // platform.Manhole() - 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 + searchRegexString := "Error: System has 2 devices with a filesystem labeled 'boot'" + if err := verifyError(builder, searchRegexString); err != nil { + return err } + return nil } // Use ignition config to create a second bootfs // 1 - produce an ignition file that format a disk with a"boot" label. // 2 - boot the VM with the ignition file and an extra disk. -// 3 - observe the failure +// 3 - observe the failure func dualBootfsIgnitionFailure(c cluster.TestCluster) error { builder := platform.NewQemuBuilder() - // inserting log file for troubleshooting - // file located in coreos-assembler directory - - // consoleFile, err := builder.TempFile("console.log") - // if err != nil { - // return err - // } - //builder.ConsoleFile = consoleFile.Name() - builder.ConsoleFile = "console.txt" + // Create a temporary log file + ConsoleFile, err := builder.TempFile("console.log") + if err != nil { + return err + } + tempLogFilePath := ConsoleFile.Name() + // Instruct builder to use it + builder.ConsoleFile = tempLogFilePath failConfig, err := conf.EmptyIgnition().Render(conf.FailWarnings) if err != nil { return errors.Wrapf(err, "creating empty config") } - // Craft an ingniton file that format a partition - formaterConfig := types.Config { + // Craft an Ingniton file that formats a partition + formaterConfig := types.Config{ Ignition: types.Ignition{ Version: "3.2.0", }, - Storage: types.Storage { + Storage: types.Storage{ Filesystems: []types.Filesystem{ { Device: "/dev/disk/by-id/virtio-extra-boot", @@ -281,54 +291,10 @@ func dualBootfsIgnitionFailure(c cluster.TestCluster) error { 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 + searchRegexString := "Error: System has 2 devices with a filesystem labeled 'boot'" + if err := verifyError(builder, searchRegexString); err != nil { + return err } + return nil } diff --git a/mantle/platform/platform.go b/mantle/platform/platform.go index 812f9e2e37..0b464dbe48 100644 --- a/mantle/platform/platform.go +++ b/mantle/platform/platform.go @@ -555,12 +555,3 @@ func WriteJSONInfo(m Machine, w io.Writer) error { e.SetIndent("", "") return e.Encode(info) } - -// Create a temporary log file -func CreateTempLogFile(builder *QemuBuilder) string { - ConsoleFile, err := builder.TempFile("console.log") - if err != nil { - return "creating console.log not successfull." - } - return ConsoleFile.Name() -}