Add GRUB config route, tests, and server route documentation

- Add /grub/{hwaddr} route for serving GRUB configuration files,
  replacing the shim handler with a config-based approach
- Add short URL aliases: 'system' for overlay-system, 'runtime' for
  overlay-runtime, and 'grub' stage in parser
- Add 'grub' to status stage tracking in request handling
- Add comprehensive test coverage for EFI, GRUB, initramfs, iPXE,
  overlay, and provision handlers (efi_test.go, grub_test.go,
  initramfs_test.go, ipxe_test.go, overlay_test.go)
- Expand existing parser and provision tests
- Remove shim.go (functionality folded into grub.go)
- Add userdocs/server/routes.rst documenting all warewulfd HTTP routes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
Jonathon Anderson
2026-03-06 11:38:40 -07:00
parent a886b4958b
commit 779434331c
14 changed files with 902 additions and 74 deletions

View File

@@ -0,0 +1,90 @@
package warewulfd
import (
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
"github.com/warewulf/warewulf/internal/pkg/testenv"
)
var efiBootTests = []struct {
description string
url string
body string
status int
ip string
}{
{"find shim", "/efiboot/shim.efi", "", 200, "10.10.10.10:9873"},
{"find shim: node with missing image returns 404", "/efiboot/shim.efi", "", 404, "10.10.10.11:9873"},
{"find grub", "/efiboot/grub.efi", "", 200, "10.10.10.10:9873"},
{"find grub: node with missing image returns 404", "/efiboot/grub.efi", "", 404, "10.10.10.11:9873"},
{"find grub.cfg", "/efiboot/grub.cfg", "dracut 10.10.0.1:9873", 200, "10.10.10.11:9873"},
}
func Test_HandleEfiBoot(t *testing.T) {
env := testenv.New(t)
defer env.RemoveAll()
env.WriteFile("/etc/warewulf/nodes.conf", `nodeprofiles:
default:
image name: suse
nodes:
n1:
network devices:
default:
hwaddr: 00:00:00:ff:ff:ff
profiles:
- default
n2:
network devices:
default:
hwaddr: 00:00:00:00:ff:ff
image name: none
tags:
GrubMenuEntry: dracut`)
env.WriteFile("/var/tmp/arpcache", `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`)
prevArpFile := arpFile
arpFile = env.GetPath("/var/tmp/arpcache")
defer func() {
arpFile = prevArpFile
}()
env.CreateFile("/var/lib/warewulf/chroots/suse/rootfs/usr/lib64/efi/shim.efi")
env.CreateFile("/var/lib/warewulf/chroots/suse/rootfs/usr/share/efi/x86_64/grub.efi")
env.WriteFile("/etc/warewulf/grub/grub.cfg.ww", "{{ .Tags.GrubMenuEntry }} {{ .Authority }}")
dbErr := LoadNodeDB()
assert.NoError(t, dbErr)
conf := warewulfconf.Get()
secureFalse := false
conf.Warewulf.SecureP = &secureFalse
conf.Ipaddr = "10.10.0.1"
conf.Ipaddr6 = "fd00:10::1"
for _, tt := range efiBootTests {
t.Run(tt.description, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, tt.url, nil)
req.RemoteAddr = tt.ip
w := httptest.NewRecorder()
HandleEfiBoot(w, req)
res := w.Result()
defer res.Body.Close()
data, readErr := io.ReadAll(res.Body)
assert.NoError(t, readErr)
if tt.body != "" {
assert.Equal(t, tt.body, string(data))
}
assert.Equal(t, tt.status, res.StatusCode)
})
}
}

View File

