diff --git a/internal/pkg/container/config.go b/internal/pkg/container/config.go index 0e29b5674..666b058ca 100644 --- a/internal/pkg/container/config.go +++ b/internal/pkg/container/config.go @@ -1,10 +1,13 @@ package container import ( + "fmt" + "io/fs" "path" + "path/filepath" + "regexp" warewulfconf "github.com/warewulf/warewulf/internal/pkg/config" - "github.com/warewulf/warewulf/internal/pkg/util" ) func SourceParentDir() string { @@ -31,16 +34,24 @@ func ImageFile(name string) string { // InitramfsBootPath returns the dracut built initramfs path, as dracut built initramfs inside container // the function returns host path of the built file -func InitramfsBootPath(image, kver string) (image_path string) { - image_path = path.Join(RootFsDir(image), "boot", "initramfs-"+kver+".img") - if util.IsFile(image_path) { - return image_path +func InitramfsBootPath(image, kver string) (image_path string, err error) { + initRegx, err := regexp.Compile("^init(rd|ramfs)-" + kver) + if err != nil { + return "", fmt.Errorf("regexp compiles failure, err: %s", err) } - image_path = path.Join(RootFsDir(image), "boot", "initrd-"+kver) - if util.IsFile(image_path) { - return image_path + var initPath string + if err := filepath.WalkDir(path.Join(RootFsDir(image), "boot"), func(path string, d fs.DirEntry, err error) error { + if d.Type().IsRegular() && initRegx.MatchString(d.Name()) { + initPath = path + } + return nil + }); err != nil { + return "", fmt.Errorf("failed to walk through the dir: %s, err: %s", path.Join(RootFsDir(image), "boot"), err) } - return "" + if initPath == "" { + return initPath, fmt.Errorf("faild to find a target kernel version initramfs") + } + return initPath, nil } diff --git a/internal/pkg/container/config_test.go b/internal/pkg/container/config_test.go new file mode 100644 index 000000000..27d2081d5 --- /dev/null +++ b/internal/pkg/container/config_test.go @@ -0,0 +1,97 @@ +package container + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + warewulfconf "github.com/warewulf/warewulf/internal/pkg/config" +) + +func TestInitramfsBootPath(t *testing.T) { + conf := warewulfconf.Get() + temp, err := os.MkdirTemp(os.TempDir(), "ww-conf-*") + assert.NoError(t, err) + defer os.RemoveAll(temp) + conf.Paths.WWChrootdir = temp + + assert.NoError(t, os.MkdirAll(filepath.Join(RootFsDir("image"), "boot"), 0700)) + + tests := []struct { + name string + initramfs []string + ver string + err error + retName string + }{ + { + name: "ok case 1", + initramfs: []string{"initramfs-1.1.1.aarch64.img"}, + ver: "1.1.1.aarch64", + err: nil, + }, + { + name: "ok case 2", + initramfs: []string{"initrd-1.1.1.aarch64"}, + ver: "1.1.1.aarch64", + err: nil, + }, + { + name: "ok case 3", + initramfs: []string{"initramfs-1.1.1.aarch64"}, + ver: "1.1.1.aarch64", + err: nil, + }, + { + name: "ok case 4", + initramfs: []string{"initrd-1.1.1.aarch64.img"}, + ver: "1.1.1.aarch64", + err: nil, + }, + { + name: "ko case, wrong init name", + initramfs: []string{"initrr-1.1.1.aarch64.img"}, + ver: "1.1.1.aarch64", + err: fmt.Errorf("faild to find a target kernel version initramfs"), + }, + { + name: "ok case 1 multiple candidates, return the last one", + initramfs: []string{"initramfs-1.1.1.aarch64.img", "initrd-1.1.1.aarch64"}, + ver: "1.1.1.aarch64", + err: nil, + retName: "initrd-1.1.1.aarch64", + }, + { + name: "ko case, wrong ver", + initramfs: []string{"initrr-1.1.1.aarch64.img"}, + ver: "1.1.2.aarch64", + err: fmt.Errorf("faild to find a target kernel version initramfs"), + }, + } + + for _, tt := range tests { + t.Logf("running test: %s", tt.name) + for _, init := range tt.initramfs { + assert.NoError(t, os.WriteFile(filepath.Join(RootFsDir("image"), "boot", init), []byte(""), 0600)) + } + initPath, err := InitramfsBootPath("image", tt.ver) + if err != tt.err && err.Error() != tt.err.Error() { + t.Errorf("unexpected success, real err: %v, expected err: %v", err, tt.err) + } + if err == nil { + assert.NotEmpty(t, initPath) + } else { + assert.Empty(t, initPath) + } + + if tt.retName != "" { + assert.Equal(t, filepath.Base(initPath), tt.retName) + } + // remove the file + for _, init := range tt.initramfs { + assert.NoError(t, os.Remove(filepath.Join(RootFsDir("image"), "boot", init))) + } + } +} diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index 17ca7af40..fdd504def 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -222,7 +222,10 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } else if rinfo.stage == "initramfs" { if node.ContainerName.Defined() { kver := container.KernelVersion(node.ContainerName.Get()) - stage_file = container.InitramfsBootPath(node.ContainerName.Get(), kver) + stage_file, err = container.InitramfsBootPath(node.ContainerName.Get(), kver) + if err != nil { + wwlog.Error("No initramfs found for container %s, err: %s", node.ContainerName.Get(), err) + } } else { wwlog.Warn("No container set for node %s", node.Id.Get()) } diff --git a/internal/pkg/warewulfd/provision_test.go b/internal/pkg/warewulfd/provision_test.go index 27ad6d339..4ef4437ac 100644 --- a/internal/pkg/warewulfd/provision_test.go +++ b/internal/pkg/warewulfd/provision_test.go @@ -31,6 +31,7 @@ var provisionSendTests = []struct { {"find grub", "/efiboot/grub.efi", "", 200, "10.10.10.10:9873"}, {"find grub", "/efiboot/grub.efi", "", 404, "10.10.10.11:9873"}, {"find initramfs", "/provision/00:00:00:ff:ff:ff?stage=initramfs", "", 200, "10.10.10.10:9873"}, + {"ipxe test with NetDevs and KernelOverrides", "/provision/00:00:00:00:00:ff?stage=ipxe", "1.1.1 ifname=net:00:00:00:00:00:ff ", 200, "10.10.10.12:9873"}, } func Test_ProvisionSend(t *testing.T) { @@ -51,7 +52,15 @@ nodes: network devices: default: hwaddr: 00:00:00:00:ff:ff - container name: none`) + container name: none + n3: + network devices: + default: + hwaddr: 00:00:00:00:00:ff + device: net + ipxe template: test + kernel: + override: 1.1.1`) assert.NoError(t, err) } assert.NoError(t, conf_file.Sync()) @@ -64,7 +73,8 @@ nodes: { _, err := arp_file.WriteString(`IP address HW type Flags HW address Mask Device 10.10.10.10 0x1 0x2 00:00:00:ff:ff:ff * dummy -10.10.10.11 0x1 0x2 00:00:00:00:ff:ff * dummy`) +10.10.10.11 0x1 0x2 00:00:00:00:ff:ff * dummy +10.10.10.12 0x1 0x2 00:00:00:00:00:ff * dummy`) assert.NoError(t, err) } assert.NoError(t, arp_file.Sync()) @@ -75,6 +85,12 @@ nodes: assert.NoError(t, imageDirErr) defer os.RemoveAll(containerDir) conf.Paths.WWChrootdir = containerDir + + sysConfDir, sysConfDirErr := os.MkdirTemp(os.TempDir(), "ww-test-sysconf-*") + assert.NoError(t, sysConfDirErr) + defer os.RemoveAll(sysConfDir) + conf.Paths.Sysconfdir = sysConfDir + assert.NoError(t, os.MkdirAll(path.Join(containerDir, "suse/rootfs/usr/lib64/efi"), 0700)) { _, err := os.Create(path.Join(containerDir, "suse/rootfs/usr/lib64/efi", "shim.efi")) @@ -90,6 +106,10 @@ nodes: _, err := os.Create(path.Join(containerDir, "suse/rootfs/boot", "initramfs-.img")) assert.NoError(t, err) } + assert.NoError(t, os.MkdirAll(path.Join(conf.Paths.Sysconfdir, "warewulf/ipxe"), 0700)) + { + assert.NoError(t, os.WriteFile(path.Join(conf.Paths.Sysconfdir, "warewulf/ipxe", "test.ipxe"), []byte("{{.KernelOverride}}{{range $devname, $netdev := .NetDevs}}{{if and $netdev.Hwaddr $netdev.Device}} ifname={{$netdev.Device}}:{{$netdev.Hwaddr}} {{end}}{{end}}"), 0600)) + } dbErr := LoadNodeDB() assert.NoError(t, dbErr)