Skip to content

Commit c044a86

Browse files
committed
tests/ignition: Modify qemufailure to include bootfs test
See: #2953 tests/ignition: Use ignition config to create a second bootfs Co-authored-by: JB Trystram <[email protected]> mantle/kola: Create functions to grep for patterns
1 parent 9b0e7e8 commit c044a86

File tree

1 file changed

+215
-25
lines changed

1 file changed

+215
-25
lines changed

mantle/kola/tests/ignition/qemufailure.go

+215-25
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ package ignition
1717
import (
1818
"context"
1919
"fmt"
20+
"os"
21+
"os/exec"
22+
"regexp"
2023
"time"
2124

2225
"github.com/pkg/errors"
@@ -26,6 +29,8 @@ import (
2629
"github.com/coreos/coreos-assembler/mantle/kola/register"
2730
"github.com/coreos/coreos-assembler/mantle/platform"
2831
"github.com/coreos/coreos-assembler/mantle/platform/conf"
32+
"github.com/coreos/coreos-assembler/mantle/util"
33+
"github.com/coreos/ignition/v2/config/v3_2/types"
2934
)
3035

3136
func init() {
@@ -37,6 +42,20 @@ func init() {
3742
Platforms: []string{"qemu"},
3843
Tags: []string{"ignition"},
3944
})
45+
register.RegisterTest(&register.Test{
46+
Name: "coreos.unique.boot.failure",
47+
ClusterSize: 0,
48+
Description: "Verify boot fails if there are pre-existing boot filesystems.",
49+
Platforms: []string{"qemu"},
50+
Run: runDualBootfsFailure,
51+
})
52+
register.RegisterTest(&register.Test{
53+
Name: "coreos.unique.boot.ignition.failure",
54+
ClusterSize: 0,
55+
Description: "Verify boot fails if there are pre-existing boot filesystems created with Ignition.",
56+
Platforms: []string{"qemu"},
57+
Run: runDualBootfsIgnitionFailure,
58+
})
4059
}
4160

4261
func runIgnitionFailure(c cluster.TestCluster) {
@@ -45,47 +64,68 @@ func runIgnitionFailure(c cluster.TestCluster) {
4564
}
4665
}
4766