@@ -2,32 +2,25 @@ package warewulfd
import (
"net/http"
"path"
"github.com/warewulf/warewulf/internal/pkg/image"
"github.com/warewulf/warewulf/internal/pkg/wwlog"
)
// HandleGrub handles direct GRUB binary requests
// HandleGrub handles GRUB configuration requests
func HandleGrub(w http.ResponseWriter, req *http.Request) {
ctx, err := initHandleRequest(w, req)
if err != nil {
return // response already written
}
var stageFile string
if !ctx.remoteNode.Valid() {
wwlog.Error("%s (unknown/unconfigured node)", ctx.rinfo.hwaddr)
} else {
if ctx.remoteNode.ImageName != "" {
stageFile = image.GrubFind(ctx.remoteNode.ImageName)
if stageFile == "" {
wwlog.Error("No grub found for image %s", ctx.remoteNode.ImageName)
}
} else {
wwlog.Warn("No conainer set for node %s", ctx.remoteNode.Id())
}
w.WriteHeader(http.StatusNotFound)
return
}
sendResponse(w, req, stageFile, nil, ctx)
stageFile := path.Join(ctx.conf.Paths.Sysconfdir, "warewulf/grub/grub.cfg.ww")
tmplData := buildTemplateVars(ctx.conf, ctx.rinfo, ctx.remoteNode)
sendResponse(w, req, stageFile, tmplData, ctx)
}

View File

@@ -0,0 +1,76 @@
package warewulfd
import (
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
"github.com/warewulf/warewulf/internal/pkg/testenv"
)
var grubHandlerTests = []struct {
description string
url string
body string
status int
ip string
}{
{"grub config for node with image", "/grub/00:00:00:ff:ff:ff", "", 200, "10.10.10.10:9873"},
{"grub config rendered with tag", "/grub/00:00:00:00:ff:ff", "dracut 10.10.0.1:9873", 200, "10.10.10.11:9873"},
}
func Test_HandleGrub(t *testing.T) {
env := testenv.New(t)
defer env.RemoveAll()
env.WriteFile("/etc/warewulf/nodes.conf", `nodeprofiles:
default:
image name: suse
nodes:
n1:
network devices:
default:
hwaddr: 00:00:00:ff:ff:ff
profiles:
- default
n2:
network devices:
default:
hwaddr: 00:00:00:00:ff:ff
image name: none
tags:
GrubMenuEntry: dracut`)
env.WriteFile("/etc/warewulf/grub/grub.cfg.ww", "{{ .Tags.GrubMenuEntry }} {{ .Authority }}")
dbErr := LoadNodeDB()
assert.NoError(t, dbErr)
conf := warewulfconf.Get()
secureFalse := false
conf.Warewulf.SecureP = &secureFalse
conf.Ipaddr = "10.10.0.1"
conf.Ipaddr6 = "fd00:10::1"
for _, tt := range grubHandlerTests {
t.Run(tt.description, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, tt.url, nil)
req.RemoteAddr = tt.ip
w := httptest.NewRecorder()
HandleGrub(w, req)
res := w.Result()
defer res.Body.Close()
data, readErr := io.ReadAll(res.Body)
assert.NoError(t, readErr)
if tt.body != "" {
assert.Equal(t, tt.body, string(data))
}
assert.Equal(t, tt.status, res.StatusCode)
})
}
}

View File

@@ -0,0 +1,60 @@
package warewulfd
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
"github.com/warewulf/warewulf/internal/pkg/testenv"
)
var initramfsHandlerTests = []struct {
description string
url string
status int
ip string
}{
{"find initramfs", "/initramfs/00:00:00:ff:ff:ff", 200, "10.10.10.10:9873"},
}
func Test_HandleInitramfs(t *testing.T) {
env := testenv.New(t)
defer env.RemoveAll()
env.WriteFile("/etc/warewulf/nodes.conf", `nodeprofiles:
default:
image name: suse
nodes:
n1:
network devices:
default:
hwaddr: 00:00:00:ff:ff:ff
profiles:
- default`)
env.CreateFile("/var/lib/warewulf/chroots/suse/rootfs/boot/vmlinuz-1.1.0")
env.CreateFile("/var/lib/warewulf/chroots/suse/rootfs/boot/initramfs-1.1.0.img")
dbErr := LoadNodeDB()
assert.NoError(t, dbErr)
conf := warewulfconf.Get()
secureFalse := false
conf.Warewulf.SecureP = &secureFalse
for _, tt := range initramfsHandlerTests {
t.Run(tt.description, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, tt.url, nil)
req.RemoteAddr = tt.ip
w := httptest.NewRecorder()
HandleInitramfs(w, req)
res := w.Result()
defer res.Body.Close()
assert.Equal(t, tt.status, res.StatusCode)
})
}
}

