Remove runtime-bundle image dependencies

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.
This commit is contained in:
Thales Maciel 2026-03-21 18:34:53 -03:00
parent 01c7cb5e65
commit 572bf32424
No known key found for this signature in database
GPG key ID: 33112E6833C34679
44 changed files with 1194 additions and 3456 deletions

View file

@ -20,6 +20,7 @@ import (
"banger/internal/config"
"banger/internal/daemon"
"banger/internal/hostnat"
"banger/internal/imagepreset"
"banger/internal/model"
"banger/internal/paths"
"banger/internal/rpc"
@ -101,7 +102,104 @@ func newInternalCommand() *cobra.Command {
Hidden: true,
RunE: helpNoArgs,
}
cmd.AddCommand(newInternalNATCommand(), newInternalWorkSeedCommand())
cmd.AddCommand(
newInternalNATCommand(),
newInternalWorkSeedCommand(),
newInternalSSHKeyPathCommand(),
newInternalFirecrackerPathCommand(),
newInternalVSockAgentPathCommand(),
newInternalPackagesCommand(),
)
return cmd
}
func newInternalSSHKeyPathCommand() *cobra.Command {
return &cobra.Command{
Use: "ssh-key-path",
Hidden: true,
Args: noArgsUsage("usage: banger internal ssh-key-path"),
RunE: func(cmd *cobra.Command, args []string) error {
layout, err := paths.Resolve()
if err != nil {
return err
}
cfg, err := config.Load(layout)
if err != nil {
return err
}
_, err = fmt.Fprintln(cmd.OutOrStdout(), cfg.SSHKeyPath)
return err
},
}
}
func newInternalFirecrackerPathCommand() *cobra.Command {
return &cobra.Command{
Use: "firecracker-path",
Hidden: true,
Args: noArgsUsage("usage: banger internal firecracker-path"),
RunE: func(cmd *cobra.Command, args []string) error {
layout, err := paths.Resolve()
if err != nil {
return err
}
cfg, err := config.Load(layout)
if err != nil {
return err
}
if strings.TrimSpace(cfg.FirecrackerBin) == "" {
return errors.New("firecracker binary not configured; install firecracker or set firecracker_bin")
}
_, err = fmt.Fprintln(cmd.OutOrStdout(), cfg.FirecrackerBin)
return err
},
}
}
func newInternalVSockAgentPathCommand() *cobra.Command {
return &cobra.Command{
Use: "vsock-agent-path",
Hidden: true,
Args: noArgsUsage("usage: banger internal vsock-agent-path"),
RunE: func(cmd *cobra.Command, args []string) error {
path, err := paths.CompanionBinaryPath("banger-vsock-agent")
if err != nil {
return err
}
_, err = fmt.Fprintln(cmd.OutOrStdout(), path)
return err
},
}
}
func newInternalPackagesCommand() *cobra.Command {
var docker bool
cmd := &cobra.Command{
Use: "packages <debian|void>",
Hidden: true,
Args: exactArgsUsage(1, "usage: banger internal packages <debian|void> [--docker]"),
RunE: func(cmd *cobra.Command, args []string) error {
var packages []string
switch strings.TrimSpace(args[0]) {
case "debian":
packages = imagepreset.DebianBasePackages()
if docker {
packages = append(packages, "docker.io")
}
case "void":
packages = imagepreset.VoidBasePackages()
default:
return fmt.Errorf("unknown package preset %q", args[0])
}
for _, pkg := range packages {
if _, err := fmt.Fprintln(cmd.OutOrStdout(), pkg); err != nil {
return err
}
}
return nil
},
}
cmd.Flags().BoolVar(&docker, "docker", false, "include docker-specific additions")
return cmd
}
@ -630,7 +728,7 @@ func newImageBuildCommand() *cobra.Command {
},
}
cmd.Flags().StringVar(&params.Name, "name", "", "image name")
cmd.Flags().StringVar(&params.BaseRootfs, "base-rootfs", "", "base rootfs path")
cmd.Flags().StringVar(&params.FromImage, "from-image", "", "registered base image id or name")
cmd.Flags().StringVar(&params.Size, "size", "", "output image size")
cmd.Flags().StringVar(&params.KernelPath, "kernel", "", "kernel path")
cmd.Flags().StringVar(&params.InitrdPath, "initrd", "", "initrd path")
@ -644,7 +742,7 @@ func newImageRegisterCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "register",
Short: "Register or update an unmanaged image",
Args: noArgsUsage("usage: banger image register --name <name> --rootfs <path> [--work-seed <path>] [--kernel <path>] [--initrd <path>] [--modules <dir>] [--packages <path>]"),
Args: noArgsUsage("usage: banger image register --name <name> --rootfs <path> [--work-seed <path>] --kernel <path> [--initrd <path>] [--modules <dir>]"),
RunE: func(cmd *cobra.Command, args []string) error {
if err := absolutizeImageRegisterPaths(&params); err != nil {
return err
@ -669,7 +767,6 @@ func newImageRegisterCommand() *cobra.Command {
cmd.Flags().StringVar(&params.KernelPath, "kernel", "", "kernel path")
cmd.Flags().StringVar(&params.InitrdPath, "initrd", "", "initrd path")
cmd.Flags().StringVar(&params.ModulesDir, "modules", "", "modules dir")
cmd.Flags().StringVar(&params.PackagesPath, "packages", "", "packages manifest path")
cmd.Flags().BoolVar(&params.Docker, "docker", false, "mark image as docker-prepared")
return cmd
}
@ -1158,13 +1255,13 @@ func validateSSHPrereqs(cfg model.DaemonConfig) error {
checks := system.NewPreflight()
checks.RequireCommand("ssh", "install openssh-client")
if strings.TrimSpace(cfg.SSHKeyPath) != "" {
checks.RequireFile(cfg.SSHKeyPath, "runtime ssh private key", `refresh the runtime bundle`)
checks.RequireFile(cfg.SSHKeyPath, "ssh private key", `set "ssh_key_path" or let banger create its default key`)
}
return checks.Err("ssh preflight failed")
}
func absolutizeImageBuildPaths(params *api.ImageBuildParams) error {
return absolutizePaths(&params.BaseRootfs, &params.KernelPath, &params.InitrdPath, &params.ModulesDir)
return absolutizePaths(&params.KernelPath, &params.InitrdPath, &params.ModulesDir)
}
func absolutizeImageRegisterPaths(params *api.ImageRegisterParams) error {
@ -1174,7 +1271,6 @@ func absolutizeImageRegisterPaths(params *api.ImageRegisterParams) error {
&params.KernelPath,
&params.InitrdPath,
&params.ModulesDir,
&params.PackagesPath,
)
}

View file

@ -163,7 +163,7 @@ func TestImageRegisterFlagsExist(t *testing.T) {
if err != nil {
t.Fatalf("find register: %v", err)
}
for _, flagName := range []string{"name", "rootfs", "work-seed", "kernel", "initrd", "modules", "packages", "docker"} {
for _, flagName := range []string{"name", "rootfs", "work-seed", "kernel", "initrd", "modules", "docker"} {
if register.Flags().Lookup(flagName) == nil {
t.Fatalf("missing flag %q", flagName)
}
@ -427,7 +427,6 @@ func TestAbsolutizeImageRegisterPaths(t *testing.T) {
KernelPath: filepath.Join(".", "runtime", "vmlinux"),
InitrdPath: filepath.Join(".", "runtime", "initrd.img"),
ModulesDir: filepath.Join(".", "runtime", "modules"),
PackagesPath: filepath.Join(".", "config", "packages.void"),
}
wd, err := os.Getwd()
@ -450,7 +449,6 @@ func TestAbsolutizeImageRegisterPaths(t *testing.T) {
params.KernelPath,
params.InitrdPath,
params.ModulesDir,
params.PackagesPath,
} {
if !filepath.IsAbs(value) {
t.Fatalf("path %q is not absolute", value)
@ -828,7 +826,7 @@ func TestAbsolutizeImageBuildPaths(t *testing.T) {
})
params := api.ImageBuildParams{
BaseRootfs: "images/base.ext4",
FromImage: "base-image",
KernelPath: "/kernel",
InitrdPath: "boot/initrd.img",
ModulesDir: "modules",
@ -838,7 +836,7 @@ func TestAbsolutizeImageBuildPaths(t *testing.T) {
}
want := api.ImageBuildParams{
BaseRootfs: filepath.Join(dir, "images/base.ext4"),
FromImage: "base-image",
KernelPath: "/kernel",
InitrdPath: filepath.Join(dir, "boot/initrd.img"),
ModulesDir: filepath.Join(dir, "modules"),