Hard-cut banger away from source-checkout runtime bundles as an implicit source of\nimage and host defaults. Managed images now own their full boot set,\nimage build starts from an existing registered image, and daemon startup\nno longer synthesizes a default image from host paths.\n\nResolve Firecracker from PATH or firecracker_bin, make SSH keys config-owned\nwith an auto-managed XDG default, replace the external name generator and\npackage manifests with Go code, and keep the vsock helper as a companion\nbinary instead of a user-managed runtime asset.\n\nUpdate the manual scripts, web/CLI forms, config surface, and docs around\nthe new build/manual flow and explicit image registration semantics.\n\nValidation: GOCACHE=/tmp/banger-gocache go test ./..., bash -n scripts/*.sh,\nand make build.
113 lines
3.7 KiB
Go
113 lines
3.7 KiB
Go
package daemon
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"strings"
|
|
|
|
"banger/internal/config"
|
|
"banger/internal/model"
|
|
"banger/internal/paths"
|
|
"banger/internal/store"
|
|
"banger/internal/system"
|
|
)
|
|
|
|
func Doctor(ctx context.Context) (system.Report, error) {
|
|
layout, err := paths.Resolve()
|
|
if err != nil {
|
|
return system.Report{}, err
|
|
}
|
|
cfg, err := config.Load(layout)
|
|
if err != nil {
|
|
return system.Report{}, err
|
|
}
|
|
d := &Daemon{
|
|
layout: layout,
|
|
config: cfg,
|
|
runner: system.NewRunner(),
|
|
}
|
|
db, err := store.Open(layout.DBPath)
|
|
if err == nil {
|
|
defer db.Close()
|
|
d.store = db
|
|
}
|
|
return d.doctorReport(ctx), nil
|
|
}
|
|
|
|
func (d *Daemon) doctorReport(ctx context.Context) system.Report {
|
|
report := system.Report{}
|
|
|
|
report.AddPreflight("host runtime", d.runtimeChecks(), runtimeStatus(d.config))
|
|
report.AddPreflight("core vm lifecycle", d.coreVMLifecycleChecks(), "required host tools available")
|
|
report.AddPreflight("vsock guest agent", d.vsockChecks(), "vsock guest agent prerequisites available")
|
|
d.addCapabilityDoctorChecks(ctx, &report)
|
|
report.AddPreflight("image build", d.imageBuildChecks(ctx), "image build prerequisites available")
|
|
|
|
return report
|
|
}
|
|
|
|
func (d *Daemon) runtimeChecks() *system.Preflight {
|
|
checks := system.NewPreflight()
|
|
checks.RequireExecutable(d.config.FirecrackerBin, "firecracker binary", `install firecracker or set "firecracker_bin"`)
|
|
checks.RequireFile(d.config.SSHKeyPath, "ssh private key", `set "ssh_key_path" or let banger create its default key`)
|
|
if helper, err := d.vsockAgentBinary(); err == nil {
|
|
checks.RequireExecutable(helper, "vsock agent helper", `run 'make build' or reinstall banger`)
|
|
} else {
|
|
checks.Addf("%v", err)
|
|
}
|
|
if d.store != nil && strings.TrimSpace(d.config.DefaultImageName) != "" {
|
|
image, err := d.store.GetImageByName(context.Background(), d.config.DefaultImageName)
|
|
switch {
|
|
case err == nil:
|
|
checks.RequireFile(image.RootfsPath, "default image rootfs", `re-register or rebuild the default image`)
|
|
checks.RequireFile(image.KernelPath, "default image kernel", `re-register or rebuild the default image`)
|
|
if strings.TrimSpace(image.InitrdPath) != "" {
|
|
checks.RequireFile(image.InitrdPath, "default image initrd", `re-register or rebuild the default image`)
|
|
}
|
|
case err != nil && err != sql.ErrNoRows:
|
|
checks.Addf("failed to inspect default image %q: %v", d.config.DefaultImageName, err)
|
|
default:
|
|
checks.Addf("default image %q is not registered", d.config.DefaultImageName)
|
|
}
|
|
}
|
|
return checks
|
|
}
|
|
|
|
func (d *Daemon) coreVMLifecycleChecks() *system.Preflight {
|
|
checks := system.NewPreflight()
|
|
d.addBaseStartCommandPrereqs(checks)
|
|
return checks
|
|
}
|
|
|
|
func (d *Daemon) imageBuildChecks(ctx context.Context) *system.Preflight {
|
|
checks := system.NewPreflight()
|
|
if d.store == nil || strings.TrimSpace(d.config.DefaultImageName) == "" {
|
|
checks.Addf("default image is not available for build inheritance")
|
|
return checks
|
|
}
|
|
image, err := d.store.GetImageByName(ctx, d.config.DefaultImageName)
|
|
if err != nil {
|
|
checks.Addf("default image %q is not registered", d.config.DefaultImageName)
|
|
return checks
|
|
}
|
|
d.addImageBuildPrereqs(ctx, checks, image.RootfsPath, image.KernelPath, image.InitrdPath, image.ModulesDir, "")
|
|
return checks
|
|
}
|
|
|
|
func (d *Daemon) vsockChecks() *system.Preflight {
|
|
checks := system.NewPreflight()
|
|
if helper, err := d.vsockAgentBinary(); err == nil {
|
|
checks.RequireExecutable(helper, "vsock agent helper", `run 'make build' or reinstall banger`)
|
|
} else {
|
|
checks.Addf("%v", err)
|
|
}
|
|
checks.RequireFile(vsockHostDevicePath, "vsock host device", "load the vhost_vsock kernel module on the host")
|
|
return checks
|
|
}
|
|
|
|
func runtimeStatus(cfg model.DaemonConfig) string {
|
|
if strings.TrimSpace(cfg.FirecrackerBin) == "" {
|
|
return "firecracker not configured"
|
|
}
|
|
return "firecracker and ssh key resolved"
|
|
}
|