View File

@@ -0,0 +1,80 @@
package warewulfd
import (
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
"github.com/warewulf/warewulf/internal/pkg/testenv"
)
var ipxeHandlerTests = []struct {
description string
url string
body string
status int
ip string
}{
{
"ipxe with NetDevs, KernelVersion, and Authority",
"/ipxe/00:00:00:00:00:ff",
"1.1.1 ifname=net:00:00:00:00:00:ff 10.10.0.1 fd00:10::1 10.10.0.1:9873",
200,
"10.10.10.12:9873",
},
{
"ipxe over ipv6",
"/ipxe/00:00:00:00:00:ff",
"1.1.1 ifname=net:00:00:00:00:00:ff 10.10.0.1 fd00:10::1 [fd00:10::1]:9873",
200,
"[fd00:10::10:12]:9873",
},
}
func Test_HandleIpxe(t *testing.T) {
env := testenv.New(t)
defer env.RemoveAll()
env.WriteFile("/etc/warewulf/nodes.conf", `nodes:
n3:
network devices:
default:
hwaddr: 00:00:00:00:00:ff
device: net
ipxe template: test
kernel:
version: 1.1.1`)
env.WriteFile("/etc/warewulf/ipxe/test.ipxe", "{{.KernelVersion}}{{range $devname, $netdev := .NetDevs}}{{if and $netdev.Hwaddr $netdev.Device}} ifname={{$netdev.Device}}:{{$netdev.Hwaddr}} {{end}}{{end}} {{.Ipaddr}} {{.Ipaddr6}} {{.Authority}}")
dbErr := LoadNodeDB()
assert.NoError(t, dbErr)
conf := warewulfconf.Get()
secureFalse := false
conf.Warewulf.SecureP = &secureFalse
conf.Ipaddr = "10.10.0.1"
conf.Ipaddr6 = "fd00:10::1"
for _, tt := range ipxeHandlerTests {
t.Run(tt.description, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, tt.url, nil)
req.RemoteAddr = tt.ip
w := httptest.NewRecorder()
HandleIpxe(w, req)
res := w.Result()
defer res.Body.Close()
data, readErr := io.ReadAll(res.Body)
assert.NoError(t, readErr)
if tt.body != "" {
assert.Equal(t, tt.body, string(data))
}
assert.Equal(t, tt.status, res.StatusCode)
})
}
}

View File

