The web UI shipped as "experimental" and was never finished — no nav
off the dashboard, no live updates, no settled design, never a
supported surface. It was opt-in by default already; leaving the code
in the tree for v0.1.0 only invited "does this work?" questions and
kept HostSummary/BangerSummary/SudoStatus types on the public RPC
surface that nothing else uses.
Removed:
internal/webui/ (all Go + templates + assets)
internal/daemon/web.go (server start / Layout / Config / ListVMs / ListImages)
internal/daemon/dashboard.go (DashboardSummary aggregator)
Simplified:
internal/api/types.go drop WebURL on PingResult, drop
HostSummary / SudoStatus / BangerSummary /
DashboardSummary / DashboardSummaryResult
internal/model/types.go drop DaemonConfig.WebListenAddr
internal/config/config.go drop web_listen_addr from fileConfig + Load
internal/daemon/daemon.go drop webListener / webServer / webURL fields +
startWebServer() call + ping WebURL population
internal/cli/banger.go `daemon status` output no longer branches on web
internal/daemon/{doc.go,ARCHITECTURE.md} drop web UI sections
README.md drop web_listen_addr config bullet + security paragraph
Tests updated to reflect the new shape. Coverage 57.3 -> 58.9% (the
webui package was largely untested; its removal lifts the ratio
without moving the numerator). `banger daemon status` output and
--help are web-free. Lint + full suite green.
118 lines
3.2 KiB
Go
118 lines
3.2 KiB
Go
package daemon
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"banger/internal/api"
|
|
"banger/internal/buildinfo"
|
|
"banger/internal/model"
|
|
"banger/internal/paths"
|
|
"banger/internal/rpc"
|
|
"banger/internal/system"
|
|
)
|
|
|
|
func TestRegisterImageRequiresKernel(t *testing.T) {
|
|
rootfs := filepath.Join(t.TempDir(), "rootfs.ext4")
|
|
if err := os.WriteFile(rootfs, []byte("rootfs"), 0o644); err != nil {
|
|
t.Fatalf("write rootfs: %v", err)
|
|
}
|
|
d := &Daemon{store: openDaemonStore(t)}
|
|
|
|
_, err := d.RegisterImage(context.Background(), api.ImageRegisterParams{
|
|
Name: "missing-kernel",
|
|
RootfsPath: rootfs,
|
|
})
|
|
if err == nil || !strings.Contains(err.Error(), "kernel path is required") {
|
|
t.Fatalf("RegisterImage() error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDispatchPingIncludesBuildInfo(t *testing.T) {
|
|
d := &Daemon{pid: 42}
|
|
|
|
resp := d.dispatch(context.Background(), rpc.Request{Version: rpc.Version, Method: "ping"})
|
|
if !resp.OK {
|
|
t.Fatalf("dispatch(ping) = %+v, want ok", resp)
|
|
}
|
|
|
|
var got api.PingResult
|
|
if err := json.Unmarshal(resp.Result, &got); err != nil {
|
|
t.Fatalf("Unmarshal(PingResult): %v", err)
|
|
}
|
|
|
|
info := buildinfo.Current()
|
|
if got.Status != "ok" || got.PID != 42 {
|
|
t.Fatalf("PingResult = %+v, want status/pid populated", got)
|
|
}
|
|
if got.Version != info.Version || got.Commit != info.Commit || got.BuiltAt != info.BuiltAt {
|
|
t.Fatalf("PingResult build info = %+v, want %+v", got, info)
|
|
}
|
|
}
|
|
|
|
func TestPromoteImageCopiesBootArtifactsIntoArtifactDir(t *testing.T) {
|
|
dir := t.TempDir()
|
|
rootfs := filepath.Join(dir, "rootfs.ext4")
|
|
kernel := filepath.Join(dir, "vmlinux")
|
|
initrd := filepath.Join(dir, "initrd.img")
|
|
modulesDir := filepath.Join(dir, "modules")
|
|
if err := os.MkdirAll(modulesDir, 0o755); err != nil {
|
|
t.Fatalf("mkdir modules: %v", err)
|
|
}
|
|
for path, data := range map[string]string{
|
|
rootfs: "rootfs",
|
|
kernel: "kernel",
|
|
initrd: "initrd",
|
|
filepath.Join(modulesDir, "depmod"): "modules",
|
|
} {
|
|
if err := os.WriteFile(path, []byte(data), 0o644); err != nil {
|
|
t.Fatalf("write %s: %v", path, err)
|
|
}
|
|
}
|
|
|
|
db := openDaemonStore(t)
|
|
image := model.Image{
|
|
ID: "img-promote",
|
|
Name: "void",
|
|
Managed: false,
|
|
RootfsPath: rootfs,
|
|
KernelPath: kernel,
|
|
InitrdPath: initrd,
|
|
ModulesDir: modulesDir,
|
|
CreatedAt: model.Now(),
|
|
UpdatedAt: model.Now(),
|
|
}
|
|
if err := db.UpsertImage(context.Background(), image); err != nil {
|
|
t.Fatalf("UpsertImage: %v", err)
|
|
}
|
|
|
|
imagesDir := filepath.Join(dir, "images")
|
|
if err := os.MkdirAll(imagesDir, 0o755); err != nil {
|
|
t.Fatalf("mkdir images dir: %v", err)
|
|
}
|
|
|
|
d := &Daemon{
|
|
layout: paths.Layout{ImagesDir: imagesDir},
|
|
store: db,
|
|
runner: system.NewRunner(),
|
|
}
|
|
got, err := d.PromoteImage(context.Background(), image.Name)
|
|
if err != nil {
|
|
t.Fatalf("PromoteImage: %v", err)
|
|
}
|
|
if !got.Managed {
|
|
t.Fatal("promoted image should be managed")
|
|
}
|
|
for _, path := range []string{got.RootfsPath, got.KernelPath, got.InitrdPath, got.ModulesDir} {
|
|
if !strings.HasPrefix(path, got.ArtifactDir) {
|
|
t.Fatalf("artifact path %q does not live under %q", path, got.ArtifactDir)
|
|
}
|
|
if _, err := os.Stat(path); err != nil {
|
|
t.Fatalf("stat %s: %v", path, err)
|
|
}
|
|
}
|
|
}
|