48-
func ignitionFailure(c cluster.TestCluster) error {
49-
// We can't create files in / due to the immutable bit OSTree creates, so
50-
// this is a convenient way to test Ignition failure.
51-
failConfig, err := conf.EmptyIgnition().Render(conf.FailWarnings)
52-
if err != nil {
53-
return errors.Wrapf(err, "creating empty config")
67+
func runDualBootfsFailure(c cluster.TestCluster) {
68+
if err := dualBootfsFailure(c); err != nil {
69+
c.Fatal(err.Error())
5470
}
55-
failConfig.AddFile("/notwritable.txt", "Hello world", 0644)
71+
}
5672

57-
builder := platform.NewQemuBuilder()
58-
defer builder.Close()
59-
builder.SetConfig(failConfig)
60-
err = builder.AddBootDisk(&platform.Disk{
61-
BackingFile: kola.QEMUOptions.DiskImage,
62-
})
73+
func runDualBootfsIgnitionFailure(c cluster.TestCluster) {
74+
if err := dualBootfsIgnitionFailure(c); err != nil {
75+
c.Fatal(err.Error())
76+
}
77+
}
78+
79+
// Read file and verify if it contains a pattern
80+
// 1. Read file, make sure it exists
81+
// 2. regex for pattern
82+
func fileContainsPattern(path string, searchPattern string) (bool, error) {
83+
file, err := os.ReadFile(path)
6384
if err != nil {
64-
return err
85+
return false, err
6586
}
66-
builder.MemoryMiB = 1024
67-
builder.Firmware = kola.QEMUOptions.Firmware
87+
// File has content, but the pattern is not present
88+
match := regexp.MustCompile(searchPattern).Match(file)
89+
if match {
90+
// Pattern found
91+
return true, nil
92+
}
93+
// Pattern not found
94+
return false, nil
95+
}
96+
97+
// Start the VM, take string and grep for it in the temporary console logs
98+
func verifyError(builder *platform.QemuBuilder, searchPattern string) error {
6899
inst, err := builder.Exec()
69100
if err != nil {
70101
return err
71102
}
72103
defer inst.Destroy()
73-
74104
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
105+
75106
defer cancel()
76107

77108
errchan := make(chan error)
78109
go func() {
79-
err := inst.WaitAll(ctx)
80-
if err == nil {
81-
err = fmt.Errorf("Ignition unexpectedly succeeded")
82-
} else if err == platform.ErrInitramfsEmergency {
83-
// The expected case
84-
err = nil
110+
resultingError := inst.WaitAll(ctx)
111+
if resultingError == nil {
112+
resultingError = fmt.Errorf("ignition unexpectedly succeeded")
113+
} else if resultingError == platform.ErrInitramfsEmergency {
114+
// Expectred initramfs failure, checking the console file to insure
115+
// that coreos.ignition.failure failed
116+
found, err := fileContainsPattern(builder.ConsoleFile, searchPattern)
117+
if err != nil {
118+
resultingError = errors.Wrapf(err, "looking for pattern '%s' in file '%s' failed", searchPattern, builder.ConsoleFile)
119+
} else if !found {
120+
resultingError = errors.Wrapf(err, "pattern '%s' not found in file '%s'", searchPattern, builder.ConsoleFile)
121+
} else {
122+
// The expected case
123+
resultingError = nil
124+
}
85125
} else {
86-
err = errors.Wrapf(err, "expected initramfs emergency.target error")
126+
resultingError = errors.Wrapf(resultingError, "expected initramfs emergency.target error")
87127
}
88-
errchan <- err
128+
errchan <- resultingError
89129
}()
90130

91131
select {
@@ -101,3 +141,153 @@ func ignitionFailure(c cluster.TestCluster) error {
101141
return nil
102142
}
103143
}
144+
145+
func ignitionFailure(c cluster.TestCluster) error {
146+
// We can't create files in / due to the immutable bit OSTree creates, so
147+
// this is a convenient way to test Ignition failure.
148+
failConfig, err := conf.EmptyIgnition().Render(conf.FailWarnings)
149+
if err != nil {
150+
return errors.Wrapf(err, "creating empty config")
151+
}
152+
failConfig.AddFile("/notwritable.txt", "Hello world", 0644)
153+
154+
builder := platform.NewQemuBuilder()
155+
156+
// Create a temporary log file
157+
consoleFile, err := builder.TempFile("console.log")
158+
if err != nil {
159+
return err
160+
}
161+
// Instruct builder to use it
162+
builder.ConsoleFile = consoleFile.Name()
163+
defer builder.Close()
164+
builder.SetConfig(failConfig)
165+
err = builder.AddBootDisk(&platform.Disk{
166+
BackingFile: kola.QEMUOptions.DiskImage,
167+
})
168+
if err != nil {
169+
return err
170+
}
171+
172+
builder.MemoryMiB = 1024
173+
builder.Firmware = kola.QEMUOptions.Firmware
174+
175+
searchPattern := "error creating /sysroot/notwritable.txt"
176+
if err := verifyError(builder, searchPattern); err != nil {
177+
return err
178+
}
179+
return nil
180+
}
181+
182+
// Verify that there is only one boot filesystem attached to the device
183+
func dualBootfsFailure(c cluster.TestCluster) error {
184+
builder := platform.NewQemuBuilder()
185+
// Create a temporary log file
186+
consoleFile, err := builder.TempFile("console.log")
187+
if err != nil {
188+
return err
189+
}
190+
// Instruct builder to use it
191+
builder.ConsoleFile = consoleFile.Name()
192+
// get current path and create tmp dir
193+
fakeBootFile, err := builder.TempFile("fakeBoot")
194+
if err != nil {
195+
return err
196+
}
197+
198+
// Truncate the file to 1 gigabyte
199+
const oneGB = 1 << 30
200+
err = fakeBootFile.Truncate(oneGB)
201+
if err != nil {
202+
return err
203+
}
204+
205+
cmd := exec.Command("mkfs.ext4", "-L", "boot", fakeBootFile.Name())
206+
cmd.Stderr = os.Stderr
207+
if err := cmd.Run(); err != nil {
208+
c.Fatal(err)
209+
}
210+
211+
err = builder.AddBootDisk(&platform.Disk{
212+
BackingFile: kola.QEMUOptions.DiskImage,
213+
})
214+
if err != nil {
215+
return err
216+
}
217+
err = builder.AddDisk(&platform.Disk{
218+
BackingFile: fakeBootFile.Name(),
219+
BackingFormat: "raw",
220+
})
221+
if err != nil {
222+
return err
223+
}
224+
builder.MemoryMiB = 1024
225+
builder.Firmware = kola.QEMUOptions.Firmware
226+
227+
searchRegexString := "Error: System has 2 devices with a filesystem labeled 'boot'"
228+
if err := verifyError(builder, searchRegexString); err != nil {
229+
return err
230+
}
231+
return nil
232+
}
233+
234+
// Use ignition config to create a second bootfs
235+
// 1 - produce an ignition file that format a disk with a"boot" label.
236+
// 2 - boot the VM with the ignition file and an extra disk.
237+
// 3 - observe the failure
238+
func dualBootfsIgnitionFailure(c cluster.TestCluster) error {
239+
builder := platform.NewQemuBuilder()
240+
241+
// Create a temporary log file
242+
consoleFile, err := builder.TempFile("console.log")
243+
if err != nil {
244+
return err
245+
}
246+
// Instruct builder to use it
247+
builder.ConsoleFile = consoleFile.Name()
248+
failConfig, err := conf.EmptyIgnition().Render(conf.FailWarnings)
249+
if err != nil {
250+
return errors.Wrapf(err, "creating empty config")
251+
}
252+
253+
// Craft an Ignition file that formats a partition
254+
formaterConfig := types.Config{
255+
Ignition: types.Ignition{
256+
Version: "3.2.0",
257+
},
258+
Storage: types.Storage{
259+
Filesystems: []types.Filesystem{
260+
{
261+
Device: "/dev/disk/by-id/virtio-extra-boot",
262+
Label: util.StrToPtr("boot"),
263+
Format: util.StrToPtr("vfat"),
264+
WipeFilesystem: util.BoolToPtr(true),
265+
},
266+
},
267+
},
268+
}
269+
failConfig.MergeV32(formaterConfig)
270+
271+
builder.SetConfig(failConfig)
272+
err = builder.AddBootDisk(&platform.Disk{
273+
BackingFile: kola.QEMUOptions.DiskImage,
274+
})
275+
276+
if err != nil {
277+
return err
278+
}
279+
280+
err = builder.AddDisksFromSpecs([]string{"1G:serial=extra-boot"})
281+
if err != nil {
282+
return err
283+
}
284+
285+
builder.MemoryMiB = 1024
286+
builder.Firmware = kola.QEMUOptions.Firmware
287+
288+
searchRegexString := "Error: System has 2 devices with a filesystem labeled 'boot'"
289+
if err := verifyError(builder, searchRegexString); err != nil {
290+
return err
291+
}
292+
return nil
293+
}

0 commit comments

Comments
 (0)