Treat `banger`, `bangerd`, and `banger-vsock-agent` as one release by stamping the same version, commit SHA, and build timestamp into every binary through a shared ldflag-backed `internal/buildinfo` package. Add `banger version`, extend daemon ping/status to report the running daemon's build tuple, and keep the guest helper linked to the same build metadata without adding a new public version surface for it. Validate with `GOCACHE=/tmp/banger-gocache go test ./...`, `make build`, `./build/bin/banger version`, and `./build/bin/banger daemon status` after the daemon restarts onto the new binary.
131 lines
3.7 KiB
Go
131 lines
3.7 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 TestBuildImageRequiresFromImage(t *testing.T) {
|
|
d := &Daemon{
|
|
layout: paths.Layout{ImagesDir: t.TempDir(), StateDir: t.TempDir()},
|
|
store: openDaemonStore(t),
|
|
runner: system.NewRunner(),
|
|
}
|
|
|
|
_, err := d.BuildImage(context.Background(), api.ImageBuildParams{Name: "missing-base"})
|
|
if err == nil || !strings.Contains(err.Error(), "from-image is required") {
|
|
t.Fatalf("BuildImage() error = %v", err)
|
|
}
|
|
}
|
|
|
|
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, webURL: "http://127.0.0.1:7777"}
|
|
|
|
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 || got.WebURL != "http://127.0.0.1:7777" {
|
|
t.Fatalf("PingResult = %+v, want status/pid/weburl 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-exp",
|
|
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)
|
|
}
|
|
}
|
|
}
|