@@ -4,10 +4,13 @@ import (
"io"
"net/http"
"net/http/httptest"
"os"
"path"
"testing"
"github.com/stretchr/testify/assert"
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
"github.com/warewulf/warewulf/internal/pkg/testenv"
)
@@ -108,3 +111,89 @@ nodes:
})
}
}
var systemOverlayTests = []struct {
description string
url string
body string
status int
ip string
}{
{"system overlay", "/system/00:00:00:ff:ff:ff", "system overlay", 200, "10.10.10.10:9873"},
{"fake overlay returns 404", "/system/00:00:00:ff:ff:ff?overlay=fake", "", 404, "10.10.10.10:9873"},
{"specific overlay", "/system/00:00:00:ff:ff:ff?overlay=o1", "specific overlay", 200, "10.10.10.10:9873"},
}
var runtimeOverlayTests = []struct {
description string
url string
body string
status int
ip string
}{
{"runtime overlay", "/runtime/00:00:00:ff:ff:ff", "runtime overlay", 200, "10.10.10.10:9873"},
}
func Test_HandleSystemRuntimeOverlay(t *testing.T) {
env := testenv.New(t)
defer env.RemoveAll()
env.WriteFile("/etc/warewulf/nodes.conf", `nodeprofiles:
default:
image name: suse
nodes:
n1:
network devices:
default:
hwaddr: 00:00:00:ff:ff:ff
profiles:
- default`)
dbErr := LoadNodeDB()
assert.NoError(t, dbErr)
conf := warewulfconf.Get()
secureFalse := false
conf.Warewulf.SecureP = &secureFalse
assert.NoError(t, os.MkdirAll(path.Join(conf.Paths.OverlayProvisiondir(), "n1"), 0700))
assert.NoError(t, os.WriteFile(path.Join(conf.Paths.OverlayProvisiondir(), "n1", "__SYSTEM__.img"), []byte("system overlay"), 0600))
assert.NoError(t, os.WriteFile(path.Join(conf.Paths.OverlayProvisiondir(), "n1", "__RUNTIME__.img"), []byte("runtime overlay"), 0600))
assert.NoError(t, os.WriteFile(path.Join(conf.Paths.OverlayProvisiondir(), "n1", "o1.img"), []byte("specific overlay"), 0600))
for _, tt := range systemOverlayTests {
t.Run(tt.description, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, tt.url, nil)
req.RemoteAddr = tt.ip
w := httptest.NewRecorder()
HandleSystemOverlay(w, req)
res := w.Result()
defer res.Body.Close()
data, readErr := io.ReadAll(res.Body)
assert.NoError(t, readErr)
if tt.body != "" {
assert.Equal(t, tt.body, string(data))
}
assert.Equal(t, tt.status, res.StatusCode)
})
}
for _, tt := range runtimeOverlayTests {
t.Run(tt.description, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, tt.url, nil)
req.RemoteAddr = tt.ip
w := httptest.NewRecorder()
HandleRuntimeOverlay(w, req)
res := w.Result()
defer res.Body.Close()
data, readErr := io.ReadAll(res.Body)
assert.NoError(t, readErr)
if tt.body != "" {
assert.Equal(t, tt.body, string(data))
}
assert.Equal(t, tt.status, res.StatusCode)
})
}
}

View File

