Skip to content

Commit

Permalink
Add initramfs stage to warewulfd
Browse files Browse the repository at this point in the history
The initramfs stage supports serving an initramfs image from within the
container image, typically generated by dracut.

Signed-off-by: xu yang <[email protected]>
Signed-off-by: Jonathon Anderson <[email protected]>
  • Loading branch information
JasonYangShadow authored and anderbubble committed Jun 6, 2024
1 parent 892db0f commit a322061
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added

- Add examples for building overlays in parallel to documentation
- Add `stage=initramfs` to warewulfd provision to serve initramfs from container image. #1115

### Changed

Expand Down
27 changes: 27 additions & 0 deletions internal/pkg/container/config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
package container

import (
"fmt"
"path"

"github.com/warewulf/warewulf/internal/pkg/util"
"github.com/warewulf/warewulf/internal/pkg/wwlog"

warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
)

var (
initramfsSearchPaths = []string{
// This is a printf format where the %s will be the kernel version
"boot/initramfs-%s",
"boot/initramfs-%s.img",
"boot/initrd-%s",
"boot/initrd-%s.img",
}
)

func SourceParentDir() string {
conf := warewulfconf.Get()
return conf.Paths.WWChrootdir
Expand All @@ -27,3 +41,16 @@ func ImageParentDir() string {
func ImageFile(name string) string {
return path.Join(ImageParentDir(), name+".img")
}

// 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) (string, error) {
for _, searchPath := range initramfsSearchPaths {
initramfs_path := path.Join(RootFsDir(image), fmt.Sprintf(searchPath, kver))
wwlog.Debug("Looking for initramfs at: %s", initramfs_path)
if util.IsFile(initramfs_path) {
return initramfs_path, nil
}
}
return "", fmt.Errorf("Failed to find a target kernel version initramfs")
}
88 changes: 88 additions & 0 deletions internal/pkg/container/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
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: "error case, wrong init name",
initramfs: []string{"initrr-1.1.1.aarch64.img"},
ver: "1.1.1.aarch64",
err: fmt.Errorf("Failed to find a target kernel version initramfs"),
},
{
name: "error case, wrong ver",
initramfs: []string{"initrr-1.1.1.aarch64.img"},
ver: "1.1.2.aarch64",
err: fmt.Errorf("Failed 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)
assert.Equal(t, tt.err, 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)))
}
}
}
2 changes: 2 additions & 0 deletions internal/pkg/warewulfd/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ func parseReq(req *http.Request) (parserInfo, error) {
ret.stage = "runtime"
} else if stage == "efiboot" {
ret.stage = "efiboot"
} else if stage == "initramfs" {
ret.stage = "initramfs"
}
}

Expand Down
26 changes: 20 additions & 6 deletions internal/pkg/warewulfd/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,13 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
}

status_stages := map[string]string{
"efiboot": "EFI",
"ipxe": "IPXE",
"kernel": "KERNEL",
"kmods": "KMODS_OVERLAY",
"system": "SYSTEM_OVERLAY",
"runtime": "RUNTIME_OVERLAY"}
"efiboot": "EFI",
"ipxe": "IPXE",
"kernel": "KERNEL",
"kmods": "KMODS_OVERLAY",
"system": "SYSTEM_OVERLAY",
"runtime": "RUNTIME_OVERLAY",
"initramfs": "INITRAMFS"}

status_stage := status_stages[rinfo.stage]
var stage_file string
Expand Down Expand Up @@ -213,6 +214,19 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
} else {
wwlog.Warn("No conainer set for node %s", node.Id.Get())
}
} else if rinfo.stage == "initramfs" {
if node.ContainerName.Defined() {
_, kver, err := kernel.FindKernel(container.RootFsDir(node.ContainerName.Get()))
if err != nil {
wwlog.Error("No kernel found for initramfs for container %s: %s", node.ContainerName.Get(), err)
}
stage_file, err = container.InitramfsBootPath(node.ContainerName.Get(), kver)
if err != nil {
wwlog.Error("No initramfs found for container %s: %s", node.ContainerName.Get(), err)
}
} else {
wwlog.Warn("No container set for node %s", node.Id.Get())
}
}

wwlog.Serv("stage_file '%s'", stage_file)
Expand Down
6 changes: 6 additions & 0 deletions internal/pkg/warewulfd/provision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ var provisionSendTests = []struct {
{"find shim", "/efiboot/shim.efi", "", 404, "10.10.10.11:9873"},
{"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"},
}

func Test_ProvisionSend(t *testing.T) {
Expand Down Expand Up @@ -84,6 +85,11 @@ nodes:
_, err := os.Create(path.Join(containerDir, "suse/rootfs/usr/share/efi/x86_64/", "grub.efi"))
assert.NoError(t, err)
}
assert.NoError(t, os.MkdirAll(path.Join(containerDir, "suse/rootfs/boot"), 0700))
{
_, err := os.Create(path.Join(containerDir, "suse/rootfs/boot", "initramfs-.img"))
assert.NoError(t, err)
}

dbErr := LoadNodeDB()
assert.NoError(t, dbErr)
Expand Down

0 comments on commit a322061

Please sign in to comment.