@@ -47,6 +47,7 @@ func initHandleRequest(w http.ResponseWriter, req *http.Request) (*requestContex
status_stages := map[string]string{
"efiboot": "EFI",
"grub": "GRUB",
"ipxe": "IPXE",
"kernel": "KERNEL",
"system": "SYSTEM_OVERLAY",
@@ -102,14 +103,23 @@ func parseRequest(req *http.Request) (parsedRequest, error) {
// handle when stage was passed in the url path /[stage]/hwaddr
stage := path_parts[1]
hwaddr := ""
if stage != "efiboot" {
switch stage {
case "efiboot":
// /efiboot/{file}: no hwaddr in path; identified via ARP
if len(path_parts) > 3 {
ret.efifile = strings.Join(path_parts[2:], "/")
} else {
ret.efifile = path_parts[2]
}
case "grub":
// /grub/{hwaddr}: hwaddr explicit in path
hwaddr = path_parts[2]
hwaddr = strings.ReplaceAll(hwaddr, "-", ":")
hwaddr = strings.ToLower(hwaddr)
default:
hwaddr = path_parts[2]
hwaddr = strings.ReplaceAll(hwaddr, "-", ":")
hwaddr = strings.ToLower(hwaddr)
} else if len(path_parts) > 3 {
ret.efifile = strings.Join(path_parts[2:], "/")
} else {
ret.efifile = path_parts[2]
}
ret.hwaddr = hwaddr
remoteAddrPort, err := netip.ParseAddrPort(req.RemoteAddr)
@@ -137,12 +147,14 @@ func parseRequest(req *http.Request) (parsedRequest, error) {
ret.stage = "kernel"
case "image", "container":
ret.stage = "image"
case "overlay-system":
case "overlay-system", "system":
ret.stage = "system"
case "overlay-runtime":
case "overlay-runtime", "runtime":
ret.stage = "runtime"
case "efiboot":
ret.stage = "efiboot"
case "grub":
ret.stage = "grub"
case "initramfs":
ret.stage = "initramfs"
}
@@ -154,14 +166,23 @@ func parseRequest(req *http.Request) (parsedRequest, error) {
if len(req.URL.Query()["compress"]) > 0 {
ret.compress = req.URL.Query()["compress"][0]
}
if ret.efifile == "" && len(req.URL.Query()["file"]) > 0 {
ret.efifile = req.URL.Query()["file"][0]
}
if ret.stage == "" {
return ret, errors.New("no stage encoded in GET")
}
if ret.hwaddr == "" {
ret.hwaddr = ArpFind(ret.ipaddr)
wwlog.Verbose("node mac not encoded, arp cache got %s for %s", ret.hwaddr, ret.ipaddr)
if ret.hwaddr == "" {
return ret, errors.New("no hwaddr encoded in GET")
if len(req.URL.Query()["wwid"]) > 0 {
ret.hwaddr = req.URL.Query()["wwid"][0]
ret.hwaddr = strings.ReplaceAll(ret.hwaddr, "-", ":")
ret.hwaddr = strings.ToLower(ret.hwaddr)
} else {
ret.hwaddr = ArpFind(ret.ipaddr)
wwlog.Verbose("node mac not encoded, arp cache got %s for %s", ret.hwaddr, ret.ipaddr)
if ret.hwaddr == "" {
return ret, errors.New("no hwaddr encoded in GET")
}
}
}
if ret.ipaddr == "" {

View File

@@ -11,6 +11,7 @@ import (
var parseReqTests = []struct {
description string
url string
rawQuery string
remoteAddr string
result parsedRequest
}{
@@ -36,13 +37,143 @@ var parseReqTests = []struct {
stage: "ipxe",
},
},
{
description: "initramfs dedicated route",
url: "/initramfs/00:00:00:ff:ff:ff",
remoteAddr: "10.5.1.1:9873",
result: parsedRequest{
hwaddr: "00:00:00:ff:ff:ff",
ipaddr: "10.5.1.1",
remoteport: 9873,
stage: "initramfs",
},
},
{
description: "grub route with hwaddr in path",
url: "/grub/00:00:00:ff:ff:ff",
remoteAddr: "10.5.1.1:9873",
result: parsedRequest{
hwaddr: "00:00:00:ff:ff:ff",
ipaddr: "10.5.1.1",
remoteport: 9873,
stage: "grub",
},
},
{
description: "wwid query param on dedicated route",
url: "/kernel/",
rawQuery: "wwid=00%3A00%3A00%3Aff%3Aff%3Aff",
remoteAddr: "10.5.1.1:9873",
result: parsedRequest{
hwaddr: "00:00:00:ff:ff:ff",
ipaddr: "10.5.1.1",
remoteport: 9873,
stage: "kernel",
},
},
{
description: "wwid and stage query params on provision route",
url: "/provision/",
rawQuery: "wwid=00%3A00%3A00%3Aff%3Aff%3Aff&stage=kernel",
remoteAddr: "10.5.1.1:9873",
result: parsedRequest{
hwaddr: "00:00:00:ff:ff:ff",
ipaddr: "10.5.1.1",
remoteport: 9873,
stage: "kernel",
},
},
{
description: "efiboot with wwid and file query params",
url: "/efiboot/",
rawQuery: "wwid=00%3A00%3A00%3Aff%3Aff%3Aff&file=shim.efi",
remoteAddr: "10.5.1.1:9873",
result: parsedRequest{
hwaddr: "00:00:00:ff:ff:ff",
ipaddr: "10.5.1.1",
remoteport: 9873,
stage: "efiboot",
efifile: "shim.efi",
},
},
{
description: "grub with wwid query param",
url: "/grub/",
rawQuery: "wwid=00%3A00%3A00%3Aff%3Aff%3Aff",
remoteAddr: "10.5.1.1:9873",
result: parsedRequest{
hwaddr: "00:00:00:ff:ff:ff",
ipaddr: "10.5.1.1",
remoteport: 9873,
stage: "grub",
},
},
{
description: "path hwaddr takes priority over wwid query param",
url: "/kernel/00:00:00:ff:ff:ff",
rawQuery: "wwid=11%3A11%3A11%3A11%3A11%3A11",
remoteAddr: "10.5.1.1:9873",
result: parsedRequest{
hwaddr: "00:00:00:ff:ff:ff",
ipaddr: "10.5.1.1",
remoteport: 9873,
stage: "kernel",
},
},
{
description: "system overlay dedicated route",
url: "/system/00:00:00:ff:ff:ff",
remoteAddr: "10.5.1.1:9873",
result: parsedRequest{
hwaddr: "00:00:00:ff:ff:ff",
ipaddr: "10.5.1.1",
remoteport: 9873,
stage: "system",
},
},
{
description: "runtime overlay dedicated route",
url: "/runtime/00:00:00:ff:ff:ff",
remoteAddr: "10.5.1.1:9873",
result: parsedRequest{
hwaddr: "00:00:00:ff:ff:ff",
ipaddr: "10.5.1.1",
remoteport: 9873,
stage: "runtime",
},
},
{
description: "stage=grub via provision route serves config (new semantics)",
url: "/provision/00:00:00:ff:ff:ff",
rawQuery: "stage=grub",
remoteAddr: "10.5.1.1:9873",
result: parsedRequest{
hwaddr: "00:00:00:ff:ff:ff",
ipaddr: "10.5.1.1",
remoteport: 9873,
stage: "grub",
},
},
{
description: "path efifile takes priority over file query param",
url: "/efiboot/shim.efi",
rawQuery: "wwid=00%3A00%3A00%3Aff%3Aff%3Aff&file=grub.cfg",
remoteAddr: "10.5.1.1:9873",
result: parsedRequest{
hwaddr: "00:00:00:ff:ff:ff",
ipaddr: "10.5.1.1",
remoteport: 9873,
stage: "efiboot",
efifile: "shim.efi",
},
},
}
func Test_ParseRequest(t *testing.T) {
for _, tt := range parseReqTests {
t.Run(tt.description, func(t *testing.T) {
req := &http.Request{
URL: &url.URL{Path: tt.url},
URL: &url.URL{Path: tt.url, RawQuery: tt.rawQuery},
RemoteAddr: tt.remoteAddr,
}
result, err := parseRequest(req)

View File

@@ -51,10 +51,6 @@ func HandleProvision(w http.ResponseWriter, req *http.Request) {
handler = HandleSystemOverlay
case "runtime":
handler = HandleRuntimeOverlay
case "efiboot":
handler = HandleEfiBoot
case "shim":
handler = HandleShim
case "grub":
handler = HandleGrub
case "initramfs":

View File

@@ -21,18 +21,15 @@ var provisionSendTests = []struct {
status int
ip string
}{
{"system overlay", "/overlay-system/00:00:00:ff:ff:ff", "system overlay", 200, "10.10.10.10:9873"},
{"runtime overlay", "/overlay-runtime/00:00:00:ff:ff:ff", "runtime overlay", 200, "10.10.10.10:9873"},
{"fake overlay", "/overlay-system/00:00:00:ff:ff:ff?overlay=fake", "", 404, "10.10.10.10:9873"},
{"specific overlay", "/overlay-system/00:00:00:ff:ff:ff?overlay=o1", "specific overlay", 200, "10.10.10.10:9873"},
{"find shim", "/efiboot/shim.efi", "", 200, "10.10.10.10:9873"},
{"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"},
{"system overlay", "/provision/00:00:00:ff:ff:ff?stage=system", "system overlay", 200, "10.10.10.10:9873"},
{"runtime overlay", "/provision/00:00:00:ff:ff:ff?stage=runtime", "runtime overlay", 200, "10.10.10.10:9873"},
{"fake overlay", "/provision/00:00:00:ff:ff:ff?stage=system&overlay=fake", "", 404, "10.10.10.10:9873"},
{"specific overlay", "/provision/00:00:00:ff:ff:ff?stage=system&overlay=o1", "specific overlay", 200, "10.10.10.10:9873"},
{"grub config", "/provision/00:00:00:ff:ff:ff?stage=grub", "", 200, "10.10.10.10:9873"},
{"grub config rendered", "/provision/00:00:00:00:ff:ff?stage=grub", "dracut 10.10.0.1:9873", 200, "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, KernelVersion, and Authority", "/provision/00:00:00:00:00:ff?stage=ipxe", "1.1.1 ifname=net:00:00:00:00:00:ff 10.10.0.1 fd00:10::1 10.10.0.1:9873", 200, "10.10.10.12:9873"},
{"ipxe ipv6", "/provision/00:00:00:00:00:ff?stage=ipxe", "1.1.1 ifname=net:00:00:00:00:00:ff 10.10.0.1 fd00:10::1 [fd00:10::1]:9873", 200, "[fd00:10::10:12]:9873"},
{"find grub.cfg", "/efiboot/grub.cfg", "dracut 10.10.0.1:9873", 200, "10.10.10.11:9873"},
}
func Test_ProvisionSend(t *testing.T) {

View File

@@ -42,13 +42,19 @@ func configureRootHandler(apiHandler http.Handler) *slashFix {
wwHandler.HandleFunc("/provision/", warewulfd.HandleProvision)
wwHandler.HandleFunc("/ipxe/", warewulfd.HandleIpxe)
wwHandler.HandleFunc("/efiboot/", warewulfd.HandleEfiBoot)
wwHandler.HandleFunc("/grub/", warewulfd.HandleGrub)
wwHandler.HandleFunc("/kernel/", warewulfd.HandleKernel)
wwHandler.HandleFunc("/image/", warewulfd.HandleImage)
wwHandler.HandleFunc("/initramfs/", warewulfd.HandleInitramfs)
wwHandler.HandleFunc("/system/", warewulfd.HandleSystemOverlay)
wwHandler.HandleFunc("/runtime/", warewulfd.HandleRuntimeOverlay)
wwHandler.HandleFunc("/status", warewulfd.HandleStatus)
/* Deprecated */
wwHandler.HandleFunc("/container/", warewulfd.HandleImage)
wwHandler.HandleFunc("/overlay-system/", warewulfd.HandleSystemOverlay)
wwHandler.HandleFunc("/overlay-runtime/", warewulfd.HandleRuntimeOverlay)
wwHandler.HandleFunc("/overlay-file/", warewulfd.HandleOverlayFile)
wwHandler.HandleFunc("/status", warewulfd.HandleStatus)
if apiHandler != nil {
wwHandler.Handle("/api/", apiHandler)

View File

@@ -1,34 +0,0 @@
package warewulfd
import (
"net/http"
"github.com/warewulf/warewulf/internal/pkg/image"
"github.com/warewulf/warewulf/internal/pkg/wwlog"
)
// HandleShim handles direct shim binary requests
func HandleShim(w http.ResponseWriter, req *http.Request) {
ctx, err := initHandleRequest(w, req)
if err != nil {
return // response already written
}
var stageFile string
if !ctx.remoteNode.Valid() {
wwlog.Error("%s (unknown/unconfigured node)", ctx.rinfo.hwaddr)
} else {
if ctx.remoteNode.ImageName != "" {
stageFile = image.ShimFind(ctx.remoteNode.ImageName)
if stageFile == "" {
wwlog.Error("No kernel found for image %s", ctx.remoteNode.ImageName)
}
} else {
wwlog.Warn("No image set for this %s", ctx.remoteNode.Id())
}
}
sendResponse(w, req, stageFile, nil, ctx)
}